diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-05-10 06:55:14 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-05-10 06:55:14 +0000 |
commit | 331730984fd02ec211feb660965c397290403995 (patch) | |
tree | 30edd180edec4d734b91ca9a8746a2756e63bbad | |
parent | 2661ad30ffdfe374fff9dad5e8cace9d01dded2b (diff) | |
parent | 261ede0f9e12d080edf651495cb2beb43eef6b38 (diff) | |
download | f2fs-tools-android13-mainline-sdkext-release.tar.gz |
Snap for 8564071 from 261ede0f9e12d080edf651495cb2beb43eef6b38 to mainline-sdkext-releaseaml_sdk_331812000aml_sdk_331811100aml_sdk_331811000aml_sdk_331412000aml_sdk_331410000aml_sdk_331310010aml_sdk_331111000aml_sdk_330810050aml_sdk_330810010android13-mainline-sdkext-release
Change-Id: Id93cf51942266c1994101fec9900cc9c19312583
-rw-r--r-- | Android.bp | 11 | ||||
-rw-r--r-- | METADATA | 6 | ||||
-rw-r--r-- | fsck/dir.c | 2 | ||||
-rw-r--r-- | fsck/dump.c | 121 | ||||
-rw-r--r-- | fsck/f2fs.h | 8 | ||||
-rw-r--r-- | fsck/fsck.c | 305 | ||||
-rw-r--r-- | fsck/fsck.h | 31 | ||||
-rw-r--r-- | fsck/main.c | 29 | ||||
-rw-r--r-- | fsck/mount.c | 64 | ||||
-rw-r--r-- | fsck/node.c | 47 | ||||
-rw-r--r-- | fsck/segment.c | 105 | ||||
-rw-r--r-- | include/f2fs_fs.h | 76 | ||||
-rw-r--r-- | lib/libf2fs.c | 23 | ||||
-rw-r--r-- | man/dump.f2fs.8 | 7 | ||||
-rw-r--r-- | man/fsck.f2fs.8 | 7 | ||||
-rw-r--r-- | mkfs/f2fs_format.c | 88 | ||||
-rw-r--r-- | mkfs/f2fs_format_main.c | 93 | ||||
-rw-r--r-- | tools/f2fs_io/f2fs_io.c | 146 |
18 files changed, 926 insertions, 243 deletions
@@ -204,6 +204,15 @@ cc_binary_host { }, } +cc_binary_host { + name: "make_f2fs.static", + defaults: [ + "make_f2fs_defaults", + ], + static_executable: true, + stl: "libc++_static", +} + cc_binary { name: "fsck.f2fs", defaults: [ @@ -211,6 +220,7 @@ cc_binary { "fsck_main_src_files", ], host_supported: true, + recovery_available: true, cflags: ["-DWITH_RESIZE", "-DWITH_DEFRAG", "-DWITH_DUMP"], srcs: ["fsck/fsck.c", "fsck/resize.c", "fsck/defrag.c"], shared_libs: [ @@ -219,6 +229,7 @@ cc_binary { "libbase", ], symlinks: ["resize.f2fs", "defrag.f2fs", "dump.f2fs"], + vendor_ramdisk_available: true, } cc_binary { @@ -11,8 +11,8 @@ third_party { version: "v1.14.0" license_type: RESTRICTED last_upgrade_date { - year: 2021 - month: 6 - day: 3 + year: 2022 + month: 3 + day: 22 } } @@ -400,7 +400,7 @@ static void page_symlink(struct f2fs_sb_info *sbi, struct f2fs_node *inode, memcpy(data_blk, symname, symlen); set_summary(&sum, ino, 0, ni.version); - ret = reserve_new_block(sbi, &blkaddr, &sum, CURSEG_WARM_DATA, 1); + ret = reserve_new_block(sbi, &blkaddr, &sum, CURSEG_WARM_DATA, 0); ASSERT(!ret); ret = dev_write_block(data_blk, blkaddr); diff --git a/fsck/dump.c b/fsck/dump.c index 042a2e5..fce86c9 100644 --- a/fsck/dump.c +++ b/fsck/dump.c @@ -23,6 +23,9 @@ #define BUF_SZ 80 +/* current extent info */ +struct extent_info dump_extent; + const char *seg_type_name[SEG_TYPE_MAX + 1] = { "SEG_TYPE_DATA", "SEG_TYPE_CUR_DATA", @@ -227,10 +230,50 @@ void ssa_dump(struct f2fs_sb_info *sbi, int start_ssa, int end_ssa) close(fd); } +static void print_extent(bool last) +{ + if (dump_extent.len == 0) + goto out; + + if (dump_extent.len == 1) + printf(" %d", dump_extent.blk); + else + printf(" %d-%d", + dump_extent.blk, + dump_extent.blk + dump_extent.len - 1); + dump_extent.len = 0; +out: + if (last) + printf("\n"); +} + static void dump_data_blk(struct f2fs_sb_info *sbi, __u64 offset, u32 blkaddr) { char buf[F2FS_BLKSIZE]; + if (c.show_file_map) { + if (c.show_file_map_max_offset < offset) { + ASSERT(blkaddr == NULL_ADDR); + return; + } + if (!is_valid_data_blkaddr(blkaddr)) { + print_extent(false); + dump_extent.blk = 0; + dump_extent.len = 1; + print_extent(false); + } else if (dump_extent.len == 0) { + dump_extent.blk = blkaddr; + dump_extent.len = 1; + } else if (dump_extent.blk + dump_extent.len == blkaddr) { + dump_extent.len++; + } else { + print_extent(false); + dump_extent.blk = blkaddr; + dump_extent.len = 1; + } + return; + } + if (blkaddr == NULL_ADDR) return; @@ -239,6 +282,7 @@ static void dump_data_blk(struct f2fs_sb_info *sbi, __u64 offset, u32 blkaddr) memset(buf, 0, F2FS_BLKSIZE); } else { int ret; + ret = dev_read_block(buf, blkaddr); ASSERT(ret >= 0); } @@ -248,27 +292,20 @@ static void dump_data_blk(struct f2fs_sb_info *sbi, __u64 offset, u32 blkaddr) } static void dump_node_blk(struct f2fs_sb_info *sbi, int ntype, - u32 nid, u64 *ofs) + u32 nid, u32 addr_per_block, u64 *ofs) { struct node_info ni; struct f2fs_node *node_blk; u32 skip = 0; u32 i, idx = 0; - get_node_info(sbi, nid, &ni); - - node_blk = calloc(BLOCK_SZ, 1); - ASSERT(node_blk); - - dev_read_block(node_blk, ni.blk_addr); - switch (ntype) { case TYPE_DIRECT_NODE: - skip = idx = ADDRS_PER_BLOCK(&node_blk->i); + skip = idx = addr_per_block; break; case TYPE_INDIRECT_NODE: idx = NIDS_PER_BLOCK; - skip = idx * ADDRS_PER_BLOCK(&node_blk->i); + skip = idx * addr_per_block; break; case TYPE_DOUBLE_INDIRECT_NODE: skip = 0; @@ -278,26 +315,37 @@ static void dump_node_blk(struct f2fs_sb_info *sbi, int ntype, if (nid == 0) { *ofs += skip; - goto out; + return; } - for (i = 0; i < idx; i++, (*ofs)++) { + get_node_info(sbi, nid, &ni); + + node_blk = calloc(BLOCK_SZ, 1); + ASSERT(node_blk); + + dev_read_block(node_blk, ni.blk_addr); + + for (i = 0; i < idx; i++) { switch (ntype) { case TYPE_DIRECT_NODE: dump_data_blk(sbi, *ofs * F2FS_BLKSIZE, le32_to_cpu(node_blk->dn.addr[i])); + (*ofs)++; break; case TYPE_INDIRECT_NODE: dump_node_blk(sbi, TYPE_DIRECT_NODE, - le32_to_cpu(node_blk->in.nid[i]), ofs); + le32_to_cpu(node_blk->in.nid[i]), + addr_per_block, + ofs); break; case TYPE_DOUBLE_INDIRECT_NODE: dump_node_blk(sbi, TYPE_INDIRECT_NODE, - le32_to_cpu(node_blk->in.nid[i]), ofs); + le32_to_cpu(node_blk->in.nid[i]), + addr_per_block, + ofs); break; } } -out: free(node_blk); } @@ -371,20 +419,24 @@ static void dump_xattr(struct f2fs_sb_info *UNUSED(sbi), } #endif -static void dump_inode_blk(struct f2fs_sb_info *sbi, u32 nid, +static int dump_inode_blk(struct f2fs_sb_info *sbi, u32 nid, struct f2fs_node *node_blk) { u32 i = 0; u64 ofs = 0; + u32 addr_per_block; if((node_blk->i.i_inline & F2FS_INLINE_DATA)) { DBG(3, "ino[0x%x] has inline data!\n", nid); /* recover from inline data */ dev_write_dump(((unsigned char *)node_blk) + INLINE_DATA_OFFSET, 0, MAX_INLINE_DATA(node_blk)); - return; + return -1; } + c.show_file_map_max_offset = f2fs_max_file_offset(&node_blk->i); + addr_per_block = ADDRS_PER_BLOCK(&node_blk->i); + /* check data blocks in inode */ for (i = 0; i < ADDRS_PER_INODE(&node_blk->i); i++, ofs++) dump_data_blk(sbi, ofs * F2FS_BLKSIZE, le32_to_cpu( @@ -394,21 +446,30 @@ static void dump_inode_blk(struct f2fs_sb_info *sbi, u32 nid, for (i = 0; i < 5; i++) { if (i == 0 || i == 1) dump_node_blk(sbi, TYPE_DIRECT_NODE, - le32_to_cpu(node_blk->i.i_nid[i]), &ofs); + le32_to_cpu(node_blk->i.i_nid[i]), + addr_per_block, + &ofs); else if (i == 2 || i == 3) dump_node_blk(sbi, TYPE_INDIRECT_NODE, - le32_to_cpu(node_blk->i.i_nid[i]), &ofs); + le32_to_cpu(node_blk->i.i_nid[i]), + addr_per_block, + &ofs); else if (i == 4) dump_node_blk(sbi, TYPE_DOUBLE_INDIRECT_NODE, - le32_to_cpu(node_blk->i.i_nid[i]), &ofs); + le32_to_cpu(node_blk->i.i_nid[i]), + addr_per_block, + &ofs); else ASSERT(0); } + /* last block in extent cache */ + print_extent(true); dump_xattr(sbi, node_blk); + return 0; } -static void dump_file(struct f2fs_sb_info *sbi, struct node_info *ni, +static int dump_file(struct f2fs_sb_info *sbi, struct node_info *ni, struct f2fs_node *node_blk, int force) { struct f2fs_inode *inode = &node_blk->i; @@ -422,17 +483,21 @@ static void dump_file(struct f2fs_sb_info *sbi, struct node_info *ni, if (is_encrypted) { MSG(force, "File is encrypted\n"); - return; + return -1; } if ((!S_ISREG(imode) && !S_ISLNK(imode)) || namelen == 0 || namelen > F2FS_NAME_LEN) { MSG(force, "Not a regular file or wrong name info\n\n"); - return; + return -1; } if (force) goto dump; + /* dump file's data */ + if (c.show_file_map) + return dump_inode_blk(sbi, ni->ino, node_blk); + printf("Do you want to dump this file into ./lost_found/? [Y/N] "); ret = scanf("%s", ans); ASSERT(ret >= 0); @@ -459,6 +524,7 @@ dump: close(c.dump_fd); } + return 0; } static bool is_sit_bitmap_set(struct f2fs_sb_info *sbi, u32 blk_addr) @@ -473,10 +539,11 @@ static bool is_sit_bitmap_set(struct f2fs_sb_info *sbi, u32 blk_addr) (const char *)se->cur_valid_map) != 0; } -void dump_node(struct f2fs_sb_info *sbi, nid_t nid, int force) +int dump_node(struct f2fs_sb_info *sbi, nid_t nid, int force) { struct node_info ni; struct f2fs_node *node_blk; + int ret = 0; get_node_info(sbi, nid, &ni); @@ -505,16 +572,18 @@ void dump_node(struct f2fs_sb_info *sbi, nid_t nid, int force) if (le32_to_cpu(node_blk->footer.ino) == ni.ino && le32_to_cpu(node_blk->footer.nid) == ni.nid) { - print_node_info(sbi, node_blk, force); + if (!c.show_file_map) + print_node_info(sbi, node_blk, force); if (ni.ino == ni.nid) - dump_file(sbi, &ni, node_blk, force); + ret = dump_file(sbi, &ni, node_blk, force); } else { print_node_info(sbi, node_blk, force); MSG(force, "Invalid (i)node block\n\n"); } out: free(node_blk); + return ret; } static void dump_node_from_blkaddr(struct f2fs_sb_info *sbi, u32 blk_addr) diff --git a/fsck/f2fs.h b/fsck/f2fs.h index 9c6b0e4..7fb328f 100644 --- a/fsck/f2fs.h +++ b/fsck/f2fs.h @@ -527,6 +527,14 @@ static inline bool IS_VALID_BLK_ADDR(struct f2fs_sb_info *sbi, u32 addr) return 1; } +static inline bool is_valid_data_blkaddr(block_t blkaddr) +{ + if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR || + blkaddr == COMPRESS_ADDR) + return 0; + return 1; +} + static inline int IS_CUR_SEGNO(struct f2fs_sb_info *sbi, u32 segno) { int i; diff --git a/fsck/fsck.c b/fsck/fsck.c index 80a6d8e..ddcede3 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -250,8 +250,12 @@ static int is_valid_summary(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, if (node_blk->footer.nid == node_blk->footer.ino) { int ofs = get_extra_isize(node_blk); + if (ofs + ofs_in_node >= DEF_ADDRS_PER_INODE) + goto out; target_blk_addr = node_blk->i.i_addr[ofs + ofs_in_node]; } else { + if (ofs_in_node >= DEF_ADDRS_PER_BLOCK) + goto out; target_blk_addr = node_blk->dn.addr[ofs_in_node]; } @@ -489,8 +493,23 @@ static int sanity_check_nid(struct f2fs_sb_info *sbi, u32 nid, ni->blk_addr); if (f2fs_test_main_bitmap(sbi, ni->blk_addr) == 0) { + fsck->chk.valid_blk_cnt++; fsck->chk.valid_node_cnt++; + + /* Progress report */ + if (sbi->total_valid_node_count > 1000) { + unsigned int p10 = sbi->total_valid_node_count / 10; + + if (sbi->fsck->chk.checked_node_cnt++ % p10) + return 0; + + printf("[FSCK] Check node %"PRIu64" / %u (%.2f%%)\n", + sbi->fsck->chk.checked_node_cnt, + sbi->total_valid_node_count, + 10 * (float)sbi->fsck->chk.checked_node_cnt / + p10); + } } return 0; } @@ -533,7 +552,8 @@ out: int fsck_chk_node_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode, u32 nid, enum FILE_TYPE ftype, enum NODE_TYPE ntype, - u32 *blk_cnt, struct child_info *child) + u32 *blk_cnt, struct f2fs_compr_blk_cnt *cbc, + struct child_info *child) { struct node_info ni; struct f2fs_node *node_blk = NULL; @@ -547,7 +567,8 @@ int fsck_chk_node_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode, if (ntype == TYPE_INODE) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); - fsck_chk_inode_blk(sbi, nid, ftype, node_blk, blk_cnt, &ni, child); + fsck_chk_inode_blk(sbi, nid, ftype, node_blk, blk_cnt, cbc, + &ni, child); quota_add_inode_usage(fsck->qctx, nid, &node_blk->i); } else { switch (ntype) { @@ -555,19 +576,19 @@ int fsck_chk_node_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode, f2fs_set_main_bitmap(sbi, ni.blk_addr, CURSEG_WARM_NODE); fsck_chk_dnode_blk(sbi, inode, nid, ftype, node_blk, - blk_cnt, child, &ni); + blk_cnt, cbc, child, &ni); break; case TYPE_INDIRECT_NODE: f2fs_set_main_bitmap(sbi, ni.blk_addr, CURSEG_COLD_NODE); fsck_chk_idnode_blk(sbi, inode, ftype, node_blk, - blk_cnt, child); + blk_cnt, cbc, child); break; case TYPE_DOUBLE_INDIRECT_NODE: f2fs_set_main_bitmap(sbi, ni.blk_addr, CURSEG_COLD_NODE); fsck_chk_didnode_blk(sbi, inode, ftype, node_blk, - blk_cnt, child); + blk_cnt, cbc, child); break; default: ASSERT(0); @@ -667,7 +688,8 @@ void fsck_reada_all_direct_node_blocks(struct f2fs_sb_info *sbi, /* start with valid nid and blkaddr */ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid, enum FILE_TYPE ftype, struct f2fs_node *node_blk, - u32 *blk_cnt, struct node_info *ni, struct child_info *child_d) + u32 *blk_cnt, struct f2fs_compr_blk_cnt *cbc, + struct node_info *ni, struct child_info *child_d) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct child_info child; @@ -675,6 +697,11 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid, u32 i_links = le32_to_cpu(node_blk->i.i_links); u64 i_size = le64_to_cpu(node_blk->i.i_size); u64 i_blocks = le64_to_cpu(node_blk->i.i_blocks); + bool compr_supported = c.feature & cpu_to_le32(F2FS_FEATURE_COMPRESSION); + u32 i_flags = le32_to_cpu(node_blk->i.i_flags); + bool compressed = i_flags & F2FS_COMPR_FL; + bool compr_rel = node_blk->i.i_inline & F2FS_COMPRESS_RELEASED; + u64 i_compr_blocks = le64_to_cpu(node_blk->i.i_compr_blocks); nid_t i_xattr_nid = le32_to_cpu(node_blk->i.i_xattr_nid); int ofs; char *en; @@ -683,7 +710,23 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid, unsigned short i_gc_failures; int need_fix = 0; int ret; + u32 cluster_size = 1 << node_blk->i.i_log_cluster_size; + if (!compr_supported && compressed) { + /* + * The 'compression' flag in i_flags affects the traverse of + * the node tree. Thus, it must be fixed unconditionally + * in the memory (node_blk). + */ + node_blk->i.i_flags &= ~cpu_to_le32(F2FS_COMPR_FL); + compressed = false; + if (c.fix_on) { + need_fix = 1; + FIX_MSG("[0x%x] i_flags=0x%x -> 0x%x", + nid, i_flags, node_blk->i.i_flags); + } + i_flags &= ~F2FS_COMPR_FL; + } memset(&child, 0, sizeof(child)); child.links = 2; child.p_ino = nid; @@ -695,6 +738,8 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid, if (ftype == F2FS_FT_DIR) { f2fs_set_main_bitmap(sbi, ni->blk_addr, CURSEG_HOT_NODE); + memcpy(child.p_name, node_blk->i.i_name, + node_blk->i.i_namelen); } else { if (f2fs_test_main_bitmap(sbi, ni->blk_addr) == 0) { f2fs_set_main_bitmap(sbi, ni->blk_addr, @@ -897,31 +942,45 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid, /* check extent info */ check_extent_info(&child, blkaddr, 0); + if (blkaddr == NULL_ADDR) + continue; if (blkaddr == COMPRESS_ADDR) { - if (node_blk->i.i_compr_blocks) { + if (!compressed || (child.pgofs & + (cluster_size - 1)) != 0) { + if (c.fix_on) { + node_blk->i.i_addr[ofs + idx] = + NULL_ADDR; + need_fix = 1; + FIX_MSG("[0x%x] i_addr[%d] = 0", nid, + ofs + idx); + } + continue; + } + if (!compr_rel) { fsck->chk.valid_blk_cnt++; *blk_cnt = *blk_cnt + 1; + cbc->cheader_pgofs = child.pgofs; + cbc->cnt++; } continue; } - - if (blkaddr != 0) { - ret = fsck_chk_data_blk(sbi, - IS_CASEFOLDED(&node_blk->i), - blkaddr, - &child, (i_blocks == *blk_cnt), - ftype, nid, idx, ni->version, - file_is_encrypt(&node_blk->i)); - if (!ret) { - *blk_cnt = *blk_cnt + 1; - if (cur_qtype != -1 && blkaddr != NEW_ADDR) - qf_last_blkofs[cur_qtype] = child.pgofs; - } else if (c.fix_on) { - node_blk->i.i_addr[ofs + idx] = 0; - need_fix = 1; - FIX_MSG("[0x%x] i_addr[%d] = 0", - nid, ofs + idx); - } + if (!compr_rel && blkaddr == NEW_ADDR && + child.pgofs - cbc->cheader_pgofs < cluster_size) + cbc->cnt++; + ret = fsck_chk_data_blk(sbi, + IS_CASEFOLDED(&node_blk->i), + blkaddr, + &child, (i_blocks == *blk_cnt), + ftype, nid, idx, ni->version, + file_is_encrypt(&node_blk->i)); + if (!ret) { + *blk_cnt = *blk_cnt + 1; + if (cur_qtype != -1 && blkaddr != NEW_ADDR) + qf_last_blkofs[cur_qtype] = child.pgofs; + } else if (c.fix_on) { + node_blk->i.i_addr[ofs + idx] = 0; + need_fix = 1; + FIX_MSG("[0x%x] i_addr[%d] = 0", nid, ofs + idx); } } @@ -948,7 +1007,7 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid, goto skip; ret = fsck_chk_node_blk(sbi, &node_blk->i, i_nid, - ftype, ntype, blk_cnt, &child); + ftype, ntype, blk_cnt, cbc, &child); if (!ret) { *blk_cnt = *blk_cnt + 1; } else if (ret == -EINVAL) { @@ -992,6 +1051,16 @@ check: nid, i_blocks, *blk_cnt); } } + + if (compressed && i_compr_blocks != cbc->cnt) { + if (c.fix_on) { + node_blk->i.i_compr_blocks = cpu_to_le64(cbc->cnt); + need_fix = 1; + FIX_MSG("[0x%x] i_compr_blocks=0x%08"PRIx64" -> 0x%x", + nid, i_compr_blocks, cbc->cnt); + } + } + skip_blkcnt_fix: en = malloc(F2FS_PRINT_NAMELEN); ASSERT(en); @@ -1077,14 +1146,10 @@ skip_blkcnt_fix: if (ftype == F2FS_FT_SYMLINK && i_size == 0 && i_blocks == (i_xattr_nid ? 3 : 2)) { - ASSERT_MSG("ino: 0x%x i_blocks: %lu with zero i_size\n", - nid, (unsigned long)i_blocks); - if (c.fix_on) { - node_blk->i.i_size = cpu_to_le64(F2FS_BLKSIZE); - need_fix = 1; - FIX_MSG("Symlink: recover 0x%x with i_size=%lu", + node_blk->i.i_size = cpu_to_le64(F2FS_BLKSIZE); + need_fix = 1; + FIX_MSG("Symlink: recover 0x%x with i_size=%lu", nid, (unsigned long)F2FS_BLKSIZE); - } } if (ftype == F2FS_FT_ORPHAN && i_links) { @@ -1130,27 +1195,47 @@ skip_blkcnt_fix: int fsck_chk_dnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode, u32 nid, enum FILE_TYPE ftype, struct f2fs_node *node_blk, - u32 *blk_cnt, struct child_info *child, struct node_info *ni) + u32 *blk_cnt, struct f2fs_compr_blk_cnt *cbc, + struct child_info *child, struct node_info *ni) { int idx, ret; int need_fix = 0; child->p_ino = nid; child->pp_ino = le32_to_cpu(inode->i_pino); + u32 i_flags = le32_to_cpu(inode->i_flags); + bool compressed = i_flags & F2FS_COMPR_FL; + bool compr_rel = inode->i_inline & F2FS_COMPRESS_RELEASED; + u32 cluster_size = 1 << inode->i_log_cluster_size; for (idx = 0; idx < ADDRS_PER_BLOCK(inode); idx++, child->pgofs++) { block_t blkaddr = le32_to_cpu(node_blk->dn.addr[idx]); check_extent_info(child, blkaddr, 0); - if (blkaddr == 0x0) + if (blkaddr == NULL_ADDR) continue; if (blkaddr == COMPRESS_ADDR) { - if (inode->i_compr_blocks) { + if (!compressed || (child->pgofs & + (cluster_size - 1)) != 0) { + if (c.fix_on) { + node_blk->dn.addr[idx] = NULL_ADDR; + need_fix = 1; + FIX_MSG("[0x%x] dn.addr[%d] = 0", nid, + idx); + } + continue; + } + if (!compr_rel) { F2FS_FSCK(sbi)->chk.valid_blk_cnt++; *blk_cnt = *blk_cnt + 1; + cbc->cheader_pgofs = child->pgofs; + cbc->cnt++; } continue; } + if (!compr_rel && blkaddr == NEW_ADDR && child->pgofs - + cbc->cheader_pgofs < cluster_size) + cbc->cnt++; ret = fsck_chk_data_blk(sbi, IS_CASEFOLDED(inode), blkaddr, child, le64_to_cpu(inode->i_blocks) == *blk_cnt, ftype, @@ -1161,7 +1246,7 @@ int fsck_chk_dnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode, if (cur_qtype != -1 && blkaddr != NEW_ADDR) qf_last_blkofs[cur_qtype] = child->pgofs; } else if (c.fix_on) { - node_blk->dn.addr[idx] = 0; + node_blk->dn.addr[idx] = NULL_ADDR; need_fix = 1; FIX_MSG("[0x%x] dn.addr[%d] = 0", nid, idx); } @@ -1175,7 +1260,7 @@ int fsck_chk_dnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode, int fsck_chk_idnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode, enum FILE_TYPE ftype, struct f2fs_node *node_blk, u32 *blk_cnt, - struct child_info *child) + struct f2fs_compr_blk_cnt *cbc, struct child_info *child) { int need_fix = 0, ret; int i = 0; @@ -1187,7 +1272,8 @@ int fsck_chk_idnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode, goto skip; ret = fsck_chk_node_blk(sbi, inode, le32_to_cpu(node_blk->in.nid[i]), - ftype, TYPE_DIRECT_NODE, blk_cnt, child); + ftype, TYPE_DIRECT_NODE, blk_cnt, + cbc, child); if (!ret) *blk_cnt = *blk_cnt + 1; else if (ret == -EINVAL) { @@ -1217,7 +1303,7 @@ skip: int fsck_chk_didnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode, enum FILE_TYPE ftype, struct f2fs_node *node_blk, u32 *blk_cnt, - struct child_info *child) + struct f2fs_compr_blk_cnt *cbc, struct child_info *child) { int i = 0; int need_fix = 0, ret = 0; @@ -1229,7 +1315,7 @@ int fsck_chk_didnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode, goto skip; ret = fsck_chk_node_blk(sbi, inode, le32_to_cpu(node_blk->in.nid[i]), - ftype, TYPE_INDIRECT_NODE, blk_cnt, child); + ftype, TYPE_INDIRECT_NODE, blk_cnt, cbc, child); if (!ret) *blk_cnt = *blk_cnt + 1; else if (ret == -EINVAL) { @@ -1298,10 +1384,12 @@ void pretty_print_filename(const u8 *raw_name, u32 len, out[len] = 0; } -static void print_dentry(__u32 depth, __u8 *name, +static void print_dentry(struct f2fs_sb_info *sbi, __u8 *name, u8 *bitmap, struct f2fs_dir_entry *dentry, int max, int idx, int last_blk, int enc_name) { + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + u32 depth = fsck->dentry_depth; int last_de = 0; int next_idx = 0; u32 name_len; @@ -1309,7 +1397,7 @@ static void print_dentry(__u32 depth, __u8 *name, int bit_offset; char new[F2FS_PRINT_NAMELEN]; - if (!c.show_dentry) + if (!c.show_dentry && !c.show_file_map) return; name_len = le16_to_cpu(dentry[idx].name_len); @@ -1334,15 +1422,31 @@ static void print_dentry(__u32 depth, __u8 *name, if (tree_mark[depth - 1] == '`') tree_mark[depth - 1] = ' '; - for (i = 1; i < depth; i++) - printf("%c ", tree_mark[i]); - pretty_print_filename(name, name_len, new, enc_name); - printf("%c-- %s <ino = 0x%x>, <encrypted (%d)>\n", + if (c.show_file_map) { + struct f2fs_dentry *d = fsck->dentry; + + if (dentry[idx].file_type != F2FS_FT_REG_FILE) + return; + + while (d) { + if (d->depth > 1) + printf("/%s", d->name); + d = d->next; + } + printf("/%s", new); + if (dump_node(sbi, le32_to_cpu(dentry[idx].ino), 0)) + printf("\33[2K\r"); + } else { + for (i = 1; i < depth; i++) + printf("%c ", tree_mark[i]); + + printf("%c-- %s <ino = 0x%x>, <encrypted (%d)>\n", last_de ? '`' : '|', new, le32_to_cpu(dentry[idx].ino), enc_name); + } } static int f2fs_check_hash_code(int encoding, int casefolded, @@ -1474,6 +1578,7 @@ static int __chk_dentries(struct f2fs_sb_info *sbi, int casefolded, enum FILE_TYPE ftype; int dentries = 0; u32 blk_cnt; + struct f2fs_compr_blk_cnt cbc; u8 *name; char en[F2FS_PRINT_NAMELEN]; u16 name_len; @@ -1609,14 +1714,16 @@ static int __chk_dentries(struct f2fs_sb_info *sbi, int casefolded, le32_to_cpu(dentry[i].ino), dentry[i].file_type); - print_dentry(fsck->dentry_depth, name, bitmap, + print_dentry(sbi, name, bitmap, dentry, max, i, last_blk, enc_name); blk_cnt = 1; + cbc.cnt = 0; + cbc.cheader_pgofs = CHEADER_PGOFS_NONE; child->i_namelen = name_len; ret = fsck_chk_node_blk(sbi, NULL, le32_to_cpu(dentry[i].ino), - ftype, TYPE_INODE, &blk_cnt, child); + ftype, TYPE_INODE, &blk_cnt, &cbc, child); if (ret && c.fix_on) { int j; @@ -1645,6 +1752,8 @@ int fsck_chk_inline_dentries(struct f2fs_sb_info *sbi, struct f2fs_node *node_blk, struct child_info *child) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + struct f2fs_dentry *cur_dentry = fsck->dentry_end; + struct f2fs_dentry *new_dentry; struct f2fs_dentry_ptr d; void *inline_dentry; int dentries; @@ -1655,6 +1764,14 @@ int fsck_chk_inline_dentries(struct f2fs_sb_info *sbi, make_dentry_ptr(&d, node_blk, inline_dentry, 2); fsck->dentry_depth++; + new_dentry = calloc(sizeof(struct f2fs_dentry), 1); + ASSERT(new_dentry != NULL); + + new_dentry->depth = fsck->dentry_depth; + memcpy(new_dentry->name, child->p_name, F2FS_NAME_LEN); + cur_dentry->next = new_dentry; + fsck->dentry_end = new_dentry; + dentries = __chk_dentries(sbi, IS_CASEFOLDED(&node_blk->i), child, d.bitmap, d.dentry, d.filename, d.max, 1, file_is_encrypt(&node_blk->i));// pass through @@ -1667,6 +1784,10 @@ int fsck_chk_inline_dentries(struct f2fs_sb_info *sbi, fsck->dentry_depth, dentries, d.max, F2FS_NAME_LEN); } + fsck->dentry = cur_dentry; + fsck->dentry_end = cur_dentry; + cur_dentry->next = NULL; + free(new_dentry); fsck->dentry_depth--; return dentries; } @@ -1676,6 +1797,8 @@ int fsck_chk_dentry_blk(struct f2fs_sb_info *sbi, int casefolded, u32 blk_addr, { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct f2fs_dentry_block *de_blk; + struct f2fs_dentry *cur_dentry = fsck->dentry_end; + struct f2fs_dentry *new_dentry; int dentries, ret; de_blk = (struct f2fs_dentry_block *)calloc(BLOCK_SZ, 1); @@ -1685,6 +1808,13 @@ int fsck_chk_dentry_blk(struct f2fs_sb_info *sbi, int casefolded, u32 blk_addr, ASSERT(ret >= 0); fsck->dentry_depth++; + new_dentry = calloc(sizeof(struct f2fs_dentry), 1); + ASSERT(new_dentry != NULL); + new_dentry->depth = fsck->dentry_depth; + memcpy(new_dentry->name, child->p_name, F2FS_NAME_LEN); + cur_dentry->next = new_dentry; + fsck->dentry_end = new_dentry; + dentries = __chk_dentries(sbi, casefolded, child, de_blk->dentry_bitmap, de_blk->dentry, de_blk->filename, @@ -1701,6 +1831,10 @@ int fsck_chk_dentry_blk(struct f2fs_sb_info *sbi, int casefolded, u32 blk_addr, fsck->dentry_depth, blk_addr, dentries, NR_DENTRY_IN_BLOCK, F2FS_NAME_LEN); } + fsck->dentry = cur_dentry; + fsck->dentry_end = cur_dentry; + cur_dentry->next = NULL; + free(new_dentry); fsck->dentry_depth--; free(de_blk); return 0; @@ -1753,6 +1887,7 @@ int fsck_chk_data_blk(struct f2fs_sb_info *sbi, int casefolded, int fsck_chk_orphan_node(struct f2fs_sb_info *sbi) { u32 blk_cnt = 0; + struct f2fs_compr_blk_cnt cbc = {0, CHEADER_PGOFS_NONE}; block_t start_blk, orphan_blkaddr, i, j; struct f2fs_orphan_block *orphan_blk, *new_blk; struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); @@ -1784,6 +1919,8 @@ int fsck_chk_orphan_node(struct f2fs_sb_info *sbi) DBG(1, "[%3d] ino [0x%x]\n", i, ino); struct node_info ni; blk_cnt = 1; + cbc.cnt = 0; + cbc.cheader_pgofs = CHEADER_PGOFS_NONE; if (c.preen_mode == PREEN_MODE_1 && !c.fix_on) { get_node_info(sbi, ino, &ni); @@ -1799,7 +1936,7 @@ int fsck_chk_orphan_node(struct f2fs_sb_info *sbi) ret = fsck_chk_node_blk(sbi, NULL, ino, F2FS_FT_ORPHAN, TYPE_INODE, &blk_cnt, - NULL); + &cbc, NULL); if (!ret) new_blk->ino[new_entry_count++] = orphan_blk->ino[j]; @@ -1829,6 +1966,7 @@ int fsck_chk_quota_node(struct f2fs_sb_info *sbi) enum quota_type qtype; int ret = 0; u32 blk_cnt = 0; + struct f2fs_compr_blk_cnt cbc = {0, CHEADER_PGOFS_NONE}; for (qtype = 0; qtype < F2FS_MAX_QUOTAS; qtype++) { cur_qtype = qtype; @@ -1839,6 +1977,8 @@ int fsck_chk_quota_node(struct f2fs_sb_info *sbi) DBG(1, "qtype [%d] ino [0x%x]\n", qtype, ino); blk_cnt = 1; + cbc.cnt = 0; + cbc.cheader_pgofs = CHEADER_PGOFS_NONE; if (c.preen_mode == PREEN_MODE_1 && !c.fix_on) { get_node_info(sbi, ino, &ni); @@ -1848,11 +1988,14 @@ int fsck_chk_quota_node(struct f2fs_sb_info *sbi) continue; } ret = fsck_chk_node_blk(sbi, NULL, ino, - F2FS_FT_REG_FILE, TYPE_INODE, &blk_cnt, NULL); + F2FS_FT_REG_FILE, TYPE_INODE, &blk_cnt, + &cbc, NULL); if (ret) { ASSERT_MSG("wrong quota inode, qtype [%d] ino [0x%x]", qtype, ino); qf_szchk_type[qtype] = QF_SZCHK_ERR; + if (c.fix_on) + f2fs_rebuild_qf_inode(sbi, qtype); } } cur_qtype = -1; @@ -1893,7 +2036,7 @@ int fsck_chk_quota_files(struct f2fs_sb_info *sbi) f2fs_filesize_update(sbi, ino, 0); ret = quota_write_inode(sbi, qtype); if (!ret) { - c.bug_on = 1; + c.quota_fixed = true; DBG(1, "OK\n"); } else { ASSERT_MSG("Unable to write quota file"); @@ -2061,6 +2204,12 @@ void fsck_init(struct f2fs_sb_info *sbi) ASSERT(tree_mark_size != 0); tree_mark = calloc(tree_mark_size, 1); ASSERT(tree_mark != NULL); + fsck->dentry = calloc(sizeof(struct f2fs_dentry), 1); + ASSERT(fsck->dentry != NULL); + memcpy(fsck->dentry->name, "/", 1); + fsck->dentry_end = fsck->dentry; + + c.quota_fixed = false; } static void fix_hard_links(struct f2fs_sb_info *sbi) @@ -2753,6 +2902,7 @@ static int fsck_reconnect_file(struct f2fs_sb_info *sbi) struct node_info ni; char *reconnect_bitmap; u32 blk_cnt; + struct f2fs_compr_blk_cnt cbc; nid_t nid; int err, cnt = 0, ftype; @@ -2796,8 +2946,10 @@ static int fsck_reconnect_file(struct f2fs_sb_info *sbi) DBG(1, "Check inode 0x%x\n", nid); blk_cnt = 1; + cbc.cnt = 0; + cbc.cheader_pgofs = CHEADER_PGOFS_NONE; fsck_chk_inode_blk(sbi, nid, ftype, node, - &blk_cnt, &ni, NULL); + &blk_cnt, &cbc, &ni, NULL); f2fs_set_bit(nid, reconnect_bitmap); } @@ -3021,6 +3173,10 @@ int fsck_verify(struct f2fs_sb_info *sbi) u32 nr_unref_nid = 0; struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct hard_link_node *node = NULL; + bool verify_failed = false; + + if (c.show_file_map) + return 0; printf("\n"); @@ -3031,7 +3187,7 @@ int fsck_verify(struct f2fs_sb_info *sbi) } else { printf(" [Fail] [0x%x]\n", fsck->chk.wp_inconsistent_zones); - c.bug_on = 1; + verify_failed = true; } if (fsck->chk.wp_fixed && c.fix_on) @@ -3077,8 +3233,7 @@ int fsck_verify(struct f2fs_sb_info *sbi) printf(" [Ok..] [0x%x]\n", nr_unref_nid); } else { printf(" [Fail] [0x%x]\n", nr_unref_nid); - ret = EXIT_ERR_CODE; - c.bug_on = 1; + verify_failed = true; } printf("[FSCK] SIT valid block bitmap checking "); @@ -3087,8 +3242,7 @@ int fsck_verify(struct f2fs_sb_info *sbi) printf("[Ok..]\n"); } else { printf("[Fail]\n"); - ret = EXIT_ERR_CODE; - c.bug_on = 1; + verify_failed = true; } printf("[FSCK] Hard link checking for regular file "); @@ -3096,8 +3250,7 @@ int fsck_verify(struct f2fs_sb_info *sbi) printf(" [Ok..] [0x%x]\n", fsck->chk.multi_hard_link_files); } else { printf(" [Fail] [0x%x]\n", fsck->chk.multi_hard_link_files); - ret = EXIT_ERR_CODE; - c.bug_on = 1; + verify_failed = true; } printf("[FSCK] valid_block_count matching with CP "); @@ -3105,8 +3258,7 @@ int fsck_verify(struct f2fs_sb_info *sbi) printf(" [Ok..] [0x%x]\n", (u32)fsck->chk.valid_blk_cnt); } else { printf(" [Fail] [0x%x]\n", (u32)fsck->chk.valid_blk_cnt); - ret = EXIT_ERR_CODE; - c.bug_on = 1; + verify_failed = true; } printf("[FSCK] valid_node_count matching with CP (de lookup) "); @@ -3114,8 +3266,7 @@ int fsck_verify(struct f2fs_sb_info *sbi) printf(" [Ok..] [0x%x]\n", fsck->chk.valid_node_cnt); } else { printf(" [Fail] [0x%x]\n", fsck->chk.valid_node_cnt); - ret = EXIT_ERR_CODE; - c.bug_on = 1; + verify_failed = true; } printf("[FSCK] valid_node_count matching with CP (nat lookup)"); @@ -3123,8 +3274,7 @@ int fsck_verify(struct f2fs_sb_info *sbi) printf(" [Ok..] [0x%x]\n", fsck->chk.valid_nat_entry_cnt); } else { printf(" [Fail] [0x%x]\n", fsck->chk.valid_nat_entry_cnt); - ret = EXIT_ERR_CODE; - c.bug_on = 1; + verify_failed = true; } printf("[FSCK] valid_inode_count matched with CP "); @@ -3132,8 +3282,7 @@ int fsck_verify(struct f2fs_sb_info *sbi) printf(" [Ok..] [0x%x]\n", fsck->chk.valid_inode_cnt); } else { printf(" [Fail] [0x%x]\n", fsck->chk.valid_inode_cnt); - ret = EXIT_ERR_CODE; - c.bug_on = 1; + verify_failed = true; } printf("[FSCK] free segment_count matched with CP "); @@ -3142,8 +3291,7 @@ int fsck_verify(struct f2fs_sb_info *sbi) printf(" [Ok..] [0x%x]\n", fsck->chk.sit_free_segs); } else { printf(" [Fail] [0x%x]\n", fsck->chk.sit_free_segs); - ret = EXIT_ERR_CODE; - c.bug_on = 1; + verify_failed = true; } printf("[FSCK] next block offset is free "); @@ -3151,8 +3299,7 @@ int fsck_verify(struct f2fs_sb_info *sbi) printf(" [Ok..]\n"); } else { printf(" [Fail]\n"); - ret = EXIT_ERR_CODE; - c.bug_on = 1; + verify_failed = true; } printf("[FSCK] fixing SIT types\n"); @@ -3167,6 +3314,11 @@ int fsck_verify(struct f2fs_sb_info *sbi) ret = EXIT_ERR_CODE; } + if (verify_failed) { + ret = EXIT_ERR_CODE; + c.bug_on = 1; + } + #ifndef WITH_ANDROID if (nr_unref_nid && !c.ro) { char ans[255] = {0}; @@ -3188,7 +3340,7 @@ int fsck_verify(struct f2fs_sb_info *sbi) if (force || (c.fix_on && f2fs_dev_is_writable())) { struct f2fs_checkpoint *cp = F2FS_CKPT(sbi); - if (force || c.bug_on || c.bug_nat_bits) { + if (force || c.bug_on || c.bug_nat_bits || c.quota_fixed) { /* flush nats to write_nit_bits below */ flush_journal_entries(sbi); fix_hard_links(sbi); @@ -3229,4 +3381,11 @@ void fsck_free(struct f2fs_sb_info *sbi) if (tree_mark) free(tree_mark); + + while (fsck->dentry) { + struct f2fs_dentry *dentry = fsck->dentry; + + fsck->dentry = fsck->dentry->next; + free(dentry); + } } diff --git a/fsck/fsck.h b/fsck/fsck.h index b9dcd5c..ce5fffe 100644 --- a/fsck/fsck.h +++ b/fsck/fsck.h @@ -72,18 +72,26 @@ struct child_info { u32 pgofs; u8 dots; u8 dir_level; - u32 p_ino; /*parent ino*/ - u32 pp_ino; /*parent parent ino*/ + u32 p_ino; /* parent ino */ + char p_name[F2FS_NAME_LEN + 1]; /* parent name */ + u32 pp_ino; /* parent parent ino*/ struct extent_info ei; u32 last_blk; u32 i_namelen; /* dentry namelen */ }; +struct f2fs_dentry { + char name[F2FS_NAME_LEN + 1]; + int depth; + struct f2fs_dentry *next; +}; + struct f2fs_fsck { struct f2fs_sb_info sbi; struct orphan_info orphani; struct chk_result { + u64 checked_node_cnt; u64 valid_blk_cnt; u32 valid_nat_entry_cnt; u32 valid_node_cnt; @@ -110,6 +118,8 @@ struct f2fs_fsck { u32 nr_nat_entries; u32 dentry_depth; + struct f2fs_dentry *dentry; + struct f2fs_dentry *dentry_end; struct f2fs_nat_entry *entries; u32 nat_valid_inode_cnt; @@ -160,16 +170,20 @@ extern int fsck_sanity_check_nid(struct f2fs_sb_info *, u32, struct node_info *); extern int fsck_chk_node_blk(struct f2fs_sb_info *, struct f2fs_inode *, u32, enum FILE_TYPE, enum NODE_TYPE, u32 *, - struct child_info *); + struct f2fs_compr_blk_cnt *, struct child_info *); extern void fsck_chk_inode_blk(struct f2fs_sb_info *, u32, enum FILE_TYPE, - struct f2fs_node *, u32 *, struct node_info *, struct child_info *); + struct f2fs_node *, u32 *, struct f2fs_compr_blk_cnt *, + struct node_info *, struct child_info *); extern int fsck_chk_dnode_blk(struct f2fs_sb_info *, struct f2fs_inode *, u32, enum FILE_TYPE, struct f2fs_node *, u32 *, - struct child_info *, struct node_info *); + struct f2fs_compr_blk_cnt *, struct child_info *, + struct node_info *); extern int fsck_chk_idnode_blk(struct f2fs_sb_info *, struct f2fs_inode *, - enum FILE_TYPE, struct f2fs_node *, u32 *, struct child_info *); + enum FILE_TYPE, struct f2fs_node *, u32 *, + struct f2fs_compr_blk_cnt *, struct child_info *); extern int fsck_chk_didnode_blk(struct f2fs_sb_info *, struct f2fs_inode *, - enum FILE_TYPE, struct f2fs_node *, u32 *, struct child_info *); + enum FILE_TYPE, struct f2fs_node *, u32 *, + struct f2fs_compr_blk_cnt *, struct child_info *); extern int fsck_chk_data_blk(struct f2fs_sb_info *, int, u32, struct child_info *, int, enum FILE_TYPE, u32, u16, u8, int); extern int fsck_chk_dentry_blk(struct f2fs_sb_info *, int, @@ -253,7 +267,7 @@ struct dump_option { extern void nat_dump(struct f2fs_sb_info *, nid_t, nid_t); extern void sit_dump(struct f2fs_sb_info *, unsigned int, unsigned int); extern void ssa_dump(struct f2fs_sb_info *, int, int); -extern void dump_node(struct f2fs_sb_info *, nid_t, int); +extern int dump_node(struct f2fs_sb_info *, nid_t, int); extern int dump_info_from_blkaddr(struct f2fs_sb_info *, u32); extern unsigned int start_bidx_of_node(unsigned int, struct f2fs_node *); @@ -277,6 +291,7 @@ void f2fs_alloc_nid(struct f2fs_sb_info *, nid_t *); void set_data_blkaddr(struct dnode_of_data *); block_t new_node_block(struct f2fs_sb_info *, struct dnode_of_data *, unsigned int); +int f2fs_rebuild_qf_inode(struct f2fs_sb_info *sbi, int qtype); /* segment.c */ struct quota_file; diff --git a/fsck/main.c b/fsck/main.c index c07be1e..e4cfdf4 100644 --- a/fsck/main.c +++ b/fsck/main.c @@ -72,8 +72,9 @@ void fsck_usage() MSG(0, " -f check/fix entire partition\n"); MSG(0, " -g add default options\n"); MSG(0, " -l show superblock/checkpoint\n"); + MSG(0, " -M show a file map\n"); MSG(0, " -O feature1[feature2,feature3,...] e.g. \"encrypt\"\n"); - MSG(0, " -p preen mode [default:0 the same as -a [0|1]]\n"); + MSG(0, " -p preen mode [default:0 the same as -a [0|1|2]]\n"); MSG(0, " -S sparse_mode\n"); MSG(0, " -t show directory tree\n"); MSG(0, " -q preserve quota limits\n"); @@ -93,6 +94,7 @@ void dump_usage() MSG(0, " -d debug level [default:0]\n"); MSG(0, " -i inode no (hex)\n"); MSG(0, " -n [NAT dump nid from #1~#2 (decimal), for all 0~-1]\n"); + MSG(0, " -M show a block map\n"); MSG(0, " -s [SIT dump segno from #1~#2 (decimal), for all 0~-1]\n"); MSG(0, " -S sparse_mode\n"); MSG(0, " -a [SSA dump segno from #1~#2 (decimal), for all 0~-1]\n"); @@ -149,7 +151,8 @@ void sload_usage() MSG(0, " * -i or -x: use it many times for multiple extensions.\n"); MSG(0, " * -i and -x cannot be used together..\n"); MSG(0, " -m <num> min compressed blocks per cluster\n"); - MSG(0, " -r readonly (IMMUTABLE) for compressed files\n"); + MSG(0, " -r read only (to release unused blocks) for compressed " + "files\n"); MSG(0, " ------------------------------------------------------\n"); MSG(0, " -d debug level [default:0]\n"); MSG(0, " -V print the version number and exit\n"); @@ -227,7 +230,7 @@ void f2fs_parse_options(int argc, char *argv[]) } if (!strcmp("fsck.f2fs", prog)) { - const char *option_string = ":aC:c:m:d:fg:lO:p:q:StyV"; + const char *option_string = ":aC:c:m:Md:fg:lO:p:q:StyV"; int opt = 0, val; char *token; struct option long_opt[] = { @@ -277,6 +280,9 @@ void f2fs_parse_options(int argc, char *argv[]) case 'l': c.layout = 1; break; + case 'M': + c.show_file_map = 1; + break; case 'O': if (parse_feature(feature_table, optarg)) fsck_usage(); @@ -376,7 +382,7 @@ void f2fs_parse_options(int argc, char *argv[]) } } else if (!strcmp("dump.f2fs", prog)) { #ifdef WITH_DUMP - const char *option_string = "d:i:n:s:Sa:b:V"; + const char *option_string = "d:i:n:Ms:Sa:b:V"; static struct dump_option dump_opt = { .nid = 0, /* default root ino */ .start_nat = -1, @@ -423,6 +429,9 @@ void f2fs_parse_options(int argc, char *argv[]) &dump_opt.start_nat, &dump_opt.end_nat); break; + case 'M': + c.show_file_map = 1; + break; case 's': ret = sscanf(optarg, "%d~%d", &dump_opt.start_sit, @@ -650,7 +659,7 @@ void f2fs_parse_options(int argc, char *argv[]) } c.compress.min_blocks = val; break; - case 'r': /* compress file to set IMMUTABLE */ + case 'r': /* for setting FI_COMPRESS_RELEASED */ c.compress.required = true; c.compress.readonly = true; break; @@ -811,6 +820,7 @@ static int do_fsck(struct f2fs_sb_info *sbi) struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); u32 flag = le32_to_cpu(ckpt->ckpt_flags); u32 blk_cnt; + struct f2fs_compr_blk_cnt cbc; errcode_t ret; fsck_init(sbi); @@ -858,6 +868,8 @@ static int do_fsck(struct f2fs_sb_info *sbi) /* Traverse all block recursively from root inode */ blk_cnt = 1; + cbc.cnt = 0; + cbc.cheader_pgofs = CHEADER_PGOFS_NONE; if (c.feature & cpu_to_le32(F2FS_FEATURE_QUOTA_INO)) { ret = quota_init_context(sbi); @@ -868,7 +880,7 @@ static int do_fsck(struct f2fs_sb_info *sbi) } fsck_chk_orphan_node(sbi); fsck_chk_node_blk(sbi, NULL, sbi->root_ino_num, - F2FS_FT_DIR, TYPE_INODE, &blk_cnt, NULL); + F2FS_FT_DIR, TYPE_INODE, &blk_cnt, &cbc, NULL); fsck_chk_quota_files(sbi); ret = fsck_verify(sbi); @@ -1108,7 +1120,7 @@ int main(int argc, char **argv) } /* Get device */ - if (f2fs_get_device_info() < 0) { + if (f2fs_get_device_info() < 0 || f2fs_get_f2fs_info() < 0) { ret = -1; if (c.func == FSCK) ret = FSCK_OPERATIONAL_ERROR; @@ -1208,7 +1220,8 @@ retry: if (c.func == SLOAD) c.compress.filter_ops->destroy(); - printf("\nDone: %lf secs\n", (get_boottime_ns() - start) / 1000000000.0); + if (!c.show_file_map) + printf("\nDone: %lf secs\n", (get_boottime_ns() - start) / 1000000000.0); return ret; out_err: diff --git a/fsck/mount.c b/fsck/mount.c index de692b6..c928a15 100644 --- a/fsck/mount.c +++ b/fsck/mount.c @@ -13,6 +13,7 @@ #include "xattr.h" #include <locale.h> #include <stdbool.h> +#include <time.h> #ifdef HAVE_LINUX_POSIX_ACL_H #include <linux/posix_acl.h> #endif @@ -424,7 +425,7 @@ printout: DISP_u32(sb, meta_ino); DISP_u32(sb, cp_payload); DISP_u32(sb, crc); - DISP("%-.256s", sb, version); + DISP("%-.252s", sb, version); printf("\n"); } @@ -489,6 +490,9 @@ printout: void print_cp_state(u32 flag) { + if (c.show_file_map) + return; + MSG(0, "Info: checkpoint state = %x : ", flag); if (flag & CP_QUOTA_NEED_FSCK_FLAG) MSG(0, "%s", " quota_need_fsck"); @@ -579,14 +583,6 @@ void print_sb_state(struct f2fs_super_block *sb) MSG(0, "\n"); } -static inline bool is_valid_data_blkaddr(block_t blkaddr) -{ - if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR || - blkaddr == COMPRESS_ADDR) - return 0; - return 1; -} - bool f2fs_is_valid_blkaddr(struct f2fs_sb_info *sbi, block_t blkaddr, int type) { @@ -944,6 +940,8 @@ int sanity_check_raw_super(struct f2fs_super_block *sb, enum SB_ADDR sb_addr) return 0; } +#define CHECK_PERIOD (3600 * 24 * 30) // one month by default + int validate_super_block(struct f2fs_sb_info *sbi, enum SB_ADDR sb_addr) { char buf[F2FS_BLKSIZE]; @@ -961,31 +959,65 @@ int validate_super_block(struct f2fs_sb_info *sbi, enum SB_ADDR sb_addr) if (!sanity_check_raw_super(sbi->raw_super, sb_addr)) { /* get kernel version */ if (c.kd >= 0) { - dev_read_version(c.version, 0, VERSION_LEN); + dev_read_version(c.version, 0, VERSION_NAME_LEN); get_kernel_version(c.version); } else { get_kernel_uname_version(c.version); } /* build sb version */ - memcpy(c.sb_version, sbi->raw_super->version, VERSION_LEN); + memcpy(c.sb_version, sbi->raw_super->version, VERSION_NAME_LEN); get_kernel_version(c.sb_version); - memcpy(c.init_version, sbi->raw_super->init_version, VERSION_LEN); + memcpy(c.init_version, sbi->raw_super->init_version, + VERSION_NAME_LEN); get_kernel_version(c.init_version); MSG(0, "Info: MKFS version\n \"%s\"\n", c.init_version); MSG(0, "Info: FSCK version\n from \"%s\"\n to \"%s\"\n", c.sb_version, c.version); +#if defined(__APPLE__) if (!c.no_kernel_check && - memcmp(c.sb_version, c.version, VERSION_LEN)) { + memcmp(c.sb_version, c.version, VERSION_NAME_LEN)) { c.auto_fix = 0; c.fix_on = 1; - } - if (c.fix_on) { memcpy(sbi->raw_super->version, - c.version, VERSION_LEN); + c.version, VERSION_NAME_LEN); update_superblock(sbi->raw_super, SB_MASK(sb_addr)); } +#else + if (!c.no_kernel_check) { + struct timespec t; + u32 prev_time, cur_time, time_diff; + __le32 *ver_ts_ptr = (__le32 *)(sbi->raw_super->version + + VERSION_NAME_LEN); + + t.tv_sec = t.tv_nsec = 0; + clock_gettime(CLOCK_REALTIME, &t); + cur_time = (u32)t.tv_sec; + prev_time = le32_to_cpu(*ver_ts_ptr); + + MSG(0, "Info: version timestamp cur: %u, prev: %u\n", + cur_time, prev_time); + if (!memcmp(c.sb_version, c.version, + VERSION_NAME_LEN)) { + /* valid prev_time */ + if (prev_time != 0 && cur_time > prev_time) { + time_diff = cur_time - prev_time; + if (time_diff < CHECK_PERIOD) + goto out; + c.auto_fix = 0; + c.fix_on = 1; + } + } else { + memcpy(sbi->raw_super->version, + c.version, VERSION_NAME_LEN); + } + + *ver_ts_ptr = cpu_to_le32(cur_time); + update_superblock(sbi->raw_super, SB_MASK(sb_addr)); + } +out: +#endif print_sb_state(sbi->raw_super); return 0; } diff --git a/fsck/node.c b/fsck/node.c index c7988cb..c3e383b 100644 --- a/fsck/node.c +++ b/fsck/node.c @@ -40,6 +40,53 @@ void f2fs_release_nid(struct f2fs_sb_info *sbi, nid_t nid) f2fs_clear_bit(nid, nm_i->nid_bitmap); } +int f2fs_rebuild_qf_inode(struct f2fs_sb_info *sbi, int qtype) +{ + struct f2fs_node *raw_node = NULL; + struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); + struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); + struct f2fs_summary sum; + struct node_info ni; + nid_t ino = QUOTA_INO(sb, qtype); + block_t blkaddr = NULL_ADDR; + __u64 cp_ver = cur_cp_version(ckpt); + int ret = 0; + + raw_node = calloc(F2FS_BLKSIZE, 1); + if (raw_node == NULL) { + MSG(1, "\tError: Calloc Failed for raw_node!!!\n"); + return -ENOMEM; + } + f2fs_init_qf_inode(sb, raw_node, qtype, time(NULL)); + + if (is_set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG)) + cp_ver |= (cur_cp_crc(ckpt) << 32); + raw_node->footer.cp_ver = cpu_to_le64(cp_ver); + + get_node_info(sbi, ino, &ni); + set_summary(&sum, ino, 0, ni.version); + ret = reserve_new_block(sbi, &blkaddr, &sum, CURSEG_HOT_NODE, 1); + if (ret) { + MSG(1, "\tError: Failed to reserve new block!\n"); + goto err_out; + } + + ret = write_inode(raw_node, blkaddr); + if (ret < 0) { + MSG(1, "\tError: While rebuilding the quota inode to disk!\n"); + goto err_out; + } + update_nat_blkaddr(sbi, ino, ino, blkaddr); + + f2fs_clear_bit(ino, F2FS_FSCK(sbi)->nat_area_bitmap); + f2fs_set_bit(ino, NM_I(sbi)->nid_bitmap); + DBG(1, "Rebuild quota inode ([%3d] ino [0x%x]) at offset:0x%x\n", + qtype, ino, blkaddr); +err_out: + free(raw_node); + return ret; +} + void set_data_blkaddr(struct dnode_of_data *dn) { __le32 *addr_array; diff --git a/fsck/segment.c b/fsck/segment.c index 0156690..fe63615 100644 --- a/fsck/segment.c +++ b/fsck/segment.c @@ -450,6 +450,109 @@ u64 f2fs_fix_mutable(struct f2fs_sb_info *sbi, nid_t ino, pgoff_t offset, return 0; } +static inline int is_consecutive(u32 prev_addr, u32 cur_addr) +{ + if (is_valid_data_blkaddr(cur_addr) && (cur_addr == prev_addr + 1)) + return 1; + return 0; +} + +static inline void copy_extent_info(struct extent_info *t_ext, + struct extent_info *s_ext) +{ + t_ext->fofs = s_ext->fofs; + t_ext->blk = s_ext->blk; + t_ext->len = s_ext->len; +} + +static inline void update_extent_info(struct f2fs_node *inode, + struct extent_info *ext) +{ + inode->i.i_ext.fofs = cpu_to_le32(ext->fofs); + inode->i.i_ext.blk_addr = cpu_to_le32(ext->blk); + inode->i.i_ext.len = cpu_to_le32(ext->len); +} + +static void update_largest_extent(struct f2fs_sb_info *sbi, nid_t ino) +{ + struct dnode_of_data dn; + struct node_info ni; + struct f2fs_node *inode; + u32 blkaddr, prev_blkaddr, cur_blk = 0, end_blk; + struct extent_info largest_ext, cur_ext; + u64 remained_blkentries = 0; + u32 cluster_size; + int count; + void *index_node = NULL; + + memset(&dn, 0, sizeof(dn)); + largest_ext.len = cur_ext.len = 0; + + inode = (struct f2fs_node *) calloc(BLOCK_SZ, 1); + ASSERT(inode); + + /* Read inode info */ + get_node_info(sbi, ino, &ni); + ASSERT(dev_read_block(inode, ni.blk_addr) >= 0); + cluster_size = 1 << inode->i.i_log_cluster_size; + + if (inode->i.i_inline & F2FS_INLINE_DATA) + goto exit; + + end_blk = f2fs_max_file_offset(&inode->i) >> F2FS_BLKSIZE_BITS; + + while (cur_blk <= end_blk) { + if (remained_blkentries == 0) { + set_new_dnode(&dn, inode, NULL, ino); + get_dnode_of_data(sbi, &dn, cur_blk, LOOKUP_NODE); + if (index_node) + free(index_node); + index_node = (dn.node_blk == dn.inode_blk) ? + NULL : dn.node_blk; + remained_blkentries = ADDRS_PER_PAGE(sbi, + dn.node_blk, dn.inode_blk); + } + ASSERT(remained_blkentries > 0); + + blkaddr = datablock_addr(dn.node_blk, dn.ofs_in_node); + if (cur_ext.len > 0) { + if (is_consecutive(prev_blkaddr, blkaddr)) + cur_ext.len++; + else { + if (cur_ext.len > largest_ext.len) + copy_extent_info(&largest_ext, + &cur_ext); + cur_ext.len = 0; + } + } + + if (cur_ext.len == 0 && is_valid_data_blkaddr(blkaddr)) { + cur_ext.fofs = cur_blk; + cur_ext.len = 1; + cur_ext.blk = blkaddr; + } + + prev_blkaddr = blkaddr; + count = blkaddr == COMPRESS_ADDR ? cluster_size : 1; + cur_blk += count; + dn.ofs_in_node += count; + remained_blkentries -= count; + ASSERT(remained_blkentries >= 0); + } + +exit: + if (cur_ext.len > largest_ext.len) + copy_extent_info(&largest_ext, &cur_ext); + if (largest_ext.len > 0) { + update_extent_info(inode, &largest_ext); + ASSERT(write_inode(inode, ni.blk_addr) >= 0); + } + + if (index_node) + free(index_node); + free(inode); +} + int f2fs_build_file(struct f2fs_sb_info *sbi, struct dentry *de) { int fd, n; @@ -595,6 +698,8 @@ int f2fs_build_file(struct f2fs_sb_info *sbi, struct dentry *de) if (n < 0) return -1; + if (!c.compress.enabled || (c.feature & cpu_to_le32(F2FS_FEATURE_RO))) + update_largest_extent(sbi, de->ino); update_free_segments(sbi); MSG(1, "Info: Create %s -> %s\n" diff --git a/include/f2fs_fs.h b/include/f2fs_fs.h index 8969ae2..412130f 100644 --- a/include/f2fs_fs.h +++ b/include/f2fs_fs.h @@ -20,6 +20,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <time.h> #ifdef HAVE_CONFIG_H #include <config.h> #endif @@ -40,6 +41,9 @@ #include <inttypes.h> #ifdef HAVE_LINUX_TYPES_H +#ifndef __SANE_USERSPACE_TYPES__ +#define __SANE_USERSPACE_TYPES__ /* For PPC64, to get LL64 types */ +#endif #include <linux/types.h> #endif #include <sys/types.h> @@ -240,14 +244,14 @@ static inline uint64_t bswap_64(uint64_t val) #define MSG(n, fmt, ...) \ do { \ - if (c.dbg_lv >= n && !c.layout) { \ + if (c.dbg_lv >= n && !c.layout && !c.show_file_map) { \ printf(fmt, ##__VA_ARGS__); \ } \ } while (0) #define DBG(n, fmt, ...) \ do { \ - if (c.dbg_lv >= n && !c.layout) { \ + if (c.dbg_lv >= n && !c.layout && !c.show_file_map) { \ printf("[%s:%4d] " fmt, \ __func__, __LINE__, ##__VA_ARGS__); \ } \ @@ -350,7 +354,9 @@ static inline uint64_t bswap_64(uint64_t val) #define DEFAULT_BLOCKS_PER_SEGMENT 512 #define DEFAULT_SEGMENTS_PER_SECTION 1 -#define VERSION_LEN 256 +#define VERSION_LEN 256 +#define VERSION_TIMESTAMP_LEN 4 +#define VERSION_NAME_LEN (VERSION_LEN - VERSION_TIMESTAMP_LEN) #define LPF "lost+found" @@ -434,8 +440,8 @@ typedef struct { filter_ops *filter_ops; /* filter ops */ } compress_config_t; -#define ALIGN_UP(value, size) ((value) + ((value) % (size) > 0 ? \ - (size) - (value) % (size) : 0)) +#define ALIGN_DOWN(addrs, size) (((addrs) / (size)) * (size)) +#define ALIGN_UP(addrs, size) ALIGN_DOWN(((addrs) + (size) - 1), (size)) struct f2fs_configuration { u_int32_t reserved_segments; @@ -488,9 +494,12 @@ struct f2fs_configuration { int defset; int bug_on; int bug_nat_bits; + bool quota_fixed; int alloc_failed; int auto_fix; int layout; + int show_file_map; + u64 show_file_map_max_offset; int quota_fix; int preen_mode; int ro; @@ -498,6 +507,7 @@ struct f2fs_configuration { int large_nat_bitmap; int fix_chksum; /* fix old cp.chksum position */ __le32 feature; /* defined features */ + unsigned int quota_bits; /* quota bits */ time_t fixed_time; /* mkfs parameters */ @@ -937,6 +947,21 @@ struct f2fs_extent { #define IS_CASEFOLDED(dir) ((dir)->i_flags & F2FS_CASEFOLD_FL) /* + * fsck i_compr_blocks counting helper + */ +struct f2fs_compr_blk_cnt { + /* counting i_compr_blocks, init 0 */ + u32 cnt; + + /* + * previous seen compression header (COMPR_ADDR) page offsets, + * use CHEADER_PGOFS_NONE for none + */ + u32 cheader_pgofs; +}; +#define CHEADER_PGOFS_NONE ((u32)-(1 << MAX_COMPRESS_LOG_SIZE)) + +/* * inode flags */ #define F2FS_COMPR_FL 0x00000004 /* Compress file */ @@ -1298,6 +1323,7 @@ extern int utf16_to_utf8(char *, const u_int16_t *, size_t, size_t); extern int log_base_2(u_int32_t); extern unsigned int addrs_per_inode(struct f2fs_inode *); extern unsigned int addrs_per_block(struct f2fs_inode *); +extern unsigned int f2fs_max_file_offset(struct f2fs_inode *); extern __u32 f2fs_inode_chksum(struct f2fs_node *); extern __u32 f2fs_checkpoint_chksum(struct f2fs_checkpoint *); extern int write_inode(struct f2fs_node *, u64); @@ -1320,6 +1346,7 @@ extern int f2fs_devs_are_umounted(void); extern int f2fs_dev_is_writable(void); extern int f2fs_dev_is_umounted(char *); extern int f2fs_get_device_info(void); +extern int f2fs_get_f2fs_info(void); extern unsigned int calc_extra_isize(void); extern int get_device_info(int); extern int f2fs_init_sparse_file(void); @@ -1551,6 +1578,45 @@ static inline void show_version(const char *prog) #endif } +static inline void f2fs_init_qf_inode(struct f2fs_super_block *sb, + struct f2fs_node *raw_node, int qtype, time_t mtime) +{ + raw_node->footer.nid = sb->qf_ino[qtype]; + raw_node->footer.ino = sb->qf_ino[qtype]; + raw_node->footer.cp_ver = cpu_to_le64(1); + raw_node->i.i_mode = cpu_to_le16(0x8180); + raw_node->i.i_links = cpu_to_le32(1); + raw_node->i.i_uid = cpu_to_le32(c.root_uid); + raw_node->i.i_gid = cpu_to_le32(c.root_gid); + + raw_node->i.i_size = cpu_to_le64(1024 * 6); /* Hard coded */ + raw_node->i.i_blocks = cpu_to_le64(1); + + raw_node->i.i_atime = cpu_to_le32(mtime); + raw_node->i.i_atime_nsec = 0; + raw_node->i.i_ctime = cpu_to_le32(mtime); + raw_node->i.i_ctime_nsec = 0; + raw_node->i.i_mtime = cpu_to_le32(mtime); + raw_node->i.i_mtime_nsec = 0; + raw_node->i.i_generation = 0; + raw_node->i.i_xattr_nid = 0; + raw_node->i.i_flags = FS_IMMUTABLE_FL; + raw_node->i.i_current_depth = cpu_to_le32(0); + raw_node->i.i_dir_level = DEF_DIR_LEVEL; + + if (c.feature & cpu_to_le32(F2FS_FEATURE_EXTRA_ATTR)) { + raw_node->i.i_inline = F2FS_EXTRA_ATTR; + raw_node->i.i_extra_isize = cpu_to_le16(calc_extra_isize()); + } + + if (c.feature & cpu_to_le32(F2FS_FEATURE_PRJQUOTA)) + raw_node->i.i_projid = cpu_to_le32(F2FS_DEF_PROJID); + + raw_node->i.i_ext.fofs = 0; + raw_node->i.i_ext.blk_addr = 0; + raw_node->i.i_ext.len = 0; +} + struct feature { char *name; u32 mask; diff --git a/lib/libf2fs.c b/lib/libf2fs.c index 0add901..94fb91d 100644 --- a/lib/libf2fs.c +++ b/lib/libf2fs.c @@ -499,7 +499,6 @@ opaque_seq: return __f2fs_dentry_hash(name, len); } -#define ALIGN_DOWN(addrs, size) (((addrs) / (size)) * (size)) unsigned int addrs_per_inode(struct f2fs_inode *i) { unsigned int addrs = CUR_ADDRS_PER_INODE(i) - get_inline_xattr_addrs(i); @@ -518,6 +517,14 @@ unsigned int addrs_per_block(struct f2fs_inode *i) return ALIGN_DOWN(DEF_ADDRS_PER_BLOCK, 1 << i->i_log_cluster_size); } +unsigned int f2fs_max_file_offset(struct f2fs_inode *i) +{ + if (!LINUX_S_ISREG(le16_to_cpu(i->i_mode)) || + !(le32_to_cpu(i->i_flags) & F2FS_COMPR_FL)) + return le64_to_cpu(i->i_size); + return ALIGN_UP(le64_to_cpu(i->i_size), 1 << i->i_log_cluster_size); +} + /* * CRC32 */ @@ -819,7 +826,7 @@ int f2fs_devs_are_umounted(void) void get_kernel_version(__u8 *version) { int i; - for (i = 0; i < VERSION_LEN; i++) { + for (i = 0; i < VERSION_NAME_LEN; i++) { if (version[i] == '\n') break; } @@ -837,10 +844,10 @@ void get_kernel_uname_version(__u8 *version) #if defined(WITH_KERNEL_VERSION) snprintf((char *)version, - VERSION_LEN, "%s %s", buf.release, buf.version); + VERSION_NAME_LEN, "%s %s", buf.release, buf.version); #else snprintf((char *)version, - VERSION_LEN, "%s", buf.release); + VERSION_NAME_LEN, "%s", buf.release); #endif #else memset(version, 0, VERSION_LEN); @@ -945,7 +952,7 @@ int get_device_info(int i) c.kd = open("/proc/version", O_RDONLY); #endif if (c.kd < 0) { - MSG(0, "\tInfo: No support kernel version!\n"); + MSG(0, "Info: not exist /proc/version!\n"); c.kd = -2; } } @@ -1176,6 +1183,12 @@ int f2fs_get_device_info(void) for (i = 0; i < c.ndevs; i++) if (get_device_info(i)) return -1; + return 0; +} + +int f2fs_get_f2fs_info(void) +{ + int i; if (c.wanted_total_sectors < c.total_sectors) { MSG(0, "Info: total device sectors = %"PRIu64" (in %u bytes)\n", diff --git a/man/dump.f2fs.8 b/man/dump.f2fs.8 index eedba85..1ddb7fc 100644 --- a/man/dump.f2fs.8 +++ b/man/dump.f2fs.8 @@ -14,6 +14,10 @@ dump.f2fs \- retrieve directory and file entries from an F2FS-formated image .I NAT range ] [ +.B \-M +.I Block map +] +[ .B \-s .I SIT range ] @@ -51,6 +55,9 @@ Specify an inode number to dump out. .BI \-n " NAT range" Specify a range presented by nids to dump NAT entries. .TP +.BI \-M " Block map" +Show all the allocated block addresses given inode number. +.TP .BI \-s " SIT range" Specify a range presented by segment numbers to dump SIT entries. .TP diff --git a/man/fsck.f2fs.8 b/man/fsck.f2fs.8 index af1076c..aff4ff2 100644 --- a/man/fsck.f2fs.8 +++ b/man/fsck.f2fs.8 @@ -14,6 +14,10 @@ fsck.f2fs \- check a Linux F2FS file system .I enable force fix ] [ +.B \-M +.I show file map +] +[ .B \-p .I enable preen mode ] @@ -44,6 +48,9 @@ module. It is disabled by default. .BI \-f " enable force fix" Enable to fix all the inconsistency in the partition. .TP +.BI \-M " show files map" +Enable to show all the filenames and inode numbers stored in the image +.TP .BI \-p " enable preen mode" Same as "-a" to support general fsck convention. .TP diff --git a/mkfs/f2fs_format.c b/mkfs/f2fs_format.c index 3565bd3..dba0cec 100644 --- a/mkfs/f2fs_format.c +++ b/mkfs/f2fs_format.c @@ -38,8 +38,6 @@ struct f2fs_checkpoint *cp; /* Return time fixed by the user or current time by default */ #define mkfs_time ((c.fixed_time == -1) ? time(NULL) : c.fixed_time) -static unsigned int quotatype_bits = 0; - const char *media_ext_lists[] = { /* common prefix */ "mp", // Covers mp3, mp4, mpeg, mpg @@ -440,6 +438,21 @@ static int f2fs_prepare_super_block(void) main_blkzone); return -1; } + /* + * Check if conventional device has enough space + * to accommodate all metadata, zoned device should + * not overlap to metadata area. + */ + for (i = 1; i < c.ndevs; i++) { + if (c.devices[i].zoned_model == F2FS_ZONED_HM && + c.devices[i].start_blkaddr < get_sb(main_blkaddr)) { + MSG(0, "\tError: Conventional device %s is too small," + " (%"PRIu64" MiB needed).\n", c.devices[0].path, + (get_sb(main_blkaddr) - + c.devices[i].start_blkaddr) >> 8); + return -1; + } + } } total_zones = get_sb(segment_count) / (c.segs_per_zone) - @@ -498,14 +511,8 @@ static int f2fs_prepare_super_block(void) set_sb(root_ino, 3); c.next_free_nid = 4; - if (c.feature & cpu_to_le32(F2FS_FEATURE_QUOTA_INO)) { - quotatype_bits = QUOTA_USR_BIT | QUOTA_GRP_BIT; - if (c.feature & cpu_to_le32(F2FS_FEATURE_PRJQUOTA)) - quotatype_bits |= QUOTA_PRJ_BIT; - } - for (qtype = 0; qtype < F2FS_MAX_QUOTAS; qtype++) { - if (!((1 << qtype) & quotatype_bits)) + if (!((1 << qtype) & c.quota_bits)) continue; sb->qf_ino[qtype] = cpu_to_le32(c.next_free_nid++); MSG(0, "Info: add quota type = %u => %u\n", @@ -571,10 +578,10 @@ static int f2fs_prepare_super_block(void) if (c.kd >= 0) { dev_read_version(c.version, 0, VERSION_LEN); get_kernel_version(c.version); - MSG(0, "Info: format version with\n \"%s\"\n", c.version); } else { get_kernel_uname_version(c.version); } + MSG(0, "Info: format version with\n \"%s\"\n", c.version); memcpy(sb->version, c.version, VERSION_LEN); memcpy(sb->init_version, c.version, VERSION_LEN); @@ -852,7 +859,7 @@ static int f2fs_write_check_point_pack(void) get_cp(cur_node_segno[0]) * c.blks_per_seg); for (qtype = 0, i = 1; qtype < F2FS_MAX_QUOTAS; qtype++) { - if (sb->qf_ino[qtype] == 0) + if (!((1 << qtype) & c.quota_bits)) continue; journal->nat_j.entries[i].nid = sb->qf_ino[qtype]; journal->nat_j.entries[i].ne.version = 0; @@ -943,10 +950,11 @@ static int f2fs_write_check_point_pack(void) off = 1; for (qtype = 0; qtype < F2FS_MAX_QUOTAS; qtype++) { - if (sb->qf_ino[qtype] == 0) - continue; int j; + if (!((1 << qtype) & c.quota_bits)) + continue; + for (j = 0; j < QUOTA_DATA(qtype); j++) { (sum_entry + off + j)->nid = sb->qf_ino[qtype]; (sum_entry + off + j)->ofs_in_node = cpu_to_le16(j); @@ -977,7 +985,7 @@ static int f2fs_write_check_point_pack(void) sum->entries[0].nid = sb->root_ino; sum->entries[0].ofs_in_node = 0; for (qtype = i = 0; qtype < F2FS_MAX_QUOTAS; qtype++) { - if (sb->qf_ino[qtype] == 0) + if (!((1 << qtype) & c.quota_bits)) continue; sum->entries[1 + i].nid = sb->qf_ino[qtype]; sum->entries[1 + i].ofs_in_node = 0; @@ -1345,7 +1353,7 @@ static int f2fs_write_default_quota(int qtype, unsigned int blkaddr, return 0; } -static int f2fs_write_qf_inode(int qtype) +static int f2fs_write_qf_inode(int qtype, int offset) { struct f2fs_node *raw_node = NULL; u_int64_t data_blk_nor; @@ -1358,49 +1366,18 @@ static int f2fs_write_qf_inode(int qtype) MSG(1, "\tError: Calloc Failed for raw_node!!!\n"); return -1; } + f2fs_init_qf_inode(sb, raw_node, qtype, mkfs_time); - raw_node->footer.nid = sb->qf_ino[qtype]; - raw_node->footer.ino = sb->qf_ino[qtype]; - raw_node->footer.cp_ver = cpu_to_le64(1); raw_node->footer.next_blkaddr = cpu_to_le32( get_sb(main_blkaddr) + c.cur_seg[CURSEG_HOT_NODE] * c.blks_per_seg + 1 + qtype + 1); - - raw_node->i.i_mode = cpu_to_le16(0x8180); - raw_node->i.i_links = cpu_to_le32(1); - raw_node->i.i_uid = cpu_to_le32(c.root_uid); - raw_node->i.i_gid = cpu_to_le32(c.root_gid); - - raw_node->i.i_size = cpu_to_le64(1024 * 6); /* Hard coded */ raw_node->i.i_blocks = cpu_to_le64(1 + QUOTA_DATA(qtype)); - raw_node->i.i_atime = cpu_to_le32(mkfs_time); - raw_node->i.i_atime_nsec = 0; - raw_node->i.i_ctime = cpu_to_le32(mkfs_time); - raw_node->i.i_ctime_nsec = 0; - raw_node->i.i_mtime = cpu_to_le32(mkfs_time); - raw_node->i.i_mtime_nsec = 0; - raw_node->i.i_generation = 0; - raw_node->i.i_xattr_nid = 0; - raw_node->i.i_flags = FS_IMMUTABLE_FL; - raw_node->i.i_current_depth = cpu_to_le32(0); - raw_node->i.i_dir_level = DEF_DIR_LEVEL; - - if (c.feature & cpu_to_le32(F2FS_FEATURE_EXTRA_ATTR)) { - raw_node->i.i_inline = F2FS_EXTRA_ATTR; - raw_node->i.i_extra_isize = cpu_to_le16(calc_extra_isize()); - } - - if (c.feature & cpu_to_le32(F2FS_FEATURE_PRJQUOTA)) - raw_node->i.i_projid = cpu_to_le32(F2FS_DEF_PROJID); - data_blk_nor = get_sb(main_blkaddr) + - c.cur_seg[CURSEG_HOT_DATA] * c.blks_per_seg + 1; + c.cur_seg[CURSEG_HOT_DATA] * c.blks_per_seg + 1 + + offset * QUOTA_DATA(i); - for (i = 0; i < qtype; i++) - if (sb->qf_ino[i]) - data_blk_nor += QUOTA_DATA(i); if (qtype == 0) raw_id = raw_node->i.i_uid; else if (qtype == 1) @@ -1419,13 +1396,10 @@ static int f2fs_write_qf_inode(int qtype) for (i = 0; i < QUOTA_DATA(qtype); i++) raw_node->i.i_addr[get_extra_isize(raw_node) + i] = cpu_to_le32(data_blk_nor + i); - raw_node->i.i_ext.fofs = 0; - raw_node->i.i_ext.blk_addr = 0; - raw_node->i.i_ext.len = 0; main_area_node_seg_blk_offset = get_sb(main_blkaddr); main_area_node_seg_blk_offset += c.cur_seg[CURSEG_HOT_NODE] * - c.blks_per_seg + qtype + 1; + c.blks_per_seg + offset + 1; DBG(1, "\tWriting quota inode (hot node), %x %x %x at offset 0x%08"PRIu64"\n", get_sb(main_blkaddr), @@ -1457,7 +1431,7 @@ static int f2fs_update_nat_root(void) /* update quota */ for (qtype = i = 0; qtype < F2FS_MAX_QUOTAS; qtype++) { - if (sb->qf_ino[qtype] == 0) + if (!((1 << qtype) & c.quota_bits)) continue; nat_blk->entries[sb->qf_ino[qtype]].block_addr = cpu_to_le32(get_sb(main_blkaddr) + @@ -1695,7 +1669,7 @@ static int f2fs_add_default_dentry_root(void) static int f2fs_create_root_dir(void) { enum quota_type qtype; - int err = 0; + int err = 0, i = 0; err = f2fs_write_root_inode(); if (err < 0) { @@ -1704,9 +1678,9 @@ static int f2fs_create_root_dir(void) } for (qtype = 0; qtype < F2FS_MAX_QUOTAS; qtype++) { - if (sb->qf_ino[qtype] == 0) + if (!((1 << qtype) & c.quota_bits)) continue; - err = f2fs_write_qf_inode(qtype); + err = f2fs_write_qf_inode(qtype, i++); if (err < 0) { MSG(1, "\tError: Failed to write quota inode!!!\n"); goto exit; diff --git a/mkfs/f2fs_format_main.c b/mkfs/f2fs_format_main.c index 03eb748..bf78756 100644 --- a/mkfs/f2fs_format_main.c +++ b/mkfs/f2fs_format_main.c @@ -12,6 +12,7 @@ #include <stdlib.h> #include <fcntl.h> #include <string.h> +#include <stdbool.h> #include <unistd.h> #include <sys/stat.h> #ifndef ANDROID_WINDOWS_HOST @@ -24,12 +25,16 @@ #include "config.h" #ifdef HAVE_LIBBLKID -# include <blkid.h> +#include <blkid.h> #endif #include "f2fs_fs.h" +#include "quota.h" #include "f2fs_format_utils.h" +#ifdef HAVE_SYS_UTSNAME_H +#include <sys/utsname.h> +#endif #ifdef WITH_ANDROID #include <sparse/sparse.h> extern struct sparse_file *f2fs_sparse_file; @@ -74,7 +79,7 @@ static void mkfs_usage() static void f2fs_show_info() { - MSG(0, "\n\tF2FS-tools: mkfs.f2fs Ver: %s (%s)\n\n", + MSG(0, "\n F2FS-tools: mkfs.f2fs Ver: %s (%s)\n\n", F2FS_TOOLS_VERSION, F2FS_TOOLS_DATE); if (c.heap == 0) @@ -103,18 +108,50 @@ static void f2fs_show_info() MSG(0, "Info: Enable Compression\n"); } +#if defined(ANDROID_TARGET) && defined(HAVE_SYS_UTSNAME_H) +static bool kernel_version_over(unsigned int min_major, unsigned int min_minor) +{ + unsigned int major, minor; + struct utsname uts; + + if ((uname(&uts) != 0) || + (sscanf(uts.release, "%u.%u", &major, &minor) != 2)) + return false; + if (major > min_major) + return true; + if (major == min_major && minor >= min_minor) + return true; + return false; +} +#else +static bool kernel_version_over(unsigned int UNUSED(min_major), + unsigned int UNUSED(min_minor)) +{ + return false; +} +#endif + static void add_default_options(void) { switch (c.defset) { case CONF_ANDROID: - /* -d1 -f -O encrypt -O quota -O verity -w 4096 -R 0:0 */ + /* -d1 -f -w 4096 -R 0:0 */ c.dbg_lv = 1; force_overwrite = 1; - c.feature |= cpu_to_le32(F2FS_FEATURE_ENCRYPT); - c.feature |= cpu_to_le32(F2FS_FEATURE_QUOTA_INO); - c.feature |= cpu_to_le32(F2FS_FEATURE_VERITY); c.wanted_sector_size = 4096; c.root_uid = c.root_gid = 0; + + /* RO doesn't need any other features */ + if (c.feature & cpu_to_le32(F2FS_FEATURE_RO)) + return; + + /* -O encrypt -O project_quota,extra_attr,{quota} -O verity */ + c.feature |= cpu_to_le32(F2FS_FEATURE_ENCRYPT); + if (!kernel_version_over(4, 14)) + c.feature |= cpu_to_le32(F2FS_FEATURE_QUOTA_INO); + c.feature |= cpu_to_le32(F2FS_FEATURE_PRJQUOTA); + c.feature |= cpu_to_le32(F2FS_FEATURE_EXTRA_ATTR); + c.feature |= cpu_to_le32(F2FS_FEATURE_VERITY); break; } #ifdef CONF_CASEFOLD @@ -122,9 +159,17 @@ static void add_default_options(void) c.feature |= cpu_to_le32(F2FS_FEATURE_CASEFOLD); #endif #ifdef CONF_PROJID + c.feature |= cpu_to_le32(F2FS_FEATURE_QUOTA_INO); c.feature |= cpu_to_le32(F2FS_FEATURE_PRJQUOTA); c.feature |= cpu_to_le32(F2FS_FEATURE_EXTRA_ATTR); #endif + + if (c.feature & cpu_to_le32(F2FS_FEATURE_QUOTA_INO)) + c.quota_bits = QUOTA_USR_BIT | QUOTA_GRP_BIT; + if (c.feature & cpu_to_le32(F2FS_FEATURE_PRJQUOTA)) { + c.feature |= cpu_to_le32(F2FS_FEATURE_QUOTA_INO); + c.quota_bits |= QUOTA_PRJ_BIT; + } } static void f2fs_parse_options(int argc, char *argv[]) @@ -391,18 +436,42 @@ int main(int argc, char *argv[]) c.func = MKFS; - if (!force_overwrite && f2fs_check_overwrite()) { - MSG(0, "\tUse the -f option to force overwrite.\n"); - return -1; - } - if (f2fs_devs_are_umounted() < 0) { if (errno != EBUSY) MSG(0, "\tError: Not available on mounted device!\n"); - return -1; + goto err_format; } if (f2fs_get_device_info() < 0) + return -1; + + if (f2fs_check_overwrite()) { + char *zero_buf = NULL; + int i; + + if (!force_overwrite) { + MSG(0, "\tUse the -f option to force overwrite.\n"); + goto err_format; + } + zero_buf = calloc(F2FS_BLKSIZE, 1); + if (!zero_buf) { + MSG(0, "\tError: Fail to allocate zero buffer.\n"); + goto err_format; + } + /* wipe out other FS magics mostly first 4MB space */ + for (i = 0; i < 1024; i++) + if (dev_fill_block(zero_buf, i)) + break; + free(zero_buf); + if (i != 1024) { + MSG(0, "\tError: Fail to fill zeros till %d.\n", i); + goto err_format; + } + if (f2fs_fsync_device()) + goto err_format; + } + + if (f2fs_get_f2fs_info() < 0) goto err_format; /* diff --git a/tools/f2fs_io/f2fs_io.c b/tools/f2fs_io/f2fs_io.c index aa1a7e4..af4a34b 100644 --- a/tools/f2fs_io/f2fs_io.c +++ b/tools/f2fs_io/f2fs_io.c @@ -496,16 +496,19 @@ static void do_erase(int argc, char **argv, const struct cmd_desc *cmd) #define write_desc "write data into file" #define write_help \ -"f2fs_io write [chunk_size in 4kb] [offset in chunk_size] [count] [pattern] [IO] [file_path]\n\n" \ +"f2fs_io write [chunk_size in 4kb] [offset in chunk_size] [count] [pattern] [IO] [file_path] {delay}\n\n" \ "Write given patten data in file_path\n" \ "pattern can be\n" \ -" zero : zeros\n" \ -" inc_num : incrementing numbers\n" \ -" rand : random numbers\n" \ +" zero : zeros\n" \ +" inc_num : incrementing numbers\n" \ +" rand : random numbers\n" \ "IO can be\n" \ -" buffered : buffered IO\n" \ -" dio : direct IO\n" \ -" osync : O_SYNC\n" \ +" buffered : buffered IO\n" \ +" dio : direct IO\n" \ +" osync : O_SYNC\n" \ +" atomic_commit : atomic write & commit\n" \ +" atomic_abort : atomic write & abort\n" \ +"{delay} is in ms unit and optional only for atomic_commit and atomic_abort\n" static void do_write(int argc, char **argv, const struct cmd_desc *cmd) { @@ -516,10 +519,12 @@ static void do_write(int argc, char **argv, const struct cmd_desc *cmd) int flags = 0; int fd; u64 total_time = 0, max_time = 0, max_time_t = 0; + bool atomic_commit = false, atomic_abort = false; + int useconds = 0; srand(time(0)); - if (argc != 7) { + if (argc < 7 || argc > 8) { fputs("Excess arguments\n\n", stderr); fputs(cmd->cmd_help, stderr); exit(1); @@ -545,11 +550,26 @@ static void do_write(int argc, char **argv, const struct cmd_desc *cmd) flags |= O_DIRECT; else if (!strcmp(argv[5], "osync")) flags |= O_SYNC; + else if (!strcmp(argv[5], "atomic_commit")) + atomic_commit = true; + else if (!strcmp(argv[5], "atomic_abort")) + atomic_abort = true; else if (strcmp(argv[5], "buffered")) die("Wrong IO type"); fd = xopen(argv[6], O_CREAT | O_WRONLY | flags, 0755); + if (atomic_commit || atomic_abort) { + if (argc == 8) + useconds = atoi(argv[7]) * 1000; + + ret = ioctl(fd, F2FS_IOC_START_ATOMIC_WRITE); + if (ret < 0) { + fputs("setting atomic file mode failed\n", stderr); + exit(1); + } + } + total_time = get_current_us(); for (i = 0; i < count; i++) { if (!strcmp(argv[4], "inc_num")) @@ -568,6 +588,23 @@ static void do_write(int argc, char **argv, const struct cmd_desc *cmd) written += ret; } + if (useconds) + usleep(useconds); + + if (atomic_commit) { + ret = ioctl(fd, F2FS_IOC_COMMIT_ATOMIC_WRITE); + if (ret < 0) { + fputs("committing atomic write failed\n", stderr); + exit(1); + } + } else if (atomic_abort) { + ret = ioctl(fd, F2FS_IOC_ABORT_VOLATILE_WRITE); + if (ret < 0) { + fputs("aborting atomic write failed\n", stderr); + exit(1); + } + } + printf("Written %"PRIu64" bytes with pattern=%s, total_time=%"PRIu64" us, max_latency=%"PRIu64" us\n", written, argv[4], get_current_us() - total_time, @@ -731,11 +768,11 @@ static void do_randread(int argc, char **argv, const struct cmd_desc *cmd) #if defined(HAVE_LINUX_FIEMAP_H) && defined(HAVE_LINUX_FS_H) static void do_fiemap(int argc, char **argv, const struct cmd_desc *cmd) { - unsigned count, i; - int fd; - __u64 phy_addr; - struct fiemap *fm = xmalloc(sizeof(struct fiemap) + - sizeof(struct fiemap_extent)); + unsigned int i; + int fd, extents_mem_size; + u64 start, length; + u32 mapped_extents; + struct fiemap *fm = xmalloc(sizeof(struct fiemap)); if (argc != 4) { fputs("Excess arguments\n\n", stderr); @@ -743,25 +780,40 @@ static void do_fiemap(int argc, char **argv, const struct cmd_desc *cmd) exit(1); } - fm->fm_start = atoi(argv[1]) * F2FS_BLKSIZE; - fm->fm_length = F2FS_BLKSIZE; - fm->fm_extent_count = 1; - count = atoi(argv[2]); + memset(fm, 0, sizeof(struct fiemap)); + start = atoi(argv[1]) * F2FS_BLKSIZE; + length = atoi(argv[2]) * F2FS_BLKSIZE; + fm->fm_start = start; + fm->fm_length = length; fd = xopen(argv[3], O_RDONLY | O_LARGEFILE, 0); - printf("Fiemap: offset = %08"PRIx64" len = %d\n", - (u64)fm->fm_start / F2FS_BLKSIZE, count); - for (i = 0; i < count; i++) { - if (ioctl(fd, FS_IOC_FIEMAP, fm) < 0) - die_errno("FIEMAP failed"); - - phy_addr = fm->fm_extents[0].fe_physical / F2FS_BLKSIZE; - if (phy_addr == NEW_ADDR) - printf("NEW_ADDR "); - else - printf("%llu ", phy_addr); - fm->fm_start += F2FS_BLKSIZE; + printf("Fiemap: offset = %"PRIu64" len = %"PRIu64"\n", + start / F2FS_BLKSIZE, length / F2FS_BLKSIZE); + if (ioctl(fd, FS_IOC_FIEMAP, fm) < 0) + die_errno("FIEMAP failed"); + + mapped_extents = fm->fm_mapped_extents; + extents_mem_size = sizeof(struct fiemap_extent) * mapped_extents; + free(fm); + fm = xmalloc(sizeof(struct fiemap) + extents_mem_size); + + memset(fm, 0, sizeof(struct fiemap) + extents_mem_size); + fm->fm_start = start; + fm->fm_length = length; + fm->fm_extent_count = mapped_extents; + + if (ioctl(fd, FS_IOC_FIEMAP, fm) < 0) + die_errno("FIEMAP failed"); + + printf("\t%-17s%-17s%-17s%s\n", "logical addr.", "physical addr.", "length", "flags"); + for (i = 0; i < fm->fm_mapped_extents; i++) { + printf("%d\t%.16llx %.16llx %.16llx %.8x\n", i, + fm->fm_extents[i].fe_logical, fm->fm_extents[i].fe_physical, + fm->fm_extents[i].fe_length, fm->fm_extents[i].fe_flags); + + if (fm->fm_extents[i].fe_flags & FIEMAP_EXTENT_LAST) + break; } printf("\n"); free(fm); @@ -1168,6 +1220,41 @@ static void do_get_filename_encrypt_mode (int argc, char **argv, exit(0); } +#define rename_desc "rename source to target file with fsync option" +#define rename_help \ +"f2fs_io rename [src_path] [target_path] [fsync_after_rename]\n\n" \ +"e.g., f2fs_io rename source dest 1\n" \ +" 1. open(source)\n" \ +" 2. rename(source, dest)\n" \ +" 3. fsync(source)\n" \ +" 4. close(source)\n" + +static void do_rename(int argc, char **argv, const struct cmd_desc *cmd) +{ + int fd = -1; + int ret; + + if (argc != 4) { + fputs("Excess arguments\n\n", stderr); + fputs(cmd->cmd_help, stderr); + exit(1); + } + + if (atoi(argv[3])) + fd = xopen(argv[1], O_WRONLY, 0); + + ret = rename(argv[1], argv[2]); + if (ret < 0) + die_errno("rename failed"); + + if (fd >= 0) { + if (fsync(fd) != 0) + die_errno("fsync failed: %s", argv[1]); + close(fd); + } + exit(0); +} + #define CMD_HIDDEN 0x0001 #define CMD(name) { #name, do_##name, name##_desc, name##_help, 0 } #define _CMD(name) { #name, do_##name, NULL, NULL, CMD_HIDDEN } @@ -1198,6 +1285,7 @@ const struct cmd_desc cmd_list[] = { CMD(decompress), CMD(compress), CMD(get_filename_encrypt_mode), + CMD(rename), { NULL, NULL, NULL, NULL, 0 } }; |