blob: a8cf2f89c8eccd5ed324e0b2e85734525817fb60 [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 {
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 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));
if (zx_status_t status = (*out)->FillSuper(); status != ZX_OK) {
FX_LOGS(ERROR) << "failed to initialize fs." << status;
return status;
}
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 LoadSuperblock(f2fs::Bcache* bc, SuperBlock* out_info) {
// TODO: define ino for superblock after cache impl.
Page* page = GrabCachePage(nullptr, 0, 0);
if (zx_status_t status = bc->Readblk(kSuperblockStart, PageAddress(page)); status != ZX_OK) {
if (status = bc->Readblk(kSuperblockStart + 1, PageAddress(page)); status != ZX_OK) {
FX_LOGS(ERROR) << "failed to read superblock." << status;
F2fsPutPage(page, 1);
return status;
}
}
memcpy(out_info, static_cast<uint8_t*>(PageAddress(page)) + kSuperOffset, sizeof(SuperBlock));
F2fsPutPage(page, 1);
return ZX_OK;
}
zx::status<std::unique_ptr<F2fs>> 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");
std::unique_ptr<F2fs> fs;
if (zx_status_t status = F2fs::Create(std::move(bcache), mount_options, &fs); status != ZX_OK) {
FX_LOGS(ERROR) << "failed to create filesystem object " << status;
return zx::error(status);
}
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 zx::error(status);
}
fs->SetUnmountCallback(std::move(on_unmount));
fs->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;
}
if (zx_status_t status = fs->ServeDirectory(std::move(export_root), std::move(mount_channel));
status != ZX_OK) {
FX_LOGS(ERROR) << "failed to establish mount_channel" << status;
return zx::error(status);
}
return zx::ok(std::move(fs));
}
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