blob: b120c8715128c2fafd6817bee76ae6e704898956 [file] [log] [blame]
// Copyright 2021 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 "f2fs.h"
#include <fcntl.h>
#include <inttypes.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/async/dispatcher.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/trace-provider/provider.h>
#include <lib/zx/event.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <zircon/assert.h>
#include <memory>
#include "src/lib/storage/vfs/cpp/pseudo_dir.h"
#include "src/lib/storage/vfs/cpp/trace.h"
#include "src/lib/uuid/uuid.h"
#include "zircon/errors.h"
namespace f2fs {
// This is the component's main class. It holds all of the component's state.
zx_status_t CreateBcache(std::unique_ptr<block_client::BlockDevice> device, bool* out_readonly,
std::unique_ptr<f2fs::Bcache>* out) {
fuchsia_hardware_block_BlockInfo info;
zx_status_t status = device->BlockGetInfo(&info);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Coult not access device info: " << status;
return status;
}
uint64_t device_size = info.block_size * info.block_count;
if (device_size == 0) {
FX_LOGS(ERROR) << "Invalid device size";
return status;
}
uint64_t block_count = device_size / kBlockSize;
if (block_count >= std::numeric_limits<uint32_t>::max()) {
FX_LOGS(ERROR) << "Block count overflow";
return ZX_ERR_OUT_OF_RANGE;
}
return f2fs::Bcache::Create(std::move(device), static_cast<uint32_t>(block_count), out,
kBlockSize);
}
zx_status_t Mkfs(const MkfsOptions& options, std::unique_ptr<f2fs::Bcache> bc) {
F2fsMkfs mkfs(std::move(bc), options);
return mkfs.Mkfs();
}
zx_status_t Fsck(const MountOptions& options, std::unique_ptr<f2fs::Bcache> bc) { return ZX_OK; }
F2fs::F2fs(std::unique_ptr<f2fs::Bcache> bc, SuperBlock* sb, const MountOptions& mount_options)
: bc_(std::move(bc)), mount_options_(mount_options) {
raw_sb_ = std::unique_ptr<SuperBlock>(sb);
zx::event event;
if (zx_status_t status = zx::event::create(0, &event); status == ZX_OK) {
zx_info_handle_basic_t info;
if (status = event.get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr);
status == ZX_OK) {
fs_id_ = info.koid;
}
}
}
F2fs::~F2fs() { vnode_table_.clear(); }
zx_status_t LoadSuperblock(f2fs::Bcache* bc, SuperBlock* out_info) {
char buf[8192];
if (zx_status_t status = bc->Readblk(kSuperblockStart, buf); status != ZX_OK) {
FX_LOGS(ERROR) << "could not read info block.";
return status;
}
memcpy(out_info, buf + 1024, sizeof(SuperBlock));
return ZX_OK;
}
zx_status_t F2fs::Create(std::unique_ptr<f2fs::Bcache> bc, const MountOptions& options,
std::unique_ptr<F2fs>* out) {
SuperBlock* info;
info = new SuperBlock();
if (zx_status_t status = LoadSuperblock(bc.get(), info); status != ZX_OK) {
return status;
}
*out = std::unique_ptr<F2fs>(new F2fs(std::move(bc), info, options));
return ZX_OK;
}
zx_status_t F2fs::FindVnode(fbl::RefPtr<VnodeF2fs>* out, ino_t ino) {
fbl::RefPtr<VnodeF2fs> vn;
fbl::AutoLock lock(&vnode_table_lock_);
auto rawVn = vnode_table_.find(ino);
if (rawVn.IsValid()) {
vn = fbl::MakeRefPtrUpgradeFromRaw(rawVn.CopyPointer(), vnode_table_lock_);
if (vn == nullptr) {
vnode_table_.erase(ino);
}
*out = std::move(vn);
return ZX_OK;
}
return ZX_ERR_NOT_FOUND;
}
void F2fs::InsertVnode(VnodeF2fs* vn) {
fbl::AutoLock lock(&vnode_table_lock_);
fbl::AutoLock lock_vn(&vn->v_lock_);
vnode_table_.insert(vn);
}
void F2fs::EraseVnodeFromTable(VnodeF2fs* vn) {
fbl::AutoLock lock(&vnode_table_lock_);
fbl::AutoLock lock_vn(&vn->v_lock_);
vnode_table_.erase(*vn);
}
zx_status_t CreateFsAndRoot(const MountOptions& mount_options, async_dispatcher_t* dispatcher,
std::unique_ptr<f2fs::Bcache> bcache, zx::channel mount_channel,
fbl::Closure on_unmount, ServeLayout serve_layout) {
TRACE_DURATION("f2fs", "CreateFsAndRoot");
f2fs::MountOptions options = mount_options;
std::unique_ptr<F2fs> fs;
if (zx_status_t status = F2fs::Create(std::move(bcache), options, &fs); status != ZX_OK) {
FX_LOGS(ERROR) << "failed to create filesystem object " << status;
return status;
}
if (zx_status_t ret = fs->FillSuper(); ret != ZX_OK) {
std::cout << "FillSuper error " << ret << std::endl;
return ret;
}
fbl::RefPtr<VnodeF2fs> data_root;
if (zx_status_t status = VnodeF2fs::Vget(fs.get(), fs->RawSb().root_ino, &data_root);
status != ZX_OK) {
FX_LOGS(ERROR) << "cannot find root inode: " << status;
return status;
}
__UNUSED auto r = fs.release();
F2fs* vfs = data_root->Vfs();
vfs->SetUnmountCallback(std::move(on_unmount));
vfs->SetDispatcher(dispatcher);
fbl::RefPtr<fs::Vnode> export_root;
switch (serve_layout) {
case ServeLayout::kDataRootOnly:
export_root = std::move(data_root);
break;
case ServeLayout::kExportDirectory:
auto outgoing = fbl::MakeRefCounted<fs::PseudoDir>();
outgoing->AddEntry("root", std::move(data_root));
export_root = std::move(outgoing);
break;
}
return vfs->ServeDirectory(std::move(export_root), std::move(mount_channel));
}
zx_status_t Mount(const MountOptions& options, std::unique_ptr<f2fs::Bcache> bc) {
zx::channel outgoing_server = zx::channel(zx_take_startup_handle(PA_DIRECTORY_REQUEST));
zx::channel root_server = zx::channel(zx_take_startup_handle(FS_HANDLE_ROOT_ID));
if (outgoing_server.is_valid() && root_server.is_valid()) {
FX_LOGS(ERROR) << "both PA_DIRECTORY_REQUEST and FS_HANDLE_ROOT_ID provided - need one or the "
"other.";
return ZX_ERR_BAD_STATE;
}
zx::channel export_root;
f2fs::ServeLayout serve_layout;
if (outgoing_server.is_valid()) {
export_root = std::move(outgoing_server);
serve_layout = f2fs::ServeLayout::kExportDirectory;
} else if (root_server.is_valid()) {
export_root = std::move(root_server);
serve_layout = f2fs::ServeLayout::kDataRootOnly;
} else {
FX_LOGS(ERROR) << "could not get startup handle to serve on";
return ZX_ERR_BAD_STATE;
}
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
trace::TraceProviderWithFdio trace_provider(loop.dispatcher());
auto on_unmount = [&loop]() {
loop.Quit();
FX_LOGS(WARNING) << "Unmounted";
};
zx_status_t status = CreateFsAndRoot(options, loop.dispatcher(), std::move(bc),
std::move(export_root), std::move(on_unmount), serve_layout);
if (status != ZX_OK) {
return -1;
}
ZX_ASSERT(loop.Run() == ZX_ERR_CANCELED);
return ZX_OK;
}
void Sync(SyncCallback closure) {
if (closure)
closure(ZX_OK);
}
void F2fs::Shutdown(fs::Vfs::ShutdownCallback cb) {
ManagedVfs::Shutdown([this, cb = std::move(cb)](zx_status_t status) mutable {
Sync([this, cb = std::move(cb)](zx_status_t) mutable {
async::PostTask(dispatcher(), [this, cb = std::move(cb)]() mutable {
auto on_unmount = std::move(on_unmount_);
PutSuper();
// Explicitly delete this (rather than just letting the memory release when
// the process exits) to ensure that the block device's fifo has been
// closed.
delete this;
// Identify to the unmounting channel that teardown is complete.
cb(ZX_OK);
// Identify to the unmounting thread that teardown is complete.
if (on_unmount) {
on_unmount();
}
});
});
});
}
zx_status_t FlushDirtyNodePage(F2fs* fs, Page* page) {
SbInfo& sbi = fs->GetSbInfo();
if (!page)
return ZX_OK;
ZX_ASSERT(page->host == nullptr);
ZX_ASSERT(page->host_nid == NodeIno(&sbi));
if (zx_status_t ret = fs->Nodemgr().F2fsWriteNodePage(page, nullptr); ret != ZX_OK) {
std::cout << "Node page write error " << ret << std::endl;
return ret;
}
return ZX_OK;
}
zx_status_t FlushDirtyMetaPage(F2fs* fs, Page* page) {
SbInfo& sbi = fs->GetSbInfo();
if (!page)
return ZX_OK;
ZX_ASSERT(page->host == nullptr);
ZX_ASSERT(page->host_nid == MetaIno(&sbi));
if (zx_status_t ret = fs->F2fsWriteMetaPage(page, nullptr); ret != ZX_OK) {
std::cout << "Meta page write error " << ret << std::endl;
return ret;
}
return ZX_OK;
}
zx_status_t FlushDirtyDataPage(F2fs* fs, Page* page) {
if (!page)
return ZX_OK;
ZX_ASSERT(page->host != nullptr);
VnodeF2fs* vnode = static_cast<VnodeF2fs*>(page->host);
if (zx_status_t ret = vnode->WriteDataPageReq(page, nullptr); ret != ZX_OK) {
std::cout << "Data page write error " << ret << std::endl;
return ret;
}
return ZX_OK;
}
} // namespace f2fs