| // 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 <sys/stat.h> |
| #include <string.h> |
| #include "f2fs.h" |
| |
| namespace f2fs { |
| |
| #if 0 // porting needed |
| // static kmem_cache *Inode_cachep; |
| |
| // enum { |
| // Opt_gc_background_off, |
| // Opt_disable_roll_forward, |
| // Opt_discard, |
| // Opt_noheap, |
| // Opt_nouser_xattr, |
| // Opt_noacl, |
| // Opt_active_logs, |
| // Opt_disable_ext_identify, |
| // Opt_err, |
| // }; |
| |
| // static match_table_t f2fs_tokens = { |
| // {Opt_gc_background_off, "background_gc_off"}, |
| // {Opt_disable_roll_forward, "disable_roll_forward"}, |
| // {Opt_discard, "discard"}, |
| // {Opt_noheap, "no_heap"}, |
| // {Opt_nouser_xattr, "nouser_xattr"}, |
| // {Opt_noacl, "noacl"}, |
| // {Opt_active_logs, "active_logs=%u"}, |
| // {Opt_disable_ext_identify, "disable_ext_identify"}, |
| // {Opt_err, NULL}, |
| // }; |
| |
| // void F2fs::InitOnce(void *foo) |
| // { |
| // InodeInfo *fi = (InodeInfo *) foo; |
| |
| // memset(fi, 0, sizeof(*fi)); |
| // inode_init_once(&fi->vfs_inode); |
| // } |
| |
| // VnodeF2fs *F2fs::F2fsAllocInode() { |
| // InodeInfo *fi; |
| |
| // // fi = kmem_cache_alloc(Inode_cachep, GFP_NOFS | __GFP_ZERO); |
| // // if (!fi) |
| // // return NULL; |
| // fi = new InodeInfo(); |
| |
| // InitOnce((void *) fi); |
| |
| // /* Initilize f2fs-specific inode info */ |
| // AtomicSet(&fi->vfs_inode.i_version, 1); |
| // AtomicSet(&fi->dirty_dents, 0); |
| // fi->i_current_depth = 1; |
| // fi->i_advise = 0; |
| // RwlockInit(&fi->ext.ext_lock); |
| |
| // SetInodeFlag(fi, InodeInfoFlag::kFiNewInode); |
| |
| // return &fi->vfs_inode; |
| // } |
| |
| // void F2fs::F2fsICallback(rcu_head *head) |
| // { |
| // [[maybe_unused]] inode *inode = container_of(head, inode, i_rcu); |
| // // kmem_cache_free(Inode_cachep, F2FS_I(inode)); |
| // } |
| |
| // void F2fs::F2fsDestroyInode(inode *inode) |
| // { |
| // call_rcu(&inode->i_rcu, F2fsICallback); |
| // } |
| #endif |
| |
| void F2fs::PutSuper() { |
| f2fs_destroy_stats(sbi_.get()); |
| #if 0 // porting needed |
| // StopGcThread(sbi_.get()); |
| #endif |
| |
| WriteCheckpoint(false, true); |
| |
| #if 0 // porting needed |
| // Iput(sbi_->node_inode); |
| // Iput(sbi_->meta_inode); |
| #endif |
| fbl::AutoLock lock(&vnode_table_lock_); |
| vnode_table_.erase(NodeIno(sbi_.get())); |
| vnode_table_.erase(MetaIno(sbi_.get())); |
| |
| /* destroy f2fs internal modules */ |
| node_mgr_->DestroyNodeManager(); |
| seg_mgr_->DestroySegmentManager(); |
| |
| delete sbi_->ckpt; |
| |
| #if 0 // porting needed |
| // brelse(sbi_->raw_super_buf); |
| #endif |
| node_mgr_.reset(); |
| seg_mgr_.reset(); |
| raw_sb_.reset(); |
| sbi_.reset(); |
| } |
| |
| zx_status_t F2fs::SyncFs(int sync) { |
| #ifdef F2FS_BU_DEBUG |
| std::cout << "F2fs::SyncFs, sbi_->s_dirty=" << sbi_->s_dirty << std::endl; |
| #endif |
| |
| #if 0 // porting needed |
| //if (!sbi_->s_dirty && !GetPages(sbi_.get(), CountType::kDirtyNodes)) |
| // return 0; |
| #endif |
| |
| if (sync) |
| WriteCheckpoint(false, false); |
| |
| return ZX_OK; |
| } |
| |
| #if 0 // porting needed |
| // int F2fs::F2fsStatfs(dentry *dentry /*, kstatfs *buf*/) { |
| // super_block *sb = dentry->d_sb; |
| // SbInfo *sbi = 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(sbi->raw_super->block_count); |
| // user_block_count = sbi->user_block_count; |
| // start_count = LeToCpu(sbi->raw_super->segment0_blkaddr); |
| // ovp_count = GetSmInfo(sbi)->ovp_segments << sbi->log_blocks_per_seg; |
| // buf->f_type = kF2fsSuperMagic; |
| // buf->f_bsize = sbi->blocksize; |
| |
| // buf->f_blocks = total_count - start_count; |
| // buf->f_bfree = buf->f_blocks - ValidUserBlocks(sbi) - ovp_count; |
| // buf->f_bavail = user_block_count - ValidUserBlocks(sbi); |
| |
| // buf->f_files = ValidInodeCount(sbi); |
| // buf->f_ffree = sbi->total_node_count - ValidNodeCount(sbi); |
| |
| // buf->f_namelen = kMaxNameLen; |
| // buf->f_fsid.val[0] = (u32)id; |
| // buf->f_fsid.val[1] = (u32)(id >> 32); |
| |
| // return 0; |
| // } |
| |
| // int F2fs::F2fsShowOptions(/*seq_file *seq*/) { |
| // if (TestOpt(sbi, kMountBgGC)) |
| // seq_puts(seq, ",background_gc_on"); |
| // else |
| // seq_puts(seq, ",background_gc_off"); |
| // if (TestOpt(sbi, kMountDisableRollForward)) |
| // seq_puts(seq, ",disable_roll_forward"); |
| // if (TestOpt(sbi, kMountDiscard)) |
| // seq_puts(seq, ",discard"); |
| // if (TestOpt(sbi, kMountNoheap)) |
| // seq_puts(seq, ",no_heap_alloc"); |
| // if (TestOpt(sbi, kMountDisableExtIdentify)) |
| // seq_puts(seq, ",disable_ext_indentify"); |
| |
| // seq_printf(seq, ",active_logs=%u", sbi->active_logs); |
| |
| // return 0; |
| // } |
| |
| // VnodeF2fs *F2fs::F2fsNfsGetInode(uint64_t ino, uint32_t generation) { |
| // fbl::RefPtr<VnodeF2fs> vnode_refptr; |
| // VnodeF2fs *vnode = nullptr; |
| // int err; |
| |
| // if (ino < RootIno(sbi_.get())) |
| // 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.. */ |
| // Iput(vnode); |
| // 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); |
| // } |
| |
| // int F2fs::ParseOptions(char *options) { |
| // substring_t args[MAX_OPT_ARGS]; |
| // char *p; |
| // int arg = 0; |
| |
| // if (!options) |
| // return 0; |
| |
| // while ((p = strsep(&options, ",")) != NULL) { |
| // int token; |
| // if (!*p) |
| // continue; |
| // /* |
| // * Initialize args struct so we know whether arg was |
| // * found; some options take optional arguments. |
| // */ |
| // args[0].to = args[0].from = NULL; |
| // token = match_token(p, f2fs_tokens, args); |
| |
| // switch (token) { |
| // case Opt_gc_background_off: |
| // ClearOpt(&sbi_.get(), kMountBgGC); |
| // break; |
| // case Opt_disable_roll_forward: |
| // SetOpt(sbi_.get(), kMountDisableRollForward); |
| // break; |
| // case Opt_discard: |
| // SetOpt(sbi_.get(), kMountDiscard); |
| // break; |
| // case Opt_noheap: |
| // SetOpt(sbi_.get(), kMountNoheap); |
| // break; |
| // case Opt_active_logs: |
| // if (args->from && match_int(args, &arg)) |
| // return -EINVAL; |
| // if (arg != 2 && arg != 4 && arg != 6) |
| // return -EINVAL; |
| // sbi_.get()->active_logs = arg; |
| // break; |
| // case Opt_disable_ext_identify: |
| // SetOpt(sbi_.get(), kMountDisableExtIdentify); |
| // break; |
| // default: |
| // return -EINVAL; |
| // } |
| // } |
| // return 0; |
| // } |
| |
| // 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; |
| // } |
| #endif |
| |
| int F2fs::SanityCheckRawSuper() { |
| unsigned int blocksize; |
| |
| if (kF2fsSuperMagic != LeToCpu(raw_sb_->magic)) |
| return 1; |
| |
| /* Currently, support only 4KB block size */ |
| blocksize = 1 << LeToCpu(raw_sb_->log_blocksize); |
| if (blocksize != kPageCacheSize) |
| return 1; |
| if (LeToCpu(raw_sb_->log_sectorsize) != kLogSectorSize) |
| return 1; |
| if (LeToCpu(raw_sb_->log_sectors_per_block) != kLogSectorsPerBlock) |
| return 1; |
| return 0; |
| } |
| |
| int 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(sbi_->ckpt->rsvd_segment_count); |
| fsmeta += LeToCpu(raw_sb_->segment_count_ssa); |
| |
| if (fsmeta >= total) |
| return 1; |
| return 0; |
| } |
| |
| void F2fs::InitSbInfo() { |
| int i; |
| |
| sbi_->log_sectors_per_block = LeToCpu(RawSb().log_sectors_per_block); |
| sbi_->log_blocksize = LeToCpu(RawSb().log_blocksize); |
| sbi_->blocksize = 1 << sbi_->log_blocksize; |
| sbi_->log_blocks_per_seg = LeToCpu(RawSb().log_blocks_per_seg); |
| sbi_->blocks_per_seg = 1 << sbi_->log_blocks_per_seg; |
| sbi_->segs_per_sec = LeToCpu(RawSb().segs_per_sec); |
| sbi_->secs_per_zone = LeToCpu(RawSb().secs_per_zone); |
| sbi_->total_sections = LeToCpu(RawSb().section_count); |
| sbi_->total_node_count = |
| (LeToCpu(RawSb().segment_count_nat) / 2) * sbi_->blocks_per_seg * kNatEntryPerBlock; |
| sbi_->root_ino_num = LeToCpu(RawSb().root_ino); |
| sbi_->node_ino_num = LeToCpu(RawSb().node_ino); |
| sbi_->meta_ino_num = LeToCpu(RawSb().meta_ino); |
| |
| for (i = 0; i < static_cast<int>(CountType::kNrCountType); i++) |
| AtomicSet(&sbi_->nr_pages[i], 0); |
| } |
| |
| zx_status_t F2fs::FillSuper() { |
| #if 0 // porting needed |
| // SuperBlock *raw_super; |
| // buffer_head *raw_super_buf = NULL; |
| #endif |
| VnodeF2fs *root; |
| zx_status_t err = ZX_ERR_INVALID_ARGS; |
| |
| /* allocate memory for f2fs-specific super block info */ |
| sbi_ = std::make_unique<SbInfo>(); |
| if (!sbi_) |
| return ZX_ERR_NO_MEMORY; |
| memset(sbi_.get(), 0, sizeof(SbInfo)); |
| |
| #if 0 // porting needed |
| // super_block *sb = sbi_->sb; |
| |
| /* set a temporary block size */ |
| // if (!SbSetBlocksize(sb, kBlkSize)) |
| // goto free_sbi; |
| #endif |
| |
| /* mount options are fixed as below */ |
| ClearOpt(sbi_.get(), kMountBgGC); |
| ClearOpt(sbi_.get(), kMountDiscard); |
| SetOpt(sbi_.get(), kMountNoheap); |
| ClearOpt(sbi_.get(), kMountXattrUser); |
| ClearOpt(sbi_.get(), kMountPosixAcl); |
| sbi_->active_logs = kNrCursegType; |
| SetOpt(sbi_.get(), kMountDisableExtIdentify); |
| |
| #if 0 // porting needed |
| /* parse mount options */ |
| // if (ParseOptions((char *)data)) |
| // goto free_sb_buf; |
| #endif |
| |
| /* sanity checking of raw super */ |
| if (SanityCheckRawSuper()) |
| goto free_sb_buf; |
| |
| #if 0 // porting needed |
| // sb->s_maxbytes = MaxFileSize(RawSb().log_blocksize); |
| // sb->s_max_links = kLinkMax; |
| |
| // For NFS support |
| // get_random_bytes(&sbi->s_next_generation, sizeof(uint32_t)); |
| |
| // sb->s_op = &f2fs_sops; |
| // sb->s_xattr = f2fs_xattr_handlers; |
| // sb->s_export_op = &f2fs_export_ops; |
| // sb->s_magic = kF2fsSuperMagic; |
| // sb->s_fs_info = sbi_.get(); |
| // sb->s_time_gran = 1; |
| // sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | |
| // (TestOpt(sbi_.get(), kMountPosixAcl) ? MS_POSIXACL : 0); |
| // memcpy(&sb->s_uuid, RawSb().uuid, sizeof(RawSb().uuid)); |
| |
| /* init f2fs-specific super block info */ |
| // sbi->sb = sb; |
| #endif |
| sbi_->raw_super = raw_sb_.get(); |
| #if 0 // porting needed |
| // sbi_->raw_super_buf = raw_super_buf; |
| #endif |
| sbi_->por_doing = 0; |
| SpinLockInit(&sbi_->stat_lock); |
| #if 0 // porting needed |
| // init_rwsem(&sbi->bio_sem); |
| #endif |
| InitSbInfo(); |
| |
| /* get an inode for meta space */ |
| #if 0 // porting needed |
| // err = VnodeF2fs::Vget(this, MetaIno(sbi_), &sbi_->meta_vnode); |
| // if (err) { |
| // goto free_sb_buf; |
| // } |
| #endif |
| |
| err = GetValidCheckpoint(); |
| if (err) |
| goto free_meta_inode; |
| |
| /* sanity checking of checkpoint */ |
| err = ZX_ERR_INVALID_ARGS; |
| if (SanityCheckCkpt()) |
| goto free_cp; |
| |
| sbi_->total_valid_node_count = LeToCpu(sbi_->ckpt->valid_node_count); |
| sbi_->total_valid_inode_count = LeToCpu(sbi_->ckpt->valid_inode_count); |
| sbi_->user_block_count = LeToCpu(sbi_->ckpt->user_block_count); |
| sbi_->total_valid_block_count = LeToCpu(sbi_->ckpt->valid_block_count); |
| sbi_->last_valid_block_count = sbi_->total_valid_block_count; |
| sbi_->alloc_valid_block_count = 0; |
| list_initialize(&sbi_->dir_inode_list); |
| SpinLockInit(&sbi_->dir_inode_lock); |
| |
| /* init super block */ |
| #if 0 // porting needed |
| // if (!SbSetBlocksize(sb, sbi_->blocksize)) |
| // goto free_cp; |
| #endif |
| |
| InitOrphanInfo(); |
| |
| /* setup f2fs internal modules */ |
| seg_mgr_ = std::make_unique<SegMgr>(this); |
| err = seg_mgr_->BuildSegmentManager(); |
| if (err) |
| goto free_sm; |
| |
| node_mgr_ = std::make_unique<NodeMgr>(this); |
| err = node_mgr_->BuildNodeManager(); |
| if (err) |
| goto free_nm; |
| |
| #if 0 // porting needed |
| // BuildGcManager(sbi); |
| |
| /* get an inode for node space */ |
| // err = VnodeF2fs::Vget(this, NodeIno(sbi_), &sbi_->node_vnode); |
| // if (err) { |
| // goto free_nm; |
| // } |
| #endif |
| |
| /* if there are nt orphan nodes free them */ |
| err = ZX_ERR_INVALID_ARGS; |
| if (!(sbi_->ckpt->ckpt_flags & kCpUmountFlag) && RecoverOrphanInodes()) |
| goto free_node_inode; |
| |
| /* read root inode and dentry */ |
| err = VnodeF2fs::Vget(this, RootIno(sbi_.get()), &root_vnode_); |
| if (err) { |
| goto free_node_inode; |
| } |
| root = root_vnode_.get(); |
| if (!S_ISDIR(root->i_mode_) || !root->i_blocks_ || !root->i_size_) |
| goto free_root_inode; |
| |
| #if 0 // porting needed |
| // sb->s_root = DMakeRoot(root); /* allocate root dentry */ |
| // if (!sb->s_root) { |
| // err = ZX_ERR_NO_MEMORY; |
| // goto free_root_inode; |
| // } |
| #endif |
| |
| /* recover fsynced data */ |
| if (!(sbi_->ckpt->ckpt_flags & kCpUmountFlag) && |
| !TestOpt(sbi_.get(), kMountDisableExtIdentify)) { |
| RecoverFsyncData(); |
| } |
| |
| #if 0 // porting needed |
| /* After POR, we can run background GC thread */ |
| // err = StartGcThread(sbi); |
| // if (err) |
| // goto fail; |
| #endif |
| |
| err = f2fs_build_stats(sbi_.get()); |
| if (err) |
| goto fail; |
| |
| return ZX_OK; |
| |
| fail: |
| #if 0 // porting needed |
| // StopGcThread(sbi); |
| #endif |
| free_root_inode: |
| #if 0 // porting needed |
| // dput(sb->s_root); |
| // sb->s_root = NULL; |
| #endif |
| free_node_inode: |
| #if 0 // porting needed |
| // Iput(sbi_->node_inode); |
| #endif |
| free_nm: |
| node_mgr_->DestroyNodeManager(); |
| node_mgr_.reset(); |
| free_sm: |
| seg_mgr_->DestroySegmentManager(); |
| seg_mgr_.reset(); |
| free_cp: |
| delete sbi_->ckpt; |
| free_meta_inode: |
| #if 0 // porting needed |
| // MakeBadInode(sbi_->meta_inode); |
| // Iput(sbi_->meta_inode); |
| #endif |
| free_sb_buf: |
| #if 0 // porting needed |
| // brelse(raw_super_buf); |
| // free_sbi: |
| #endif |
| sbi_.reset(); |
| return err; |
| } |
| |
| #if 0 // porting needed |
| // dentry *F2fs::F2fsMount(file_system_type *fs_type, int flags, |
| // const char *dev_name, void *data) |
| // { |
| // // return mount_bdev(fs_type, flags, dev_name, data, F2fs::FillSuper); |
| // return mount_bdev(fs_type, flags, dev_name, data, NULL); |
| // } |
| |
| // int F2fs::InitInodecache(void) { |
| // Inode_cachep = KmemCacheCreate("Inode_cache", |
| // sizeof(InodeInfo), NULL); |
| // if (Inode_cachep == NULL) |
| // return -ENOMEM; |
| // } |
| |
| // void F2fs::DestroyInodecache(void) { |
| // /* |
| // * Make sure all delayed rcu free inodes are flushed before we |
| // * destroy cache. |
| // */ |
| // rcu_barrier(); |
| // kmem_cache_destroy(Inode_cachep); |
| // } |
| |
| // int /*__init*/ F2fs::initF2fsFs(void) |
| // { |
| // int err; |
| |
| // err = InitInodecache(); |
| // if (err) |
| // goto fail; |
| // // TODO(unknown): should decide how to use slab cache before it |
| // //err = CreateNodeManagerCaches(); |
| // if (err) |
| // goto fail; |
| // return register_filesystem(&f2fs_fs_type); |
| // fail: |
| // return err; |
| // } |
| |
| // void /*__exit*/ F2fs::exitF2fsFs(void) |
| // { |
| // unregister_filesystem(&f2fs_fs_type); |
| // // TODO(unknown): should decide how to use slab cache before it |
| // //DestroyNodeManagerCaches(); |
| // DestroyInodecache(); |
| // } |
| #endif |
| |
| } // namespace f2fs |