blob: 8b56d6fa5dc685a0a030fdb5c2ff1b4e2e1cab03 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "garnet/lib/farfs/file_system.h"
#include <fcntl.h>
#include <lib/async/default.h>
#include "lib/fsl/tasks/message_loop.h"
namespace archive {
namespace {
struct DirRecord {
DirRecord() = default;
DirRecord(DirRecord&& other)
: names(std::move(other.names)), children(std::move(other.children)) {}
DirRecord(const DirRecord& other) = delete;
void swap(DirRecord& other) {
names.swap(other.names);
children.swap(other.children);
}
fbl::RefPtr<vmofs::VnodeDir> CreateDirectory() {
size_t count = names.size();
fbl::Array<fbl::StringPiece> names_array(new fbl::StringPiece[count],
count);
fbl::Array<fbl::RefPtr<vmofs::Vnode>> children_array(
new fbl::RefPtr<vmofs::Vnode>[count], count);
for (size_t i = 0; i < count; ++i) {
names_array[i] = names[i];
children_array[i] = std::move(children[i]);
}
return fbl::AdoptRef(
new vmofs::VnodeDir(std::move(names_array), std::move(children_array)));
}
std::vector<fbl::StringPiece> names;
std::vector<fbl::RefPtr<vmofs::Vnode>> children;
};
fxl::StringView PopLastDirectory(fxl::StringView* path) {
FXL_DCHECK(path->size() >= 2); // Shortest path: "x/".
size_t end = path->size() - 1;
FXL_DCHECK(path->at(end) == '/'); // Must end with '/'.
size_t begin = path->rfind('/', end - 1);
if (begin == fxl::StringView::npos) {
fxl::StringView result = path->substr(0, end);
*path = fxl::StringView();
return result;
}
fxl::StringView result = path->substr(begin + 1, end - begin - 1);
FXL_DCHECK(!result.empty()); // "//" is disallowed.
*path = path->substr(0, begin + 1);
return result;
}
bool PopFirstDirectory(fxl::StringView* path) {
size_t end = path->find('/');
if (end == fxl::StringView::npos) return false;
*path = path->substr(end + 1);
return true;
}
fbl::StringPiece ToStringPiece(fxl::StringView view) {
return fbl::StringPiece(view.data(), view.size());
}
fbl::RefPtr<vmofs::VnodeFile> CreateFile(zx_handle_t vmo,
const DirectoryTableEntry& entry) {
return fbl::AdoptRef(
new vmofs::VnodeFile(vmo, entry.data_offset, entry.data_length));
}
void LeaveDirectory(fxl::StringView name, std::vector<DirRecord>* stack) {
auto child = stack->back().CreateDirectory();
stack->pop_back();
DirRecord& parent = stack->back();
parent.names.push_back(ToStringPiece(name));
parent.children.push_back(std::move(child));
}
bool GetDirectoryEntryByPath(ArchiveReader* const reader, fxl::StringView path,
DirectoryTableEntry* const entry) {
if (!reader) {
return false;
}
if (!reader->GetDirectoryEntryByPath(path, entry)) {
return false;
}
return true;
}
} // namespace
FileSystem::FileSystem(zx::vmo vmo)
: vmo_(vmo.get()), vfs_(async_get_default()) {
uint64_t num_bytes = 0;
zx_status_t status = vmo.get_size(&num_bytes);
if (status != ZX_OK) return;
fxl::UniqueFD fd(fdio_vmo_fd(vmo.release(), 0, num_bytes));
if (!fd.is_valid()) return;
reader_ = std::make_unique<ArchiveReader>(std::move(fd));
CreateDirectory();
}
FileSystem::~FileSystem() = default;
bool FileSystem::Serve(zx::channel channel) {
return directory_ &&
vfs_.ServeDirectory(directory_, std::move(channel)) == ZX_OK;
}
zx::channel FileSystem::OpenAsDirectory() {
zx::channel h1, h2;
if (zx::channel::create(0, &h1, &h2) < 0) return zx::channel();
if (!Serve(std::move(h1))) return zx::channel();
return h2;
}
bool FileSystem::IsFile(fxl::StringView path) {
DirectoryTableEntry entry;
return GetDirectoryEntryByPath(reader_.get(), path, &entry);
;
}
fsl::SizedVmo FileSystem::GetFileAsVMO(fxl::StringView path) {
DirectoryTableEntry entry;
if (!GetDirectoryEntryByPath(reader_.get(), path, &entry)) {
return nullptr;
}
zx_handle_t result = ZX_HANDLE_INVALID;
zx_status_t status =
zx_vmo_clone(vmo_, ZX_VMO_CLONE_COPY_ON_WRITE, entry.data_offset,
entry.data_length, &result);
if (status != ZX_OK) {
return nullptr;
}
return fsl::SizedVmo(zx::vmo(result), entry.data_length);
}
bool FileSystem::GetFileAsString(fxl::StringView path, std::string* result) {
DirectoryTableEntry entry;
if (!GetDirectoryEntryByPath(reader_.get(), path, &entry)) {
return false;
}
std::string data;
data.resize(entry.data_length);
zx_status_t status =
zx_vmo_read(vmo_, &data[0], entry.data_offset, entry.data_length);
if (status != ZX_OK) return false;
result->swap(data);
return true;
}
void FileSystem::CreateDirectory() {
if (!reader_ || !reader_->Read()) return;
std::vector<DirRecord> stack;
stack.push_back(DirRecord());
fxl::StringView current_dir;
reader_->ListDirectory([&](const DirectoryTableEntry& entry) {
fxl::StringView path = reader_->GetPathView(entry);
while (path.substr(0, current_dir.size()) != current_dir)
LeaveDirectory(PopLastDirectory(&current_dir), &stack);
fxl::StringView remaining = path.substr(current_dir.size());
while (PopFirstDirectory(&remaining))
stack.push_back(DirRecord()); // Enter directory.
current_dir = path.substr(0, path.size() - remaining.size());
DirRecord& parent = stack.back();
parent.names.push_back(ToStringPiece(remaining));
parent.children.push_back(CreateFile(vmo_, entry));
});
while (!current_dir.empty())
LeaveDirectory(PopLastDirectory(&current_dir), &stack);
FXL_DCHECK(stack.size() == 1);
directory_ = stack.back().CreateDirectory();
}
} // namespace archive