blob: 2d3e9ef7ebbff2bb1a73cd18039236921a7abe85 [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/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 {
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() {}
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 LoadSuperblock(f2fs::Bcache* bc, SuperBlock* out_info, block_t bno) {
// TODO: define ino for superblock after cache impl.
// the 1st and the 2nd blocks each have a identical Superblock.
ZX_ASSERT(bno <= 1);
Page* page = GrabCachePage(nullptr, 0, 0);
if (zx_status_t status = bc->Readblk(kSuperblockStart + bno, PageAddress(page));
status != ZX_OK) {
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_t LoadSuperblock(f2fs::Bcache* bc, SuperBlock* out_info) {
// TODO: define ino for superblock after cache impl.
if (zx_status_t status = LoadSuperblock(bc, out_info, kSuperblockStart); status != ZX_OK) {
if (zx_status_t status = LoadSuperblock(bc, out_info, kSuperblockStart + 1); status != ZX_OK) {
FX_LOGS(ERROR) << "failed to read superblock." << status;
return status;
}
}
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;
}
FX_LOGS(INFO) << "CreateFsAndRoot";
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();
bc_.reset();
// 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();
}
});
});
});
}
void F2fs::DecValidBlockCount(VnodeF2fs* vnode, block_t count) {
fbl::AutoLock lock(&sbi_->stat_lock);
ZX_ASSERT(sbi_->total_valid_block_count >= count);
vnode->DecBlocks(count);
sbi_->total_valid_block_count -= count;
}
zx_status_t F2fs::IncValidBlockCount(VnodeF2fs* vnode, block_t count) {
block_t valid_block_count;
fbl::AutoLock lock(&sbi_->stat_lock);
valid_block_count = sbi_->total_valid_block_count + count;
if (valid_block_count > sbi_->user_block_count) {
return ZX_ERR_NO_SPACE;
}
vnode->IncBlocks(count);
sbi_->total_valid_block_count = valid_block_count;
sbi_->alloc_valid_block_count += count;
return ZX_OK;
}
// Check whether the given nid is within node id range.
void F2fs::CheckNidRange(const nid_t& nid) { ZX_ASSERT(nid < sbi_->nm_info->max_nid); }
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) {
FX_LOGS(ERROR) << "Node page write error " << ret;
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) {
FX_LOGS(ERROR) << "Meta page write error " << ret;
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) {
FX_LOGS(ERROR) << "Data page write error " << ret;
return ret;
}
return ZX_OK;
}
} // namespace f2fs