blob: c3ef8c04c08f4e73130449271c973129bf3f5045 [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 <string.h>
#include <safemath/checked_math.h>
#include "src/storage/f2fs/f2fs.h"
namespace f2fs {
void F2fs::PutSuper() {
#if 0 // porting needed
// DestroyStats(superblock_info_.get());
// StopGcThread(superblock_info_.get());
#endif
WriteCheckpoint(false, true);
if (superblock_info_->TestCpFlags(CpFlag::kCpErrorFlag)) {
// In the checkpoint error case, flush the dirty vnode list.
GetVCache().ForDirtyVnodesIf([&](fbl::RefPtr<VnodeF2fs> &vnode) {
GetVCache().RemoveDirty(vnode.get());
return ZX_OK;
});
}
writer_.reset();
ResetPsuedoVnodes();
GetVCache().Reset();
#ifdef __Fuchsia__
GetDirEntryCache().Reset();
#endif // __Fuchsia__
node_manager_->DestroyNodeManager();
segment_manager_->DestroySegmentManager();
node_manager_.reset();
segment_manager_.reset();
raw_sb_.reset();
superblock_info_.reset();
}
void F2fs::SyncFs(bool bShutdown) {
// TODO:: Consider !superblock_info_.IsDirty()
if (bShutdown) {
FX_LOGS(INFO) << "[f2fs] Unmount triggered";
WritebackOperation op;
// Checkpointing will flush all Pages that Writer is holding.
op.if_vnode = [](fbl::RefPtr<VnodeF2fs> &vnode) {
if (!vnode->IsDir()) {
return ZX_OK;
}
return ZX_ERR_NEXT;
};
SyncDirtyDataPages(op);
} else {
WriteCheckpoint(false, false);
}
}
#if 0 // porting needed
// int F2fs::F2fsStatfs(dentry *dentry /*, kstatfs *buf*/) {
// super_block *sb = dentry->d_sb;
// SuperblockInfo *superblock_info = F2FS_SB(sb);
// u64 id = huge_encode_dev(sb->s_bdev->bd_dev);
// block_t total_count, user_block_count, start_count, ovp_count;
// total_count = LeToCpu(superblock_info->raw_super->block_count);
// user_block_count = superblock_info->GetUserBlockCount();
// start_count = LeToCpu(superblock_info->raw_super->segment0_blkaddr);
// ovp_count = GetSmInfo(superblock_info).ovp_segments << superblock_info->GetLogBlocksPerSeg();
// buf->f_type = kF2fsSuperMagic;
// buf->f_bsize = superblock_info->GetBlocksize();
// buf->f_blocks = total_count - start_count;
// buf->f_bfree = buf->f_blocks - ValidUserBlocks(superblock_info) - ovp_count;
// buf->f_bavail = user_block_count - ValidUserBlocks(superblock_info);
// buf->f_files = ValidInodeCount(superblock_info);
// buf->f_ffree = superblock_info->GetTotalNodeCount() - ValidNodeCount(superblock_info);
// buf->f_namelen = kMaxNameLen;
// buf->f_fsid.val[0] = (u32)id;
// buf->f_fsid.val[1] = (u32)(id >> 32);
// return 0;
// }
// VnodeF2fs *F2fs::F2fsNfsGetInode(uint64_t ino, uint32_t generation) {
// fbl::RefPtr<VnodeF2fs> vnode_refptr;
// VnodeF2fs *vnode = nullptr;
// int err;
// if (ino < superblock_info_->GetRootIno())
// return (VnodeF2fs *)ErrPtr(-ESTALE);
// /*
// * f2fs_iget isn't quite right if the inode is currently unallocated!
// * However f2fs_iget currently does appropriate checks to handle stale
// * inodes so everything is OK.
// */
// err = VnodeF2fs::Vget(this, ino, &vnode_refptr);
// if (err)
// return (VnodeF2fs *)ErrPtr(err);
// vnode = vnode_refptr.get();
// if (generation && vnode->i_generation != generation) {
// /* we didn't find the right inode.. */
// return (VnodeF2fs *)ErrPtr(-ESTALE);
// }
// return vnode;
// }
// struct fid {};
// dentry *F2fs::F2fsFhToDentry(fid *fid, int fh_len, int fh_type) {
// return generic_fh_to_dentry(sb, fid, fh_len, fh_type,
// f2fs_nfs_get_inode);
// }
// dentry *F2fs::F2fsFhToParent(fid *fid, int fh_len, int fh_type) {
// return generic_fh_to_parent(sb, fid, fh_len, fh_type,
// f2fs_nfs_get_inode);
// }
#endif
void F2fs::ParseOptions() {
for (uint32_t i = 0; i < kOptMaxNum; ++i) {
uint32_t value;
if (mount_options_.GetValue(i, &value) == ZX_OK) {
switch (i) {
case kOptActiveLogs:
superblock_info_->SetActiveLogs(safemath::checked_cast<int>(value));
break;
case kOptDiscard:
if (value)
superblock_info_->SetOpt(kMountDiscard);
break;
case kOptBgGcOff:
if (value)
superblock_info_->SetOpt(kMountBgGcOff);
break;
case kOptNoHeap:
if (value)
superblock_info_->SetOpt(kMountNoheap);
break;
case kOptDisableExtIdentify:
if (value)
superblock_info_->SetOpt(kMountDisableExtIdentify);
break;
case kOptNoUserXAttr:
if (value)
superblock_info_->SetOpt(kMountNoXAttr);
break;
case kOptNoAcl:
if (value)
superblock_info_->SetOpt(kMountNoAcl);
break;
case kOptDisableRollForward:
if (value)
superblock_info_->SetOpt(kMountDisableRollForward);
break;
case kOptInlineXattr:
if (value)
superblock_info_->SetOpt(kMountInlineXattr);
break;
case kOptInlineData:
if (value)
superblock_info_->SetOpt(kMountInlineData);
break;
case kOptInlineDentry:
if (value)
superblock_info_->SetOpt(kMountInlineDentry);
break;
default:
FX_LOGS(WARNING) << mount_options_.GetNameView(i) << " is not supported.";
break;
};
}
}
}
loff_t F2fs::MaxFileSize(unsigned bits) {
loff_t result = kAddrsPerInode;
loff_t leaf_count = kAddrsPerBlock;
/* two direct node blocks */
result += (leaf_count * 2);
/* two indirect node blocks */
leaf_count *= kNidsPerBlock;
result += (leaf_count * 2);
/* one double indirect node block */
leaf_count *= kNidsPerBlock;
result += leaf_count;
result <<= bits;
return result;
}
zx_status_t F2fs::SanityCheckRawSuper() {
unsigned int blocksize;
if (kF2fsSuperMagic != LeToCpu(raw_sb_->magic)) {
return ZX_ERR_INVALID_ARGS;
}
// Currently, support 512/1024/2048/4096 block size
blocksize = 1 << LeToCpu(raw_sb_->log_blocksize);
if (blocksize != kPageSize)
return ZX_ERR_INVALID_ARGS;
if (LeToCpu(raw_sb_->log_sectorsize) > kMaxLogSectorSize ||
LeToCpu(raw_sb_->log_sectorsize) < kMinLogSectorSize)
return ZX_ERR_INVALID_ARGS;
if ((LeToCpu(raw_sb_->log_sectors_per_block) + LeToCpu(raw_sb_->log_sectorsize)) !=
kMaxLogSectorSize)
return ZX_ERR_INVALID_ARGS;
return ZX_OK;
}
zx_status_t F2fs::SanityCheckCkpt() {
unsigned int total, fsmeta;
total = LeToCpu(raw_sb_->segment_count);
fsmeta = LeToCpu(raw_sb_->segment_count_ckpt);
fsmeta += LeToCpu(raw_sb_->segment_count_sit);
fsmeta += LeToCpu(raw_sb_->segment_count_nat);
fsmeta += LeToCpu(superblock_info_->GetCheckpoint().rsvd_segment_count);
fsmeta += LeToCpu(raw_sb_->segment_count_ssa);
if (fsmeta >= total) {
return ZX_ERR_BAD_STATE;
}
uint32_t sit_ver_bitmap_bytesize =
((LeToCpu(raw_sb_->segment_count_sit) / 2) << LeToCpu(raw_sb_->log_blocks_per_seg)) / 8;
uint32_t nat_ver_bitmap_bytesize =
((LeToCpu(raw_sb_->segment_count_nat) / 2) << LeToCpu(raw_sb_->log_blocks_per_seg)) / 8;
block_t nat_blocks = (LeToCpu(raw_sb_->segment_count_nat) >> 1)
<< LeToCpu(raw_sb_->log_blocks_per_seg);
if (superblock_info_->GetCheckpoint().sit_ver_bitmap_bytesize != sit_ver_bitmap_bytesize ||
superblock_info_->GetCheckpoint().nat_ver_bitmap_bytesize != nat_ver_bitmap_bytesize ||
superblock_info_->GetCheckpoint().next_free_nid >= kNatEntryPerBlock * nat_blocks) {
return ZX_ERR_BAD_STATE;
}
return ZX_OK;
}
void F2fs::InitSuperblockInfo() {
superblock_info_->SetLogSectorsPerBlock(LeToCpu(RawSb().log_sectors_per_block));
superblock_info_->SetLogBlocksize(LeToCpu(RawSb().log_blocksize));
superblock_info_->SetBlocksize(1 << superblock_info_->GetLogBlocksize());
superblock_info_->SetLogBlocksPerSeg(LeToCpu(RawSb().log_blocks_per_seg));
superblock_info_->SetBlocksPerSeg(1 << superblock_info_->GetLogBlocksPerSeg());
superblock_info_->SetSegsPerSec(LeToCpu(RawSb().segs_per_sec));
superblock_info_->SetSecsPerZone(LeToCpu(RawSb().secs_per_zone));
superblock_info_->SetTotalSections(LeToCpu(RawSb().section_count));
superblock_info_->SetTotalNodeCount((LeToCpu(RawSb().segment_count_nat) / 2) *
superblock_info_->GetBlocksPerSeg() * kNatEntryPerBlock);
superblock_info_->SetRootIno(LeToCpu(RawSb().root_ino));
superblock_info_->SetNodeIno(LeToCpu(RawSb().node_ino));
superblock_info_->SetMetaIno(LeToCpu(RawSb().meta_ino));
std::vector<std::string> extension_list;
for (int index = 0; index < safemath::checked_cast<int>(RawSb().extension_count); ++index) {
ZX_ASSERT(index < kMaxExtension);
ZX_ASSERT(RawSb().extension_list[index][7] == '\0');
extension_list.push_back(reinterpret_cast<char *>(RawSb().extension_list[index]));
}
ZX_ASSERT(RawSb().extension_count == extension_list.size());
superblock_info_->SetExtensionList(std::move(extension_list));
}
void F2fs::Reset() {
if (root_vnode_) {
root_vnode_.reset();
}
if (node_manager_) {
node_manager_->DestroyNodeManager();
node_manager_.reset();
}
if (segment_manager_) {
segment_manager_->DestroySegmentManager();
segment_manager_.reset();
}
if (superblock_info_) {
superblock_info_.reset();
}
}
zx_status_t F2fs::FillSuper() {
zx_status_t err = ZX_OK;
auto reset = fit::defer([&] { Reset(); });
// allocate memory for f2fs-specific super block info
superblock_info_ = std::make_unique<SuperblockInfo>();
ParseOptions();
// sanity checking of raw super
if (err = SanityCheckRawSuper(); err != ZX_OK) {
return err;
}
superblock_info_->SetRawSuperblock(raw_sb_);
superblock_info_->ClearOnRecovery();
InitSuperblockInfo();
node_vnode_ = std::make_unique<VnodeF2fs>(this, GetSuperblockInfo().GetNodeIno());
meta_vnode_ = std::make_unique<VnodeF2fs>(this, GetSuperblockInfo().GetMetaIno());
writer_ = std::make_unique<Writer>(bc_.get());
if (err = GetValidCheckpoint(); err != ZX_OK) {
return err;
}
// sanity checking of checkpoint
if (err = SanityCheckCkpt(); err != ZX_OK) {
return err;
}
superblock_info_->SetTotalValidNodeCount(
LeToCpu(superblock_info_->GetCheckpoint().valid_node_count));
superblock_info_->SetTotalValidInodeCount(
LeToCpu(superblock_info_->GetCheckpoint().valid_inode_count));
superblock_info_->SetUserBlockCount(
static_cast<block_t>(LeToCpu(superblock_info_->GetCheckpoint().user_block_count)));
superblock_info_->SetTotalValidBlockCount(
static_cast<block_t>(LeToCpu(superblock_info_->GetCheckpoint().valid_block_count)));
superblock_info_->SetLastValidBlockCount(superblock_info_->GetTotalValidBlockCount());
superblock_info_->SetAllocValidBlockCount(0);
InitOrphanInfo();
segment_manager_ = std::make_unique<SegmentManager>(this);
node_manager_ = std::make_unique<NodeManager>(this);
if (err = segment_manager_->BuildSegmentManager(); err != ZX_OK) {
return err;
}
if (err = node_manager_->BuildNodeManager(); err != ZX_OK) {
return err;
}
// TODO: Enable gc after impl dirty data cache
// BuildGcManager(superblock_info);
// if there are nt orphan nodes free them
if (err = RecoverOrphanInodes(); err != ZX_OK) {
return err;
}
// read root inode and dentry
if (err = VnodeF2fs::Vget(this, superblock_info_->GetRootIno(), &root_vnode_); err != ZX_OK) {
err = ZX_ERR_NO_MEMORY;
return err;
}
// root vnode is corrupted
if (!root_vnode_->IsDir() || !root_vnode_->GetBlocks() || !root_vnode_->GetSize()) {
return err;
}
if (!superblock_info_->TestOpt(kMountDisableRollForward)) {
RecoverFsyncData();
}
// After POR, we can run background GC thread
// TODO: Enable wb thread first, and then impl gc thread
// err = StartGcThread(superblock_info);
// if (err)
// goto fail;
reset.cancel();
return err;
}
void SuperblockInfo::IncNrOrphans() { n_orphans_ = safemath::CheckAdd(n_orphans_, 1).ValueOrDie(); }
void SuperblockInfo::DecNrOrphans() { n_orphans_ = safemath::CheckSub(n_orphans_, 1).ValueOrDie(); }
} // namespace f2fs