diff --git a/bundle.json b/bundle.json index 8bd4b649a25a4ff0e1f9d3bc72a88702f9f5e3bc..11af7fde062e10f947cf5e02e8878fe408cced17 100644 --- a/bundle.json +++ b/bundle.json @@ -2,7 +2,7 @@ "name": "@ohos/f2fs-tools", "description": "The f2fs-tools package contains the utilities for handling the f2fs file system.", "version": "3.1", - "license": "GPL-2.0", + "license": "GNU GPL-2.0", "publishAs": "code-segment", "segment": { "destPath": "third_party/f2fs-tools" @@ -24,16 +24,20 @@ "rom": "", "ram": "", "deps": { - "components": [], - "third_party": [ - "e2fsprogs" - ] + "components": [ "bounds_checking_function" ], + "third_party": [ "e2fsprogs" ] }, "build": { "sub_component": [], "inner_kits": [ { - "name": "//third_party/f2fs-tools/lib:libf2fs" + "name": "//third_party/f2fs-tools/lib:libf2fs", + "header": { + "header_files": [ + "utf8data.h" + ], + "header_base": "//third_party/f2fs-tools/lib" + } }, { "name": "//third_party/f2fs-tools/fsck:fsck.f2fs" diff --git a/config.h b/config.h index af3eb1ffcbedbc0695e1337d01fca87a742c5821..dba0f076a64c9ff84a57b127bca37ba7d6cd0391 100644 --- a/config.h +++ b/config.h @@ -18,10 +18,10 @@ #define F2FS_MINOR_VERSION 14 /* f2fs-tools date based on Source releases */ -#define F2FS_TOOLS_DATE "2023-04-11" +#define F2FS_TOOLS_DATE "2022-05-13" /* f2fs-tools version */ -#define F2FS_TOOLS_VERSION "v1.16.0" +#define F2FS_TOOLS_VERSION "1.15.0" /* Define to 1 if you have the `add_key' function. */ /* #undef HAVE_ADD_KEY */ @@ -73,7 +73,7 @@ /* #define HAVE_LIBSELINUX 1 */ /* Define to 1 if you have the header file. */ -#define HAVE_LINUX_BLKZONED_H 1 +/* #define HAVE_LINUX_BLKZONED_H 1 */ /* Define to 1 if you have the header file. */ #define HAVE_LINUX_FALLOC_H 1 @@ -198,9 +198,9 @@ /* Version number of package */ #define VERSION "1.15.0" -#define HAVE_UUID_UUID_H 1 +#define HAVE_CLOCK_GETTIME 1 -#define HAVE_LIBUUID 1 +#define HAVE_CLOCK_BOOTTIME 1 #define WITH_BLKDISCARD 1 diff --git a/fsck/BUILD.gn b/fsck/BUILD.gn index 4b233f18305642f5802eadec2bb21e4afac5a0b0..cd6ee5505698586193fe1d4201262ed695a0b8ae 100644 --- a/fsck/BUILD.gn +++ b/fsck/BUILD.gn @@ -16,6 +16,7 @@ config("f2fs-defaults") { "-Wno-unused-parameter", "-Wno-incompatible-pointer-types", ] + ldflags = [ "-lpthread" ] } ################################################### @@ -23,8 +24,11 @@ config("f2fs-defaults") { ohos_executable("fsck.f2fs") { configs = [ ":f2fs-defaults" ] sources = [ + "../lib/extra_fsck.c", "../tools/debug_tools/fsck_debug.c", + "../tools/hmfs_tools/hmfs_tools.c", "compress.c", + "dedup.c", "defrag.c", "dict.c", "dir.c", @@ -41,11 +45,13 @@ ohos_executable("fsck.f2fs") { "segment.c", "sload.c", "xattr.c", + "queue.c" ] include_dirs = [ ".", "../tools/debug_tools", + "../tools/hmfs_tools", "//third_party/f2fs-tools", "//third_party/f2fs-tools/include", "//third_party/f2fs-tools/lib", @@ -53,8 +59,8 @@ ohos_executable("fsck.f2fs") { deps = [ "//third_party/f2fs-tools/lib:libf2fs" ] - public_external_deps = [ - "e2fsprogs:e2fsdroid", + external_deps = [ + "bounds_checking_function:libsec_static", "e2fsprogs:libdacconfig", ] diff --git a/fsck/Makefile.am b/fsck/Makefile.am index 579dd26cd6c9c1486e3465019e9b70834de67986..1f5e854c09b4559c189ade975851d75280b4b791 100644 --- a/fsck/Makefile.am +++ b/fsck/Makefile.am @@ -4,11 +4,12 @@ AM_CPPFLAGS = ${libuuid_CFLAGS} -I$(top_srcdir)/include AM_CFLAGS = -Wall sbin_PROGRAMS = fsck.f2fs noinst_HEADERS = common.h dict.h dqblk_v2.h f2fs.h fsck.h node.h quotaio.h \ - quotaio_tree.h quotaio_v2.h xattr.h compress.h + quotaio_tree.h quotaio_v2.h xattr.h compress.h dedup.h include_HEADERS = $(top_srcdir)/include/quota.h fsck_f2fs_SOURCES = main.c fsck.c dump.c mount.c defrag.c resize.c \ node.c segment.c dir.c sload.c xattr.c compress.c \ - dict.c mkquota.c quotaio.c quotaio_tree.c quotaio_v2.c + dict.c mkquota.c quotaio.c quotaio_tree.c quotaio_v2.c \ + dedup.c fsck_f2fs_LDADD = ${libselinux_LIBS} ${libuuid_LIBS} \ ${liblzo2_LIBS} ${liblz4_LIBS} ${libwinpthread_LIBS} \ $(top_builddir)/lib/libf2fs.la diff --git a/fsck/dedup.c b/fsck/dedup.c new file mode 100644 index 0000000000000000000000000000000000000000..8a937c4512cfc7a1417ccda8b9d0287d192080b0 --- /dev/null +++ b/fsck/dedup.c @@ -0,0 +1,415 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. + * + * dedup.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "dedup.h" +#include "quotaio.h" + +static const int IDX_DIRECT_NODE_0 = 0; +static const int IDX_DIRECT_NODE_1 = 1; +static const int IDX_INDIRECT_NODE_0 = 2; +static const int IDX_INDIRECT_NODE_1 = 3; +static const int IDX_DOUBLE_INDIRECT_NODE = 4; +static const int IDX_NODE_SIZE = 5; + +static void add_into_dedup_inner_list(struct f2fs_sb_info *sbi, nid_t nid, + bool is_valid, u32 link_cnt) +{ + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + struct dedup_inner_node *node = NULL, *tmp = NULL, *prev = NULL; + + node = calloc(sizeof(struct dedup_inner_node), 1); + ASSERT(node != NULL); + + node->nid = nid; + node->links = link_cnt; + node->actual_links = 0; + node->is_valid = is_valid; + node->next = NULL; + + if (fsck->dedup_inner_list_head == NULL) { + fsck->dedup_inner_list_head = node; + goto out; + } + + tmp = fsck->dedup_inner_list_head; + + /* Find insertion position */ + while (tmp && (nid < tmp->nid)) { + ASSERT(tmp->nid != nid); + prev = tmp; + tmp = tmp->next; + } + + if (tmp == fsck->dedup_inner_list_head) { + node->next = tmp; + fsck->dedup_inner_list_head = node; + } else { + prev->next = node; + prev->next = tmp; + } +out: + DBG(2, "inner ino[0x%x] has dedup links [0x%x]\n", nid, link_cnt); +} + +static bool inode_dedup_feature_check(struct f2fs_node *node_blk) +{ + if ((c.feature & cpu_to_le32(F2FS_FEATURE_DEDUP)) && + f2fs_has_extra_isize(&node_blk->i) && + F2FS_FITS_IN_INODE(le16_to_cpu(node_blk->i.i_extra_isize), + i_inner_ino, sizeof(node_blk->i.i_extra_isize))) { + return true; + } + return false; +} + +bool f2fs_is_deduped_inode(struct f2fs_node *node_blk) +{ + if ((node_blk->footer.nid == node_blk->footer.ino) && + inode_dedup_feature_check(node_blk) && + (node_blk->i.i_dedup_flags & F2FS_DEDUPED_FL)) { + return true; + } + return false; +} + +bool f2fs_is_inner_inode(struct f2fs_node *node_blk) +{ + if ((node_blk->footer.nid == node_blk->footer.ino) && + inode_dedup_feature_check(node_blk) && + (node_blk->i.i_dedup_flags & F2FS_INNER_FL)) { + return true; + } + return false; +} + +bool f2fs_is_out_inode(struct f2fs_node *node_blk) +{ + if (f2fs_is_deduped_inode(node_blk) && + !f2fs_is_inner_inode(node_blk)) { + return true; + } + return false; +} + +bool f2fs_is_revoke_inode(struct f2fs_node *node_blk) +{ + if (f2fs_is_deduped_inode(node_blk) && + (node_blk->i.i_dedup_flags & F2FS_REVOKE_FL)) { + return true; + } + return false; +} + +static bool f2fs_is_deduping_inode(struct f2fs_node *node_blk) +{ + if (f2fs_is_deduped_inode(node_blk) && + (node_blk->i.i_dedup_flags & F2FS_DOING_DEDUP_FL)) { + return true; + } + return false; +} + +bool f2fs_is_unstable_dedup_inode(struct f2fs_node *node_blk) +{ + if (f2fs_is_revoke_inode(node_blk) || + f2fs_is_deduping_inode(node_blk)) { + return true; + } + return false; +} + +nid_t f2fs_get_dedup_inner_ino(struct f2fs_node *node_blk) +{ + return node_blk->i.i_inner_ino; +} + +static struct dedup_inner_node *find_dedup_inner_node(struct f2fs_sb_info *sbi, nid_t nid) +{ + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + struct dedup_inner_node *node = NULL; + + if (fsck->dedup_inner_list_head == NULL) { + return NULL; + } + + node = fsck->dedup_inner_list_head; + + while (node && (nid < node->nid)) { + node = node->next; + } + + if (node == NULL || (nid != node->nid)) { + return NULL; + } + + return node; +} + +void f2fs_inc_inner_actual_links(struct f2fs_sb_info *sbi, nid_t inner_ino) +{ + struct dedup_inner_node *node = find_dedup_inner_node(sbi, inner_ino); + + if (node != NULL) { + node->actual_links++; + } +} + +bool f2fs_sanity_check_dedup_inner_nid(struct f2fs_sb_info *sbi, nid_t inner_ino) +{ + struct dedup_inner_node *dedup_inner_node = NULL; + u32 i_links; + bool is_inner_inode_valid = true; + struct node_info ni; + struct f2fs_node *node_blk = NULL; + u32 blk_cnt; + struct f2fs_compr_blk_cnt cbc; + + if (!IS_VALID_NID(sbi, inner_ino)) { + return false; + } + + dedup_inner_node = find_dedup_inner_node(sbi, inner_ino); + /* have already checked, only need check once */ + if (dedup_inner_node) { + return dedup_inner_node->is_valid; + } + + node_blk = (struct f2fs_node *)calloc(BLOCK_SZ, 1); + ASSERT(node_blk != NULL); + + if (sanity_check_nid(sbi, inner_ino, node_blk, F2FS_FT_DEDUP_INNER, TYPE_INODE, &ni)) { + is_inner_inode_valid = false; + i_links = 0; + goto out; + } + + blk_cnt = 1; + cbc.cnt = 0; + cbc.cheader_pgofs = CHEADER_PGOFS_NONE; + fsck_chk_inode_blk(sbi, inner_ino, F2FS_FT_DEDUP_INNER, node_blk, &blk_cnt, &cbc, &ni, NULL); + i_links = le32_to_cpu(node_blk->i.i_links); +out: + add_into_dedup_inner_list(sbi, inner_ino, is_inner_inode_valid, i_links); + free(node_blk); + return is_inner_inode_valid; +} + +static void drop_node_blk(struct f2fs_sb_info *sbi, nid_t nid, enum NODE_TYPE ntype); +static void drop_inode_blk(struct f2fs_sb_info *sbi, struct f2fs_node *node_blk) +{ + unsigned int idx = 0; + int ofs; + block_t blkaddr; + nid_t i_nid; + enum NODE_TYPE ntype; + struct node_info xattr_ni; + nid_t x_nid = le32_to_cpu(node_blk->i.i_xattr_nid); + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + + /* drop xattr addr */ + if (x_nid != 0 && IS_VALID_NID(sbi, x_nid)) { + get_node_info(sbi, x_nid, &xattr_ni); + + if (f2fs_test_main_bitmap(sbi, xattr_ni.blk_addr) != 0) { + f2fs_clear_main_bitmap(sbi, xattr_ni.blk_addr); + fsck->chk.valid_blk_cnt--; + fsck->chk.valid_node_cnt--; + } + } + + ofs = get_extra_isize(node_blk); + /* drop data blocks in inode */ + for (idx = 0; idx < ADDRS_PER_INODE(&node_blk->i); idx++) { + blkaddr = le32_to_cpu(node_blk->i.i_addr[ofs + idx]); + if (IS_VALID_BLK_ADDR(sbi, blkaddr)) { + if (blkaddr == NULL_ADDR) { + continue; + } + + if (blkaddr == NEW_ADDR) { + fsck->chk.valid_blk_cnt--; + } else { + if (f2fs_test_main_bitmap(sbi, blkaddr) != 0) { + f2fs_clear_main_bitmap(sbi, blkaddr); + fsck->chk.valid_blk_cnt--; + } + } + } + } + + /* drop node blocks in inode */ + for (idx = IDX_DIRECT_NODE_0; idx < IDX_NODE_SIZE; idx++) { + i_nid = le32_to_cpu(node_blk->i.i_nid[idx]); + + if (idx == IDX_DIRECT_NODE_0 || idx == IDX_DIRECT_NODE_1) { + ntype = TYPE_DIRECT_NODE; + } else if (idx == IDX_INDIRECT_NODE_0 || idx == IDX_INDIRECT_NODE_1) { + ntype = TYPE_INDIRECT_NODE; + } else if (idx == IDX_DOUBLE_INDIRECT_NODE) { + ntype = TYPE_DOUBLE_INDIRECT_NODE; + } else { + ASSERT(0); + } + + if (i_nid == 0x0) { + continue; + } + drop_node_blk(sbi, i_nid, ntype); + } +} + +static void drop_dnode_blk(struct f2fs_sb_info *sbi, struct f2fs_node *node_blk) +{ + unsigned int idx; + block_t blkaddr; + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + + for (idx = 0; idx < ADDRS_PER_BLOCK(&node_blk->i); idx++) { + blkaddr = le32_to_cpu(node_blk->dn.addr[idx]); + if (IS_VALID_BLK_ADDR(sbi, blkaddr)) { + if (blkaddr == NULL_ADDR) { + continue; + } + + if (blkaddr == NEW_ADDR) { + fsck->chk.valid_blk_cnt--; + } else { + if (f2fs_test_main_bitmap(sbi, blkaddr) != 0) { + f2fs_clear_main_bitmap(sbi, blkaddr); + fsck->chk.valid_blk_cnt--; + } + } + } + } +} + +static void drop_idnode_blk(struct f2fs_sb_info *sbi, struct f2fs_node *node_blk) +{ + int i = 0; + nid_t nid; + + for (i = 0; i < NIDS_PER_BLOCK; i++) { + nid = le32_to_cpu(node_blk->in.nid[i]); + drop_node_blk(sbi, nid, TYPE_DIRECT_NODE); + } +} + +static void drop_didnode_blk(struct f2fs_sb_info *sbi, struct f2fs_node *node_blk) +{ + int i = 0; + nid_t nid; + + for (i = 0; i < NIDS_PER_BLOCK; i++) { + nid = le32_to_cpu(node_blk->in.nid[i]); + drop_node_blk(sbi, nid, TYPE_INDIRECT_NODE); + } +} + +static void drop_node_blk(struct f2fs_sb_info *sbi, nid_t nid, enum NODE_TYPE ntype) +{ + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + struct node_info ni; + struct f2fs_node *node_blk = NULL; + int ret; + + if (nid == 0 || !IS_VALID_NID(sbi, nid)) { + return; + } + + get_node_info(sbi, nid, &ni); + node_blk = (struct f2fs_node *)calloc(BLOCK_SZ, 1); + ASSERT(node_blk != NULL); + + ret = dev_read_block(node_blk, ni.blk_addr); + ASSERT(ret >= 0); + + if (f2fs_test_main_bitmap(sbi, ni.blk_addr) != 0) { + f2fs_clear_main_bitmap(sbi, ni.blk_addr); + fsck->chk.valid_blk_cnt--; + fsck->chk.valid_node_cnt--; + + if (ntype == TYPE_INODE) { + fsck->chk.valid_inode_cnt--; + } + } + + if (ntype == TYPE_INODE) { + drop_inode_blk(sbi, node_blk); + } else { + switch (ntype) { + case TYPE_DIRECT_NODE: + drop_dnode_blk(sbi, node_blk); + break; + case TYPE_INDIRECT_NODE: + drop_idnode_blk(sbi, node_blk); + break; + case TYPE_DOUBLE_INDIRECT_NODE: + drop_didnode_blk(sbi, node_blk); + break; + default: + ASSERT(0); + } + } + free(node_blk); +} + +void f2fs_fix_dedup_inner_list(struct f2fs_sb_info *sbi) +{ + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + struct dedup_inner_node *tmp, *node; + struct f2fs_node *node_blk = NULL; + struct node_info ni; + int ret; + + if (fsck->dedup_inner_list_head == NULL) { + return; + } + node = fsck->dedup_inner_list_head; + + node_blk = (struct f2fs_node *)calloc(BLOCK_SZ, 1); + ASSERT(node_blk != NULL); + while (node) { + if (node->is_valid) { + get_node_info(sbi, node->nid, &ni); + if (node->actual_links == 0) { + ASSERT_MSG("Inner inode: 0x%x i_links= 0x%x -> 0x%x", + node->nid, node->links, node->actual_links); + drop_node_blk(sbi, node->nid, TYPE_INODE); + } else { + ret = dev_read_block(node_blk, ni.blk_addr); + ASSERT(ret >= 0); + if (node->links != node->actual_links && c.fix_on && + f2fs_dev_is_writable()) { + node_blk->i.i_links = cpu_to_le32(node->actual_links); + FIX_MSG("Inner inode: 0x%x i_links= 0x%x -> 0x%x", + node->nid, node->links, node->actual_links); + + ret = dev_write_block(node_blk, ni.blk_addr); + ASSERT(ret >= 0); + } + quota_add_inode_usage(fsck->qctx, node->nid, &node_blk->i); + } + } + tmp = node; + node = node->next; + free(tmp); + } + free(node_blk); +} + +void f2fs_check_dedup_extent_info(struct child_info *child) +{ + struct extent_info *ei = &child->ei; + + if (!ei->len) { + return; + } + child->state |= FSCK_UNMATCHED_EXTENT; +} diff --git a/fsck/dedup.h b/fsck/dedup.h new file mode 100644 index 0000000000000000000000000000000000000000..d3938a47ba5192d51ca867b9408977c12c8ffe73 --- /dev/null +++ b/fsck/dedup.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. + * + * dedup.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _DEDUP_H_ +#define _DEDUP_H_ + +#include "f2fs_fs.h" +#include "fsck.h" + +#define F2FS_DEDUPED_FL 0x00000001 +#define F2FS_INNER_FL 0x00000002 +#define F2FS_REVOKE_FL 0x00000008 +#define F2FS_DOING_DEDUP_FL 0x00000010 + +struct dedup_inner_node { + u32 nid; + u32 links; + u32 actual_links; + bool is_valid; + struct dedup_inner_node *next; +}; + +bool f2fs_is_deduped_inode(struct f2fs_node *node_blk); +bool f2fs_is_inner_inode(struct f2fs_node *node_blk); +bool f2fs_is_out_inode(struct f2fs_node *node_blk); +bool f2fs_is_unstable_dedup_inode(struct f2fs_node *node_blk); +bool f2fs_is_revoke_inode(struct f2fs_node *node_blk); +nid_t f2fs_get_dedup_inner_ino(struct f2fs_node *node_blk); +bool f2fs_sanity_check_dedup_inner_nid(struct f2fs_sb_info *sbi, + nid_t inner_ino); +void f2fs_inc_inner_actual_links(struct f2fs_sb_info *sbi, nid_t inner_ino); +void f2fs_fix_dedup_inner_list(struct f2fs_sb_info *sbi); +void f2fs_check_dedup_extent_info(struct child_info *child); +#endif /* _DEDUP_H_ */ diff --git a/fsck/dump.c b/fsck/dump.c index b8f6144a7fdd231146dd9f648c73f3fc6ad52bad..5f44fae47cc1f4643c18f0158727886331d8c423 100644 --- a/fsck/dump.c +++ b/fsck/dump.c @@ -250,6 +250,7 @@ out: static void dump_data_blk(struct f2fs_sb_info *sbi, __u64 offset, u32 blkaddr) { char buf[F2FS_BLKSIZE]; + int ret; if (c.show_file_map) { if (c.show_file_map_max_offset < offset) { @@ -281,14 +282,13 @@ static void dump_data_blk(struct f2fs_sb_info *sbi, __u64 offset, u32 blkaddr) if (blkaddr == NEW_ADDR || !IS_VALID_BLK_ADDR(sbi, blkaddr)) { memset(buf, 0, F2FS_BLKSIZE); } else { - int ret; - ret = dev_read_block(buf, blkaddr); ASSERT(ret >= 0); } /* write blkaddr */ - dev_write_dump(buf, offset, F2FS_BLKSIZE); + ret = dev_write_dump(buf, offset, F2FS_BLKSIZE); + ASSERT(ret >= 0); } static void dump_node_blk(struct f2fs_sb_info *sbi, int ntype, @@ -298,6 +298,7 @@ static void dump_node_blk(struct f2fs_sb_info *sbi, int ntype, struct f2fs_node *node_blk; u32 skip = 0; u32 i, idx = 0; + int ret; switch (ntype) { case TYPE_DIRECT_NODE: @@ -323,7 +324,8 @@ static void dump_node_blk(struct f2fs_sb_info *sbi, int ntype, node_blk = calloc(BLOCK_SZ, 1); ASSERT(node_blk); - dev_read_block(node_blk, ni.blk_addr); + ret = dev_read_block(node_blk, ni.blk_addr); + ASSERT(ret >= 0); for (i = 0; i < idx; i++) { switch (ntype) { @@ -353,18 +355,28 @@ static void dump_node_blk(struct f2fs_sb_info *sbi, int ntype, static void dump_xattr(struct f2fs_sb_info *sbi, struct f2fs_node *node_blk) { void *xattr; + void *last_base_addr; struct f2fs_xattr_entry *ent; char xattr_name[F2FS_NAME_LEN] = {0}; + u64 max_total_xattr_size = XATTR_SIZE(&node_blk->i); int ret; - xattr = read_all_xattrs(sbi, node_blk); + xattr = read_all_xattrs(sbi, node_blk, true); if (!xattr) return; - list_for_each_xattr(ent, xattr) { + last_base_addr = (void *)xattr + XATTR_SIZE(&node_blk->i); + + list_for_each_xattr(ent, xattr, max_total_xattr_size) { char *name = strndup(ent->e_name, ent->e_name_len); void *value = ent->e_name + ent->e_name_len; + if ((void *)(ent) + sizeof(__u32) > last_base_addr || + (void *)XATTR_NEXT_ENTRY(ent) > last_base_addr) { + MSG(0, "xattr entry crosses the end of xattr space\n"); + break; + } + if (!name) continue; @@ -425,12 +437,14 @@ static int dump_inode_blk(struct f2fs_sb_info *sbi, u32 nid, u32 i = 0; u64 ofs = 0; u32 addr_per_block; + int ret; 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, + ret = dev_write_dump(((unsigned char *)node_blk) + INLINE_DATA_OFFSET, 0, MAX_INLINE_DATA(node_blk)); + ASSERT(ret >= 0); return -1; } @@ -473,7 +487,7 @@ 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; - u32 imode = le16_to_cpu(inode->i_mode); + u16 imode = le16_to_cpu(inode->i_mode); u32 namelen = le32_to_cpu(inode->i_namelen); char name[F2FS_NAME_LEN + 1] = {0}; char path[1024] = {0}; @@ -593,7 +607,8 @@ int dump_node(struct f2fs_sb_info *sbi, nid_t nid, int force) goto out; } - dev_read_block(node_blk, ni.blk_addr); + ret = dev_read_block(node_blk, ni.blk_addr); + ASSERT(ret >= 0); if (ni.blk_addr == 0x0) MSG(force, "Invalid nat entry\n\n"); diff --git a/fsck/f2fs.h b/fsck/f2fs.h index e65644e9ccf3b60389f41899b4c5f355b634c22f..6ad61c999e3c151cb7a889e5e8307fe5dda2ac32 100644 --- a/fsck/f2fs.h +++ b/fsck/f2fs.h @@ -42,6 +42,10 @@ typecheck(unsigned long long, b) && \ ((long long)((a) - (b)) > 0)) +#define POISON_POINTER_DELTA 0 +#define LIST_POISON1 ((void *) (0x00100100 + POISON_POINTER_DELTA)) +#define LIST_POISON2 ((void *) (0x00200200 + POISON_POINTER_DELTA)) + #define container_of(ptr, type, member) ({ \ const typeof(((type *)0)->member) * __mptr = (ptr); \ (type *)((char *)__mptr - offsetof(type, member)); }) @@ -69,6 +73,8 @@ static inline void __list_del(struct list_head * prev, struct list_head * next) static inline void list_del(struct list_head *entry) { __list_del(entry->prev, entry->next); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; } static inline void list_add_tail(struct list_head *new, struct list_head *head) @@ -76,6 +82,29 @@ static inline void list_add_tail(struct list_head *new, struct list_head *head) __list_add(new, head->prev, head); } +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +static inline void list_del_entry(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + list_del_entry(list); + list_add_tail(list, head); +} + #define LIST_HEAD_INIT(name) { &(name), &(name) } #define list_entry(ptr, type, member) \ @@ -98,6 +127,9 @@ static inline void list_add_tail(struct list_head *new, struct list_head *head) &pos->member != (head); \ pos = n, n = list_next_entry(n, member)) +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + /* * indicate meta/data type */ @@ -178,6 +210,7 @@ struct sit_info { struct curseg_info { struct f2fs_summary_block *sum_blk; /* cached summary block */ + struct f2fs_journal *journal; /* cached journal info */ unsigned char alloc_type; /* current allocation type */ unsigned int segno; /* current segment number */ unsigned short next_blkoff; /* next block offset to write */ diff --git a/fsck/fsck.c b/fsck/fsck.c index 143b1dae4f4ecd14abb1aa49fd2093124ce631ac..201ce67eef8514e9499bfe182bae6f94e3eab722 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -8,10 +8,15 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include #include "fsck.h" #include "xattr.h" #include "quotaio.h" -#include +#include "dedup.h" +#include "extra_fsck.h" +#include "fsck_debug.h" +#include "securec.h" +#include "hmfs_tools.h" char *tree_mark; uint32_t tree_mark_size = 256; @@ -30,6 +35,7 @@ int f2fs_set_main_bitmap(struct f2fs_sb_info *sbi, u32 blk, int type) /* just check data and node types */ if (fix) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_SIT_TYPE_IS_ERROR); DBG(1, "Wrong segment type [0x%x] %x -> %x", GET_SEGNO(sbi, blk), se->type, type); se->type = type; @@ -37,7 +43,7 @@ int f2fs_set_main_bitmap(struct f2fs_sb_info *sbi, u32 blk, int type) return f2fs_set_bit(BLKOFF_FROM_MAIN(sbi, blk), fsck->main_area_bitmap); } -static inline int f2fs_test_main_bitmap(struct f2fs_sb_info *sbi, u32 blk) +int f2fs_test_main_bitmap(struct f2fs_sb_info *sbi, u32 blk) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); @@ -45,7 +51,7 @@ static inline int f2fs_test_main_bitmap(struct f2fs_sb_info *sbi, u32 blk) fsck->main_area_bitmap); } -static inline int f2fs_clear_main_bitmap(struct f2fs_sb_info *sbi, u32 blk) +int f2fs_clear_main_bitmap(struct f2fs_sb_info *sbi, u32 blk) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); @@ -67,6 +73,13 @@ int f2fs_set_sit_bitmap(struct f2fs_sb_info *sbi, u32 blk) return f2fs_set_bit(BLKOFF_FROM_MAIN(sbi, blk), fsck->sit_area_bitmap); } +static inline int f2fs_clear_sit_bitmap(struct f2fs_sb_info *sbi, u32 blk) +{ + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + + return f2fs_clear_bit(BLKOFF_FROM_MAIN(sbi, blk), fsck->sit_area_bitmap); +} + static int add_into_hard_link_list(struct f2fs_sb_info *sbi, u32 nid, u32 link_cnt) { @@ -158,7 +171,13 @@ static int is_valid_ssa_node_blk(struct f2fs_sb_info *sbi, u32 nid, segno = GET_SEGNO(sbi, blk_addr); offset = OFFSET_IN_SEG(sbi, blk_addr); - sum_blk = get_sum_block(sbi, segno, &type); + sum_blk = get_sum_node_block_from_cache(sbi, segno, &type); + if (!sum_blk) { + /* do not free the sum_blk, it is saved to cache, and will + * be freed when cache is destroied + */ + sum_blk = get_sum_block(sbi, segno, &type); + } if (type != SEG_TYPE_NODE && type != SEG_TYPE_CUR_NODE) { /* can't fix current summary, then drop the block */ @@ -212,9 +231,6 @@ static int is_valid_ssa_node_blk(struct f2fs_sb_info *sbi, u32 nid, ASSERT(ret2 >= 0); } out: - if (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA || - type == SEG_TYPE_MAX) - free(sum_blk); return ret; } @@ -231,8 +247,10 @@ static int is_valid_summary(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, node_blk = (struct f2fs_node *)calloc(BLOCK_SZ, 1); ASSERT(node_blk != NULL); - if (!IS_VALID_NID(sbi, nid)) + if (!IS_VALID_NID(sbi, nid)) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_INVALID_NID); goto out; + } get_node_info(sbi, nid, &ni); @@ -283,7 +301,13 @@ static int is_valid_ssa_data_blk(struct f2fs_sb_info *sbi, u32 blk_addr, segno = GET_SEGNO(sbi, blk_addr); offset = OFFSET_IN_SEG(sbi, blk_addr); - sum_blk = get_sum_block(sbi, segno, &type); + sum_blk = get_sum_data_block_from_cache(sbi, segno, &type); + if (!sum_blk) { + /* do not free the sum_blk, it is saved to cache, and will + * be freed when cache is destroied + */ + sum_blk = get_sum_block(sbi, segno, &type); + } if (type != SEG_TYPE_DATA && type != SEG_TYPE_CUR_DATA) { /* can't fix current summary, then drop the block */ @@ -346,9 +370,6 @@ static int is_valid_ssa_data_blk(struct f2fs_sb_info *sbi, u32 blk_addr, ASSERT(ret2 >= 0); } out: - if (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA || - type == SEG_TYPE_MAX) - free(sum_blk); return ret; } @@ -386,31 +407,37 @@ err: return -1; } -static int sanity_check_nid(struct f2fs_sb_info *sbi, u32 nid, +int sanity_check_nid(struct f2fs_sb_info *sbi, u32 nid, struct f2fs_node *node_blk, enum FILE_TYPE ftype, enum NODE_TYPE ntype, struct node_info *ni) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); int ret; + nid_t inner_ino; + bool dedup_supported = c.feature & cpu_to_le32(F2FS_FEATURE_DEDUP); if (!IS_VALID_NID(sbi, nid)) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_INVALID_NID); ASSERT_MSG("nid is not valid. [0x%x]", nid); return -EINVAL; } get_node_info(sbi, nid, ni); if (ni->ino == 0) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_INO_IS_ZERO); ASSERT_MSG("nid[0x%x] ino is 0", nid); return -EINVAL; } if (ni->blk_addr == NEW_ADDR) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_BLKADDR_IS_NEW_ADDR); ASSERT_MSG("nid is NEW_ADDR. [0x%x]", nid); return -EINVAL; } if (!IS_VALID_BLK_ADDR(sbi, ni->blk_addr)) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_NODE_INVALID_BLKADDR); ASSERT_MSG("blkaddress is not valid. [0x%x]", ni->blk_addr); return -EINVAL; } @@ -418,20 +445,39 @@ static int sanity_check_nid(struct f2fs_sb_info *sbi, u32 nid, ret = dev_read_block(node_blk, ni->blk_addr); ASSERT(ret >= 0); + if (dedup_supported && ftype == F2FS_FT_DEDUP_INNER && ntype == TYPE_INODE) { + if (!f2fs_is_deduped_inode(node_blk) || + !f2fs_is_inner_inode(node_blk)) { + ASSERT_MSG("nid is not a valid inner inode. [0x%x]", nid); + return -EINVAL; + } + } + /* + * Do not check the revoked orphan node. + * It will be checked in the normal process. + */ + if (dedup_supported && ftype == F2FS_FT_ORPHAN && + f2fs_is_unstable_dedup_inode(node_blk)) { + return 0; + } + if (ntype == TYPE_INODE && node_blk->footer.nid != node_blk->footer.ino) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_INODE_FOOTER_INO_NOT_EQUAL_NID); ASSERT_MSG("nid[0x%x] footer.nid[0x%x] footer.ino[0x%x]", nid, le32_to_cpu(node_blk->footer.nid), le32_to_cpu(node_blk->footer.ino)); return -EINVAL; } if (ni->ino != le32_to_cpu(node_blk->footer.ino)) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_NODE_INO_NOT_EQUAL_FOOTER_INO); ASSERT_MSG("nid[0x%x] nat_entry->ino[0x%x] footer.ino[0x%x]", nid, ni->ino, le32_to_cpu(node_blk->footer.ino)); return -EINVAL; } if (ntype != TYPE_INODE && node_blk->footer.nid == node_blk->footer.ino) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_NON_INODE_FOOTER_INO_EQUAL_NID); ASSERT_MSG("nid[0x%x] footer.nid[0x%x] footer.ino[0x%x]", nid, le32_to_cpu(node_blk->footer.nid), le32_to_cpu(node_blk->footer.ino)); @@ -439,6 +485,7 @@ static int sanity_check_nid(struct f2fs_sb_info *sbi, u32 nid, } if (le32_to_cpu(node_blk->footer.nid) != nid) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_NODE_NID_NOT_EQUAL_FOOTER_NID); ASSERT_MSG("nid[0x%x] blk_addr[0x%x] footer.nid[0x%x]", nid, ni->blk_addr, le32_to_cpu(node_blk->footer.nid)); @@ -449,6 +496,7 @@ static int sanity_check_nid(struct f2fs_sb_info *sbi, u32 nid, u32 flag = le32_to_cpu(node_blk->footer.flag); if ((flag >> OFFSET_BIT_SHIFT) != XATTR_NODE_OFFSET) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_INVALID_XATTR_OFFSET); ASSERT_MSG("xnid[0x%x] has wrong ofs:[0x%x]", nid, flag); return -EINVAL; @@ -459,6 +507,7 @@ static int sanity_check_nid(struct f2fs_sb_info *sbi, u32 nid, (ntype == TYPE_XATTR && ftype == F2FS_FT_XATTR)) { /* not included '.' & '..' */ if (f2fs_test_main_bitmap(sbi, ni->blk_addr) != 0) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_DUPLICATE_NODE_BLKADDR_IN_MAIN_BITMAP); ASSERT_MSG("Duplicated node blk. nid[0x%x][0x%x]\n", nid, ni->blk_addr); return -EINVAL; @@ -470,8 +519,18 @@ static int sanity_check_nid(struct f2fs_sb_info *sbi, u32 nid, return 0; if (ntype == TYPE_INODE && - __check_inode_mode(nid, ftype, le16_to_cpu(node_blk->i.i_mode))) + __check_inode_mode(nid, ftype, le16_to_cpu(node_blk->i.i_mode))) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_INODE_MISMATCH_MODE); return -EINVAL; + } + + if (dedup_supported && f2fs_is_out_inode(node_blk)) { + inner_ino = f2fs_get_dedup_inner_ino(node_blk); + if (!f2fs_sanity_check_dedup_inner_nid(sbi, inner_ino)) { + ASSERT_MSG("inner inode is not valid. [0x%x]", inner_ino); + return -EINVAL; + } + } /* workaround to fix later */ if (ftype != F2FS_FT_ORPHAN || @@ -479,18 +538,23 @@ static int sanity_check_nid(struct f2fs_sb_info *sbi, u32 nid, f2fs_clear_bit(nid, fsck->nat_area_bitmap); /* avoid reusing nid when reconnecting files */ f2fs_set_bit(nid, NM_I(sbi)->nid_bitmap); - } else + } else { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_DUPLICATE_ORPHAN_OR_XATTR_NID); ASSERT_MSG("orphan or xattr nid is duplicated [0x%x]\n", nid); + } if (is_valid_ssa_node_blk(sbi, nid, ni->blk_addr)) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_INVALID_SUM_NODE_BLOCK); ASSERT_MSG("summary node block is not valid. [0x%x]", nid); return -EINVAL; } - if (f2fs_test_sit_bitmap(sbi, ni->blk_addr) == 0) + if (f2fs_test_sit_bitmap(sbi, ni->blk_addr) == 0) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_NAT_BLKADDR_OUT_SIT_BITMAP); ASSERT_MSG("SIT bitmap is 0x0. blk_addr[0x%x]", ni->blk_addr); + } if (f2fs_test_main_bitmap(sbi, ni->blk_addr) == 0) { @@ -550,6 +614,86 @@ out: return ret; } +static int fsck_chk_xattr_entries(struct f2fs_sb_info *sbi, + struct f2fs_node *node) +{ + struct f2fs_inode *inode = &node->i; + void *xattr_blk; + struct f2fs_xattr_entry *ent; + nid_t ino = le32_to_cpu(node->footer.ino); + size_t max_xattr_size, offs = 0, inline_size = (size_t)inline_xattr_size(inode); + int err = 0; + int i = 0, size = 0; + u8 *value; + u64 max_total_xattr_size = XATTR_SIZE(inode); + + if (inode->i_xattr_nid) { + max_xattr_size = inline_size + MIN_OFFSET; + } else { + max_xattr_size = inline_size; + } + if (max_xattr_size == 0) { + return 0; + } + xattr_blk = read_all_xattrs(sbi, node, false); + ASSERT(xattr_blk); + list_for_each_xattr(ent, xattr_blk, max_total_xattr_size) { + offs = (char *)ent - (char *)xattr_blk; + if (le16_to_cpu(ent->e_value_size) > MAX_VALUE_LEN(inode)) { + ASSERT_MSG("inode[0x%x] has wrong xattr e_value_size 0x%x", + ino, le16_to_cpu(ent->e_value_size)); + err = -ERANGE; + break; + } else if (offs + ENTRY_SIZE(ent) > max_xattr_size) { + ASSERT_MSG("inode[0x%x] xattr (offs %zu size %zu) exceeds max xattr size(%zu)", + ino, offs, ENTRY_SIZE(ent), max_xattr_size); + size = le16_to_cpu(ent->e_value_size); + value = (u8 *)&ent->e_name[ent->e_name_len]; + MSG(0, " xattr: e_name_index:%d e_name:", ent->e_name_index); + for (i = 0; i < ent->e_name_len; i++) + MSG(0, "%c", ent->e_name[i]); + MSG(0, " e_name_len:%d e_value_size:%d e_value:\n", + ent->e_name_len, size); + for (i = 0; i < size; i++) + MSG(0, "%02X", value[i]); + MSG(0, "\n"); + err = -ERANGE; + break; + } + } + + if (err) { + struct node_info ni; + nid_t xattr_nid = le32_to_cpu(inode->i_xattr_nid); + int ret; + + DBG(0, "inode[0x%x] remove xattrs from offset %zu\n", ino, offs); + + if (inline_size && offs <= inline_size) { + memset_s((char *)inline_xattr_addr(inode) + offs, inline_size - offs, 0, inline_size - offs); + FIX_MSG("inode[0x%x] write inline xattr entries [0x%x]", ino, xattr_nid); + } else { + err = 0; + } + if (xattr_nid && c.fix_on) { + /* set 4 bytes before node_footer as 0 diretly */ + size_t left = inline_size + BLOCK_SZ - offs - + sizeof(struct node_footer); + memset_s(ent, left, 0, left); + get_node_info(sbi, xattr_nid, &ni); + ret = dev_write_block((char *)xattr_blk + inline_size, ni.blk_addr); + ASSERT(ret >= 0); + FIX_MSG("inode[0x%x] write xattr node block [0x%x]", + ino, xattr_nid); + } + DBG(0, "inode[0x%x] remove corrupted xattr entry succes!\n", ino); + } + + free(xattr_blk); + return err; +} + + 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 f2fs_compr_blk_cnt *cbc, @@ -669,7 +813,7 @@ void fsck_reada_node_block(struct f2fs_sb_info *sbi, u32 nid) if (nid != 0 && IS_VALID_NID(sbi, nid)) { get_node_info(sbi, nid, &ni); if (IS_VALID_BLK_ADDR(sbi, ni.blk_addr)) - dev_reada_block(ni.blk_addr); + queue_reada_block(sbi, ni.blk_addr, NODE); } } @@ -685,6 +829,36 @@ void fsck_reada_all_direct_node_blocks(struct f2fs_sb_info *sbi, } } +bool is_special_orphan_file(struct f2fs_inode *inode, enum FILE_TYPE ftype) +{ + u16 mode = le16_to_cpu(inode->i_mode); + if (ftype == F2FS_FT_ORPHAN && (S_ISCHR(mode) || + S_ISBLK(mode) || S_ISFIFO(mode) || S_ISSOCK(mode))) { + return true; + } + return false; +} + +static int missing_inline_xattr(struct f2fs_inode *inode) +{ + int idx = DEF_ADDRS_PER_INODE - get_inline_xattr_addrs(inode); + u32 magic, refcount; + + if (inode->i_inline & F2FS_INLINE_XATTR) { + return 0; + } + if (idx >= DEF_ADDRS_PER_INODE - 1) { + return 0; + } + + magic = le32_to_cpu(inode->i_addr[idx]); + refcount = le32_to_cpu(inode->i_addr[idx + 1]); + if ((magic == F2FS_XATTR_MAGIC) && (refcount == 1)) { + return 1; + } + return 0; +} + /* 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, @@ -703,6 +877,8 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid, 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); + uint8_t i_log_cluster_size = node_blk->i.i_log_cluster_size; + uint8_t i_compress_algrithm = node_blk->i.i_compress_algrithm; int ofs; char *en; u32 namelen; @@ -711,11 +887,19 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid, int need_fix = 0; int ret; u32 cluster_size = 1 << node_blk->i.i_log_cluster_size; + bool dedup_supported = c.feature & cpu_to_le32(F2FS_FEATURE_DEDUP); + nid_t inner_ino; - if (!compressed) - goto check_next; + /* + * Do not check the revoked orphan node. + * It will be checked in the normal process. + */ + if (dedup_supported && ftype == F2FS_FT_ORPHAN && + f2fs_is_unstable_dedup_inode(node_blk)) { + return; + } - if (!compr_supported || (node_blk->i.i_inline & F2FS_INLINE_DATA)) { + if (!compr_supported && compressed) { /* * The 'compression' flag in i_flags affects the traverse of * the node tree. Thus, it must be fixed unconditionally @@ -730,7 +914,37 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid, } i_flags &= ~F2FS_COMPR_FL; } -check_next: + + /* + * The 'compress_algorithm' and 'log_cluster_size' is fixed to + * default values in hmfs. + */ + if (compressed && (ftype == F2FS_FT_DIR || ftype == F2FS_FT_REG_FILE)) { + if (i_compress_algrithm >= (uint8_t)COMPRESS_ALGO_MAX) { + ASSERT_MSG("[0x%x] wrong i_compress_algrithm=0x%x", + nid, i_compress_algrithm); + if (c.fix_on) { + node_blk->i.i_compress_algrithm = COMPRESS_ALGO_LZ4; + need_fix = 1; + FIX_MSG("ino[0x%x] set i_compress_algrithm %d", COMPRESS_ALGO_LZ4); + } + } + + if (i_log_cluster_size != HMFS_MIN_COMPRESS_LOG_SIZE) { + ASSERT_MSG("[0x%x] wrong i_log_cluster_size=0x%x", + nid, i_log_cluster_size); + if (c.fix_on) { + node_blk->i.i_log_cluster_size = HMFS_MIN_COMPRESS_LOG_SIZE; + need_fix = 1; + FIX_MSG("ino[0x%x] set i_log_cluster_size %d", nid, HMFS_MIN_COMPRESS_LOG_SIZE); + } + } + + if (need_fix == 1) { + dump_node(sbi, nid, 1); + } + } + memset(&child, 0, sizeof(child)); child.links = 2; child.p_ino = nid; @@ -751,6 +965,7 @@ check_next: f2fs_set_main_bitmap(sbi, ni->blk_addr, CURSEG_WARM_NODE); if (i_links > 1 && ftype != F2FS_FT_ORPHAN && + ftype != F2FS_FT_DEDUP_INNER && !is_qf_ino(F2FS_RAW_SUPER(sbi), nid)) { /* First time. Create new hard link node */ add_into_hard_link_list(sbi, nid, i_links); @@ -759,6 +974,7 @@ check_next: } else { DBG(3, "[0x%x] has hard links [0x%x]\n", nid, i_links); if (find_and_dec_hard_link_list(sbi, nid)) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_HARD_LINK_NUM_IS_ERROR); ASSERT_MSG("[0x%x] needs more i_links=0x%x", nid, i_links); if (c.fix_on) { @@ -775,6 +991,10 @@ check_next: return; } } + if (dedup_supported && f2fs_is_out_inode(node_blk)) { + inner_ino = f2fs_get_dedup_inner_ino(node_blk); + f2fs_inc_inner_actual_links(sbi, inner_ino); + } /* readahead xattr node block */ fsck_reada_node_block(sbi, i_xattr_nid); @@ -792,6 +1012,10 @@ check_next: ftype == F2FS_FT_FIFO || ftype == F2FS_FT_SOCK) goto check; + if (is_special_orphan_file(&node_blk->i, ftype)) { + goto check; + } + /* init extent info */ get_extent_info(&child.ei, &node_blk->i.i_ext); child.last_blk = 0; @@ -850,8 +1074,9 @@ check_next: ofs = get_extra_isize(node_blk); if ((node_blk->i.i_flags & cpu_to_le32(F2FS_CASEFOLD_FL)) && - (ftype != F2FS_FT_DIR || - !(c.feature & cpu_to_le32(F2FS_FEATURE_CASEFOLD)))) { + ((ftype != F2FS_FT_DIR && ftype != F2FS_FT_ORPHAN) || + (ftype == F2FS_FT_ORPHAN && !S_ISDIR(le16_to_cpu(node_blk->i.i_mode))) || + !(c.feature & cpu_to_le32(F2FS_FEATURE_CASEFOLD)))) { ASSERT_MSG("[0x%x] unexpected casefold flag", nid); if (c.fix_on) { FIX_MSG("ino[0x%x] clear casefold flag", nid); @@ -860,6 +1085,13 @@ check_next: } } + if (fsck_chk_xattr_entries(sbi, node_blk)) { + if (c.fix_on) { + FIX_MSG("ino[0x%x] rewrite inline xattr", nid); + need_fix = 1; + } + } + if ((node_blk->i.i_inline & F2FS_INLINE_DATA)) { unsigned int inline_size = MAX_INLINE_DATA(node_blk); if (cur_qtype != -1) @@ -867,6 +1099,7 @@ check_next: block_t blkaddr = le32_to_cpu(node_blk->i.i_addr[ofs]); if (blkaddr != 0) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_INLINE_DATA_ADDR0_NOT_ZERO); ASSERT_MSG("[0x%x] wrong inline reserve blkaddr:%u", nid, blkaddr); if (c.fix_on) { @@ -893,6 +1126,7 @@ check_next: if (memcmp(buf, inline_data_addr(node_blk), MAX_INLINE_DATA(node_blk))) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_INLINE_DATA_INEXISTENCE); ASSERT_MSG("[0x%x] junk inline data", nid); if (c.fix_on) { FIX_MSG("inline_data has DATA_EXIST"); @@ -906,24 +1140,44 @@ check_next: goto check; } - if ((node_blk->i.i_inline & F2FS_INLINE_DENTRY)) { - block_t blkaddr = le32_to_cpu(node_blk->i.i_addr[ofs]); + /* check F2FS_INLINE_XATTR flag before check data */ + if (missing_inline_xattr(&node_blk->i)) { + ASSERT_MSG("ino[0x%x] missing F2FS_INLINE_XATTR flag\n", nid); + node_blk->i.i_inline |= F2FS_INLINE_XATTR; + if (c.fix_on) { + FIX_MSG("ino[0x%x] set F2FS_INLINE_XATTR\n", nid); + need_fix = 1; + } + } - DBG(3, "ino[0x%x] has inline dentry!\n", nid); - if (blkaddr != 0) { - ASSERT_MSG("[0x%x] wrong inline reserve blkaddr:%u", - nid, blkaddr); - if (c.fix_on) { - FIX_MSG("inline_dentry has wrong 0'th block = %x", - blkaddr); - node_blk->i.i_addr[ofs] = 0; - node_blk->i.i_blocks = cpu_to_le64(*blk_cnt); - need_fix = 1; - } + if ((node_blk->i.i_inline & F2FS_INLINE_DENTRY) && + le32_to_cpu(node_blk->i.i_addr[ofs]) != 0) { + nid_t xnid = le32_to_cpu(node_blk->i.i_xattr_nid); + + /* + * If i_blocks or i_size is matches of inline node, think of it has inline dentry + * but i_addr[ofs] != 0, so fix it. + * else think of it no inline dentry + * but i.i_inline & F2FS_INLINE_DENTRY is true, fix it. + */ + ASSERT_MSG("ino[0x%x] i_blocks = %u, blk_cnt = %u\n", nid, i_blocks, *blk_cnt); + if (i_blocks == (1 + (xnid ? 1 : 0)) || i_size <= MAX_INLINE_DATA(node_blk)) { + FIX_MSG("ino[0x%x] has inline_dentry with wrong 0'th block = %x\n", + nid, le32_to_cpu(node_blk->i.i_addr[ofs])); + node_blk->i.i_addr[ofs] = cpu_to_le32(0); + } else { + FIX_MSG("ino[0x%x] has no inline_dentry but has inline dentry flag\n", nid); + node_blk->i.i_inline &= ~(F2FS_INLINE_DENTRY); } + need_fix = 1; + c.bug_on = 1; + } + if ((node_blk->i.i_inline & F2FS_INLINE_DENTRY)) { + DBG(3, "ino[0x%x] has inline dentry!\n", nid); ret = fsck_chk_inline_dentries(sbi, node_blk, &child); if (ret < 0) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_INVALID_INLINE_DENTRY); if (c.fix_on) need_fix = 1; } @@ -931,6 +1185,29 @@ check_next: goto check; } + if (!S_ISDIR(le16_to_cpu(node_blk->i.i_mode)) && (node_blk->i.i_inline & F2FS_INLINE_DENTRY)) { + ASSERT_MSG("[0x%x] wrong i_inline=0x%x", + nid, node_blk->i.i_inline); + if (c.fix_on) { + node_blk->i.i_inline &= ~(F2FS_INLINE_DENTRY); + need_fix = 1; + FIX_MSG("ino[0x%x] remove F2FS_INLINE_DENTRY " + "flag in i_inline", nid); + } + dump_node(sbi, nid, 1); + } + + /* readahead DIR data blocks */ + if (ftype == F2FS_FT_DIR) { + for (idx = 0; idx < ADDRS_PER_INODE(&node_blk->i); idx++) { + block_t blkaddr = le32_to_cpu(node_blk->i.i_addr[ofs + idx]); + if (blkaddr == 0) + continue; + if (IS_VALID_BLK_ADDR(sbi, blkaddr)) + queue_reada_block(sbi, blkaddr, DATA); + } + } + /* check data blocks in inode */ addrs = ADDRS_PER_INODE(&node_blk->i); if (cur_qtype != -1) { @@ -944,6 +1221,23 @@ check_next: for (idx = 0; idx < addrs; idx++, child.pgofs++) { block_t blkaddr = le32_to_cpu(node_blk->i.i_addr[ofs + idx]); + if (dedup_supported && f2fs_is_out_inode(node_blk)) { + if (f2fs_is_unstable_dedup_inode(node_blk) && + blkaddr == DEDUP_ADDR) { + continue; + } + if (!f2fs_is_unstable_dedup_inode(node_blk)) { + if (blkaddr != DEDUP_ADDR && c.fix_on) { + node_blk->i.i_addr[ofs + idx] = + cpu_to_le32(DEDUP_ADDR); + need_fix = 1; + FIX_MSG("dedup nid [0x%x] i_addr[%d]:%x->DEDUP_ADDR", + nid, ofs + idx, blkaddr); + } + continue; + } + } + /* check extent info */ check_extent_info(&child, blkaddr, 0); @@ -991,14 +1285,25 @@ check_next: /* readahead node blocks */ for (idx = 0; idx < 5; idx++) { - u32 nid = le32_to_cpu(node_blk->i.i_nid[idx]); - fsck_reada_node_block(sbi, nid); + u32 _nid = le32_to_cpu(node_blk->i.i_nid[idx]); + fsck_reada_node_block(sbi, _nid); } /* check node blocks in inode */ for (idx = 0; idx < 5; idx++) { nid_t i_nid = le32_to_cpu(node_blk->i.i_nid[idx]); + if (dedup_supported && f2fs_is_out_inode(node_blk) && + !f2fs_is_unstable_dedup_inode(node_blk)) { + if (i_nid != 0 && c.fix_on) { + node_blk->i.i_nid[idx] = 0; + need_fix = 1; + FIX_MSG("dedup nid [0x%x] i_nid[%d]:%x->0", + nid, idx, i_nid); + } + continue; + } + if (idx == 0 || idx == 1) ntype = TYPE_DIRECT_NODE; else if (idx == 2 || idx == 3) @@ -1035,10 +1340,16 @@ skip: } check: - /* check uncovered range in the back of extent */ - check_extent_info(&child, 0, 1); + if (dedup_supported && f2fs_is_out_inode(node_blk) && + !f2fs_is_revoke_inode(node_blk)) { + f2fs_check_dedup_extent_info(&child); + } else { + /* check uncovered range in the back of extent */ + check_extent_info(&child, 0, 1); + } if (child.state & FSCK_UNMATCHED_EXTENT) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_INVALID_EXTENT_VALUE); ASSERT_MSG("ino: 0x%x has wrong ext: [pgofs:%u, blk:%u, len:%u]", nid, child.ei.fofs, child.ei.blk, child.ei.len); if (c.fix_on) @@ -1046,6 +1357,7 @@ check: } if (i_blocks != *blk_cnt) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_INVALID_I_BLOCKS); ASSERT_MSG("ino: 0x%x has i_blocks: %08"PRIx64", " "but has %u blocks", nid, i_blocks, *blk_cnt); @@ -1066,6 +1378,11 @@ check: } } + if (compr_rel && i_compr_blocks) { + ASSERT_MSG("ino: 0x%x released, but has i_compr_blocks: %u", + nid, i_compr_blocks); + } + skip_blkcnt_fix: en = malloc(F2FS_PRINT_NAMELEN); ASSERT(en); @@ -1105,6 +1422,7 @@ skip_blkcnt_fix: child.files); if (i_links != child.links) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_INVALID_I_LINKS); ASSERT_MSG("ino: 0x%x i_links: %u, real links: %u", nid, i_links, child.links); if (c.fix_on) { @@ -1116,6 +1434,7 @@ skip_blkcnt_fix: } if (child.dots < 2 && !(node_blk->i.i_inline & F2FS_INLINE_DOTS)) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_LOST_DOT_OR_DOTDOT); ASSERT_MSG("ino: 0x%x dots: %u", nid, child.dots); if (c.fix_on) { @@ -1129,11 +1448,11 @@ skip_blkcnt_fix: i_gc_failures = le16_to_cpu(node_blk->i.i_gc_failures); /* - * old kernel initialized i_gc_failures as 0x01, in preen mode 2, + * old kernel initialized i_gc_failures as 0x01 * let's skip repairing. */ - if (ftype == F2FS_FT_REG_FILE && i_gc_failures && - (c.preen_mode != PREEN_MODE_2 || i_gc_failures != 0x01)) { + if ((ftype == F2FS_FT_REG_FILE || ftype == F2FS_FT_DEDUP_INNER) && + i_gc_failures > 0x01) { DBG(1, "Regular Inode: 0x%x [%s] depth: %d\n\n", le32_to_cpu(node_blk->footer.ino), en, @@ -1158,6 +1477,7 @@ skip_blkcnt_fix: } if (ftype == F2FS_FT_ORPHAN && i_links) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_ORPHAN_INODE_HAS_I_LINKS); ASSERT_MSG("ino: 0x%x is orphan inode, but has i_links: %u", nid, i_links); if (c.fix_on) { @@ -1212,6 +1532,21 @@ int fsck_chk_dnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode, bool compr_rel = inode->i_inline & F2FS_COMPRESS_RELEASED; u32 cluster_size = 1 << inode->i_log_cluster_size; + if (is_special_orphan_file(inode, ftype)) { + return 0; + } + + /* readahead DIR data blocks */ + if (ftype == F2FS_FT_DIR) { + for (idx = 0; idx < ADDRS_PER_INODE(&node_blk->i); idx++) { + block_t blkaddr = le32_to_cpu(node_blk->dn.addr[idx]); + if (blkaddr == 0) + continue; + if (IS_VALID_BLK_ADDR(sbi, blkaddr)) + queue_reada_block(sbi, blkaddr, DATA); + } + } + for (idx = 0; idx < ADDRS_PER_BLOCK(inode); idx++, child->pgofs++) { block_t blkaddr = le32_to_cpu(node_blk->dn.addr[idx]); @@ -1283,14 +1618,14 @@ int fsck_chk_idnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode, *blk_cnt = *blk_cnt + 1; else if (ret == -EINVAL) { if (!c.fix_on) - printf("should delete in.nid[i] = 0;\n"); + MSG(0, "should delete in.nid[i] = 0;\n"); else { node_blk->in.nid[i] = 0; need_fix = 1; FIX_MSG("Set indirect node 0x%x -> 0", i); } skip: - child->pgofs += ADDRS_PER_BLOCK(&node_blk->i); + child->pgofs += ADDRS_PER_BLOCK(inode); } } @@ -1325,15 +1660,14 @@ int fsck_chk_didnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode, *blk_cnt = *blk_cnt + 1; else if (ret == -EINVAL) { if (!c.fix_on) - printf("should delete in.nid[i] = 0;\n"); + MSG(0, "should delete in.nid[i] = 0;\n"); else { node_blk->in.nid[i] = 0; need_fix = 1; FIX_MSG("Set double indirect node 0x%x -> 0", i); } skip: - child->pgofs += ADDRS_PER_BLOCK(&node_blk->i) * - NIDS_PER_BLOCK; + child->pgofs += ADDRS_PER_BLOCK(inode) * NIDS_PER_BLOCK; } } @@ -1445,9 +1779,9 @@ static void print_dentry(struct f2fs_sb_info *sbi, __u8 *name, printf("\33[2K\r"); } else { for (i = 1; i < depth; i++) - printf("%c ", tree_mark[i]); + MSG(0, "%c ", tree_mark[i]); - printf("%c-- %s , \n", + MSG(0, "%c-- %s , \n", last_de ? '`' : '|', new, le32_to_cpu(dentry[idx].ino), enc_name); @@ -1597,6 +1931,10 @@ static int __chk_dentries(struct f2fs_sb_info *sbi, int casefolded, if (test_bit_le(i, bitmap) == 0) continue; + /* skip . and .. */ + if (le32_to_cpu(dentry[i].ino) == child->p_ino || + le32_to_cpu(dentry[i].ino) == child->pp_ino) + continue; ino = le32_to_cpu(dentry[i].ino); @@ -1605,7 +1943,7 @@ static int __chk_dentries(struct f2fs_sb_info *sbi, int casefolded, get_node_info(sbi, ino, &ni); if (IS_VALID_BLK_ADDR(sbi, ni.blk_addr)) { - dev_reada_block(ni.blk_addr); + queue_reada_block(sbi, ni.blk_addr, NODE); name_len = le16_to_cpu(dentry[i].name_len); if (name_len > 0) i += (name_len + F2FS_SLOT_LEN - 1) / F2FS_SLOT_LEN - 1; @@ -1619,6 +1957,7 @@ static int __chk_dentries(struct f2fs_sb_info *sbi, int casefolded, continue; } if (!IS_VALID_NID(sbi, le32_to_cpu(dentry[i].ino))) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_INVALID_NID); ASSERT_MSG("Bad dentry 0x%x with invalid NID/ino 0x%x", i, le32_to_cpu(dentry[i].ino)); if (c.fix_on) { @@ -1633,6 +1972,7 @@ static int __chk_dentries(struct f2fs_sb_info *sbi, int casefolded, ftype = dentry[i].file_type; if ((ftype <= F2FS_FT_UNKNOWN || ftype > F2FS_FT_LAST_FILE_TYPE)) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_INVALID_FTYPE); ASSERT_MSG("Bad dentry 0x%x with unexpected ftype 0x%x", le32_to_cpu(dentry[i].ino), ftype); if (c.fix_on) { @@ -1648,6 +1988,7 @@ static int __chk_dentries(struct f2fs_sb_info *sbi, int casefolded, name_len = le16_to_cpu(dentry[i].name_len); if (name_len == 0 || name_len > F2FS_NAME_LEN) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_NAME_LEN_IS_ZERO); ASSERT_MSG("Bad dentry 0x%x with invalid name_len", i); if (c.fix_on) { FIX_MSG("Clear bad dentry 0x%x", i); @@ -1694,8 +2035,10 @@ static int __chk_dentries(struct f2fs_sb_info *sbi, int casefolded, } } - if (f2fs_check_hash_code(get_encoding(sbi), casefolded, dentry + i, name, name_len, enc_name)) + if (f2fs_check_hash_code(get_encoding(sbi), casefolded, dentry + i, name, name_len, enc_name)) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_INVALID_HASH_CODE); fixed = 1; + } pretty_print_filename(name, name_len, en, enc_name); @@ -1860,23 +2203,29 @@ int fsck_chk_data_blk(struct f2fs_sb_info *sbi, int casefolded, } if (!IS_VALID_BLK_ADDR(sbi, blk_addr)) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_INVALID_NID); ASSERT_MSG("blkaddress is not valid. [0x%x]", blk_addr); return -EINVAL; } if (is_valid_ssa_data_blk(sbi, blk_addr, parent_nid, idx_in_node, ver)) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_INVALID_SUM_DATA_BLOCK); ASSERT_MSG("summary data block is not valid. [0x%x]", parent_nid); return -EINVAL; } - if (f2fs_test_sit_bitmap(sbi, blk_addr) == 0) + if (f2fs_test_sit_bitmap(sbi, blk_addr) == 0) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_DATA_BLKADDR_OUT_SIT_BITMAP); ASSERT_MSG("SIT bitmap is 0x0. blk_addr[0x%x]", blk_addr); + } - if (f2fs_test_main_bitmap(sbi, blk_addr) != 0) + if (f2fs_test_main_bitmap(sbi, blk_addr) != 0) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_DUPLICATE_DATA_BLKADDR_IN_MAIN_BITMAP); ASSERT_MSG("Duplicated data [0x%x]. pnid[0x%x] idx[0x%x]", blk_addr, parent_nid, idx_in_node); + } fsck->chk.valid_blk_cnt++; @@ -1948,8 +2297,10 @@ int fsck_chk_orphan_node(struct f2fs_sb_info *sbi) orphan_blk->ino[j]; else if (ret && c.fix_on) FIX_MSG("[0x%x] remove from orphan list", ino); - else if (ret) + else if (ret) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_ORPAHN_INODE_ERROR); ASSERT_MSG("[0x%x] wrong orphan inode", ino); + } } if (f2fs_dev_is_writable() && c.fix_on && entry_count != new_entry_count) { @@ -2036,9 +2387,11 @@ int fsck_chk_quota_files(struct f2fs_sb_info *sbi) } /* Something is wrong */ - if (c.fix_on) { + if (c.fix_on && !c.dry_run) { DBG(0, "Fixing Quota file ([%3d] ino [0x%x])\n", qtype, ino); + fsck_disconnect_file(sbi, ino, true); + f2fs_rebuild_qf_inode(sbi, qtype); f2fs_filesize_update(sbi, ino, 0); ret = quota_write_inode(sbi, qtype); if (!ret) { @@ -2049,7 +2402,8 @@ int fsck_chk_quota_files(struct f2fs_sb_info *sbi) } } else { ASSERT_MSG("Quota file is missing or invalid" - " quota file content found."); + " quota file content found, or" + " fsck is in dry-run mode."); } } return ret; @@ -2078,6 +2432,7 @@ int fsck_chk_meta(struct f2fs_sb_info *sbi) } if (fsck->chk.sit_free_segs + sit_valid_segs != get_usable_seg_count(sbi)) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_SIT_SEGMENT_COUNT_MISMATCH_WITH_TOTAL); ASSERT_MSG("SIT usage does not match: sit_free_segs %u, " "sit_valid_segs %u, total_segs %u", fsck->chk.sit_free_segs, sit_valid_segs, @@ -2087,6 +2442,7 @@ int fsck_chk_meta(struct f2fs_sb_info *sbi) /* 2. check node count */ if (fsck->chk.valid_nat_entry_cnt != sit_node_blks) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_NAT_NODE_COUNT_MISMATCH_WITH_SIT); ASSERT_MSG("node count does not match: valid_nat_entry_cnt %u," " sit_node_blks %u", fsck->chk.valid_nat_entry_cnt, sit_node_blks); @@ -2095,6 +2451,7 @@ int fsck_chk_meta(struct f2fs_sb_info *sbi) /* 3. check SIT with CP */ if (fsck->chk.sit_free_segs != le32_to_cpu(cp->free_segment_count)) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_SIT_FREESEG_COUNT_MISMATCH_WITH_CP); ASSERT_MSG("free segs does not match: sit_free_segs %u, " "free_segment_count %u", fsck->chk.sit_free_segs, @@ -2105,6 +2462,7 @@ int fsck_chk_meta(struct f2fs_sb_info *sbi) /* 4. check NAT with CP */ if (fsck->chk.valid_nat_entry_cnt != le32_to_cpu(cp->valid_node_count)) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_NAT_NODE_COUNT_MISMATCH_WITH_CP); ASSERT_MSG("valid node does not match: valid_nat_entry_cnt %u," " valid_node_count %u", fsck->chk.valid_nat_entry_cnt, @@ -2112,7 +2470,7 @@ int fsck_chk_meta(struct f2fs_sb_info *sbi) return -EINVAL; } - /* 4. check orphan inode simply */ + /* 5. check orphan inode simply */ if (fsck_chk_orphan_node(sbi)) return -EINVAL; @@ -2129,6 +2487,7 @@ int fsck_chk_meta(struct f2fs_sb_info *sbi) continue; if (!IS_VALID_BLK_ADDR(sbi, blk)) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_NODE_INVALID_BLKADDR); MSG(0, "\tError: nat entry[ino %u block_addr 0x%x]" " is in valid\n", ino, blk); @@ -2136,6 +2495,7 @@ int fsck_chk_meta(struct f2fs_sb_info *sbi) } if (!f2fs_test_sit_bitmap(sbi, blk)) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_NAT_BLKADDR_OUT_SIT_BITMAP); MSG(0, "\tError: nat entry[ino %u block_addr 0x%x]" " not find it in sit_area_bitmap\n", ino, blk); @@ -2143,6 +2503,7 @@ int fsck_chk_meta(struct f2fs_sb_info *sbi) } if (!IS_VALID_NID(sbi, ino)) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_INVALID_NID); MSG(0, "\tError: nat_entry->ino %u exceeds the range" " of nat entries %u\n", ino, fsck->nr_nat_entries); @@ -2150,6 +2511,7 @@ int fsck_chk_meta(struct f2fs_sb_info *sbi) } if (!f2fs_test_bit(ino, fsck->nat_area_bitmap)) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_NAT_INO_OUT_NAT_BITMAP); MSG(0, "\tError: nat_entry->ino %u is not set in" " nat_area_bitmap\n", ino); return -EINVAL; @@ -2161,6 +2523,7 @@ int fsck_chk_meta(struct f2fs_sb_info *sbi) return -EINVAL; if (fsck->nat_valid_inode_cnt != le32_to_cpu(cp->valid_inode_count)) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_NAT_INODE_COUNT_MISMATCH_WITH_CP); ASSERT_MSG("valid inode does not match: nat_valid_inode_cnt %u," " valid_inode_count %u", fsck->nat_valid_inode_cnt, @@ -2184,7 +2547,7 @@ void fsck_chk_checkpoint(struct f2fs_sb_info *sbi) } } -void fsck_init(struct f2fs_sb_info *sbi) +void fsck_init(struct f2fs_sb_info *sbi, bool caller_is_fsck) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct f2fs_sm_info *sm_i = SM_I(sbi); @@ -2216,6 +2579,11 @@ void fsck_init(struct f2fs_sb_info *sbi) fsck->dentry_end = fsck->dentry; c.quota_fixed = false; + + if (caller_is_fsck) { + build_sum_cache_list(sbi); + init_reada_queue(sbi); + } } static void fix_hard_links(struct f2fs_sb_info *sbi) @@ -2319,6 +2687,7 @@ static void fix_checkpoint(struct f2fs_sb_info *sbi) u32 i; int ret; uint32_t crc = 0; + struct f2fs_journal *nat_journal = CURSEG_I(sbi, CURSEG_HOT_DATA)->journal; /* should call from fsck */ ASSERT(c.func == FSCK); @@ -2327,6 +2696,14 @@ static void fix_checkpoint(struct f2fs_sb_info *sbi) orphan_blks = __start_sum_addr(sbi) - 1; flags |= CP_ORPHAN_PRESENT_FLAG; } + + /* if has append nat block,we need set append flag + * and sit count is nullified,no need to set sit flag + */ + if (nats_in_cursum(nat_journal) > NAT_JOURNAL_ENTRIES) { + flags |= CP_APPEND_NAT_FLAG; + } + if (is_set_ckpt_flags(cp, CP_TRIMMED_FLAG)) flags |= CP_TRIMMED_FLAG; if (is_set_ckpt_flags(cp, CP_DISABLED_FLAG)) @@ -2346,9 +2723,10 @@ static void fix_checkpoint(struct f2fs_sb_info *sbi) set_cp(cp_pack_total_block_count, cp_blocks + orphan_blks + get_sb(cp_payload)); - flags = update_nat_bits_flags(sb, cp, flags); flags |= CP_NOCRC_RECOVERY_FLAG; set_cp(ckpt_flags, flags); + set_cp(cp_pack_total_block_count, cp_blocks + + orphan_blks + get_sb(cp_payload)); set_cp(free_segment_count, get_free_segments(sbi)); set_cp(valid_block_count, fsck->chk.valid_blk_cnt); @@ -2380,13 +2758,24 @@ static void fix_checkpoint(struct f2fs_sb_info *sbi) if (!(flags & CP_UMOUNT_FLAG) && IS_NODESEG(i)) continue; + if ((i == CURSEG_HOT_DATA) || (i == CURSEG_COLD_DATA)) { + memcpy(&curseg->sum_blk->journal, curseg->journal, + sizeof(struct f2fs_journal)); + } + ret = dev_write_block(curseg->sum_blk, cp_blk_no++); ASSERT(ret >= 0); } - /* Write nat bits */ - if (flags & CP_NAT_BITS_FLAG) - write_nat_bits(sbi, sb, cp, sbi->cur_cp); + /* write nat append block */ + if (flags & CP_APPEND_NAT_FLAG) { + struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); + block_t start_blk = __start_cp_addr(sbi) + + le32_to_cpu(F2FS_CKPT(sbi)->cp_pack_total_block_count); + ret = dev_write_block((char *)curseg->journal + SUM_JOURNAL_SIZE - NAT_JOURNAL_RESERVED, + start_blk); + ASSERT(ret >= 0); + } ret = f2fs_fsync_device(); ASSERT(ret >= 0); @@ -2555,6 +2944,7 @@ int check_curseg_offset(struct f2fs_sb_info *sbi, int type) se = get_seg_entry(sbi, curseg->segno); if (f2fs_test_bit(curseg->next_blkoff, (const char *)se->cur_valid_map)) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_CUR_NEXT_BLK_IS_NOT_FREE); ASSERT_MSG("Next block offset is not free, type:%d", type); return -EINVAL; } @@ -2564,6 +2954,7 @@ int check_curseg_offset(struct f2fs_sb_info *sbi, int type) nblocks = sbi->blocks_per_seg; for (j = curseg->next_blkoff + 1; j < nblocks; j++) { if (f2fs_test_bit(j, (const char *)se->cur_valid_map)) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_LFS_HAS_NO_FREE_SECTION); ASSERT_MSG("For LFS curseg, space after .next_blkoff " "should be unused, type:%d", type); return -EINVAL; @@ -2619,6 +3010,7 @@ int check_sit_types(struct f2fs_sb_info *sbi) se->type <= CURSEG_COLD_DATA) { se->type = se->orig_type; } else { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_SIT_TYPE_IS_ERROR); FIX_MSG("Wrong segment type [0x%x] %x -> %x", i, se->orig_type, se->type); err = -EINVAL; @@ -2730,10 +3122,52 @@ static int fsck_do_reconnect_file(struct f2fs_sb_info *sbi, return 0; } -static void fsck_failed_reconnect_file_dnode(struct f2fs_sb_info *sbi, - nid_t nid) +static inline void release_inode_cnt(struct f2fs_sb_info *sbi, bool dealloc) +{ + F2FS_FSCK(sbi)->chk.valid_inode_cnt--; + if (dealloc) { + sbi->total_valid_inode_count--; + } +} + +static inline void release_node_cnt(struct f2fs_sb_info *sbi, bool dealloc) +{ + F2FS_FSCK(sbi)->chk.valid_node_cnt--; + if (dealloc) { + sbi->total_valid_node_count--; + } +} + +static inline void release_block_cnt(struct f2fs_sb_info *sbi, bool dealloc) +{ + F2FS_FSCK(sbi)->chk.valid_blk_cnt--; + if (dealloc) { + sbi->total_valid_block_count--; + } +} + +static inline void release_block(struct f2fs_sb_info *sbi, u64 blkaddr, bool dealloc) +{ + f2fs_clear_main_bitmap(sbi, blkaddr); + if (dealloc) { + struct seg_entry *se; + + se = get_seg_entry(sbi, GET_SEGNO(sbi, blkaddr)); + se->valid_blocks--; + f2fs_clear_bit(OFFSET_IN_SEG(sbi, blkaddr), (char *)se->cur_valid_map); + se->dirty = 1; + f2fs_clear_sit_bitmap(sbi, blkaddr); + } +} + +static inline void release_nat_entry(struct f2fs_sb_info *sbi, u32 nid) +{ + nullify_nat_entry(sbi, nid); + F2FS_FSCK(sbi)->chk.valid_nat_entry_cnt--; +} + +static void fsck_disconnect_file_dnode(struct f2fs_sb_info *sbi, struct f2fs_inode *inode, nid_t nid, bool dealloc) { - struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct f2fs_node *node; struct node_info ni; u32 addr; @@ -2746,27 +3180,29 @@ static void fsck_failed_reconnect_file_dnode(struct f2fs_sb_info *sbi, err = dev_read_block(node, ni.blk_addr); ASSERT(err >= 0); - fsck->chk.valid_node_cnt--; - fsck->chk.valid_blk_cnt--; - f2fs_clear_main_bitmap(sbi, ni.blk_addr); + release_node_cnt(sbi, dealloc); + release_block_cnt(sbi, dealloc); + release_block(sbi, ni.blk_addr, dealloc); - for (i = 0; i < ADDRS_PER_BLOCK(&node->i); i++) { + for (i = 0; i < ADDRS_PER_BLOCK(inode); i++) { addr = le32_to_cpu(node->dn.addr[i]); if (!addr) continue; - fsck->chk.valid_blk_cnt--; - if (addr == NEW_ADDR) + release_block_cnt(sbi, dealloc); + if (addr == NEW_ADDR || addr == COMPRESS_ADDR || addr == DEDUP_ADDR) continue; - f2fs_clear_main_bitmap(sbi, addr); + release_block(sbi, addr, dealloc); + } + + if (dealloc) { + release_nat_entry(sbi, nid); } free(node); } -static void fsck_failed_reconnect_file_idnode(struct f2fs_sb_info *sbi, - nid_t nid) +static void fsck_disconnect_file_idnode(struct f2fs_sb_info *sbi, struct f2fs_inode *inode, nid_t nid, bool dealloc) { - struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct f2fs_node *node; struct node_info ni; nid_t tmp; @@ -2779,22 +3215,25 @@ static void fsck_failed_reconnect_file_idnode(struct f2fs_sb_info *sbi, err = dev_read_block(node, ni.blk_addr); ASSERT(err >= 0); - fsck->chk.valid_node_cnt--; - fsck->chk.valid_blk_cnt--; - f2fs_clear_main_bitmap(sbi, ni.blk_addr); + release_node_cnt(sbi, dealloc); + release_block_cnt(sbi, dealloc); + release_block(sbi, ni.blk_addr, dealloc); for (i = 0; i < NIDS_PER_BLOCK; i++) { tmp = le32_to_cpu(node->in.nid[i]); if (!tmp) continue; - fsck_failed_reconnect_file_dnode(sbi, tmp); + fsck_disconnect_file_dnode(sbi, inode, tmp, dealloc); + } + + if (dealloc) { + release_nat_entry(sbi, nid); } free(node); } -static void fsck_failed_reconnect_file_didnode(struct f2fs_sb_info *sbi, - nid_t nid) +static void fsck_disconnect_file_didnode(struct f2fs_sb_info *sbi, struct f2fs_inode *inode, nid_t nid, bool dealloc) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct f2fs_node *node; @@ -2809,28 +3248,26 @@ static void fsck_failed_reconnect_file_didnode(struct f2fs_sb_info *sbi, err = dev_read_block(node, ni.blk_addr); ASSERT(err >= 0); - fsck->chk.valid_node_cnt--; - fsck->chk.valid_blk_cnt--; - f2fs_clear_main_bitmap(sbi, ni.blk_addr); + release_node_cnt(sbi, dealloc); + release_block_cnt(sbi, dealloc); + release_block(sbi, ni.blk_addr, dealloc); for (i = 0; i < NIDS_PER_BLOCK; i++) { tmp = le32_to_cpu(node->in.nid[i]); if (!tmp) continue; - fsck_failed_reconnect_file_idnode(sbi, tmp); + fsck_disconnect_file_idnode(sbi, inode, tmp, dealloc); + } + + if (dealloc) { + release_nat_entry(sbi, nid); } free(node); } -/* - * Counters and main_area_bitmap are already changed during checking - * inode block, so clear them. There is no need to clear new blocks - * allocted to lost+found. - */ -static void fsck_failed_reconnect_file(struct f2fs_sb_info *sbi, nid_t ino) +void fsck_disconnect_file(struct f2fs_sb_info *sbi, nid_t ino, bool dealloc) { - struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct f2fs_node *node; struct node_info ni; nid_t nid; @@ -2844,31 +3281,31 @@ static void fsck_failed_reconnect_file(struct f2fs_sb_info *sbi, nid_t ino) ASSERT(err >= 0); /* clear inode counters */ - fsck->chk.valid_inode_cnt--; - fsck->chk.valid_node_cnt--; - fsck->chk.valid_blk_cnt--; - f2fs_clear_main_bitmap(sbi, ni.blk_addr); + release_inode_cnt(sbi, dealloc); + release_node_cnt(sbi, dealloc); + release_block_cnt(sbi, dealloc); + release_block(sbi, ni.blk_addr, dealloc); /* clear xnid counters */ if (node->i.i_xattr_nid) { nid = le32_to_cpu(node->i.i_xattr_nid); - fsck->chk.valid_node_cnt--; - fsck->chk.valid_blk_cnt--; + release_node_cnt(sbi, dealloc); + release_block_cnt(sbi, dealloc); get_node_info(sbi, nid, &ni); - f2fs_clear_main_bitmap(sbi, ni.blk_addr); + release_block(sbi, ni.blk_addr, dealloc); } /* clear data counters */ - if(!(node->i.i_inline & F2FS_INLINE_DATA)) { + if (!(node->i.i_inline & F2FS_INLINE_DATA)) { ofs = get_extra_isize(node); for (i = 0; i < ADDRS_PER_INODE(&node->i); i++) { block_t addr = le32_to_cpu(node->i.i_addr[ofs + i]); if (!addr) continue; - fsck->chk.valid_blk_cnt--; - if (addr == NEW_ADDR) + release_block_cnt(sbi, dealloc); + if (addr == NEW_ADDR || addr == COMPRESS_ADDR || addr == DEDUP_ADDR) continue; - f2fs_clear_main_bitmap(sbi, addr); + release_block(sbi, addr, dealloc); } } @@ -2880,18 +3317,25 @@ static void fsck_failed_reconnect_file(struct f2fs_sb_info *sbi, nid_t ino) switch (i) { case 0: /* direct node */ case 1: - fsck_failed_reconnect_file_dnode(sbi, nid); + fsck_disconnect_file_dnode(sbi, &node->i, nid, + dealloc); break; case 2: /* indirect node */ case 3: - fsck_failed_reconnect_file_idnode(sbi, nid); + fsck_disconnect_file_idnode(sbi, &node->i, nid, + dealloc); break; case 4: /* double indirect node */ - fsck_failed_reconnect_file_didnode(sbi, nid); + fsck_disconnect_file_didnode(sbi, &node->i, nid, + dealloc); break; } } + if (dealloc) { + release_nat_entry(sbi, ino); + } + free(node); } @@ -2977,7 +3421,7 @@ static int fsck_reconnect_file(struct f2fs_sb_info *sbi) if (fsck_do_reconnect_file(sbi, lpf_node, node)) { DBG(1, "Failed to reconnect inode [0x%x]\n", nid); - fsck_failed_reconnect_file(sbi, nid); + fsck_disconnect_file(sbi, nid, false); continue; } @@ -3188,7 +3632,7 @@ int fsck_verify(struct f2fs_sb_info *sbi) if (c.show_file_map) return 0; - printf("\n"); + MSG(0, "\n"); if (c.zoned_model == F2FS_ZONED_HM) { printf("[FSCK] Write pointers consistency "); @@ -3219,16 +3663,24 @@ int fsck_verify(struct f2fs_sb_info *sbi) struct node_info ni; get_node_info(sbi, i, &ni); - printf("NID[0x%x] is unreachable, blkaddr:0x%x\n", - i, ni.blk_addr); + if (nr_unref_nid == 0) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_NID_IS_UNREACHABLE); + MSG(0, "Unreachable NIDs:\n"); + MSG(0, "["); + } + MSG(0, "0x%x,0x%x ", i, ni.blk_addr); nr_unref_nid++; } } + if (nr_unref_nid) { + MSG(0, "]\n"); + } if (fsck->hard_link_list_head != NULL) { node = fsck->hard_link_list_head; while (node) { - printf("NID[0x%x] has [0x%x] more unreachable links\n", + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_NID_HAS_MORE_UNREACHABLE_LINKS); + MSG(0, "NID[0x%x] has [0x%x] more unreachable links\n", node->nid, node->links); node = node->next; } @@ -3242,91 +3694,111 @@ int fsck_verify(struct f2fs_sb_info *sbi) BLKS_PER_SEC(sbi); max_blks = SM_I(sbi)->main_blkaddr + (data_secs + node_secs) * BLKS_PER_SEC(sbi); - printf("[FSCK] Max image size: %"PRIu64" MB, Free space: %"PRIu64" MB\n", + MSG(0, "[FSCK] Max image size: %"PRIu64" MB, Free space: %"PRIu64" MB\n", max_blks >> 8, free_blks >> 8); - printf("[FSCK] Unreachable nat entries "); + DMD_SET_VALUE(usedSpace, max_blks >> MB_BLK_SHIFT); + DMD_SET_VALUE(freeSpace, free_blks >> MB_BLK_SHIFT); + MSG(0, "[FSCK] Unreachable nat entries "); if (nr_unref_nid == 0x0) { - printf(" [Ok..] [0x%x]\n", nr_unref_nid); + MSG(0, " [Ok..] [0x%x]\n", nr_unref_nid); } else { - printf(" [Fail] [0x%x]\n", nr_unref_nid); + MSG(0, " [Fail] [0x%x]\n", nr_unref_nid); + DMD_ADD_MSG_ERROR(LOG_TYP_FSCK, PR_NID_IS_UNREACHABLE, "nr_unref_nid=%u", nr_unref_nid); verify_failed = true; } - printf("[FSCK] SIT valid block bitmap checking "); + MSG(0, "[FSCK] SIT valid block bitmap checking "); if (memcmp(fsck->sit_area_bitmap, fsck->main_area_bitmap, fsck->sit_area_bitmap_sz) == 0x0) { - printf("[Ok..]\n"); + MSG(0, "[Ok..]\n"); } else { - printf("[Fail]\n"); + MSG(0, "[Fail]\n"); + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_SIT_INVALID_BLOCK_BITMAP); + dump_bitmap_diff(sbi, fsck->sit_area_bitmap, fsck->main_area_bitmap); verify_failed = true; } - printf("[FSCK] Hard link checking for regular file "); + MSG(0, "[FSCK] Hard link checking for regular file "); if (fsck->hard_link_list_head == NULL) { - printf(" [Ok..] [0x%x]\n", fsck->chk.multi_hard_link_files); + MSG(0, " [Ok..] [0x%x]\n", fsck->chk.multi_hard_link_files); } else { - printf(" [Fail] [0x%x]\n", fsck->chk.multi_hard_link_files); + MSG(0, " [Fail] [0x%x]\n", fsck->chk.multi_hard_link_files); + DMD_ADD_MSG_ERROR(LOG_TYP_FSCK, PR_MULTI_HARD_LINK_FILES, + "multi_hard_link_files=%u", fsck->chk.multi_hard_link_files); verify_failed = true; } - printf("[FSCK] valid_block_count matching with CP "); + MSG(0, "[FSCK] valid_block_count matching with CP "); if (sbi->total_valid_block_count == fsck->chk.valid_blk_cnt) { - printf(" [Ok..] [0x%x]\n", (u32)fsck->chk.valid_blk_cnt); + MSG(0, " [Ok..] [0x%x]\n", (u32)fsck->chk.valid_blk_cnt); } else { - printf(" [Fail] [0x%x]\n", (u32)fsck->chk.valid_blk_cnt); + MSG(0, " [Fail] [0x%x]\n", (u32)fsck->chk.valid_blk_cnt); + DMD_ADD_MSG_ERROR(LOG_TYP_FSCK, PR_VALID_BLOCK_COUNT_MISMATCH_WITH_CP, + "nrSbiValidBlk=%u, nrCpValidBlk=%u;", sbi->total_valid_block_count, (u32)fsck->chk.valid_blk_cnt); verify_failed = true; } - printf("[FSCK] valid_node_count matching with CP (de lookup) "); + MSG(0, "[FSCK] valid_node_count matching with CP (de lookup) "); if (sbi->total_valid_node_count == fsck->chk.valid_node_cnt) { - printf(" [Ok..] [0x%x]\n", fsck->chk.valid_node_cnt); + MSG(0, " [Ok..] [0x%x]\n", fsck->chk.valid_node_cnt); } else { - printf(" [Fail] [0x%x]\n", fsck->chk.valid_node_cnt); + MSG(0, " [Fail] [0x%x]\n", fsck->chk.valid_node_cnt); + DMD_ADD_MSG_ERROR(LOG_TYP_FSCK, PR_VALID_NODE_COUNT_MISMATCH_WITH_CP_NAT, + "nrSbiValidNode=%u, nrCpValidNodeDE=%u;", sbi->total_valid_node_count, fsck->chk.valid_node_cnt); verify_failed = true; } - printf("[FSCK] valid_node_count matching with CP (nat lookup)"); + MSG(0, "[FSCK] valid_node_count matching with CP (nat lookup)"); if (sbi->total_valid_node_count == fsck->chk.valid_nat_entry_cnt) { - printf(" [Ok..] [0x%x]\n", fsck->chk.valid_nat_entry_cnt); + MSG(0, " [Ok..] [0x%x]\n", fsck->chk.valid_nat_entry_cnt); } else { - printf(" [Fail] [0x%x]\n", fsck->chk.valid_nat_entry_cnt); + MSG(0, " [Fail] [0x%x]\n", fsck->chk.valid_nat_entry_cnt); + DMD_ADD_MSG_ERROR(LOG_TYP_FSCK, PR_VALID_NODE_COUNT_MISMATCH_WITH_CP_NAT, + "nrSbiValidNode=%u, nrCpValidNatEnty=%u;", sbi->total_valid_node_count, fsck->chk.valid_nat_entry_cnt); verify_failed = true; } - printf("[FSCK] valid_inode_count matched with CP "); + MSG(0, "[FSCK] valid_inode_count matched with CP "); if (sbi->total_valid_inode_count == fsck->chk.valid_inode_cnt) { - printf(" [Ok..] [0x%x]\n", fsck->chk.valid_inode_cnt); + MSG(0, " [Ok..] [0x%x]\n", fsck->chk.valid_inode_cnt); } else { - printf(" [Fail] [0x%x]\n", fsck->chk.valid_inode_cnt); + MSG(0, " [Fail] [0x%x]\n", fsck->chk.valid_inode_cnt); + DMD_ADD_MSG_ERROR(LOG_TYP_FSCK, PR_VALID_INODE_COUNT_MISMATCH_WITH_CP, + "nrSbiValidInode=%u, nrCpValidInode=%u;", sbi->total_valid_inode_count, fsck->chk.valid_inode_cnt); verify_failed = true; } - printf("[FSCK] free segment_count matched with CP "); + MSG(0, "[FSCK] free segment_count matched with CP "); if (le32_to_cpu(F2FS_CKPT(sbi)->free_segment_count) == fsck->chk.sit_free_segs) { - printf(" [Ok..] [0x%x]\n", fsck->chk.sit_free_segs); + MSG(0, " [Ok..] [0x%x]\n", fsck->chk.sit_free_segs); } else { - printf(" [Fail] [0x%x]\n", fsck->chk.sit_free_segs); + MSG(0, " [Fail] [0x%x]\n", fsck->chk.sit_free_segs); + DMD_ADD_MSG_ERROR(LOG_TYP_FSCK, PR_SEGMENT_COUNT_MISMATCH_WITH_CP, + "nrSbiFreeSegs=%u, cpSitFreeSegs=%u", + le32_to_cpu(F2FS_CKPT(sbi)->free_segment_count), fsck->chk.sit_free_segs); verify_failed = true; } - printf("[FSCK] next block offset is free "); + MSG(0, "[FSCK] next block offset is free "); if (check_curseg_offsets(sbi) == 0) { - printf(" [Ok..]\n"); + MSG(0, " [Ok..]\n"); } else { - printf(" [Fail]\n"); + MSG(0, " [Fail]\n"); + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_NEXT_BLOCK_OFFSET_FREE); verify_failed = true; } - printf("[FSCK] fixing SIT types\n"); + MSG(0, "[FSCK] fixing SIT types\n"); if (check_sit_types(sbi) != 0) force = 1; - printf("[FSCK] other corrupted bugs "); + MSG(0, "[FSCK] other corrupted bugs "); if (c.bug_on == 0) { - printf(" [Ok..]\n"); + MSG(0, " [Ok..]\n"); } else { - printf(" [Fail]\n"); + MSG(0, " [Fail]\n"); + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_OTHER_CORRUPT); ret = EXIT_ERR_CODE; } @@ -3382,15 +3854,21 @@ int fsck_verify(struct f2fs_sb_info *sbi) update_superblock(sb, SB_MASK_ALL); /* to return FSCK_ERROR_CORRECTED */ + ClearExtraFlag(sbi->raw_super, EXTRA_NEED_FSCK_FLAG); ret = 0; } return ret; } -void fsck_free(struct f2fs_sb_info *sbi) +void fsck_free(struct f2fs_sb_info *sbi, bool caller_is_fsck) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + if (caller_is_fsck) { + exit_reada_queue(sbi); + destroy_sum_cache_list(sbi); + } + if (fsck->qctx) quota_release_context(&fsck->qctx); diff --git a/fsck/fsck.h b/fsck/fsck.h index dabd8b9c6dfd4423ea209c422873d8c2d20041b4..3aa31e04251f5e4c3a77690b60670e511cb02090 100644 --- a/fsck/fsck.h +++ b/fsck/fsck.h @@ -12,6 +12,8 @@ #define _FSCK_H_ #include "f2fs.h" +#include "queue.h" +#include "xattr.h" enum { FSCK_SUCCESS = 0, @@ -86,6 +88,21 @@ struct f2fs_dentry { struct f2fs_dentry *next; }; +enum { + DATA, + NODE, + MAX_TYPE +}; + +struct sum_cache { + unsigned int segno; + struct f2fs_summary_block *sum_blk; + struct list_head list; +}; +#define MAX_SUM_CACHE_CNT 20 +#define HASHTABLE_SIZE 10 + +#include struct f2fs_fsck { struct f2fs_sb_info sbi; @@ -104,6 +121,7 @@ struct f2fs_fsck { } chk; struct hard_link_node *hard_link_list_head; + struct dedup_inner_node *dedup_inner_list_head; char *main_seg_usage; char *main_area_bitmap; @@ -124,6 +142,19 @@ struct f2fs_fsck { u32 nat_valid_inode_cnt; struct quota_ctx *qctx; + + int force_drop_recovery; + + /* thread for async readahead queue: [0] for DATA, [1] for NODE */ + struct list_head ra_queue[MAX_TYPE]; + pthread_t thread[MAX_TYPE]; + pthread_cond_t cond[MAX_TYPE]; + pthread_mutex_t mutex[MAX_TYPE]; + int quit_thread[MAX_TYPE]; + + /* SSA block cache LRU hash table: [0] for DATA, [1] for NODE */ + struct list_head sum_cache_head[MAX_TYPE][HASHTABLE_SIZE]; + int sum_cache_cnt[MAX_TYPE][HASHTABLE_SIZE]; }; #define BLOCK_SZ 4096 @@ -158,6 +189,9 @@ struct selabel_handle; static inline bool need_fsync_data_record(struct f2fs_sb_info *sbi) { + if (is_set_ckpt_flags(F2FS_CKPT(sbi), CP_DISABLED_FLAG)) { + return false; + } return !is_set_ckpt_flags(F2FS_CKPT(sbi), CP_UMOUNT_FLAG) || c.zoned_model == F2FS_ZONED_HM; } @@ -214,9 +248,11 @@ extern void build_nat_area_bitmap(struct f2fs_sb_info *); extern void build_sit_area_bitmap(struct f2fs_sb_info *); extern int f2fs_set_main_bitmap(struct f2fs_sb_info *, u32, int); extern int f2fs_set_sit_bitmap(struct f2fs_sb_info *, u32); -extern void fsck_init(struct f2fs_sb_info *); +/* only when caller from do_fsck, the caller_is_fsck = true */ +extern void fsck_init(struct f2fs_sb_info *, bool); extern int fsck_verify(struct f2fs_sb_info *); -extern void fsck_free(struct f2fs_sb_info *); +/* only when caller from do_fsck, the caller_is_fsck = true */ +extern void fsck_free(struct f2fs_sb_info *, bool); extern int f2fs_ra_meta_pages(struct f2fs_sb_info *, block_t, int, int); extern int f2fs_do_mount(struct f2fs_sb_info *); extern void f2fs_do_umount(struct f2fs_sb_info *); @@ -247,12 +283,14 @@ extern void get_current_sit_page(struct f2fs_sb_info *, extern void rewrite_current_sit_page(struct f2fs_sb_info *, unsigned int, struct f2fs_sit_block *); -extern u32 update_nat_bits_flags(struct f2fs_super_block *, - struct f2fs_checkpoint *, u32); -extern void write_nat_bits(struct f2fs_sb_info *, struct f2fs_super_block *, - struct f2fs_checkpoint *, int); +extern int sanity_check_nid(struct f2fs_sb_info *, u32 , struct f2fs_node *, + enum FILE_TYPE, enum NODE_TYPE, struct node_info *); +extern int f2fs_test_main_bitmap(struct f2fs_sb_info *, u32); +extern int f2fs_clear_main_bitmap(struct f2fs_sb_info *, u32); + extern unsigned int get_usable_seg_count(struct f2fs_sb_info *); extern bool is_usable_seg(struct f2fs_sb_info *, unsigned int); +extern bool is_special_orphan_file(struct f2fs_inode *, enum FILE_TYPE); /* dump.c */ struct dump_option { @@ -326,8 +364,9 @@ int f2fs_add_link(struct f2fs_sb_info *, struct f2fs_node *, const unsigned char *, int, nid_t, int, block_t, int); struct hardlink_cache_entry *f2fs_search_hardlink(struct f2fs_sb_info *sbi, struct dentry *de); +void fsck_disconnect_file(struct f2fs_sb_info *sbi, nid_t ino, bool dealloc); /* xattr.c */ -void *read_all_xattrs(struct f2fs_sb_info *, struct f2fs_node *); +void *read_all_xattrs(struct f2fs_sb_info *, struct f2fs_node *, bool); #endif /* _FSCK_H_ */ diff --git a/fsck/main.c b/fsck/main.c index f83a4afb89e8bc75d3d26aa0e9b516afcd34e2b1..b0f96dfccd7f4e1686596af1003e31a39df6de03 100644 --- a/fsck/main.c +++ b/fsck/main.c @@ -29,6 +29,7 @@ #include #include "quotaio.h" #include "compress.h" +#include "dedup.h" struct f2fs_fsck gfsck; @@ -850,7 +851,7 @@ static int do_fsck(struct f2fs_sb_info *sbi) struct f2fs_compr_blk_cnt cbc; errcode_t ret; - fsck_init(sbi); + fsck_init(sbi, true); print_cp_state(flag); @@ -862,12 +863,13 @@ static int do_fsck(struct f2fs_sb_info *sbi) switch (c.preen_mode) { case PREEN_MODE_1: if (fsck_chk_meta(sbi)) { - MSG(0, "[FSCK] F2FS metadata [Fail]"); + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_FSCK_META_MISMATCH); + MSG(0, "[FSCK] F2FS metadata [Fail]\n"); MSG(0, "\tError: meta does not match, " "force check all\n"); } else { - MSG(0, "[FSCK] F2FS metadata [Ok..]"); - fsck_free(sbi); + MSG(0, "[FSCK] F2FS metadata [Ok..]\n"); + fsck_free(sbi, true); return FSCK_SUCCESS; } @@ -889,6 +891,10 @@ static int do_fsck(struct f2fs_sb_info *sbi) c.fix_on = 1; } + if (c.fix_on || c.bug_on) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_FULL_DISK_FSCK); + } + fsck_chk_checkpoint(sbi); fsck_chk_quota_node(sbi); @@ -908,10 +914,11 @@ 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, &cbc, NULL); + f2fs_fix_dedup_inner_list(sbi); fsck_chk_quota_files(sbi); ret = fsck_verify(sbi); - fsck_free(sbi); + fsck_free(sbi, true); if (!c.bug_on) return FSCK_SUCCESS; @@ -1016,6 +1023,7 @@ static int do_resize(struct f2fs_sb_info *sbi) if (c.target_sectors > c.total_sectors) { ASSERT_MSG("Out-of-range Target=0x%"PRIx64" / 0x%"PRIx64"", c.target_sectors, c.total_sectors); + g_logI.needTruncate = 1; return -1; } @@ -1119,13 +1127,24 @@ int main(int argc, char **argv) struct f2fs_sb_info *sbi; int ret = 0, ret2; u64 start = get_boottime_ns(); + u64 end; + u64 cost_ms; f2fs_init_configuration(); f2fs_parse_options(argc, argv); + if (SlogInit(c.func) < 0) { + /* should not exit. fsck may have no permissions for + * log or splash2 partition. + */ + printf("Failed to init slog\n"); + } + MSG(0, "Start time: %llu ns\n", start); + if (c.func != DUMP && f2fs_devs_are_umounted() < 0) { if (errno == EBUSY) { + SlogExit(); ret = -1; if (c.func == FSCK) ret = FSCK_OPERATIONAL_ERROR; @@ -1133,6 +1152,7 @@ int main(int argc, char **argv) } if (!c.ro || c.func == DEFRAG) { MSG(0, "\tError: Not available on mounted device!\n"); + SlogExit(); ret = -1; if (c.func == FSCK) ret = FSCK_OPERATIONAL_ERROR; @@ -1151,6 +1171,7 @@ int main(int argc, char **argv) /* Get device */ if (f2fs_get_device_info() < 0 || f2fs_get_f2fs_info() < 0) { + SlogExit(); ret = -1; if (c.func == FSCK) ret = FSCK_OPERATIONAL_ERROR; @@ -1189,13 +1210,15 @@ fsck_again: #endif #ifdef WITH_RESIZE case RESIZE: - if (do_resize(sbi)) + ret = do_resize(sbi); + if (ret) goto out_err; break; #endif #ifdef WITH_SLOAD case SLOAD: - if (do_sload(sbi)) + ret = do_sload(sbi); + if (ret) goto out_err; ret = f2fs_sparse_initialize_meta(sbi); @@ -1242,6 +1265,7 @@ retry: } ret2 = f2fs_finalize_device(); if (ret2) { + F2FS_EXT_EXIT(); if (c.func == FSCK) return FSCK_OPERATIONAL_ERROR; return ret2; @@ -1250,11 +1274,20 @@ retry: if (c.func == SLOAD) c.compress.filter_ops->destroy(); - if (!c.show_file_map) - printf("\nDone: %lf secs\n", (get_boottime_ns() - start) / 1000000000.0); - return ret; - out_err: + end = get_boottime_ns(); + MSG(0, "End time: %llu ns\n", end); + + cost_ms = (end - start) / (1000000); + MSG(0, "Cost time: %llu ms\n", cost_ms); + DMD_CHECK_COST_TIME(sbi, cost_ms); + + F2FS_EXT_EXIT(); + + if (!ret || c.func == FSCK) { + return ret; + } + if (sbi->ckpt) free(sbi->ckpt); if (sbi->raw_super) diff --git a/fsck/mkquota.c b/fsck/mkquota.c index d18141e2460fab3e01fca8bdb6d02eda87c858bc..43eb5b4a0dce18441422583ae6997ad3fb42bed1 100644 --- a/fsck/mkquota.c +++ b/fsck/mkquota.c @@ -320,6 +320,9 @@ static int scan_dquots_callback(struct dquot *dquot, void *cb_data) struct scan_dquots_data *scan_data = cb_data; dict_t *quota_dict = scan_data->quota_dict; struct dquot *dq; + static int unmatched_num = 0; + /* Only display 10 records. Otherwise, the log buffer overflows. */ + const int max_printed = 10; dq = get_dq(quota_dict, dquot->dq_id); dq->dq_id = dquot->dq_id; @@ -337,6 +340,15 @@ static int scan_dquots_callback(struct dquot *dquot, void *cb_data) (long long) dq->dq_dqb.dqb_curinodes, (long long) dquot->dq_dqb.dqb_curspace, (long long) dquot->dq_dqb.dqb_curinodes); + if (unmatched_num < max_printed) { + MSG(0, "[QUOTA WARNING] Usage inconsistent for ID %u:" + "scaned (%lld, %lld) != quota file (%lld, %lld)\n", + dq->dq_id, (long long) dq->dq_dqb.dqb_curspace, + (long long) dq->dq_dqb.dqb_curinodes, + (long long) dquot->dq_dqb.dqb_curspace, + (long long) dquot->dq_dqb.dqb_curinodes); + unmatched_num++; + } } if (scan_data->update_limits) { diff --git a/fsck/mount.c b/fsck/mount.c index 0e0c3f9edcbc26900bdcc62876bfe736c27ba252..ccef8ac187bef59aa47888892e6b285ea281e1b8 100644 --- a/fsck/mount.c +++ b/fsck/mount.c @@ -11,6 +11,7 @@ #include "fsck.h" #include "fsck_debug.h" #include "node.h" +#include "securec.h" #include "xattr.h" #include #include @@ -21,6 +22,7 @@ #ifdef HAVE_SYS_ACL_H #include #endif +#include "extra_fsck.h" #ifndef ACL_UNDEFINED_TAG #define ACL_UNDEFINED_TAG (0x00) @@ -239,12 +241,14 @@ void print_inode_info(struct f2fs_sb_info *sbi, { struct f2fs_inode *inode = &node->i; void *xattr_addr; + void *last_base_addr; struct f2fs_xattr_entry *ent; char en[F2FS_PRINT_NAMELEN]; unsigned int i = 0; u32 namelen = le32_to_cpu(inode->i_namelen); int enc_name = file_enc_name(inode); int ofs = get_extra_isize(node); + u64 max_total_xattr_size = XATTR_SIZE(inode); pretty_print_filename(inode->i_name, namelen, en, enc_name); if (name && en[0]) { @@ -284,7 +288,7 @@ void print_inode_info(struct f2fs_sb_info *sbi, printf("%-30s\t\t[%s]\n", "i_name", en); } - printf("i_ext: fofs:%x blkaddr:%x len:%x\n", + MSG(0, "i_ext: fofs:%x blkaddr:%x len:%x\n", le32_to_cpu(inode->i_ext.fofs), le32_to_cpu(inode->i_ext.blk_addr), le32_to_cpu(inode->i_ext.len)); @@ -307,6 +311,11 @@ void print_inode_info(struct f2fs_sb_info *sbi, DISP_u32(inode, i_log_cluster_size); DISP_u32(inode, i_padding); } + if (c.feature & cpu_to_le32(F2FS_FEATURE_DEDUP)) { + DISP_u64(inode, i_inner_ino); + DISP_u32(inode, i_dedup_flags); + DISP_u32(inode, i_dedup_rsvd); + } } for (i = 0; i < ADDRS_PER_INODE(inode); i++) { @@ -324,7 +333,7 @@ void print_inode_info(struct f2fs_sb_info *sbi, flag = "cluster flag"; else if (blkaddr == NEW_ADDR) flag = "reserved flag"; - printf("i_addr[0x%x] %-16s\t\t[0x%8x : %u]\n", i + ofs, flag, + MSG(0, "i_addr[0x%x] %-16s\t\t[0x%8x : %u]\n", i + ofs, flag, blkaddr, blkaddr); } @@ -334,15 +343,25 @@ void print_inode_info(struct f2fs_sb_info *sbi, DISP_u32(inode, i_nid[3]); /* indirect */ DISP_u32(inode, i_nid[4]); /* double indirect */ - xattr_addr = read_all_xattrs(sbi, node); - if (xattr_addr) { - list_for_each_xattr(ent, xattr_addr) { - print_xattr_entry(ent); + xattr_addr = read_all_xattrs(sbi, node, true); + if (!xattr_addr) { + goto out; + } + + last_base_addr = (void *)xattr_addr + XATTR_SIZE(&node->i); + + list_for_each_xattr(ent, xattr_addr, max_total_xattr_size) { + if ((void *)(ent) + sizeof(__u32) > last_base_addr || + (void *)XATTR_NEXT_ENTRY(ent) > last_base_addr) { + MSG(0, "xattr entry crosses the end of xattr space\n"); + break; } - free(xattr_addr); + print_xattr_entry(ent); } + free(xattr_addr); - printf("\n"); +out: + MSG(0, "\n"); } void print_node_info(struct f2fs_sb_info *sbi, @@ -361,7 +380,7 @@ void print_node_info(struct f2fs_sb_info *sbi, "Node ID [0x%x:%u] is direct node or indirect node.\n", nid, nid); for (i = 0; i < DEF_ADDRS_PER_BLOCK; i++) - MSG(verbose, "[%d]\t\t\t[0x%8x : %d]\n", + MSG(verbose, "[%d]\t\t\t[0x%8x : %u]\n", i, dump_blk[i], dump_blk[i]); } } @@ -374,7 +393,7 @@ static void DISP_label(uint16_t *name) if (c.layout) printf("%-30s %s\n", "Filesystem volume name:", buffer); else - printf("%-30s" "\t\t[%s]\n", "volum_name", buffer); + MSG(0, "%-30s" "\t\t[%s]\n", "volum_name", buffer); } void print_raw_sb_info(struct f2fs_super_block *sb) @@ -384,10 +403,10 @@ void print_raw_sb_info(struct f2fs_super_block *sb) if (!c.dbg_lv) return; - printf("\n"); - printf("+--------------------------------------------------------+\n"); - printf("| Super block |\n"); - printf("+--------------------------------------------------------+\n"); + MSG(0, "\n"); + MSG(0, "+--------------------------------------------------------+\n"); + MSG(0, "| Super block |\n"); + MSG(0, "+--------------------------------------------------------+\n"); printout: DISP_u32(sb, magic); DISP_u32(sb, major_ver); @@ -427,7 +446,7 @@ printout: DISP_u32(sb, cp_payload); DISP_u32(sb, crc); DISP("%-.252s", sb, version); - printf("\n"); + MSG(0, "\n"); } void print_ckpt_info(struct f2fs_sb_info *sbi) @@ -439,10 +458,10 @@ void print_ckpt_info(struct f2fs_sb_info *sbi) if (!c.dbg_lv) return; - printf("\n"); - printf("+--------------------------------------------------------+\n"); - printf("| Checkpoint |\n"); - printf("+--------------------------------------------------------+\n"); + MSG(0, "\n"); + MSG(0, "+--------------------------------------------------------+\n"); + MSG(0, "| Checkpoint |\n"); + MSG(0, "+--------------------------------------------------------+\n"); printout: DISP_u64(cp, checkpoint_ver); DISP_u64(cp, user_block_count); @@ -486,7 +505,7 @@ printout: DISP_u64(cp, elapsed_time); DISP_u32(cp, sit_nat_version_bitmap[0]); - printf("\n\n"); + MSG(0, "\n\n"); } void print_cp_state(u32 flag) @@ -495,6 +514,7 @@ void print_cp_state(u32 flag) return; MSG(0, "Info: checkpoint state = %x : ", flag); + DMD_SET_VALUE(ckptState, flag); if (flag & CP_QUOTA_NEED_FSCK_FLAG) MSG(0, "%s", " quota_need_fsck"); if (flag & CP_LARGE_NAT_BITMAP_FLAG) @@ -503,8 +523,6 @@ void print_cp_state(u32 flag) MSG(0, "%s", " allow_nocrc"); if (flag & CP_TRIMMED_FLAG) MSG(0, "%s", " trimmed"); - if (flag & CP_NAT_BITS_FLAG) - MSG(0, "%s", " nat_bits"); if (flag & CP_CRC_RECOVERY_FLAG) MSG(0, "%s", " crc"); if (flag & CP_FASTBOOT_FLAG) @@ -533,6 +551,7 @@ void print_sb_state(struct f2fs_super_block *sb) __le32 f = sb->feature; int i; + DMD_SET_VALUE(features, f); MSG(0, "Info: superblock features = %x : ", f); if (f & cpu_to_le32(F2FS_FEATURE_ENCRYPT)) { MSG(0, "%s", " encrypt"); @@ -576,6 +595,9 @@ void print_sb_state(struct f2fs_super_block *sb) if (f & cpu_to_le32(F2FS_FEATURE_RO)) { MSG(0, "%s", " ro"); } + if (f & cpu_to_le32(F2FS_FEATURE_DEDUP)) { + MSG(0, "%s", " dedup"); + } MSG(0, "\n"); MSG(0, "Info: superblock encrypt level = %d, salt = ", sb->encryption_level); @@ -762,6 +784,7 @@ void update_superblock(struct f2fs_super_block *sb, int sb_mask) } } + f2fs_fsync_device(); free(buf); DBG(0, "Info: Done to update superblock\n"); } @@ -1013,14 +1036,18 @@ static int check_and_set_one_feature(struct f2fs_sb_info *sbi, int feature) static void check_and_set_features(struct f2fs_sb_info *sbi, enum SB_ADDR sb_addr) { bool need_fix = false; - if (check_and_set_one_feature(sbi, F2FS_FEATURE_EXTRA_ATTR)) { - MSG(0, "Fix set feature: extra_attr\n"); - need_fix = true; - } - - if (check_and_set_one_feature(sbi, F2FS_FEATURE_PRJQUOTA)) { - MSG(0, "Fix set feature: project_quota\n"); - need_fix = true; + int check_list[] = { + F2FS_FEATURE_EXTRA_ATTR, + F2FS_FEATURE_PRJQUOTA, + F2FS_FEATURE_COMPRESSION, + F2FS_FEATURE_DEDUP + }; + int list_len = sizeof(check_list) / sizeof(check_list[0]); + for (int i = 0; i < list_len; i++) { + if (check_and_set_one_feature(sbi, check_list[i])) { + MSG(0, "Fix set feature: 0x%x\n", check_list[i]); + need_fix = true; + } } if (check_and_set_one_feature(sbi, F2FS_FEATURE_CASEFOLD)) { @@ -1033,7 +1060,7 @@ static void check_and_set_features(struct f2fs_sb_info *sbi, enum SB_ADDR sb_add } if (need_fix) { - update_superblock(sbi->raw_super, SB_MASK(sb_addr)); + update_superblock(sbi->raw_super, SB_MASK_ALL); } } @@ -1044,11 +1071,12 @@ int validate_super_block(struct f2fs_sb_info *sbi, enum SB_ADDR sb_addr) char buf[F2FS_BLKSIZE]; sbi->raw_super = malloc(sizeof(struct f2fs_super_block)); - if (!sbi->raw_super) + if (!sbi->raw_super) { return -ENOMEM; + } if (dev_read_block(buf, sb_addr)) - return -1; + goto free; memcpy(sbi->raw_super, buf + F2FS_SUPER_OFFSET, sizeof(struct f2fs_super_block)); @@ -1083,6 +1111,7 @@ int validate_super_block(struct f2fs_sb_info *sbi, enum SB_ADDR sb_addr) return 0; } +free: free(sbi->raw_super); sbi->raw_super = NULL; MSG(0, "\tCan't find a valid F2FS superblock at 0x%x\n", sb_addr); @@ -1156,6 +1185,10 @@ int init_sb_info(struct f2fs_sb_info *sbi) MSG(0, "Info: total FS sectors = %"PRIu64" (%"PRIu64" MB)\n", total_sectors, total_sectors >> (20 - get_sb(log_sectorsize))); + + DMD_SET_VALUE(segsPerSec, sbi->segs_per_sec); + DMD_SET_VALUE(secsPerZone, sbi->secs_per_zone); + DMD_SET_VALUE(totalFsSectors, total_sectors); return 0; } @@ -1261,6 +1294,7 @@ int get_valid_checkpoint(struct f2fs_sb_info *sbi) */ cp_start_blk_no = get_sb(cp_blkaddr); cp1 = validate_checkpoint(sbi, cp_start_blk_no, &cp1_version); + CheckExtraFlag(sbi->raw_super, EXTRA_NEED_FSCK_FLAG); /* The second checkpoint pack should start at the next segment */ cp_start_blk_no += 1 << get_sb(log_blocks_per_seg); @@ -1288,6 +1322,7 @@ int get_valid_checkpoint(struct f2fs_sb_info *sbi) goto fail_no_cp; MSG(0, "Info: CKPT version = %llx\n", version); + DMD_SET_VALUE(ckptVersion, version); memcpy(sbi->ckpt, cur_page, blk_size); @@ -1510,10 +1545,10 @@ static int f2fs_early_init_nid_bitmap(struct f2fs_sb_info *sbi) struct f2fs_nm_info *nm_i = NM_I(sbi); int nid_bitmap_size = (nm_i->max_nid + BITS_PER_BYTE - 1) / BITS_PER_BYTE; struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); - struct f2fs_summary_block *sum = curseg->sum_blk; - struct f2fs_journal *journal = &sum->journal; + struct f2fs_journal *journal = curseg->journal; nid_t nid; int i; + unsigned long nat_journal_entries; if (!(c.func == SLOAD || c.func == FSCK)) return 0; @@ -1524,12 +1559,16 @@ static int f2fs_early_init_nid_bitmap(struct f2fs_sb_info *sbi) /* arbitrarily set 0 bit */ f2fs_set_bit(0, nm_i->nid_bitmap); + nat_journal_entries = NAT_JOURNAL_ENTRIES; + if (is_set_ckpt_flags(F2FS_CKPT(sbi), CP_APPEND_NAT_FLAG)) { + nat_journal_entries += NAT_APPEND_JOURNAL_ENTRIES; + } - if (nats_in_cursum(journal) > NAT_JOURNAL_ENTRIES) { + if (nats_in_cursum(journal) > nat_journal_entries) { MSG(0, "\tError: f2fs_init_nid_bitmap truncate n_nats(%u) to " "NAT_JOURNAL_ENTRIES(%zu)\n", - nats_in_cursum(journal), NAT_JOURNAL_ENTRIES); - journal->n_nats = cpu_to_le16(NAT_JOURNAL_ENTRIES); + nats_in_cursum(journal), nat_journal_entries); + journal->n_nats = cpu_to_le16(nat_journal_entries); c.fix_on = 1; } @@ -1593,176 +1632,6 @@ static int f2fs_late_init_nid_bitmap(struct f2fs_sb_info *sbi) return 0; } -u32 update_nat_bits_flags(struct f2fs_super_block *sb, - struct f2fs_checkpoint *cp, u32 flags) -{ - uint32_t nat_bits_bytes, nat_bits_blocks; - - nat_bits_bytes = get_sb(segment_count_nat) << 5; - nat_bits_blocks = F2FS_BYTES_TO_BLK((nat_bits_bytes << 1) + 8 + - F2FS_BLKSIZE - 1); - if (get_cp(cp_pack_total_block_count) <= - (1 << get_sb(log_blocks_per_seg)) - nat_bits_blocks) - flags |= CP_NAT_BITS_FLAG; - else - flags &= (~CP_NAT_BITS_FLAG); - - return flags; -} - -/* should call flush_journal_entries() bfore this */ -void write_nat_bits(struct f2fs_sb_info *sbi, - struct f2fs_super_block *sb, struct f2fs_checkpoint *cp, int set) -{ - struct f2fs_nm_info *nm_i = NM_I(sbi); - uint32_t nat_blocks = get_sb(segment_count_nat) << - (get_sb(log_blocks_per_seg) - 1); - uint32_t nat_bits_bytes = nat_blocks >> 3; - uint32_t nat_bits_blocks = F2FS_BYTES_TO_BLK((nat_bits_bytes << 1) + - 8 + F2FS_BLKSIZE - 1); - unsigned char *nat_bits, *full_nat_bits, *empty_nat_bits; - struct f2fs_nat_block *nat_block; - uint32_t i, j; - block_t blkaddr; - int ret; - - nat_bits = calloc(F2FS_BLKSIZE, nat_bits_blocks); - ASSERT(nat_bits); - - nat_block = malloc(F2FS_BLKSIZE); - ASSERT(nat_block); - - full_nat_bits = nat_bits + 8; - empty_nat_bits = full_nat_bits + nat_bits_bytes; - - memset(full_nat_bits, 0, nat_bits_bytes); - memset(empty_nat_bits, 0, nat_bits_bytes); - - for (i = 0; i < nat_blocks; i++) { - int seg_off = i >> get_sb(log_blocks_per_seg); - int valid = 0; - - blkaddr = (pgoff_t)(get_sb(nat_blkaddr) + - (seg_off << get_sb(log_blocks_per_seg) << 1) + - (i & ((1 << get_sb(log_blocks_per_seg)) - 1))); - - /* - * Should consider new nat_blocks is larger than old - * nm_i->nat_blocks, since nm_i->nat_bitmap is based on - * old one. - */ - if (i < nm_i->nat_blocks && f2fs_test_bit(i, nm_i->nat_bitmap)) - blkaddr += (1 << get_sb(log_blocks_per_seg)); - - ret = dev_read_block(nat_block, blkaddr); - ASSERT(ret >= 0); - - for (j = 0; j < NAT_ENTRY_PER_BLOCK; j++) { - if ((i == 0 && j == 0) || - nat_block->entries[j].block_addr != NULL_ADDR) - valid++; - } - if (valid == 0) - test_and_set_bit_le(i, empty_nat_bits); - else if (valid == NAT_ENTRY_PER_BLOCK) - test_and_set_bit_le(i, full_nat_bits); - } - *(__le64 *)nat_bits = get_cp_crc(cp); - free(nat_block); - - blkaddr = get_sb(segment0_blkaddr) + (set << - get_sb(log_blocks_per_seg)) - nat_bits_blocks; - - DBG(1, "\tWriting NAT bits pages, at offset 0x%08x\n", blkaddr); - - for (i = 0; i < nat_bits_blocks; i++) { - if (dev_write_block(nat_bits + i * F2FS_BLKSIZE, blkaddr + i)) - ASSERT_MSG("\tError: write NAT bits to disk!!!\n"); - } - MSG(0, "Info: Write valid nat_bits in checkpoint\n"); - - free(nat_bits); -} - -static int check_nat_bits(struct f2fs_sb_info *sbi, - struct f2fs_super_block *sb, struct f2fs_checkpoint *cp) -{ - struct f2fs_nm_info *nm_i = NM_I(sbi); - uint32_t nat_blocks = get_sb(segment_count_nat) << - (get_sb(log_blocks_per_seg) - 1); - uint32_t nat_bits_bytes = nat_blocks >> 3; - uint32_t nat_bits_blocks = F2FS_BYTES_TO_BLK((nat_bits_bytes << 1) + - 8 + F2FS_BLKSIZE - 1); - unsigned char *nat_bits, *full_nat_bits, *empty_nat_bits; - struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); - struct f2fs_journal *journal = &curseg->sum_blk->journal; - uint32_t i, j; - block_t blkaddr; - int err = 0; - - nat_bits = calloc(F2FS_BLKSIZE, nat_bits_blocks); - ASSERT(nat_bits); - - full_nat_bits = nat_bits + 8; - empty_nat_bits = full_nat_bits + nat_bits_bytes; - - blkaddr = get_sb(segment0_blkaddr) + (sbi->cur_cp << - get_sb(log_blocks_per_seg)) - nat_bits_blocks; - - for (i = 0; i < nat_bits_blocks; i++) { - if (dev_read_block(nat_bits + i * F2FS_BLKSIZE, blkaddr + i)) - ASSERT_MSG("\tError: read NAT bits to disk!!!\n"); - } - - if (*(__le64 *)nat_bits != get_cp_crc(cp) || nats_in_cursum(journal)) { - /* - * if there is a journal, f2fs was not shutdown cleanly. Let's - * flush them with nat_bits. - */ - if (c.fix_on) - err = -1; - /* Otherwise, kernel will disable nat_bits */ - goto out; - } - - for (i = 0; i < nat_blocks; i++) { - uint32_t start_nid = i * NAT_ENTRY_PER_BLOCK; - uint32_t valid = 0; - int empty = test_bit_le(i, empty_nat_bits); - int full = test_bit_le(i, full_nat_bits); - - for (j = 0; j < NAT_ENTRY_PER_BLOCK; j++) { - if (f2fs_test_bit(start_nid + j, nm_i->nid_bitmap)) - valid++; - } - if (valid == 0) { - if (!empty || full) { - err = -1; - goto out; - } - } else if (valid == NAT_ENTRY_PER_BLOCK) { - if (empty || !full) { - err = -1; - goto out; - } - } else { - if (empty || full) { - err = -1; - goto out; - } - } - } -out: - free(nat_bits); - if (!err) { - MSG(0, "Info: Checked valid nat_bits in checkpoint\n"); - } else { - c.bug_nat_bits = 1; - MSG(0, "Info: Corrupted valid nat_bits in checkpoint\n"); - } - return err; -} - int init_node_manager(struct f2fs_sb_info *sbi) { struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); @@ -1924,16 +1793,16 @@ static void read_compacted_summaries(struct f2fs_sb_info *sbi) ASSERT(ret >= 0); curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); - memcpy(&curseg->sum_blk->journal.n_nats, kaddr, SUM_JOURNAL_SIZE); + memcpy(&curseg->journal->n_nats, kaddr, SUM_JOURNAL_SIZE); curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); - memcpy(&curseg->sum_blk->journal.n_sits, kaddr + SUM_JOURNAL_SIZE, + memcpy(&curseg->journal->n_sits, kaddr + SUM_JOURNAL_SIZE, SUM_JOURNAL_SIZE); offset = 2 * SUM_JOURNAL_SIZE; for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { unsigned short blk_off; - struct curseg_info *curseg = CURSEG_I(sbi, i); + curseg = CURSEG_I(sbi, i); reset_curseg(sbi, i); @@ -2021,6 +1890,8 @@ static void read_normal_summaries(struct f2fs_sb_info *sbi, int type) curseg = CURSEG_I(sbi, type); memcpy(curseg->sum_blk, sum_blk, sizeof(*sum_blk)); + /* copy node journal */ + memcpy(curseg->journal, &sum_blk->journal, SUM_JOURNAL_SIZE); reset_curseg(sbi, type); free(sum_blk); } @@ -2029,7 +1900,7 @@ void update_sum_entry(struct f2fs_sb_info *sbi, block_t blk_addr, struct f2fs_summary *sum) { struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); - struct f2fs_summary_block *sum_blk; + struct f2fs_summary_block *sum_blk = NULL; u32 segno, offset; int type, ret; struct seg_entry *se; @@ -2042,7 +1913,14 @@ void update_sum_entry(struct f2fs_sb_info *sbi, block_t blk_addr, se = get_seg_entry(sbi, segno); - sum_blk = get_sum_block(sbi, segno, &type); + if (c.func == FSCK) { + if (IS_NODESEG(se->type)) + sum_blk = get_sum_node_block_from_cache(sbi, segno, &type); + else + sum_blk = get_sum_data_block_from_cache(sbi, segno, &type); + } + if (!sum_blk) + sum_blk = get_sum_block(sbi, segno, &type); memcpy(&sum_blk->entries[offset], sum, sizeof(*sum)); sum_blk->footer.entry_type = IS_NODESEG(se->type) ? SUM_TYPE_NODE : SUM_TYPE_DATA; @@ -2051,11 +1929,50 @@ void update_sum_entry(struct f2fs_sb_info *sbi, block_t blk_addr, ret = dev_write_block(sum_blk, GET_SUM_BLKADDR(sbi, segno)); ASSERT(ret >= 0); - if (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA || - type == SEG_TYPE_MAX) + if (c.func != FSCK && + (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA || + type == SEG_TYPE_MAX)) free(sum_blk); } +static void read_append_journal(struct f2fs_sb_info *sbi) +{ + struct curseg_info *curseg; + char *kaddr; + block_t start_blk; + int ret; + struct f2fs_checkpoint *cp = F2FS_CKPT(sbi); + + start_blk = __start_cp_addr(sbi) + + le32_to_cpu(F2FS_CKPT(sbi)->cp_pack_total_block_count); + + kaddr = (char *)malloc(F2FS_BLKSIZE); + ASSERT(kaddr); + + if (is_set_ckpt_flags(cp, CP_APPEND_NAT_FLAG)) { + curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); + memset(kaddr, 0, F2FS_BLKSIZE); // lint !e668 + ret = dev_read_block(kaddr, start_blk++); + ASSERT(ret >= 0); + memcpy((char *)curseg->journal + SUM_JOURNAL_SIZE - NAT_JOURNAL_RESERVED, + kaddr, + NAT_APPEND_JOURNAL_ENTRIES * + sizeof(struct nat_journal_entry)); + } + + if (is_set_ckpt_flags(cp, CP_APPEND_SIT_FLAG)) { + curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); + memset(kaddr, 0, F2FS_BLKSIZE); //lint !e668 + ret = dev_read_block(kaddr, start_blk); + ASSERT(ret >= 0); + memcpy((char *)curseg->journal + SUM_JOURNAL_SIZE - SIT_JOURNAL_RESERVED, + kaddr, + SIT_APPEND_JOURNAL_ENTRIES * + sizeof(struct sit_journal_entry)); + } + free(kaddr); +} + static void restore_curseg_summaries(struct f2fs_sb_info *sbi) { int type = CURSEG_HOT_DATA; @@ -2067,6 +1984,11 @@ static void restore_curseg_summaries(struct f2fs_sb_info *sbi) for (; type <= CURSEG_COLD_NODE; type++) read_normal_summaries(sbi, type); + + if (is_set_ckpt_flags(F2FS_CKPT(sbi),CP_APPEND_NAT_FLAG) || + is_set_ckpt_flags(F2FS_CKPT(sbi),CP_APPEND_SIT_FLAG)) { + read_append_journal(sbi); + } } static int build_curseg(struct f2fs_sb_info *sbi) @@ -2076,6 +1998,7 @@ static int build_curseg(struct f2fs_sb_info *sbi) unsigned short blk_off; unsigned int segno; int i; + unsigned int append; array = malloc(sizeof(*array) * NR_CURSEG_TYPE); if (!array) { @@ -2086,12 +2009,27 @@ static int build_curseg(struct f2fs_sb_info *sbi) SM_I(sbi)->curseg_array = array; for (i = 0; i < NR_CURSEG_TYPE; i++) { + append = 0; + if (i == CURSEG_HOT_DATA && is_set_ckpt_flags(cp,CP_APPEND_NAT_FLAG)) { + append = NAT_APPEND_JOURNAL_ENTRIES * + sizeof(struct nat_journal_entry) - + NAT_JOURNAL_RESERVED; + } + if (i == CURSEG_COLD_DATA && is_set_ckpt_flags(cp,CP_APPEND_SIT_FLAG)) { + append = SIT_APPEND_JOURNAL_ENTRIES * + sizeof(struct sit_journal_entry) - + SIT_JOURNAL_RESERVED; + } array[i].sum_blk = calloc(sizeof(*(array[i].sum_blk)), 1); if (!array[i].sum_blk) { MSG(1, "\tError: Calloc failed for build_curseg!!\n"); goto seg_cleanup; } - + append = (append + sizeof(*(array[i].sum_blk)) - 1) & + (~(sizeof(*(array[i].sum_blk)) - 1)); + array[i].journal = malloc(sizeof(struct f2fs_journal) + append); + ASSERT(array[i].journal); + memset(array[i].journal, 0, sizeof(struct f2fs_journal) + append); if (i <= CURSEG_COLD_DATA) { blk_off = get_cp(cur_data_blkoff[i]); segno = get_cp(cur_data_segno[i]); @@ -2167,25 +2105,33 @@ void check_block_count(struct f2fs_sb_info *sbi, unsigned int i; /* check segment usage */ - if (GET_SIT_VBLOCKS(raw_sit) > sbi->blocks_per_seg) + if (GET_SIT_VBLOCKS(raw_sit) > sbi->blocks_per_seg) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_INVALID_SIT_VBLOCKS); ASSERT_MSG("Invalid SIT vblocks: segno=0x%x, %u", segno, GET_SIT_VBLOCKS(raw_sit)); + } /* check boundary of a given segment number */ - if (segno > end_segno) + if (segno > end_segno) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_INVALID_SEGNO); ASSERT_MSG("Invalid SEGNO: 0x%x", segno); + } /* check bitmap with valid block count */ for (i = 0; i < SIT_VBLOCK_MAP_SIZE; i++) valid_blocks += get_bits_in_byte(raw_sit->valid_map[i]); - if (GET_SIT_VBLOCKS(raw_sit) != valid_blocks) + if (GET_SIT_VBLOCKS(raw_sit) != valid_blocks) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_SIT_VBLOCKS_IS_ERROR); ASSERT_MSG("Wrong SIT valid blocks: segno=0x%x, %u vs. %u", segno, GET_SIT_VBLOCKS(raw_sit), valid_blocks); + } - if (GET_SIT_TYPE(raw_sit) >= NO_CHECK_TYPE) + if (GET_SIT_TYPE(raw_sit) >= NO_CHECK_TYPE) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_INVALID_SIT_TYPE); ASSERT_MSG("Wrong SIT type: segno=0x%x, %u", segno, GET_SIT_TYPE(raw_sit)); + } } void __seg_info_from_raw_sit(struct seg_entry *se, @@ -2253,6 +2199,13 @@ struct f2fs_summary_block *get_sum_block(struct f2fs_sb_info *sbi, *ret_type= SEG_TYPE_MAX; ssa_blk = GET_SUM_BLKADDR(sbi, segno); + + /* fsck has already checked curseg in + * get_sum_{data|node}_block_from_cache + */ + if (c.func == FSCK) + goto read_ssa_block; + for (type = 0; type < NR_CURSEG_NODE_TYPE; type++) { if (segno == get_cp(cur_node_segno[type])) { curseg = CURSEG_I(sbi, CURSEG_HOT_NODE + type); @@ -2283,6 +2236,7 @@ struct f2fs_summary_block *get_sum_block(struct f2fs_sb_info *sbi, } } +read_ssa_block: sum_blk = calloc(BLOCK_SZ, 1); ASSERT(sum_blk); @@ -2294,6 +2248,9 @@ struct f2fs_summary_block *get_sum_block(struct f2fs_sb_info *sbi, else if (IS_SUM_DATA_SEG(sum_blk->footer)) *ret_type = SEG_TYPE_DATA; + if (c.func == FSCK) + add_sum_block_to_cache(sbi, segno, *ret_type, sum_blk); + return sum_blk; } @@ -2396,10 +2353,25 @@ void update_data_blkaddr(struct f2fs_sb_info *sbi, nid_t nid, void update_nat_blkaddr(struct f2fs_sb_info *sbi, nid_t ino, nid_t nid, block_t newaddr) { - struct f2fs_nat_block *nat_block; + struct f2fs_nat_block *nat_block = NULL; + struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); + struct f2fs_journal *journal = curseg->journal; + struct f2fs_nat_entry *entry; pgoff_t block_addr; int entry_off; - int ret; + int ret, i; + + for (i = 0; i < nats_in_cursum(journal); i++) { + if (le32_to_cpu(nid_in_journal(journal, i)) == nid) { + entry = &nat_in_journal(journal, i); + entry->block_addr = cpu_to_le32(newaddr); + if (ino) + entry->ino = cpu_to_le32(ino); + MSG(0, "update nat(nid:%d) blkaddr [0x%x] in journal\n", + nid, newaddr); + goto update_cache; + } + } nat_block = (struct f2fs_nat_block *)calloc(BLOCK_SZ, 1); ASSERT(nat_block); @@ -2410,15 +2382,21 @@ void update_nat_blkaddr(struct f2fs_sb_info *sbi, nid_t ino, ret = dev_read_block(nat_block, block_addr); ASSERT(ret >= 0); + entry = &nat_block->entries[entry_off]; if (ino) - nat_block->entries[entry_off].ino = cpu_to_le32(ino); - nat_block->entries[entry_off].block_addr = cpu_to_le32(newaddr); - if (c.func == FSCK) - F2FS_FSCK(sbi)->entries[nid] = nat_block->entries[entry_off]; + entry->ino = cpu_to_le32(ino); + entry->block_addr = cpu_to_le32(newaddr); ret = dev_write_block(nat_block, block_addr); ASSERT(ret >= 0); - free(nat_block); + +update_cache: + if (c.func == FSCK) + F2FS_FSCK(sbi)->entries[nid] = *entry; + + if (nat_block) { + free(nat_block); + } } void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni) @@ -2437,17 +2415,66 @@ void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni) node_info_from_raw_nat(ni, &raw_nat); } +void check_sit_nat_journal(struct f2fs_sb_info *sbi) +{ + struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); + struct curseg_info *cold_data_curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); + struct curseg_info *hot_data_curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); + struct f2fs_journal *sit_journal = &cold_data_curseg->sum_blk->journal; + struct f2fs_journal *nat_journal = &hot_data_curseg->sum_blk->journal; + unsigned int i, segno, sit_journal_num, nat_journal_num; + u32 nid, nr_nat_blks, max_nid; + /* segment_count_nat includes pair segment so divide to 2. */ + nr_nat_blks = ((get_sb(segment_count_nat) / 2) << + sbi->log_blocks_per_seg); + max_nid = nr_nat_blks * NAT_ENTRY_PER_BLOCK; + + sit_journal_num = sits_in_cursum(sit_journal); + nat_journal_num = nats_in_cursum(nat_journal); + + /* check sit journal value */ + if (sit_journal_num > SIT_JOURNAL_ENTRIES) { + ASSERT_MSG("Invalid sit journal num: %u", sit_journal_num); + sit_journal->n_sits = cpu_to_le16(SIT_JOURNAL_ENTRIES); + } + + for (i = 0; i < sits_in_cursum(sit_journal); i++) { + segno = le32_to_cpu(segno_in_journal(sit_journal, i)); + if (segno > (MAIN_SEGS(sbi) - 1)) { + ASSERT_MSG("Invalid SEGNO: 0x%x", segno); + } + } + + /* check nat journal value */ + if (nat_journal_num > NAT_JOURNAL_ENTRIES) { + ASSERT_MSG("Invalid nat journal num: %u", nat_journal_num); + nat_journal->n_nats = cpu_to_le16(NAT_JOURNAL_ENTRIES); + } + + for (i = 0; i < nats_in_cursum(nat_journal); i++) { + nid = le32_to_cpu(nid_in_journal(nat_journal, i)); + if (nid >= max_nid) { + ASSERT_MSG("Invalid nid: 0%x", nid); + nid_in_journal(nat_journal, i) = 0; + memset_s(&nat_in_journal(nat_journal, i), + sizeof(struct f2fs_nat_entry), 0, sizeof(struct f2fs_nat_entry)); + FIX_MSG("Remove nid [0x%x] in nat journal\n", nid); + } + } +} + static int build_sit_entries(struct f2fs_sb_info *sbi) { struct sit_info *sit_i = SIT_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); - struct f2fs_journal *journal = &curseg->sum_blk->journal; + struct f2fs_journal *journal = curseg->journal; struct f2fs_sit_block *sit_blk; struct seg_entry *se; struct f2fs_sit_entry sit; int sit_blk_cnt = SIT_BLK_CNT(sbi); unsigned int i, segno, end; unsigned int readed, start_blk = 0; + unsigned int sit_journal_entries; sit_blk = calloc(BLOCK_SZ, 1); if (!sit_blk) { @@ -2476,8 +2503,12 @@ static int build_sit_entries(struct f2fs_sb_info *sbi) free(sit_blk); + sit_journal_entries = SIT_JOURNAL_ENTRIES; + if (is_set_ckpt_flags(F2FS_CKPT(sbi), CP_APPEND_SIT_FLAG)) { + sit_journal_entries += SIT_APPEND_JOURNAL_ENTRIES; + } - if (sits_in_cursum(journal) > SIT_JOURNAL_ENTRIES) { + if (sits_in_cursum(journal) > sit_journal_entries) { MSG(0, "\tError: build_sit_entries truncate n_sits(%u) to " "SIT_JOURNAL_ENTRIES(%zu)\n", sits_in_cursum(journal), SIT_JOURNAL_ENTRIES); @@ -2540,6 +2571,7 @@ static int late_build_segment_manager(struct f2fs_sb_info *sbi) return 1; /* this function was already called */ sbi->seg_manager_done = true; + check_sit_nat_journal(sbi); if (build_sit_entries(sbi)) { free (sbi->sm_info); return -ENOMEM; @@ -2608,6 +2640,7 @@ void rewrite_sit_area_bitmap(struct f2fs_sb_info *sbi) ASSERT(sit_blk); /* remove sit journal */ sum->journal.n_sits = 0; + curseg->journal->n_sits = 0; ptr = fsck->main_area_bitmap; @@ -2648,7 +2681,7 @@ void rewrite_sit_area_bitmap(struct f2fs_sb_info *sbi) static int flush_sit_journal_entries(struct f2fs_sb_info *sbi) { struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); - struct f2fs_journal *journal = &curseg->sum_blk->journal; + struct f2fs_journal *journal = curseg->journal; struct sit_info *sit_i = SIT_I(sbi); struct f2fs_sit_block *sit_blk; unsigned int segno; @@ -2661,6 +2694,9 @@ static int flush_sit_journal_entries(struct f2fs_sb_info *sbi) struct seg_entry *se; segno = segno_in_journal(journal, i); + if (segno >= MAIN_SEGS(sbi)) { + continue; + } se = get_seg_entry(sbi, segno); get_current_sit_page(sbi, segno, sit_blk); @@ -2682,7 +2718,7 @@ static int flush_sit_journal_entries(struct f2fs_sb_info *sbi) static int flush_nat_journal_entries(struct f2fs_sb_info *sbi) { struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); - struct f2fs_journal *journal = &curseg->sum_blk->journal; + struct f2fs_journal *journal = curseg->journal; struct f2fs_nat_block *nat_block; pgoff_t block_addr; int entry_off; @@ -2983,7 +3019,7 @@ void zero_journal_entries(struct f2fs_sb_info *sbi) int i; for (i = 0; i < NO_CHECK_TYPE; i++) - CURSEG_I(sbi, i)->sum_blk->journal.n_nats = 0; + CURSEG_I(sbi, i)->journal->n_nats = 0; } void write_curseg_info(struct f2fs_sb_info *sbi) @@ -3011,7 +3047,7 @@ int lookup_nat_in_journal(struct f2fs_sb_info *sbi, u32 nid, struct f2fs_nat_entry *raw_nat) { struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); - struct f2fs_journal *journal = &curseg->sum_blk->journal; + struct f2fs_journal *journal = curseg->journal; int i = 0; for (i = 0; i < nats_in_cursum(journal); i++) { @@ -3028,13 +3064,16 @@ int lookup_nat_in_journal(struct f2fs_sb_info *sbi, u32 nid, void nullify_nat_entry(struct f2fs_sb_info *sbi, u32 nid) { struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); - struct f2fs_journal *journal = &curseg->sum_blk->journal; + struct f2fs_journal *journal = curseg->journal; struct f2fs_nat_block *nat_block; pgoff_t block_addr; int entry_off; int ret; int i = 0; + if (c.func == FSCK) + F2FS_FSCK(sbi)->entries[nid].block_addr = 0; + /* check in journal */ for (i = 0; i < nats_in_cursum(journal); i++) { if (le32_to_cpu(nid_in_journal(journal, i)) == nid) { @@ -3060,7 +3099,7 @@ void nullify_nat_entry(struct f2fs_sb_info *sbi, u32 nid) } else { memset(&nat_block->entries[entry_off], 0, sizeof(struct f2fs_nat_entry)); - FIX_MSG("Remove nid [0x%x] in NAT", nid); + MSG(1, "Remove nid [0x%x] in NAT\n", nid); } ret = dev_write_block(nat_block, block_addr); @@ -3118,6 +3157,9 @@ void write_checkpoint(struct f2fs_sb_info *sbi) u32 flags = CP_UMOUNT_FLAG; int i, ret; uint32_t crc = 0; + struct f2fs_journal *nat_journal = CURSEG_I(sbi, CURSEG_HOT_DATA)->journal; + struct f2fs_journal *sit_journal = CURSEG_I(sbi, CURSEG_COLD_DATA)->journal; + u32 nat_block = 0; if (is_set_ckpt_flags(cp, CP_ORPHAN_PRESENT_FLAG)) { orphan_blks = __start_sum_addr(sbi) - 1; @@ -3134,6 +3176,17 @@ void write_checkpoint(struct f2fs_sb_info *sbi) set_cp(checksum_offset, CP_CHKSUM_OFFSET); } + /* if has append nat or sit block,we need set append flag */ + if (nats_in_cursum(nat_journal) > NAT_JOURNAL_ENTRIES) { + flags |= CP_APPEND_NAT_FLAG; + } + + if (sits_in_cursum(sit_journal) > SIT_JOURNAL_ENTRIES) { + flags |= CP_APPEND_SIT_FLAG; + } + + set_cp(ckpt_flags, flags); + set_cp(free_segment_count, get_free_segments(sbi)); if (c.func == FSCK) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); @@ -3148,9 +3201,6 @@ void write_checkpoint(struct f2fs_sb_info *sbi) } set_cp(cp_pack_total_block_count, 8 + orphan_blks + get_sb(cp_payload)); - flags = update_nat_bits_flags(sb, cp, flags); - set_cp(ckpt_flags, flags); - crc = f2fs_checkpoint_chksum(cp); *((__le32 *)((unsigned char *)cp + get_cp(checksum_offset))) = cpu_to_le32(crc); @@ -3173,6 +3223,10 @@ void write_checkpoint(struct f2fs_sb_info *sbi) struct curseg_info *curseg = CURSEG_I(sbi, i); u64 ssa_blk; + if ((i == CURSEG_HOT_DATA) || (i == CURSEG_COLD_DATA)) { + memcpy(&curseg->sum_blk->journal, curseg->journal, + sizeof(struct f2fs_journal)); + } ret = dev_write_block(curseg->sum_blk, cp_blk_no++); ASSERT(ret >= 0); @@ -3184,14 +3238,30 @@ void write_checkpoint(struct f2fs_sb_info *sbi) } } - /* Write nat bits */ - if (flags & CP_NAT_BITS_FLAG) - write_nat_bits(sbi, sb, cp, sbi->cur_cp); - /* in case of sudden power off */ ret = f2fs_fsync_device(); ASSERT(ret >= 0); + /* write nat append block */ + if (flags & CP_APPEND_NAT_FLAG) { + struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); + block_t nat_blk = __start_cp_addr(sbi) + + le32_to_cpu(F2FS_CKPT(sbi)->cp_pack_total_block_count); + ret = dev_write_block((char *)curseg->journal + SUM_JOURNAL_SIZE - NAT_JOURNAL_RESERVED, + nat_blk); + nat_block = 1; + ASSERT(ret >= 0); + } + + /* write sit append block */ + if (flags & CP_APPEND_SIT_FLAG) { + struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); + block_t sit_blk = __start_cp_addr(sbi) + + le32_to_cpu(F2FS_CKPT(sbi)->cp_pack_total_block_count) + nat_block; + ret = dev_write_block((char *)curseg->journal + SUM_JOURNAL_SIZE - SIT_JOURNAL_RESERVED, + sit_blk); + ASSERT(ret >= 0); + } /* write the last cp */ ret = dev_write_block(cp, cp_blk_no++); ASSERT(ret >= 0); @@ -3213,7 +3283,7 @@ void write_checkpoints(struct f2fs_sb_info *sbi) void build_nat_area_bitmap(struct f2fs_sb_info *sbi) { struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); - struct f2fs_journal *journal = &curseg->sum_blk->journal; + struct f2fs_journal *journal = curseg->journal; struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); struct f2fs_nm_info *nm_i = NM_I(sbi); @@ -3266,6 +3336,7 @@ void build_nat_area_bitmap(struct f2fs_sb_info *sbi) * Set this bit, and fsck_verify will fix it. */ if (le32_to_cpu(nat_block->entries[i].block_addr) != 0x1) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_INVALID_NAT_ENTRY1_ENTRY2); ASSERT_MSG("\tError: ino[0x%x] block_addr[0x%x] is invalid\n", nid + i, le32_to_cpu(nat_block->entries[i].block_addr)); f2fs_set_bit(nid + i, fsck->nat_area_bitmap); @@ -3277,6 +3348,7 @@ void build_nat_area_bitmap(struct f2fs_sb_info *sbi) if (ni.blk_addr == 0x0) continue; if (ni.ino == 0x0) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_INVALID_INO_OR_BLKADDR); ASSERT_MSG("\tError: ino[0x%8x] or blk_addr[0x%16x]" " is invalid\n", ni.ino, ni.blk_addr); } @@ -3291,6 +3363,7 @@ void build_nat_area_bitmap(struct f2fs_sb_info *sbi) * nat_area_bitmap, fsck_verify will * nullify it */ + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_INVALID_NAT_ENTRY0); ASSERT_MSG("Invalid nat entry[0]: " "blk_addr[0x%x]\n", ni.blk_addr); fsck->chk.valid_nat_entry_cnt--; @@ -3313,6 +3386,9 @@ void build_nat_area_bitmap(struct f2fs_sb_info *sbi) DBG(3, "==> Found nid [0x%x] in nat cache, update it\n", nid); + if (nid == 0) { + continue; + } /* Clear the original bit and count */ if (fsck->entries[nid].block_addr != 0x0) { fsck->chk.valid_nat_entry_cnt--; @@ -3326,9 +3402,11 @@ void build_nat_area_bitmap(struct f2fs_sb_info *sbi) sizeof(struct f2fs_nat_entry)); node_info_from_raw_nat(&ni, &raw_nat); if (ni.blk_addr != 0x0) { - if (ni.ino == 0x0) + if (ni.ino == 0x0) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_INVALID_INO_OR_BLKADDR); ASSERT_MSG("\tError: ino[0x%8x] or blk_addr[0x%16x]" " is invalid\n", ni.ino, ni.blk_addr); + } if (ni.ino == nid) { fsck->nat_valid_inode_cnt++; DBG(3, "ino[0x%8x] maybe is inode\n", ni.ino); @@ -3655,12 +3733,14 @@ int f2fs_do_mount(struct f2fs_sb_info *sbi) struct f2fs_super_block *sb = NULL; int ret; + c.bug_on = 0; sbi->active_logs = NR_CURSEG_TYPE; ret = validate_super_block(sbi, SB0_ADDR); if (ret) { ret = validate_super_block(sbi, SB1_ADDR); if (ret) { dump_sbi_info(sbi); + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_INVALID_SUPER_BLOCK); return -1; } } @@ -3676,6 +3756,7 @@ int f2fs_do_mount(struct f2fs_sb_info *sbi) ret = get_valid_checkpoint(sbi); if (ret) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_INVALID_CHECKPOINT); ERR_MSG("Can't find valid checkpoint\n"); dump_sbi_info(sbi); print_ckpt_info(sbi); @@ -3685,6 +3766,7 @@ int f2fs_do_mount(struct f2fs_sb_info *sbi) c.bug_on = 0; if (sanity_check_ckpt(sbi)) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_POLLUTE_CHECKPOINT); ERR_MSG("Checkpoint is polluted\n"); dump_sbi_info(sbi); print_ckpt_info(sbi); @@ -3764,16 +3846,19 @@ out: sbi->alloc_valid_block_count = 0; if (early_build_segment_manager(sbi)) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_BUILD_SEGMENT_MANAGER_ERROR); ERR_MSG("early_build_segment_manager failed\n"); return -1; } if (build_node_manager(sbi)) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_BUILD_NODE_MANAGER_ERROR); ERR_MSG("build_node_manager failed\n"); return -1; } if (record_fsync_data(sbi)) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_RECORD_FSYNC_DATA_ERROR); ERR_MSG("record_fsync_data failed\n"); return -1; } @@ -3782,6 +3867,7 @@ out: return 1; if (late_build_segment_manager(sbi) < 0) { + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_BUILD_SEGMENT_MANAGER_ERROR); ERR_MSG("late_build_segment_manager failed\n"); return -1; } @@ -3791,11 +3877,6 @@ out: return -1; } - /* Check nat_bits */ - if (c.func == FSCK && is_set_ckpt_flags(cp, CP_NAT_BITS_FLAG)) { - if (check_nat_bits(sbi, sb, cp) && c.fix_on) - write_nat_bits(sbi, sb, cp, sbi->cur_cp); - } return 0; } @@ -3819,14 +3900,18 @@ void f2fs_do_umount(struct f2fs_sb_info *sbi) free(sm_i->sit_info); /* free sm_info */ - for (i = 0; i < NR_CURSEG_TYPE; i++) + for (i = 0; i < NR_CURSEG_TYPE; i++) { free(sm_i->curseg_array[i].sum_blk); + free(sm_i->curseg_array[i].journal); + } free(sm_i->curseg_array); free(sbi->sm_info); free(sbi->ckpt); + sbi->ckpt = NULL; free(sbi->raw_super); + sbi->raw_super = NULL; } #ifdef WITH_ANDROID diff --git a/fsck/queue.c b/fsck/queue.c new file mode 100644 index 0000000000000000000000000000000000000000..468698af25f25fd9b49a6099abfa535473acc692 --- /dev/null +++ b/fsck/queue.c @@ -0,0 +1,271 @@ +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include "fsck.h" + +#ifdef POSIX_FADV_WILLNEED +struct work_reada_arg { + struct f2fs_sb_info *sbi; + int type; +}; +static struct work_reada_arg *args[MAX_TYPE] = {NULL, NULL}; + +static void *work_reada_block(void *arg) +{ + struct f2fs_sb_info *sbi = ((struct work_reada_arg *)arg)->sbi; + int type = ((struct work_reada_arg *)arg)->type; + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + struct ra_work *w, *t; + struct list_head tmp_queue; + + for (; ;) { + if ((fsck->quit_thread[type]) != 0) { + return NULL; + } + + pthread_mutex_lock(&fsck->mutex[type]); + while (list_empty(&fsck->ra_queue[type]) != 0) { + pthread_cond_wait(&fsck->cond[type], &fsck->mutex[type]); + if (fsck->quit_thread[type] != 0) { + pthread_mutex_unlock(&fsck->mutex[type]); + return NULL; + } + } + /* move all works to tmp_queue */ + tmp_queue = fsck->ra_queue[type]; + tmp_queue.next->prev = &tmp_queue; + tmp_queue.prev->next = &tmp_queue; + INIT_LIST_HEAD(&fsck->ra_queue[type]); + pthread_mutex_unlock(&fsck->mutex[type]); + + list_for_each_entry_safe(w, t, &tmp_queue, entry) { + list_del(&w->entry); + if (!fsck->quit_thread[type]) + dev_reada_block(w->blkaddr); + free(w); + } + } +} + +void init_reada_queue(struct f2fs_sb_info *sbi) +{ + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + int i, ret; + + for (i = 0; i < MAX_TYPE; i++) { + INIT_LIST_HEAD(&fsck->ra_queue[i]); + fsck->quit_thread[i] = 0; + pthread_cond_init(&fsck->cond[i], NULL); + pthread_mutex_init(&fsck->mutex[i], NULL); + + args[i] = malloc(sizeof(struct work_reada_arg)); + if (args[i] == NULL) { + MSG(0, "args[i] malloc failed\n"); + return; + } + ASSERT(args[i]); + args[i]->sbi = sbi; + args[i]->type = i; + ret = pthread_create(&fsck->thread[i], NULL, work_reada_block, args[i]); + if (ret != 0) { + MSG(0, "pthread_create failed\n"); + return; + } + } + + MSG(0, "Info: readahead queue is enabled\n"); +} + +void exit_reada_queue(struct f2fs_sb_info *sbi) +{ + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + struct ra_work *w, *t; + int i; + + /* tell thread to exit */ + for (i = 0; i < MAX_TYPE; i++) { + fsck->quit_thread[i] = 1; + pthread_cond_signal(&fsck->cond[i]); + } + for (i = 0; i < MAX_TYPE; i++) { + pthread_join(fsck->thread[i], NULL); + pthread_mutex_destroy(&fsck->mutex[i]); + pthread_cond_destroy(&fsck->cond[i]); + free(args[i]); + args[i] = NULL; + list_for_each_entry_safe(w, t, &fsck->ra_queue[i], entry) { + list_del(&w->entry); + free(w); + } + } +} + +void queue_reada_block(struct f2fs_sb_info *sbi, block_t blkaddr, int type) +{ + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + struct ra_work *work; + + work = malloc(sizeof(struct ra_work)); + if (!work) { + MSG(0, "work malloc failed\n"); + return; + } + + INIT_LIST_HEAD(&work->entry); + work->blkaddr = blkaddr; + + pthread_mutex_lock(&fsck->mutex[type]); + list_add_tail(&work->entry, &fsck->ra_queue[type]); + pthread_mutex_unlock(&fsck->mutex[type]); + + pthread_cond_signal(&fsck->cond[type]); +} + +void build_sum_cache_list(struct f2fs_sb_info *sbi) +{ + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + int i, j; + + for (i = 0; i < MAX_TYPE; i++) { + for (j = 0; j < HASHTABLE_SIZE; j++) { + INIT_LIST_HEAD(&fsck->sum_cache_head[i][j]); + fsck->sum_cache_cnt[i][j] = 0; + } + } +} + +void destroy_sum_cache_list(struct f2fs_sb_info *sbi) +{ + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + struct sum_cache *sum, *tmp; + struct list_head *head; + int i, j; + + for (i = 0; i < MAX_TYPE; i++) { + for (j = 0; j < HASHTABLE_SIZE; j++) { + head = &fsck->sum_cache_head[i][j]; + list_for_each_entry_safe(sum, tmp, head, list) { + list_del(&sum->list); + free(sum->sum_blk); + free(sum); + } + fsck->sum_cache_cnt[i][j] = 0; + } + } +} + +struct f2fs_summary_block *get_sum_node_block_from_cache(struct f2fs_sb_info *sbi, + unsigned int segno, int *type) +{ + struct f2fs_checkpoint *cp = F2FS_CKPT(sbi); + struct curseg_info *curseg; + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + struct sum_cache *sum, *tmp; + struct list_head *head; + int tb_idx, i; + + for (i = 0; i < NR_CURSEG_NODE_TYPE; i++) { + if (segno == get_cp(cur_node_segno[i])) { + curseg = CURSEG_I(sbi, CURSEG_HOT_NODE + i); + if (!IS_SUM_NODE_SEG(curseg->sum_blk->footer)) { + ASSERT_MSG("segno [0x%x] indicates a data " + "segment, but should be node", + segno); + *type = -SEG_TYPE_CUR_NODE; + } else { + *type = SEG_TYPE_CUR_NODE; + } + return curseg->sum_blk; + } + } + + tb_idx = segno % HASHTABLE_SIZE; + head = &fsck->sum_cache_head[SUM_TYPE_NODE][tb_idx]; + list_for_each_entry_safe(sum, tmp, head, list) { + if (sum->segno != segno) + continue; + list_move_tail(&sum->list, head); + *type = SEG_TYPE_NODE; + return sum->sum_blk; + } + + return NULL; +} + +struct f2fs_summary_block *get_sum_data_block_from_cache(struct f2fs_sb_info *sbi, + unsigned int segno, int *type) +{ + struct f2fs_checkpoint *cp = F2FS_CKPT(sbi); + struct curseg_info *curseg; + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + struct sum_cache *sum, *tmp; + struct list_head *head; + int tb_idx, i; + + for (i = 0; i < NR_CURSEG_DATA_TYPE; i++) { + if (segno == get_cp(cur_data_segno[i])) { + curseg = CURSEG_I(sbi, i); + if (!IS_SUM_NODE_SEG(curseg->sum_blk->footer)) { + ASSERT_MSG("segno [0x%x] indicates a node " + "segment, but should be data", + segno); + *type = -SEG_TYPE_CUR_DATA; + } else { + *type = SEG_TYPE_CUR_DATA; + } + return curseg->sum_blk; + } + } + + tb_idx = segno % HASHTABLE_SIZE; + head = &fsck->sum_cache_head[SUM_TYPE_DATA][tb_idx]; + list_for_each_entry_safe(sum, tmp, head, list) { + if (sum->segno != segno) + continue; + /* sum cache hit, move it to the end of the list */ + list_move_tail(&sum->list, head); + *type = SEG_TYPE_DATA; + return sum->sum_blk; + } + + return NULL; +} + +void add_sum_block_to_cache(struct f2fs_sb_info *sbi, unsigned int segno, + int type, struct f2fs_summary_block *blk) +{ + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + struct sum_cache *sum; + int sum_type = type == SEG_TYPE_NODE ? SUM_TYPE_NODE : SUM_TYPE_DATA; + int tb_idx = segno % HASHTABLE_SIZE; + int cnt = fsck->sum_cache_cnt[sum_type][tb_idx]; + struct list_head *head = &fsck->sum_cache_head[sum_type][tb_idx]; + + if (cnt < MAX_SUM_CACHE_CNT) { + /* add to list */ + sum = malloc(sizeof(struct sum_cache)); + if (!sum) { + MSG(0, "Memory allocation failed\n"); + return; + } + sum->segno = segno; + sum->sum_blk = blk; + INIT_LIST_HEAD(&sum->list); + list_add_tail(&sum->list, head); + fsck->sum_cache_cnt[sum_type][tb_idx]++; + } else { + /* cache list full, replace the oldest one */ + sum = list_first_entry(head, struct sum_cache, list); + free(sum->sum_blk); + sum->sum_blk = NULL; + sum->segno = segno; + sum->sum_blk = blk; + list_move_tail(&sum->list, head); + } +} + +#endif // POSIX_FADV_WILLNEED \ No newline at end of file diff --git a/fsck/queue.h b/fsck/queue.h new file mode 100644 index 0000000000000000000000000000000000000000..25b9dc0cc6378e269af07ef6cd787347a2c1922d --- /dev/null +++ b/fsck/queue.h @@ -0,0 +1,42 @@ +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef _FSCK_QUEUE_H_ +#define _FSCK_QUEUE_H_ + +#ifdef POSIX_FADV_WILLNEED +#include "f2fs.h" + +struct ra_work { + struct list_head entry; + block_t blkaddr; +}; + +/* queue.c */ +extern void queue_reada_block(struct f2fs_sb_info *sbi, block_t blkaddr, int type); +extern void init_reada_queue(struct f2fs_sb_info *sbi); +extern void exit_reada_queue(struct f2fs_sb_info *sbi); +extern void build_sum_cache_list(struct f2fs_sb_info *sbi); +extern void destroy_sum_cache_list(struct f2fs_sb_info *sbi); +extern struct f2fs_summary_block *get_sum_node_block_from_cache(struct f2fs_sb_info *sbi, + unsigned int segno, int *type); +extern struct f2fs_summary_block *get_sum_data_block_from_cache(struct f2fs_sb_info *sbi, + unsigned int segno, int *type); +extern void add_sum_block_to_cache(struct f2fs_sb_info *sbi, unsigned int segno, + int type, struct f2fs_summary_block *blk); +#else // POSIX_FADV_WILLNEED +#define queue_reada_block(sbi, blkaddr, type) dev_reada_block(blkaddr) +#define init_reada_queue(sbi) MSG(0, "Info: readahead queue is not enabled\n") +#define exit_reada_queue(sbi) +#define build_sum_cache_list(sbi) +#define destroy_sum_cache_list(sbi) +#define get_sum_node_block_from_cache(sbi, segno, type) +#define get_sum_data_block_from_cache(sbi, segno, type) +#define add_sum_block_to_cache(segno, type, blk) +#endif // POSIX_FADV_WILLNEED + +#endif // _FSCK_QUEUE_H_ diff --git a/fsck/resize.c b/fsck/resize.c index ceaedb7085c465124168525439b27bb98072a3cb..4e344dd98e5932b346b10a41be410bb3ab2285d3 100644 --- a/fsck/resize.c +++ b/fsck/resize.c @@ -9,6 +9,9 @@ */ #include "fsck.h" +extern void hmfs_enable_large_nat_bitmap(struct f2fs_sb_info *sbi, unsigned long long total_size, + unsigned long long cur_size); + static int get_new_sb(struct f2fs_super_block *sb) { uint32_t zone_size_bytes; @@ -175,6 +178,12 @@ static void migrate_main(struct f2fs_sb_info *sbi, unsigned int offset) ASSERT(raw != NULL); + if (offset == 0) { + DBG(0, "Info: The address of the main area has not changed, so no migration is required.\n"); + free(raw); + return; + } + for (i = MAIN_SEGS(sbi) - 1; i >= 0; i--) { se = get_seg_entry(sbi, i); if (!se->valid_blocks) @@ -232,6 +241,24 @@ static void move_ssa(struct f2fs_sb_info *sbi, unsigned int segno, DBG(1, "Info: Done to migrate SSA blocks\n"); } +static void init_ssa_end_to_main_addr(struct f2fs_sb_info *sbi, + struct f2fs_super_block *new_sb, block_t start, block_t end) +{ + int ret; + block_t offset = 0; + void *zero_blk = calloc(F2FS_BLKSIZE, 1); + + ASSERT(zero_blk); + while (start + offset < end) { + ret = dev_write_block(zero_blk, start + offset); + DBG(3, "Write zero: 0x%x\n", start + offset); + ASSERT(ret >= 0); + offset++; + } + + free(zero_blk); +} + static void migrate_ssa(struct f2fs_sb_info *sbi, struct f2fs_super_block *new_sb, unsigned int offset) { @@ -242,10 +269,20 @@ static void migrate_ssa(struct f2fs_sb_info *sbi, block_t expand_sum_blkaddr = new_sum_blkaddr + MAIN_SEGS(sbi) - offset; block_t blkaddr; + bool only_init_tail = is_fs_layout_unchanged(sb, new_sb); + int ret; void *zero_block = calloc(BLOCK_SZ, 1); ASSERT(zero_block); + if (only_init_tail) { + init_ssa_end_to_main_addr(sbi, new_sb, get_newsb(ssa_blkaddr) + MAIN_SEGS(sbi), end_sum_blkaddr); + DBG(0, "Info: Done to initial SSA expand blocks: sum_blkaddr = 0x%x -> 0x%x\n", + get_newsb(ssa_blkaddr) + MAIN_SEGS(sbi), end_sum_blkaddr); + free(zero_block); + return; + } + if (offset && new_sum_blkaddr < old_sum_blkaddr + offset) { blkaddr = new_sum_blkaddr; while (blkaddr < end_sum_blkaddr) { @@ -333,7 +370,7 @@ static void migrate_nat(struct f2fs_sb_info *sbi, void *nat_block; int nid, ret, new_max_nid; pgoff_t block_off; - pgoff_t block_addr; + pgoff_t block_addr, dst_block_addr; int seg_off; nat_block = malloc(BLOCK_SZ); @@ -352,15 +389,19 @@ static void migrate_nat(struct f2fs_sb_info *sbi, f2fs_clear_bit(block_off, nm_i->nat_bitmap); } + dst_block_addr = (pgoff_t)(new_nat_blkaddr + + (seg_off << sbi->log_blocks_per_seg << 1) + + (block_off & ((1 << sbi->log_blocks_per_seg) - 1))); + + /* No need to do migrate in place. */ + if (dst_block_addr == block_addr) { + continue; + } ret = dev_read_block(nat_block, block_addr); ASSERT(ret >= 0); - block_addr = (pgoff_t)(new_nat_blkaddr + - (seg_off << sbi->log_blocks_per_seg << 1) + - (block_off & ((1 << sbi->log_blocks_per_seg) - 1))); - /* new bitmap should be zeros */ - ret = dev_write_block(nat_block, block_addr); + ret = dev_write_block(nat_block, dst_block_addr); ASSERT(ret >= 0); } /* zero out newly assigned nids */ @@ -404,7 +445,6 @@ static void migrate_sit(struct f2fs_sb_info *sbi, int ret; ASSERT(sit_blk); - /* initialize with zeros */ for (index = 0; index < sit_blks; index++) { ret = dev_write_block(sit_blk, get_newsb(sit_blkaddr) + index); @@ -458,9 +498,9 @@ static void rebuild_checkpoint(struct f2fs_sb_info *sbi, unsigned int free_segment_count, new_segment_count; block_t new_cp_blks = 1 + get_newsb(cp_payload); block_t orphan_blks = 0; - block_t new_cp_blk_no, old_cp_blk_no; + block_t new_cp_blk_no, old_cp_blk_no, old_cp_bak_blk_no; uint32_t crc = 0; - u32 flags; + u32 flags = get_cp(ckpt_flags); void *buf; int i, ret; @@ -518,20 +558,16 @@ static void rebuild_checkpoint(struct f2fs_sb_info *sbi, ((get_newsb(segment_count_nat) / 2) << get_newsb(log_blocks_per_seg)) / 8); - /* update nat_bits flag */ - flags = update_nat_bits_flags(new_sb, cp, get_cp(ckpt_flags)); - if (c.large_nat_bitmap) + if (c.large_nat_bitmap) { flags |= CP_LARGE_NAT_BITMAP_FLAG; + set_cp(ckpt_flags, flags); + } - if (flags & CP_COMPACT_SUM_FLAG) - flags &= ~CP_COMPACT_SUM_FLAG; if (flags & CP_LARGE_NAT_BITMAP_FLAG) set_cp(checksum_offset, CP_MIN_CHKSUM_OFFSET); else set_cp(checksum_offset, CP_CHKSUM_OFFSET); - set_cp(ckpt_flags, flags); - memcpy(new_cp, cp, (unsigned char *)cp->sit_nat_version_bitmap - (unsigned char *)cp); if (c.safe_resize) @@ -584,13 +620,13 @@ static void rebuild_checkpoint(struct f2fs_sb_info *sbi, ret = dev_write_block(new_cp, new_cp_blk_no++); ASSERT(ret >= 0); - /* Write nat bits */ - if (flags & CP_NAT_BITS_FLAG) - write_nat_bits(sbi, new_sb, new_cp, sbi->cur_cp == 1 ? 2 : 1); - - /* disable old checkpoint */ - memset(buf, 0, BLOCK_SZ); - ret = dev_write_block(buf, old_cp_blk_no); + /* update crc of old checkpoint */ + crc = f2fs_checkpoint_chksum(cp); + *((__le32 *)((unsigned char *)cp + get_cp(checksum_offset))) = cpu_to_le32(crc); + ret = dev_write_block(cp, old_cp_blk_no); + ASSERT(ret >= 0); + old_cp_bak_blk_no = old_cp_blk_no + get_cp(cp_pack_total_block_count) - 1; + ret = dev_write_block(cp, old_cp_bak_blk_no); ASSERT(ret >= 0); free(buf); @@ -618,6 +654,41 @@ static int f2fs_resize_check(struct f2fs_sb_info *sbi, struct f2fs_super_block * return 0; } +/* + * old filesystem layout: + * | sb | cp |---sit---|----nat----|-----ssa-----|------main area------| + * When the partition size decreases, get_new_sb will yield such f2fs filesystem layout: + * get_new_sb filesystem layout: + * | sb | cp |---sit---|---nat---|-----ssa-----|---------main area---------| + * but resize.f2fs not support performing migration when the main_blkaddr moves forward, + * so we need to revert the old filesystem layout in old_sb, and only increase the segment + * and section count. The final layout is as follows: + * | sb | cp |---sit---|---nat---|-----ssa-----|---------main area---------| + * */ +static void revert_old_fs_layout(struct f2fs_sb_info *sbi, struct f2fs_super_block *sb, struct f2fs_super_block *new_sb) +{ + set_newsb(sit_blkaddr, get_sb(sit_blkaddr)); + set_newsb(segment_count_sit, get_sb(segment_count_sit)); + set_newsb(nat_blkaddr, get_sb(nat_blkaddr)); + set_newsb(segment_count_nat, get_sb(segment_count_nat)); + set_newsb(ssa_blkaddr, get_sb(ssa_blkaddr)); + set_newsb(segment_count_ssa, get_sb(segment_count_ssa)); + set_newsb(main_blkaddr, get_sb(main_blkaddr)); + + set_newsb(segment_count_main, get_newsb(segment_count - + (get_newsb(segment_count_ckpt) + + get_newsb(segment_count_sit) + + get_newsb(segment_count_nat) + + get_newsb(segment_count_ssa)))); + + set_newsb(section_count, get_newsb(segment_count_main) / get_newsb(segs_per_sec)); + set_newsb(segment_count_main, get_newsb(section_count) / get_newsb(segs_per_sec)); + set_newsb(cp_payload, get_sb(cp_payload)); + + c.new_overprovision = get_best_overprovision(new_sb); + c.new_reserved_segments = count_overprovision(sb); +} + static int f2fs_resize_grow(struct f2fs_sb_info *sbi) { struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); @@ -635,6 +706,15 @@ static int f2fs_resize_grow(struct f2fs_sb_info *sbi) if (get_new_sb(new_sb)) return -1; + /* + * resize.f2fs not support main area to move forward, so here will reuse + * f2fs filesystem layout from old sb. + * */ + if (get_newsb(main_blkaddr) < get_sb(main_blkaddr)) { + MSG(0, "Info: keep filesystem layout unchanged, only change main area size.\n"); + revert_old_fs_layout(sbi, sb, new_sb); + } + if (f2fs_resize_check(sbi, new_sb) < 0) return -1; @@ -659,7 +739,7 @@ static int f2fs_resize_grow(struct f2fs_sb_info *sbi) new_main_blkaddr, 0); if (!err) offset_seg = offset >> get_sb(log_blocks_per_seg); - MSG(0, "Try to do defragement: %s\n", err ? "Skip": "Done"); + MSG(0, "Try to do defragment: %s\n", err ? "Skip": "Done"); } /* move whole data region */ if (err) @@ -745,19 +825,20 @@ int f2fs_resize(struct f2fs_sb_info *sbi) struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); /* may different sector size */ - if ((c.target_sectors * c.sector_size >> - get_sb(log_blocksize)) < get_sb(block_count)) + unsigned long long cur_size = (get_sb(block_count) << get_sb(log_blocksize)) >> F2FS_GB_SHIFT; + unsigned long long target_size = (c.target_sectors * c.sector_size) >> F2FS_GB_SHIFT; + if ((c.target_sectors * c.sector_size >> get_sb(log_blocksize)) < get_sb(block_count)) if (!c.safe_resize) { ASSERT_MSG("Nothing to resize, now only supports resizing with safe resize flag\n"); return -1; } else { + hmfs_enable_large_nat_bitmap(sbi, target_size, cur_size); return f2fs_resize_shrink(sbi); } - else if (((c.target_sectors * c.sector_size >> - get_sb(log_blocksize)) > get_sb(block_count)) || - c.force) + else if (((c.target_sectors * c.sector_size >> get_sb(log_blocksize)) > get_sb(block_count)) || c.force) { + hmfs_enable_large_nat_bitmap(sbi, target_size, cur_size); return f2fs_resize_grow(sbi); - else { + } else { MSG(0, "Nothing to resize.\n"); return 0; } diff --git a/fsck/segment.c b/fsck/segment.c index 0307bdd7edaedea30084f8dd2abb4c3cb0d3305e..5ffb3c2761b009b9d36d171a39a33c28da772db8 100644 --- a/fsck/segment.c +++ b/fsck/segment.c @@ -37,7 +37,7 @@ int reserve_new_block(struct f2fs_sb_info *sbi, block_t *to, return -ENOSPC; } if (is_node && fsck->chk.valid_node_cnt >= - sbi->total_valid_node_count) { + sbi->total_node_count) { ERR_MSG("Not enough space for node block\n"); return -ENOSPC; } @@ -76,7 +76,7 @@ int reserve_new_block(struct f2fs_sb_info *sbi, block_t *to, se = get_seg_entry(sbi, GET_SEGNO(sbi, blkaddr)); offset = OFFSET_IN_SEG(sbi, blkaddr); - se->type = type; + se->type = se->orig_type = type; se->valid_blocks++; f2fs_set_bit(offset, (char *)se->cur_valid_map); if (need_fsync_data_record(sbi)) { @@ -99,6 +99,7 @@ int reserve_new_block(struct f2fs_sb_info *sbi, block_t *to, if (c.func == FSCK) { fsck->chk.valid_blk_cnt++; if (is_node) { + fsck->chk.valid_nat_entry_cnt++; fsck->chk.valid_node_cnt++; if (is_inode) fsck->chk.valid_inode_cnt++; diff --git a/fsck/sload.c b/fsck/sload.c index 5b6e530227bef3e137568553d840f0c3f80c04f6..c246171653eede9e475def45b20583b24db90612 100644 --- a/fsck/sload.c +++ b/fsck/sload.c @@ -349,7 +349,7 @@ int f2fs_sload(struct f2fs_sb_info *sbi) int ret = 0; /* this requires for the below sanity checks */ - fsck_init(sbi); + fsck_init(sbi, false); ret = configure_files(); if (ret) { diff --git a/fsck/xattr.c b/fsck/xattr.c index 20f2c0a35cf88e53ab7ccd63e3b4c7aa88d49dc9..75595328423f5b73ac218ff0be60c839d360d792 100644 --- a/fsck/xattr.c +++ b/fsck/xattr.c @@ -17,14 +17,14 @@ #include "node.h" #include "xattr.h" -void *read_all_xattrs(struct f2fs_sb_info *sbi, struct f2fs_node *inode) +void *read_all_xattrs(struct f2fs_sb_info *sbi, struct f2fs_node *inode, bool nid_check) { struct f2fs_xattr_header *header; void *txattr_addr; u64 inline_size = inline_xattr_size(&inode->i); nid_t xnid = le32_to_cpu(inode->i.i_xattr_nid); - if (c.func == FSCK && xnid) { + if (c.func == FSCK && xnid && nid_check) { struct f2fs_node *node_blk = NULL; struct node_info ni; int ret; @@ -65,11 +65,21 @@ void *read_all_xattrs(struct f2fs_sb_info *sbi, struct f2fs_node *inode) return txattr_addr; } -static struct f2fs_xattr_entry *__find_xattr(void *base_addr, int index, - size_t len, const char *name) +static struct f2fs_xattr_entry *__find_xattr(void *base_addr, + void *last_base_addr, int index, + size_t len, const char *name, + struct f2fs_inode *inode) { struct f2fs_xattr_entry *entry; - list_for_each_xattr(entry, base_addr) { + u64 max_total_xattr_size = XATTR_SIZE(inode); + + list_for_each_xattr(entry, base_addr, max_total_xattr_size) { + if ((void *)(entry) + sizeof(__u32) > last_base_addr || + (void *)XATTR_NEXT_ENTRY(entry) > last_base_addr) { + MSG(0, "xattr entry crosses the end of xattr space\n"); + return NULL; + } + if (entry->e_name_index != index) continue; if (entry->e_name_len != len) @@ -135,6 +145,7 @@ int f2fs_setxattr(struct f2fs_sb_info *sbi, nid_t ino, int index, const char *na { struct f2fs_node *inode; void *base_addr; + void *last_base_addr; struct f2fs_xattr_entry *here, *last; struct node_info ni; int error = 0; @@ -172,12 +183,19 @@ int f2fs_setxattr(struct f2fs_sb_info *sbi, nid_t ino, int index, const char *na return -ERANGE; } - base_addr = read_all_xattrs(sbi, inode); + base_addr = read_all_xattrs(sbi, inode, true); ASSERT(base_addr); - here = __find_xattr(base_addr, index, len, name); + last_base_addr = (void *)base_addr + XATTR_SIZE(&inode->i); - found = IS_XATTR_LAST_ENTRY(here) ? 0 : 1; + here = __find_xattr(base_addr, last_base_addr, index, len, name, &inode->i); + if (!here) { + MSG(0, "Need to run fsck due to corrupted xattr.\n"); + error = -EINVAL; + goto exit; + } + + found = IS_VALID_ENTRY(here, base_addr, XATTR_SIZE(&inode->i)) ? 1 : 0; if ((flags & XATTR_REPLACE) && !found) { error = -ENODATA; @@ -188,8 +206,9 @@ int f2fs_setxattr(struct f2fs_sb_info *sbi, nid_t ino, int index, const char *na } last = here; - while (!IS_XATTR_LAST_ENTRY(last)) + while (IS_VALID_ENTRY(last, base_addr, XATTR_SIZE(&inode->i))) { last = XATTR_NEXT_ENTRY(last); + } newsize = XATTR_ALIGN(sizeof(struct f2fs_xattr_entry) + len + size); diff --git a/fsck/xattr.h b/fsck/xattr.h index 8491407ab2225d319fcce45d077843fad8d4e774..f870780185ffeab0f0d8b387061d1fd7e806da60 100644 --- a/fsck/xattr.h +++ b/fsck/xattr.h @@ -127,20 +127,33 @@ static inline int f2fs_acl_count(int size) #define ENTRY_SIZE(entry) (XATTR_ALIGN(sizeof(struct f2fs_xattr_entry) + \ entry->e_name_len + le16_to_cpu(entry->e_value_size))) -#define list_for_each_xattr(entry, addr) \ +#define VALID_XATTR_BLOCK_SIZE (F2FS_BLKSIZE - sizeof(struct node_footer)) + +#define XATTR_SIZE(i) ((le32_to_cpu((i)->i_xattr_nid) ? \ + VALID_XATTR_BLOCK_SIZE : 0) + \ + (inline_xattr_size(i))) + +#define NOT_EXCEED_XATTR_BOUNDARY(entry, addr, max_total_xattr_size) \ + ((char *) (entry) - (char *) (addr) < (max_total_xattr_size)) + +#define IS_VALID_ENTRY(entry, addr, max_total_xattr_size) \ + NOT_EXCEED_XATTR_BOUNDARY(entry, addr, max_total_xattr_size) && \ + !IS_XATTR_LAST_ENTRY(entry) + +#define list_for_each_xattr(entry, addr, max_total_xattr_size) \ for (entry = XATTR_FIRST_ENTRY(addr); \ - !IS_XATTR_LAST_ENTRY(entry); \ + IS_VALID_ENTRY(entry, addr, max_total_xattr_size); \ entry = XATTR_NEXT_ENTRY(entry)) #define MIN_OFFSET XATTR_ALIGN(F2FS_BLKSIZE - \ - sizeof(struct node_footer) - sizeof(__u32)) + sizeof(struct node_footer)) #define MAX_XATTR_BLOCK_SIZE (F2FS_BLKSIZE - sizeof(struct node_footer)) #define MAX_XATTR_SIZE(inode) (XATTR_ALIGN((MAX_INLINE_XATTR_SIZE(inode)) + \ - (MAX_XATTR_BLOCK_SIZE))) + (MAX_XATTR_BLOCK_SIZE))) -#define MAX_VALUE_LEN(inode) (MAX_XATTR_SIZE(inode) - \ +#define MAX_VALUE_LEN(inode) (MAX_XATTR_SIZE(inode) - \ sizeof(struct f2fs_xattr_header) - \ sizeof(struct f2fs_xattr_entry)) diff --git a/include/f2fs_dfx_common.h b/include/f2fs_dfx_common.h new file mode 100644 index 0000000000000000000000000000000000000000..cc36522bdc336f2abb19309a0debf5a805ee702f --- /dev/null +++ b/include/f2fs_dfx_common.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * Description: enhance log records for fsck-tools + * Create: 2024-3-4 +*/ +#ifndef __F2FS_DFX_COMMON_H__ +#define __F2FS_DFX_COMMON_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef UNUSED +#elif defined(__GNUC__) +# define UNUSED(x) UNUSED_ ## x __attribute__((unused)) +#elif defined(__LCLINT__) +# define UNUSED(x) x +#elif defined(__cplusplus) +# define UNUSED(x) +#else +# define UNUSED(x) x +#endif + +/* + * for now, only fsck records log. If you want to add slog/klog to other + * components, you should: + * 1. add new log type and tag to logType and log_tag; + * 2. add case in init_log_info(); + * 3. add SlogInit/SlogExit to corresponding main(). + */ +enum LogType { + LOG_TYP_NONE, + LOG_TYP_FSCK, + LOG_TYP_DUMP, + LOG_TYP_DEFRAG, + LOG_TYP_RESIZE, + LOG_TYP_MKFS, + LOG_TYP_MAX, +}; + +#ifdef __cplusplus +} +#endif +#endif diff --git a/include/f2fs_dmd.h b/include/f2fs_dmd.h new file mode 100644 index 0000000000000000000000000000000000000000..d8474f3f97533f16e0ca3c49ba7e2b505fc0e250 --- /dev/null +++ b/include/f2fs_dmd.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * Description: enhance log records for fsck-tools + * Create: 2024-3-4 +*/ + +#ifndef __F2FS_DMD_H__ +#define __F2FS_DMD_H__ + +#include "f2fs_dfx_common.h" +#include "f2fs_dmd_errno.h" + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MB_BLK_SHIFT 8 +#define NR_ERR_BITMAP_UINT64 3 +#define FSCK_REPORT_MSG_SIZE 1536 +#define FSCK_OVERTIME_MS 1000 + +struct DmdMsg { + char *buffer; + unsigned int size; + unsigned int offset; +}; + +#ifndef CONF_TARGET_HOST +struct __attribute__((packed)) DmdReport { + uint64_t errBitmap[NR_ERR_BITMAP_UINT64]; + unsigned int propBitmap; /* used as flag bitmap of properties */ + unsigned long usedSpace; /* space taken by node and data blocks in MB */ + unsigned long freeSpace; /* free space in MB */ + unsigned int features; + unsigned short segsPerSec; + unsigned short secsPerZone; + unsigned long totalFsSectors; + unsigned long ckptVersion; + unsigned int ckptState; + unsigned long costTime; + char msg[FSCK_REPORT_MSG_SIZE]; +}; + +#define IS_UNISTORE_FL 0x00000001 +#define FB_LOCKED_FL 0x00000002 + +extern struct DmdReport g_dmdReport; +extern struct DmdMsg g_reportMsg; +extern struct DmdMsg g_assertMsg; + +#define DMD_SET_VALUE(field, value) ((g_dmdReport.field) = (value)) +#define DMD_ADD_ERROR(type, err) DmdInsertError(type, err, __func__, __LINE__) +#define DMD_ADD_MSG_ERROR(type, err, fmt, ...) DmdInsertMsgError(type, err, __func__, __LINE__, \ + "[ERRMSG(%s:%d)"fmt"]", __func__, __LINE__, ##__VA_ARGS__) +#define DMD_ASSERT_MSG(func, line, fmt, ...) DmdAssertMsg("[ASSERT(%s:%d)"fmt"]", func, line, ##__VA_ARGS__) +#define DmdInsertMsgError(type, err, func, line, fmt, ...) \ + do { \ + DmdInsertError(type, err, func, line); \ + PrintToMsg(&g_reportMsg, fmt, ##__VA_ARGS__); \ + } while (0) +#define DmdAssertMsg(fmt, ...) PrintToMsg(&g_assertMsg, fmt, ##__VA_ARGS__) +#define COMPUTE_SIZE(sbi) \ + do { \ + uint64_t nodeSecs = round_up((sbi)->total_valid_node_count, BLKS_PER_SEC(sbi)); \ + uint64_t dataSecs = round_up((sbi)->total_valid_block_count - \ + (sbi)->total_valid_node_count, BLKS_PER_SEC(sbi)); \ + uint64_t freeBlks = ((sbi)->total_sections - dataSecs - nodeSecs) * BLKS_PER_SEC(sbi); \ + uint64_t totalSize = g_dmdReport.totalFsSectors << (sbi)->log_sectors_per_block >> MB_BLK_SHIFT; \ + g_dmdReport.freeSpace = freeBlks >> MB_BLK_SHIFT; \ + g_dmdReport.usedSpace = totalSize - g_dmdReport.freeSpace; \ + } while (0) +#define DMD_CHECK_COST_TIME(sbi, costMs) \ + do { \ + g_dmdReport.costTime = (costMs); \ + if (g_dmdReport.usedSpace == 0) { \ + COMPUTE_SIZE(sbi); /* Compute usedSpace if not set by fsck_verify */ \ + } \ + DmdCheckCostTime(__func__, __LINE__); \ + } while (0) + +void DmdInsertError(int type, unsigned int err, const char *func, int line); +void PrintToMsg(struct DmdMsg *dmdMsg, const char *fmt, ...); +void DmdCheckCostTime(const char *func, int line); + +#else +#define DMD_SET_VALUE(field, value) +#define DMD_ADD_ERROR(type, err) +#define DMD_ADD_MSG_ERROR(type, err, fmt, ...) +#define DMD_ASSERT_MSG(func, line, fmt, ...) +#define DMD_CHECK_COST_TIME(sbi, costMs) +#endif /* CONF_TARGET_HOST */ + +int DmdReport(void); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/include/f2fs_dmd_cfg.h b/include/f2fs_dmd_cfg.h new file mode 100644 index 0000000000000000000000000000000000000000..2751e2f7a2d282e9dbd86f95a7bcc970ce2abaf1 --- /dev/null +++ b/include/f2fs_dmd_cfg.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * Description: enhance log records for fsck-tools + * Create: 2024-3-4 +*/ + +#ifndef __F2FS_DMD_CFG_H__ +#define __F2FS_DMD_CFG_H__ +#ifdef __cplusplus +extern "C" { +#endif + +#define DMD_ERR (-1) +#define DMD_OK (0) + +#ifdef __cplusplus +} +#endif +#endif diff --git a/include/f2fs_dmd_errno.h b/include/f2fs_dmd_errno.h new file mode 100644 index 0000000000000000000000000000000000000000..8da973e3b86313a62e1dd674da542ddfc10b3889 --- /dev/null +++ b/include/f2fs_dmd_errno.h @@ -0,0 +1,209 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * Description: enhance log records for fsck-tools + * Create: 2024-3-4 +*/ + +#ifndef __F2FS_DMD_ERRNO_H__ +#define __F2FS_DMD_ERRNO_H__ +#ifdef __cplusplus +extern "C" { +#endif + +/* super block is invalid */ +#define PR_INVALID_SUPER_BLOCK 0x01 + +/* checkpoint is invalid */ +#define PR_INVALID_CHECKPOINT 0x02 + +/* checkpoint is polluted */ +#define PR_POLLUTE_CHECKPOINT 0x03 + +/* build_segment_manager error */ +#define PR_BUILD_SEGMENT_MANAGER_ERROR 0x04 + +/* build_node_manager error */ +#define PR_BUILD_NODE_MANAGER_ERROR 0x05 + +/* sit vblocks big than sbi->blocks_per_seg */ +#define PR_INVALID_SIT_VBLOCKS 0x06 + +/* invalid sit vblocks */ +#define PR_INVALID_SEGNO 0x07 + +/* sit vblocks count is error */ +#define PR_SIT_VBLOCKS_IS_ERROR 0x08 + +/* invalid sit type */ +#define PR_INVALID_SIT_TYPE 0x09 + +/* invalid nat entry[0] */ +#define PR_INVALID_NAT_ENTRY0 0x0A + +/* invalid nat entry[1/2] */ +#define PR_INVALID_NAT_ENTRY1_ENTRY2 0x0B + +/* invalid ino or blkaddr */ +#define PR_INVALID_INO_OR_BLKADDR 0x0C + +/* check meta does not match */ +#define PR_FSCK_META_MISMATCH 0x0D + +/* sit segment count mismatch with total segment */ +#define PR_SIT_SEGMENT_COUNT_MISMATCH_WITH_TOTAL 0x0E + +/* nat valid node count mismatch with sit */ +#define PR_NAT_NODE_COUNT_MISMATCH_WITH_SIT 0x0F + +/* sit free segment count mismatch with cp */ +#define PR_SIT_FREESEG_COUNT_MISMATCH_WITH_CP 0x10 + +/* nat valid node count mismatch with cp */ +#define PR_NAT_NODE_COUNT_MISMATCH_WITH_CP 0x11 + +/* nat valid inode count mismatch with cp */ +#define PR_NAT_INODE_COUNT_MISMATCH_WITH_CP 0x12 + +/* nat entry blkaddr is invalid */ +#define PR_NODE_INVALID_BLKADDR 0x13 + +/* nat entry blkaddr not in sit area bitmap */ +#define PR_NAT_BLKADDR_OUT_SIT_BITMAP 0x14 + +/* nat entry ino is invalid */ +#define PR_INVALID_NID 0x15 + +/* nat entry ino not in nat area bitmap */ +#define PR_NAT_INO_OUT_NAT_BITMAP 0x16 + +/* wrong orphan inode */ +#define PR_ORPAHN_INODE_ERROR 0x17 + +/* ino is zero */ +#define PR_INO_IS_ZERO 0x18 + +/* blkaddr is NEW_ADDR */ +#define PR_BLKADDR_IS_NEW_ADDR 0x19 + +/* invalid sum node block */ +#define PR_INVALID_SUM_NODE_BLOCK 0x1A + +/* inode footer ino is not equal with nid */ +#define PR_INODE_FOOTER_INO_NOT_EQUAL_NID 0x1B + +/* node ino is not equal with node footer ino */ +#define PR_NODE_INO_NOT_EQUAL_FOOTER_INO 0x1C + +/* non-inode footer ino is equal with nid */ +#define PR_NON_INODE_FOOTER_INO_EQUAL_NID 0x1D + +/* node nid is not equal with node footer nid */ +#define PR_NODE_NID_NOT_EQUAL_FOOTER_NID 0x1E + +/* xattr offset is invalid */ +#define PR_INVALID_XATTR_OFFSET 0x1F + +/* duplicate node blkaddr in main bitmap */ +#define PR_DUPLICATE_NODE_BLKADDR_IN_MAIN_BITMAP 0x20 + +/* direct name mismatch with node block i_name */ +#define PR_DIR_NAME_MISMATCH_I_NAME 0x21 + +/* inode mismatch mode */ +#define PR_INODE_MISMATCH_MODE 0x22 + +/* duplicate orphan or xattr nid */ +#define PR_DUPLICATE_ORPHAN_OR_XATTR_NID 0x23 + +/* i_link num is error */ +#define PR_HARD_LINK_NUM_IS_ERROR 0x24 + +/* inline data addr[0] is not zero */ +#define PR_INLINE_DATA_ADDR0_NOT_ZERO 0x25 + +/* inline data has not data exist */ +#define PR_INLINE_DATA_INEXISTENCE 0x26 + +/* inline dentry is invalid */ +#define PR_INVALID_INLINE_DENTRY 0x27 + +/* invalid extent values */ +#define PR_INVALID_EXTENT_VALUE 0x28 + +/* invalid i_blocks */ +#define PR_INVALID_I_BLOCKS 0x29 + +/* invalid i_link */ +#define PR_INVALID_I_LINKS 0x2A + +/* dot or dotdot is error */ +#define PR_LOST_DOT_OR_DOTDOT 0x2B + +/* orphan inode has i_links */ +#define PR_ORPHAN_INODE_HAS_I_LINKS 0x2C + +/* invalid sum data block */ +#define PR_INVALID_SUM_DATA_BLOCK 0x2D + +/* invalid data blkaddr is not in sit bitmap */ +#define PR_DATA_BLKADDR_OUT_SIT_BITMAP 0x2E + +/* duplicate data blkaddr in main bitmap */ +#define PR_DUPLICATE_DATA_BLKADDR_IN_MAIN_BITMAP 0x2F + +/* data block sit type is error */ +#define PR_SIT_TYPE_IS_ERROR 0x30 + +/* invalid ftype in dentry */ +#define PR_INVALID_FTYPE 0x31 + +/* file name len is zero */ +#define PR_NAME_LEN_IS_ZERO 0x32 + +/* file name len is zero */ +#define PR_INVALID_HASH_CODE 0x33 + +/* nid is unreachable */ +#define PR_NID_IS_UNREACHABLE 0x34 + +/* nid has more unreachable links */ +#define PR_NID_HAS_MORE_UNREACHABLE_LINKS 0x35 + +/* next block offset is not free */ +#define PR_CUR_NEXT_BLK_IS_NOT_FREE 0x36 + +/* LFS must have free section */ +#define PR_LFS_HAS_NO_FREE_SECTION 0x37 + +/* record_fsync_data error */ +#define PR_RECORD_FSYNC_DATA_ERROR 0x38 + +/* full disk fsck triggered */ +#define PR_FULL_DISK_FSCK 0x39 + +#define PR_SIT_INVALID_BLOCK_BITMAP 0x40 + +#define PR_MULTI_HARD_LINK_FILES 0x41 + +#define PR_VALID_BLOCK_COUNT_MISMATCH_WITH_CP 0x42 + +#define PR_VALID_NODE_COUNT_MISMATCH_WITH_CP_DE 0x43 + +#define PR_VALID_NODE_COUNT_MISMATCH_WITH_CP_NAT 0x44 + +#define PR_VALID_INODE_COUNT_MISMATCH_WITH_CP 0x45 + +#define PR_SEGMENT_COUNT_MISMATCH_WITH_CP 0x46 + +#define PR_NEXT_BLOCK_OFFSET_FREE 0x47 + +#define PR_OTHER_CORRUPT 0x48 + +#define PR_EXTRA_NEED_FSCK_FLAG_SET 0x49 + +#define PR_FSCK_TIME_OVERCOST 0x50 + +#ifdef __cplusplus +} +#endif +#endif diff --git a/include/f2fs_ext.h b/include/f2fs_ext.h new file mode 100644 index 0000000000000000000000000000000000000000..5c6e9f86398033709b5f87f1b907b0663ee6be61 --- /dev/null +++ b/include/f2fs_ext.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * Description: enhance log records for fsck-tools + * Create: 2024-3-4 +*/ +#ifndef __F2FS_EXT_H__ +#define __F2FS_EXT_H__ + +#include +#include + +#define F2FS_EXT_EXIT() \ +do { \ + SlogExit(); \ + DmdReport(); \ +} while (0) + +#endif \ No newline at end of file diff --git a/include/f2fs_fs.h b/include/f2fs_fs.h index 740142cdc6473a6f25ae34db2f9cd71c3fbcd7ec..9b8cb337c7c2718aba4ace1eba3ae4f199b9c3d5 100644 --- a/include/f2fs_fs.h +++ b/include/f2fs_fs.h @@ -26,6 +26,7 @@ #include #include #include +#include #ifdef HAVE_CONFIG_H #include @@ -237,13 +238,18 @@ static inline uint64_t bswap_64(uint64_t val) #define FIX_MSG(fmt, ...) \ do { \ printf("[FIX] (%s:%4d) ", __func__, __LINE__); \ + SLOG("[FIX] (%s:%4d) ", __func__, __LINE__); \ printf(" --> "fmt"\n", ##__VA_ARGS__); \ + SLOG(" --> "fmt"\n", ##__VA_ARGS__); \ } while (0) #define ASSERT_MSG(fmt, ...) \ do { \ printf("[ASSERT] (%s:%4d) ", __func__, __LINE__); \ + SLOG("[ASSERT] (%s:%4d) ", __func__, __LINE__); \ printf(" --> "fmt"\n", ##__VA_ARGS__); \ + SLOG(" --> "fmt"\n", ##__VA_ARGS__); \ + DMD_ASSERT_MSG(__func__, __LINE__, fmt, ##__VA_ARGS__); \ c.bug_on = 1; \ } while (0) @@ -252,19 +258,24 @@ static inline uint64_t bswap_64(uint64_t val) if (!(exp)) { \ printf("[ASSERT] (%s:%4d) %s\n", \ __func__, __LINE__, #exp); \ - exit(-1); \ + SLOG("[ASSERT] (%s:%4d) %s\n", \ + __func__, __LINE__, #exp); \ + F2FS_EXT_EXIT(); \ + exit(-1); \ } \ } while (0) #define ERR_MSG(fmt, ...) \ do { \ printf("[%s:%d] " fmt, __func__, __LINE__, ##__VA_ARGS__); \ + SLOG("[%s:%d] " fmt, __func__, __LINE__, ##__VA_ARGS__); \ } while (0) #define MSG(n, fmt, ...) \ do { \ if (c.dbg_lv >= n && !c.layout && !c.show_file_map) { \ printf(fmt, ##__VA_ARGS__); \ + SLOG(fmt, ##__VA_ARGS__); \ } \ } while (0) @@ -273,6 +284,8 @@ static inline uint64_t bswap_64(uint64_t val) if (c.dbg_lv >= n && !c.layout && !c.show_file_map) { \ printf("[%s:%4d] " fmt, \ __func__, __LINE__, ##__VA_ARGS__); \ + SLOG("[%s:%4d] " fmt, \ + __func__, __LINE__, ##__VA_ARGS__); \ } \ } while (0) @@ -280,52 +293,72 @@ static inline uint64_t bswap_64(uint64_t val) #define DISP(fmt, ptr, member) \ do { \ printf("%-30s" fmt, #member, ((ptr)->member)); \ + SLOG("%-30s" fmt, #member, ((ptr)->member)); \ } while (0) #define DISP_u16(ptr, member) \ do { \ assert(sizeof((ptr)->member) == 2); \ - if (c.layout) \ + if (c.layout) { \ printf("%-30s %u\n", \ #member":", le16_to_cpu(((ptr)->member))); \ - else \ + SLOG("%-30s %u\n", \ + #member":", le16_to_cpu(((ptr)->member))); \ + } else { \ printf("%-30s" "\t\t[0x%8x : %u]\n", \ #member, le16_to_cpu(((ptr)->member)), \ le16_to_cpu(((ptr)->member))); \ + SLOG("%-30s" "\t\t[0x%8x : %u]\n", \ + #member, le16_to_cpu(((ptr)->member)), \ + le16_to_cpu(((ptr)->member))); \ + } \ } while (0) #define DISP_u32(ptr, member) \ do { \ assert(sizeof((ptr)->member) <= 4); \ - if (c.layout) \ + if (c.layout) { \ printf("%-30s %u\n", \ #member":", le32_to_cpu(((ptr)->member))); \ - else \ + SLOG("%-30s %u\n", \ + #member":", le32_to_cpu(((ptr)->member))); \ + } else { \ printf("%-30s" "\t\t[0x%8x : %u]\n", \ #member, le32_to_cpu(((ptr)->member)), \ le32_to_cpu(((ptr)->member))); \ + SLOG("%-30s" "\t\t[0x%8x : %u]\n", \ + #member, le32_to_cpu(((ptr)->member)), \ + le32_to_cpu(((ptr)->member))); \ + } \ } while (0) #define DISP_u64(ptr, member) \ do { \ assert(sizeof((ptr)->member) == 8); \ - if (c.layout) \ + if (c.layout) { \ printf("%-30s %" PRIu64 "\n", \ #member":", le64_to_cpu(((ptr)->member))); \ - else \ + SLOG("%-30s %" PRIu64 "\n", \ + #member":", le64_to_cpu(((ptr)->member))); \ + } else { \ printf("%-30s" "\t\t[0x%8" PRIx64 " : %" PRIu64 "]\n", \ #member, le64_to_cpu(((ptr)->member)), \ le64_to_cpu(((ptr)->member))); \ + SLOG("%-30s" "\t\t[0x%8" PRIx64 " : %" PRIu64 "]\n", \ + #member, le64_to_cpu(((ptr)->member)), \ + le64_to_cpu(((ptr)->member))); \ + } \ } while (0) #define DISP_utf(ptr, member) \ do { \ - if (c.layout) \ - printf("%-30s %s\n", #member":", \ - ((ptr)->member)); \ - else \ - printf("%-30s" "\t\t[%s]\n", #member, \ - ((ptr)->member)); \ + if (c.layout) { \ + printf("%-30s %s\n", #member":", ((ptr)->member)); \ + SLOG("%-30s %s\n", #member":", ((ptr)->member)); \ + } else { \ + printf("%-30s" "\t\t[%s]\n", #member, ((ptr)->member)); \ + SLOG("%-30s" "\t\t[%s]\n", #member, ((ptr)->member)); \ + } \ } while (0) /* Display to buffer */ @@ -372,6 +405,8 @@ static inline uint64_t bswap_64(uint64_t val) #define VERSION_LEN 256 #define VERSION_TIMESTAMP_LEN 4 #define VERSION_NAME_LEN (VERSION_LEN - VERSION_TIMESTAMP_LEN) +#define HMFS_LARGE_NAT_BITMAP_MIN_SIZE 512 /* HMFS min GB when enable large nat bitmap */ +#define HMFS_MIN_COMPRESS_LOG_SIZE 3 #define LPF "lost+found" @@ -584,6 +619,10 @@ struct f2fs_configuration { #define get_sb_le64(member) le64_to_cpu(sb->member) #define get_sb_le32(member) le32_to_cpu(sb->member) #define get_sb_le16(member) le16_to_cpu(sb->member) + +#define set_newsb_le64(member, val) (new_sb->member = cpu_to_le64(val)) +#define set_newsb_le32(member, val) (new_sb->member = cpu_to_le32(val)) +#define set_newsb_le16(member, val) (new_sb->member = cpu_to_le16(val)) #define get_newsb_le64(member) le64_to_cpu(new_sb->member) #define get_newsb_le32(member) le32_to_cpu(new_sb->member) #define get_newsb_le16(member) le16_to_cpu(new_sb->member) @@ -608,6 +647,16 @@ struct f2fs_configuration { } \ t; \ }) +#define set_newsb(member, val) \ + do { \ + typeof(new_sb->member) t = (val); \ + switch (sizeof(t)) { \ + case 8: set_newsb_le64(member, t); break; \ + case 4: set_newsb_le32(member, t); break; \ + case 2: set_newsb_le16(member, t); break; \ + } \ + } while(0) + #define get_newsb(member) \ ({ \ typeof(new_sb->member) t; \ @@ -701,10 +750,12 @@ enum { #define F2FS_MAX_EXTENSION 64 /* # of extension entries */ #define F2FS_EXTENSION_LEN 8 /* max size of extension */ #define F2FS_BLK_ALIGN(x) (((x) + F2FS_BLKSIZE - 1) / F2FS_BLKSIZE) +#define F2FS_GB_SHIFT 30 #define NULL_ADDR 0x0U #define NEW_ADDR -1U #define COMPRESS_ADDR -2U +#define DEDUP_ADDR -3U /* used as dedup addresses */ #define F2FS_ROOT_INO(sbi) (sbi->root_ino_num) #define F2FS_NODE_INO(sbi) (sbi->node_ino_num) @@ -747,6 +798,7 @@ enum { #define F2FS_FEATURE_CASEFOLD 0x1000 #define F2FS_FEATURE_COMPRESSION 0x2000 #define F2FS_FEATURE_RO 0x4000 +#define F2FS_FEATURE_DEDUP 0x8000 #define MAX_VOLUME_NAME 512 @@ -851,13 +903,12 @@ static_assert(sizeof(struct f2fs_super_block) == 3072, ""); /* * For checkpoint */ -#define CP_RESIZEFS_FLAG 0x00004000 -#define CP_DISABLED_FLAG 0x00001000 -#define CP_QUOTA_NEED_FSCK_FLAG 0x00000800 -#define CP_LARGE_NAT_BITMAP_FLAG 0x00000400 +#define CP_RESIZEFS_FLAG 0x00008000 +#define CP_DISABLED_FLAG 0x00004000 +#define CP_QUOTA_NEED_FSCK_FLAG 0x00001000 +#define CP_LARGE_NAT_BITMAP_FLAG 0x00002000 #define CP_NOCRC_RECOVERY_FLAG 0x00000200 #define CP_TRIMMED_FLAG 0x00000100 -#define CP_NAT_BITS_FLAG 0x00000080 #define CP_CRC_RECOVERY_FLAG 0x00000040 #define CP_FASTBOOT_FLAG 0x00000020 #define CP_FSCK_FLAG 0x00000010 @@ -865,6 +916,8 @@ static_assert(sizeof(struct f2fs_super_block) == 3072, ""); #define CP_COMPACT_SUM_FLAG 0x00000004 #define CP_ORPHAN_PRESENT_FLAG 0x00000002 #define CP_UMOUNT_FLAG 0x00000001 +#define CP_APPEND_SIT_FLAG 0x00000400 +#define CP_APPEND_NAT_FLAG 0x00000800 #define F2FS_CP_PACKS 2 /* # of checkpoint packs */ @@ -1008,6 +1061,10 @@ static_assert(sizeof(struct f2fs_extent) == 12, ""); #define F2FS_CASEFOLD_FL 0x40000000 /* Casefolded file */ #define IS_CASEFOLDED(dir) ((dir)->i_flags & F2FS_CASEFOLD_FL) +#define F2FS_OLD_ATTRIBUTE_SIZE (offsetof(struct f2fs_inode, i_addr)) +#define F2FS_FITS_IN_INODE(extra_isize, field, field_size) \ + ((offsetof(struct f2fs_inode, field) + field_size) \ + <= (F2FS_OLD_ATTRIBUTE_SIZE + extra_isize)) /* * fsck i_compr_blocks counting helper @@ -1072,6 +1129,9 @@ struct f2fs_inode { __u8 i_compress_algrithm; /* compress algrithm */ __u8 i_log_cluster_size; /* log of cluster size */ __le16 i_padding; /* padding */ + __le32 i_inner_ino; /* for layered inode */ + __le32 i_dedup_flags; /* dedup file attributes */ + __le32 i_dedup_rsvd; /* reserved for dedup */ __le32 i_extra_end[0]; /* for attribute size calculation */ } __attribute__((packed)); __le32 i_addr[DEF_ADDRS_PER_INODE]; /* Pointers to data blocks */ @@ -1081,7 +1141,7 @@ struct f2fs_inode { }; static_assert(offsetof(struct f2fs_inode, i_extra_end) - - offsetof(struct f2fs_inode, i_extra_isize) == 36, ""); + offsetof(struct f2fs_inode, i_extra_isize) == 48, ""); static_assert(sizeof(struct f2fs_inode) == 4072, ""); struct direct_node { @@ -1255,6 +1315,10 @@ static_assert(sizeof(struct summary_footer) == 5, ""); sizeof(struct sit_journal_entry)) #define SIT_JOURNAL_RESERVED ((SUM_JOURNAL_SIZE - 2) %\ sizeof(struct sit_journal_entry)) +#define NAT_APPEND_JOURNAL_ENTRIES (F2FS_BLKSIZE /\ + sizeof(struct nat_journal_entry)) +#define SIT_APPEND_JOURNAL_ENTRIES (F2FS_BLKSIZE /\ + sizeof(struct sit_journal_entry)) /* * Reserved area should make size of f2fs_extra_info equals to @@ -1410,6 +1474,7 @@ enum FILE_TYPE { F2FS_FT_SYMLINK, F2FS_FT_MAX, /* added for fsck */ + F2FS_FT_DEDUP_INNER, F2FS_FT_ORPHAN, F2FS_FT_XATTR, F2FS_FT_LAST_FILE_TYPE = F2FS_FT_XATTR, @@ -1647,15 +1712,9 @@ static inline double get_best_overprovision(struct f2fs_super_block *sb) return max_ovp; } -static inline __le64 get_cp_crc(struct f2fs_checkpoint *cp) +static inline uint32_t count_overprovision(struct f2fs_super_block *sb) { - uint64_t cp_ver = get_cp(checkpoint_ver); - size_t crc_offset = get_cp(checksum_offset); - uint32_t crc = le32_to_cpu(*(__le32 *)((unsigned char *)cp + - crc_offset)); - - cp_ver |= ((uint64_t)crc << 32); - return cpu_to_le64(cp_ver); + return (2 * (100 / c.new_overprovision + 1) + 6) * get_sb(segs_per_sec); } static inline int exist_qf_ino(struct f2fs_super_block *sb) @@ -1746,6 +1805,7 @@ struct feature feature_table[] = { \ { "casefold", F2FS_FEATURE_CASEFOLD }, \ { "compression", F2FS_FEATURE_COMPRESSION }, \ { "ro", F2FS_FEATURE_RO}, \ + { "dedup", F2FS_FEATURE_DEDUP }, \ { NULL, 0x0}, \ }; @@ -1819,6 +1879,14 @@ static inline int parse_root_owner(char *ids, return 0; } +static inline bool is_fs_layout_unchanged(struct f2fs_super_block *sb, struct f2fs_super_block *new_sb) +{ + return (get_sb(main_blkaddr) == get_newsb(main_blkaddr)) && + (get_sb(sit_blkaddr) == get_newsb(sit_blkaddr) && + (get_sb(nat_blkaddr) == get_newsb(nat_blkaddr)) && + (get_sb(ssa_blkaddr) == get_newsb(ssa_blkaddr))); +} + /* * NLS definitions */ diff --git a/include/f2fs_log.h b/include/f2fs_log.h new file mode 100644 index 0000000000000000000000000000000000000000..3296d831c09330a83b4205553031b0acce9c8bb4 --- /dev/null +++ b/include/f2fs_log.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved. + * Description: enhance log records for fsck-tools + * Create: 2023-11-24 +*/ +#ifndef __F2FS_LOG_H__ +#define __F2FS_LOG_H__ + +#include "f2fs_dfx_common.h" + +#define LOG_ERR (-1) +#define LOG_OK (0) + +extern char *g_logTag[7]; +struct LogInfo { + enum LogType type; + int slogFd; + int klogFd; + int klogLevel; + long slogOffset; + int needTruncate; + char *slogFile; + char *slogFileBak; + char *logDir; +}; +extern struct LogInfo g_logI; + +#define KLOG_ERROR_LEVEL 3 +#define KLOG_INFO_LEVEL 6 +#define KLOG_DEFAULT_LEVEL KLOG_INFO_LEVEL +#define LOG_BUF_MAX 512 + +#define KLOGE(fmt, ...) \ + KlogWrite(KLOG_ERROR_LEVEL, "<3> %s: " fmt, \ + g_logTag[g_logI.type], ##__VA_ARGS__) +#define KLOGI(fmt, ...) \ + KlogWrite(KLOG_INFO_LEVEL, "<6> %s: " fmt, \ + g_logTag[g_logI.type], ##__VA_ARGS__) +#define SLOG(x...) SlogWrite(x) + +/* + * TEMP_FAILURE_RETRY is defined by some, but not all, versions of + * . (Alas, it is not as standard as we'd hoped!) So, if it's + * not already defined, then define it here. + */ +/* Copied from system/core/include/cutils/fs.h */ +#ifndef TEMP_FAILURE_RETRY +/* Used to retry syscalls that can return EINTR. */ +#define TEMP_FAILURE_RETRY(exp) ({ \ + typeof (exp) _rc; \ + do { \ + _rc = (exp); \ + } while (_rc == -1 && errno == EINTR); \ +_rc; }) +#endif + +int SlogInit(int funcType); +void SlogExit(void); +int SlogWrite(const char *fmt, ...); +void KlogWrite(int level, const char* fmt, ...); + +#endif diff --git a/lib/BUILD.gn b/lib/BUILD.gn index d664443ddaf6a0a8241c20d28467730d2b8f522c..ca2e2ff57d8ee0e416d00b85e871d7e7449b0bcc 100644 --- a/lib/BUILD.gn +++ b/lib/BUILD.gn @@ -5,6 +5,7 @@ # published by the Free Software Foundation. import("//build/ohos.gni") + config("f2fs-defaults") { cflags = [ "-Wall", @@ -29,10 +30,16 @@ ohos_shared_library("libf2fs") { "libf2fs_io.c", "libf2fs_zoned.c", "nls_utf8.c", + "libf2fs_log.c", + "libf2fs_dmd.c" ] include_dirs = [ "." ] + external_deps = [ + "bounds_checking_function:libsec_static", + ] + configs = [ ":f2fs-defaults", ":libf2fs-headers", diff --git a/lib/Makefile.am b/lib/Makefile.am index 871d773176e940274335ffd5f95d5ca8d1a29014..0bfe49530cd2b53818b58400449d4b5173c13036 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -2,7 +2,7 @@ lib_LTLIBRARIES = libf2fs.la -libf2fs_la_SOURCES = libf2fs.c libf2fs_io.c libf2fs_zoned.c nls_utf8.c +libf2fs_la_SOURCES = libf2fs_log.c libf2fs.c libf2fs_io.c libf2fs_zoned.c nls_utf8.c libf2fs_la_CFLAGS = -Wall libf2fs_la_CPPFLAGS = -I$(top_srcdir)/include libf2fs_la_LDFLAGS = -version-info $(LIBF2FS_CURRENT):$(LIBF2FS_REVISION):$(LIBF2FS_AGE) diff --git a/lib/extra_fsck.c b/lib/extra_fsck.c new file mode 100644 index 0000000000000000000000000000000000000000..f44ed0a473789d490a6b063fcf6b91ae038fedd1 --- /dev/null +++ b/lib/extra_fsck.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * Description: enhance log records for fsck-tools + * Create: 2024-3-4 +*/ +#include "extra_fsck.h" + +void ClearExtraFlag(struct f2fs_super_block *sb, unsigned int flag) +{ + struct ExtraFlagsBlock *efBlk; + unsigned long long cpBlkaddr; + unsigned int blocksPerSeg; + int ret; + + cpBlkaddr = le32_to_cpu(sb->cp_blkaddr); + efBlk = calloc(F2FS_BLKSIZE, 1); + if (!efBlk) { + ERR_MSG("failed to alloc ExtraFlagsBlock\n"); + return; + } + blocksPerSeg = 1 << get_sb(log_blocks_per_seg); + if (dev_read_block(efBlk, cpBlkaddr + blocksPerSeg - 1) < 0) { + ERR_MSG("failed to read ExtraFlagsBlock\n"); + goto free; + } + + switch (flag) { + case EXTRA_NEED_FSCK_FLAG: + if (!efBlk->needFsck) { + goto free; + } + efBlk->needFsck = 0; + break; + default: + ERR_MSG("unknown extra flag 0x%x\n", flag); + goto free; + } + + /* do not use crc for now */ + ret = dev_write_block(efBlk, cpBlkaddr + blocksPerSeg - 1); + if (ret < 0) { + ERR_MSG("failed to write ExtraFlagsBlock\n"); + } + f2fs_fsync_device(); +free: + free(efBlk); +} + +void CheckExtraFlag(struct f2fs_super_block *sb, unsigned int flag) +{ + struct ExtraFlagsBlock *efBlk; + unsigned long long cpBlkaddr; + unsigned int blocksPerSeg; + + cpBlkaddr = le32_to_cpu(sb->cp_blkaddr); + efBlk = calloc(F2FS_BLKSIZE, 1); + if (!efBlk) { + return; + } + blocksPerSeg = 1 << get_sb(log_blocks_per_seg); + if (dev_read_block(efBlk, cpBlkaddr + blocksPerSeg - 1) < 0) { + goto free; + } + + /* do not use crc for now */ + switch (flag) { + case EXTRA_NEED_FSCK_FLAG: + if (le32_to_cpu(efBlk->needFsck) == flag) { + ASSERT_MSG("EXTRA_NEED_FSCK_FLAG is set"); + c.fix_on = 1; + DMD_ADD_ERROR(LOG_TYP_FSCK, PR_EXTRA_NEED_FSCK_FLAG_SET); + } + break; + default: + break; + } + +free: + free(efBlk); +} diff --git a/lib/extra_fsck.h b/lib/extra_fsck.h new file mode 100644 index 0000000000000000000000000000000000000000..111339e565ce3a92520f4817fe1140fd3ee2ef08 --- /dev/null +++ b/lib/extra_fsck.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * Description: enhance log records for fsck-tools + * Create: 2024-3-4 +*/ +#ifndef EXTRA_FSCK_H_ +#define EXTRA_FSCK_H_ + +#include +#include + +/* extra_flags is placed at the last block of CP 0 segment */ +struct ExtraFlagsBlock { + __le32 needFsck; + __u8 reserved[4088]; + __le32 crc; +} __attribute__((packed)); + +#define EXTRA_NEED_FSCK_FLAG 0x4653434b // ascii of "FSCK" + +void ClearExtraFlag(struct f2fs_super_block *sb, unsigned int flag); +void CheckExtraFlag(struct f2fs_super_block *sb, unsigned int flag); + +#endif diff --git a/lib/libf2fs.c b/lib/libf2fs.c index 148ed3d7e2afe411f4ebe4447bef3d56aa4f6f39..aae9915b54c0e6b50623782c365a14d7906b44f7 100644 --- a/lib/libf2fs.c +++ b/lib/libf2fs.c @@ -542,7 +542,6 @@ uint32_t f2fs_cal_crc32(uint32_t crc, void *buf, int len) int f2fs_crc_valid(uint32_t blk_crc, void *buf, int len) { uint32_t cal_crc = 0; - cal_crc = f2fs_cal_crc32(F2FS_SUPER_MAGIC, buf, len); if (cal_crc != blk_crc) { @@ -1295,6 +1294,8 @@ unsigned int calc_extra_isize(void) if (c.feature & cpu_to_le32(F2FS_FEATURE_INODE_CRTIME)) size = offsetof(struct f2fs_inode, i_compr_blocks); if (c.feature & cpu_to_le32(F2FS_FEATURE_COMPRESSION)) + size = offsetof(struct f2fs_inode, i_inner_ino); + if (c.feature & cpu_to_le32(F2FS_FEATURE_DEDUP)) size = offsetof(struct f2fs_inode, i_extra_end); return size - F2FS_EXTRA_ISIZE_OFFSET; diff --git a/lib/libf2fs_dmd.c b/lib/libf2fs_dmd.c new file mode 100644 index 0000000000000000000000000000000000000000..9a3d0d53d2593b4c58ab0842c71ca485ae1ec0e7 --- /dev/null +++ b/lib/libf2fs_dmd.c @@ -0,0 +1,260 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * Description: enhance log records for fsck-tools + * Create: 2024-3-4 +*/ +#include "f2fs_dmd_cfg.h" +#include "f2fs_dmd.h" + +#include +#include +#include +#include +#include + +#include "f2fs_log.h" +#include "securec.h" + +#ifndef CONF_TARGET_HOST +#define HIEVENT_DRIVER_NODE "/dev/storage" +#define HM_STORAGE_IOCTL_BASE 'S' +#define HM_STORAGE_CMD_EVENT_REPORT_FSCK 1 +#define EVENT_REPORT_FSCK_CMD \ + _IOW(HM_STORAGE_IOCTL_BASE, HM_STORAGE_CMD_EVENT_REPORT_FSCK, struct DmdReport) + +enum EVENT_REPORT_FSCK_TYPE { + EVENT_REPORT_FSCK_TOOLS_ERROR = 1 +}; + +#define FAULT_FUNC_SIZE 20U +#define FAULT_STORE_SIZE 64U /* maximum number of tallied faults */ +#define ASSERT_MSG_SIZE 1536U +#define BITS_PER_UINT64 64U +#define LOG2_BITS_PER_UINT64 6U + +struct __attribute__((packed)) DmdFault { + char func[FAULT_FUNC_SIZE]; /* func name buffer size */ + int line; + unsigned int errnum; + unsigned int count; +}; + +#define NR_OVERTIME_THRESHOLD_LEVEL 3U +enum { + idxSpaceThreshold = 0, + idxTimeThreshold = 1, + idxThresholdMax +}; +const unsigned long g_overtimeThresholdBySpace[NR_OVERTIME_THRESHOLD_LEVEL][idxThresholdMax] = { + /* total FS size in MB, Time cost threshold in millisecs */ + {524288uL, 1000uL}, + {1048576uL, 3000uL}, + {2097152uL, 5000uL} +}; + +struct DmdFault g_dmdErrorStore[FAULT_STORE_SIZE] = {0}; +unsigned int g_nrStoredErrors = 0; +struct DmdReport g_dmdReport = {0}; +bool g_dmdMarkReport = false; + +struct DmdMsg g_reportMsg = { + .buffer = g_dmdReport.msg, + .size = FSCK_REPORT_MSG_SIZE, + .offset = 0 +}; + +char g_dmdAssertMsg[ASSERT_MSG_SIZE] = {0}; +struct DmdMsg g_assertMsg = { + .buffer = g_dmdAssertMsg, + .size = ASSERT_MSG_SIZE, + .offset = 0 +}; + +static void DmdSetPropertyBit(unsigned int bit) +{ + g_dmdReport.propBitmap |= bit; +} + +static void DmdClearPropertyBit(unsigned int bit) +{ + g_dmdReport.propBitmap &= (~bit); +} + +static int DmdMarkReportWrite(void) +{ + if (!g_dmdMarkReport) { + return DMD_OK; + } + + int fd = open(HIEVENT_DRIVER_NODE, O_RDWR); + if (fd < 0) { + KLOGE("Fail to open hievent node: %d.\n", errno); + return DMD_ERR; + } + + if (ioctl(fd, EVENT_REPORT_FSCK_CMD, &g_dmdReport) < 0) { + KLOGE("Fail to write DmdReportFields with errno: %d.\n", errno); + close(fd); + return DMD_ERR; + } + close(fd); + KLOGI("Mark sysfs tools_report success.\n"); + g_dmdMarkReport = false; + return DMD_OK; +} + +static void ReadDeviceState(void) +{ + const char cmdlinePath[] = "/proc/cmdline"; + const char matchStr[] = "ohos.boot.hvb.device_state=locked"; + char *line = NULL; + size_t len = 0; + ssize_t nread; + + FILE *stream = fopen(cmdlinePath, "r"); + if (stream == NULL) { + return; + } + + while ((nread = getline(&line, &len, stream)) != -1) { + if (strstr(line, matchStr) != NULL) { + DmdSetPropertyBit(FB_LOCKED_FL); + break; + } + } + if (line) { + free(line); + } +} + +static void FillReportMsg(void) +{ + unsigned int i = 0; + + while (i < g_nrStoredErrors) { + PrintToMsg(&g_reportMsg, "[ERR=%d(%s:%d)%u]", + g_dmdErrorStore[i].errnum, g_dmdErrorStore[i].func, + g_dmdErrorStore[i].line, g_dmdErrorStore[i].count); + i++; + } + + PrintToMsg(&g_reportMsg, "%s", g_assertMsg.buffer); +} + +int DmdReport(void) +{ + int ret; + + FillReportMsg(); + ReadDeviceState(); + ret = DmdMarkReportWrite(); + if (ret < 0) { + KLOGE("Write sysfs tools_report failed\n"); + } + + return ret; +} + +static void DmdMarkReport(void) +{ + if (g_dmdMarkReport) { + return; + } + + g_dmdMarkReport = true; +} + +static int SetErrBitmap(unsigned int errnum) +{ + unsigned int index = errnum >> LOG2_BITS_PER_UINT64; + uint64_t mask = 1ULL << (errnum & (BITS_PER_UINT64 - 1U)); + + if (index >= NR_ERR_BITMAP_UINT64) { + KLOGE("err=%u larger than bits of errBitmap\n", errnum); + return DMD_ERR; + } + g_dmdReport.errBitmap[index] |= mask; + return DMD_OK; +} + +void DmdInsertError(int type, unsigned int err, const char *func, int line) +{ + unsigned int i; + int ret = SetErrBitmap(err); + if (ret < 0) { + return; + } + + /* check for similar error occurence */ + for (i = 0; i < g_nrStoredErrors; ++i) { + if (g_dmdErrorStore[i].errnum == err && g_dmdErrorStore[i].line == line && + strncmp(g_dmdErrorStore[i].func, func, FAULT_FUNC_SIZE) == 0) { + g_dmdErrorStore[i].count++; + return; + } + } + + if (g_nrStoredErrors >= FAULT_STORE_SIZE) { + KLOGE("DMD more faults than dump storage, DMD MSG: %s:%d %x;\n", func, line, err); + return; + } + + ret = strncpy_s(g_dmdErrorStore[g_nrStoredErrors].func, FAULT_FUNC_SIZE, func, FAULT_FUNC_SIZE - 1U); + if (ret) { + KLOGE("strcpy failed, DMD MSG: %s:%d %x;\n", func, line, err); + return; + } + g_dmdErrorStore[g_nrStoredErrors].line = line; + g_dmdErrorStore[g_nrStoredErrors].errnum = err; + g_dmdErrorStore[g_nrStoredErrors].count = 1U; + g_nrStoredErrors++; + DmdMarkReport(); +} + +void PrintToMsg(struct DmdMsg *dmdMsg, const char *fmt, ...) +{ + va_list argList; + va_start(argList, fmt); + + if (dmdMsg->offset < dmdMsg->size - 1U) { + int ret = vsnprintf_s(dmdMsg->buffer + dmdMsg->offset, + dmdMsg->size - dmdMsg->offset, + dmdMsg->size - dmdMsg->offset - 1U, + fmt, argList); + if (ret > 0) { + dmdMsg->offset += (unsigned int)ret; + } else { + /* Ignore errors, print to buffer as much as buffer size */ + dmdMsg->offset = strlen(dmdMsg->buffer); + } + } + + va_end(argList); + (void)argList; +} + +void DmdCheckCostTime(const char *func, int line) +{ + unsigned int iLevel; + unsigned long totalSize = g_dmdReport.usedSpace + g_dmdReport.freeSpace; + + for (iLevel = 0; iLevel < NR_OVERTIME_THRESHOLD_LEVEL; iLevel++) { + if (totalSize <= g_overtimeThresholdBySpace[iLevel][idxSpaceThreshold]) { + if (g_dmdReport.costTime > g_overtimeThresholdBySpace[iLevel][idxTimeThreshold]) { + DmdInsertError(LOG_TYP_FSCK, PR_FSCK_TIME_OVERCOST, func, line); + } + break; + } + } + + DmdMarkReport(); /* Currently we report everytime for statistics */ +} + +#else + +int DmdReport(void) +{ + return DMD_OK; +} + +#endif diff --git a/lib/libf2fs_io.c b/lib/libf2fs_io.c index 0b98f29ca93e658eed6a5186d0cb2f6a23484205..2f211267e2750960a3adf902341103437bf0119c 100644 --- a/lib/libf2fs_io.c +++ b/lib/libf2fs_io.c @@ -686,22 +686,41 @@ int f2fs_init_sparse_file(void) #endif } -void f2fs_release_sparse_resource(void) +void f2fs_release_sparse_blocks(void) { #ifdef HAVE_SPARSE_SPARSE_H int j; + if (blocks != NULL) { + for (j = 0; j < blocks_count; j++) { + if (blocks[j]) { + free(blocks[j]); + blocks[j] = NULL; + } else { + continue; + } + } + free(blocks); + blocks = NULL; + } +#endif +} + +void f2fs_release_sparse_resource(void) +{ +#ifdef HAVE_SPARSE_SPARSE_H if (c.sparse_mode) { if (f2fs_sparse_file != NULL) { sparse_file_destroy(f2fs_sparse_file); f2fs_sparse_file = NULL; } - for (j = 0; j < blocks_count; j++) - free(blocks[j]); - free(blocks); - blocks = NULL; - free(zeroed_block); - zeroed_block = NULL; + + f2fs_release_sparse_blocks(); + + if (zeroed_block != NULL) { + free(zeroed_block); + zeroed_block = NULL; + } } #endif } diff --git a/lib/libf2fs_log.c b/lib/libf2fs_log.c new file mode 100644 index 0000000000000000000000000000000000000000..c7b448e9a6aa3331d69cb0c638c26e36409ec485 --- /dev/null +++ b/lib/libf2fs_log.c @@ -0,0 +1,527 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved. + * Description: enhance log records for fsck-tools + * Create: 2023-11-24 +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "securec.h" +#include + +#include +#include + +#define LOG_PARTITION_DEV "/dev/" +#define LOG_PARTITION_LOG "/log/" +#define LOG_MAX_SIZE 0x40000 /* 256KB */ +#define LOG_FILE_O_FLAGS 0664 +#define LOG_DIR_MODE 0775 +#define LOG_KMSG_NODE_BASE_MODE 0600 +#define LOG_PARTITION_SIZE 2 +#define LOG_SLOG_FILENAME_DIFFERENCE 2 +#define LOG_SLOG_INIT_MEM_BUF_ELEMENT_NUM 1 +#define LOG_SLOG_INIT_MEM_BUF_SIZE 64 +#define LOG_BUF_INITIAL_SIZE 64 +#define LOG_TM_STARTING_YEAR 1900 +#define LOG_KMSG_NODE_DEV ((1 << 8) | 11) +#define LOG_BUF_SIZE_PROTECT_THRESHOLD 2048 /* maximum buffer size allowed */ +#define UID_ROOT 0 +#define GID_SYSTEM 1000 + +char *g_logTag[7] = { + "F2FS-tools", + "F2FS.fsck", + "F2FS.dump", + "F2FS.defrag", + "F2FS.resize", + "F2FS.mkfs", + "", +}; + +static char *g_logPartition[LOG_PARTITION_SIZE] = { + LOG_PARTITION_LOG, + LOG_PARTITION_DEV +}; + +struct LogInfo g_logI = { + .type = LOG_TYP_NONE, + .slogFd = -1, + .klogFd = -1, + .klogLevel = KLOG_DEFAULT_LEVEL, + .slogOffset = 0, + .needTruncate = 0, + .slogFile = NULL, + .slogFileBak = NULL, + .logDir = NULL +}; + +static int DoSlogInit(int logType); +static void KlogInit(void); +static void KlogWritev(int level, const struct iovec* iov, int iovCount); +static int SlogFixSize(int requestSize); +static int InitLogStruct(); +static int InitLogFilenames(int logType, char *name); +static int InitLogInfo(int logType); + +/* + * klog_* are copied from system/core/libcutils/klog.c + * klog write data to /dev/kmsg, to save message in kernel log + */ + +static void KlogInit(void) +{ + if (g_logI.klogFd >= 0) { + return; /* Already initialized */ + } + + g_logI.klogFd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC); + if (g_logI.klogFd >= 0) { + return; + } + + static const char* name = "/dev/__kmsg__"; + if (mknod(name, S_IFCHR | LOG_KMSG_NODE_BASE_MODE, LOG_KMSG_NODE_DEV) == 0) { + g_logI.klogFd = open(name, O_WRONLY | O_CLOEXEC); + unlink(name); + } +} + +static void KlogWritev(int level, const struct iovec* iov, int iovCount) +{ + if (level > g_logI.klogLevel) { + return; + } + if (g_logI.klogFd < 0) { + KlogInit(); + } + if (g_logI.klogFd < 0) { + return; + } + TEMP_FAILURE_RETRY(writev(g_logI.klogFd, iov, iovCount)); +} + +void KlogWrite(int level, const char* fmt, ...) +{ + int ret; + char *buf; + va_list ap; + + buf = calloc(1, LOG_BUF_MAX); + if (!buf) { + KLOGE("calloc klog buf failed\n"); + return; + } + + va_start(ap, fmt); + ret = vsnprintf_s(buf, LOG_BUF_MAX, LOG_BUF_MAX - 1, fmt, ap); + printf("dbg klog : "); + printf(buf); + va_end(ap); + + if (ret < 0) { + printf("klog string is oversized.\n"); + } + + struct iovec iov[1]; + iov[0].iov_base = buf; + iov[0].iov_len = strlen(buf); + KlogWritev(level, iov, 1); + free(buf); +} + +static int SlogFixSize(int requestSize) +{ + struct stat st; + int mode; + + if (stat(g_logI.slogFile, &st) < 0) { + if (errno == ENOENT) { + return 0; + } + KLOGE("cannot stat %s errno %d\n", g_logI.slogFile, errno); + return LOG_ERR; + } + + if (!S_ISREG(st.st_mode)) { + KLOGE("file %s is not a regular file\n", g_logI.slogFile); + return LOG_ERR; + } + + if (LOG_MAX_SIZE - st.st_size > requestSize) { + return 0; + } + + /* + * LOG_FILE does not have enough size, we rename it to LOG_FILE_BAK, + * then unlink LOG_FILE, so that we can write new log to LOG_FILE + * again + */ + close(g_logI.slogFd); + if (rename(g_logI.slogFile, g_logI.slogFileBak)) { + KLOGE("rename fail errno %d\n", errno); + /* force unlink LOG_FILE */ + unlink(g_logI.slogFile); + } + + KLOGI("%s is full, rename it to %s\n", g_logI.slogFile, + g_logI.slogFileBak); + if (access(g_logI.slogFile, F_OK) < 0) { + if (errno != ENOENT) { + KLOGE("cannot unlink %s, errno %d\n", g_logI.slogFile, + errno); + return LOG_ERR; + } + } else { + KLOGE("fail to rename %s\n", g_logI.slogFile); + return LOG_ERR; + } + + mode = O_RDWR | O_CLOEXEC | O_CREAT; + g_logI.slogFd = open(g_logI.slogFile, mode, LOG_FILE_O_FLAGS); + if (g_logI.slogFd < 0) { + KLOGE("re-open slog file fail errno %d\n", errno); + return g_logI.slogFd; + } + if (fchown(g_logI.slogFd, UID_ROOT, GID_SYSTEM) < 0) { + KLOGE("chown slog file fail errno %d\n", errno); + } + + return 0; +} + +static int InitLogStruct() +{ + unsigned int size; + struct stat st; + int ret, i; + mode_t old_umask; + + for (i = 0; i < LOG_PARTITION_SIZE; i++) { + if (stat(g_logPartition[i], &st)) { + KLOGE("failed to stat %s\n", g_logPartition[i]); + continue; + } + if (!S_ISDIR(st.st_mode)) { + KLOGE("%s is not a directory\n", g_logPartition[i]); + continue; + } + size = strlen(g_logPartition[i]) + strlen("f2fs-tools/") + 1; + g_logI.logDir = malloc(size); + if (g_logI.logDir == NULL) { + KLOGE("Log dir name fails to initialize\n"); + return LOG_ERR; + } + ret = snprintf_s(g_logI.logDir, size, size - 1, "%s%s", g_logPartition[i], "f2fs-tools/"); + if (ret < 0) { + KLOGE("Log dir name is oversized\n"); + free(g_logI.logDir); + g_logI.logDir = NULL; + continue; + } + old_umask = umask(0); + ret = mkdir(g_logI.logDir, LOG_DIR_MODE); + umask(old_umask); + if (ret < 0 && errno != EEXIST) { + KLOGE("mkdir %s fail errno %d\n", g_logI.logDir, errno); + KLOGE("failed to log into %s ret : %d\n", g_logPartition[i], ret); + free(g_logI.logDir); + g_logI.logDir = NULL; + continue; + } + if (chown(g_logI.logDir, UID_ROOT, GID_SYSTEM) < 0) { + KLOGE("chown log dir fail errno %d\n", errno); + } + KLOGI("fsck logging to %s\n", g_logPartition[i]); + break; + } + if (i >= LOG_PARTITION_SIZE) { + KLOGE("init log fail\n"); + return LOG_ERR; + } + return 0; +} + +static int InitLogFilenames(int logType, char *name) +{ + unsigned int size; + int ret = 0; + + g_logI.type = logType; + size = strlen(g_logI.logDir) + strlen(name) + 1; + g_logI.slogFile = malloc(size); + if (g_logI.slogFile == NULL) { + KLOGE("slogFile name fails to initialize\n"); + return LOG_ERR; + } + ret = snprintf_s(g_logI.slogFile, size, size - 1, "%s%s", g_logI.logDir, name); + if (ret < 0) { + KLOGE("slogFile name fails to setup, return : %d\n", ret); + free(g_logI.slogFile); + g_logI.slogFile = NULL; + return ret; + } + ret = 0; + size += LOG_SLOG_FILENAME_DIFFERENCE; + g_logI.slogFileBak = malloc(size); + if (g_logI.slogFileBak == NULL) { + KLOGE("slogFileBak name fails to initialize\n"); + free(g_logI.slogFile); + g_logI.slogFile = NULL; + return LOG_ERR; + } + ret = snprintf_s(g_logI.slogFileBak, size, size - 1, "%s%s%s", g_logI.logDir, name, ".1"); + if (ret < 0) { + KLOGE("slogFileBak name fails to setup, return : %d\n", ret); + free(g_logI.slogFile); + g_logI.slogFile = NULL; + free(g_logI.slogFileBak); + g_logI.slogFileBak = NULL; + return ret; + } + return ret; +} + +static int InitLogInfo(int logType) +{ + char *name = ""; + int ret = 0; + + /* the file and back file are: + * g_logI.logDir/buf + * g_logI.logDir/buf".1" + */ + switch (logType) { + case LOG_TYP_FSCK: + name = "fsck.log"; + break; + case LOG_TYP_DUMP: + name = "dump.log"; + break; + case LOG_TYP_DEFRAG: + name = "defrag.log"; + break; + case LOG_TYP_RESIZE: + name = "resize.log"; + break; + case LOG_TYP_MKFS: + name = "mkfs.log"; + break; + default: + KLOGE("unknown log type %d\n", logType); + return LOG_ERR; + } + + ret = InitLogStruct(); + if (ret != 0) { + return ret; + } + + ret = InitLogFilenames(logType, name); + return ret; +} + +int SlogInit(int funcType) +{ + int ret; + printf("======== c.func ======== %d\n", (int)funcType); + switch (funcType) { + case FSCK: + ret = DoSlogInit(LOG_TYP_FSCK); + printf("======== SlogInit(LOG_TYP_FSCK) ======== %d\n", ret); + break; + case DUMP: + ret = DoSlogInit(LOG_TYP_DUMP); + printf("======== SlogInit(LOG_TYP_DUMP) ======== %d\n", ret); + break; + case DEFRAG: + ret = DoSlogInit(LOG_TYP_DEFRAG); + printf("======== SlogInit(LOG_TYP_DEFRAG) ======== %d\n", ret); + break; + case RESIZE: + ret = DoSlogInit(LOG_TYP_RESIZE); + printf("======== SlogInit(LOG_TYP_RESIZE) ======== %d\n", ret); + break; + default: + ret = -1; + break; + } + return ret; +} + +static int DoSlogInit(int logType) +{ + time_t t; + struct tm *tm; + char *buf = NULL; + int mode = O_RDWR | O_CLOEXEC | O_CREAT | O_APPEND; + int ret = 0; + + if (InitLogInfo(logType) < 0) { + return LOG_ERR; + } + + g_logI.slogFd = open(g_logI.slogFile, mode, LOG_FILE_O_FLAGS); + if (g_logI.slogFd < 0) { + KLOGE("open slog file [%s] fail errno %d\n, strerror %s", g_logI.slogFile, errno, strerror(errno)); + return g_logI.slogFd; + } + if (fchown(g_logI.slogFd, UID_ROOT, GID_SYSTEM) < 0) { + KLOGE("chown slog file fail errno %d\n", errno); + } + g_logI.slogOffset = lseek(g_logI.slogFd, 0, SEEK_END); + + KLOGI("Starting logging to %s, offset %ld\n", + g_logI.slogFile, g_logI.slogOffset); + /* get current date + * the string looks like "=== 2016/03/16 14:01:01 ===", its max + * length is 32. + */ + buf = calloc(LOG_SLOG_INIT_MEM_BUF_ELEMENT_NUM, LOG_SLOG_INIT_MEM_BUF_SIZE); + if (!buf) { + return LOG_ERR; + } + + t = time(NULL); + tm = localtime(&t); + if (!tm) { + free(buf); + return LOG_ERR; + } + ret = snprintf_s(buf, LOG_SLOG_INIT_MEM_BUF_SIZE, LOG_SLOG_INIT_MEM_BUF_SIZE - 1, + "\n=== %d/%02d/%02d %02d:%02d:%02d ===\n", + LOG_TM_STARTING_YEAR + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + if (ret < 0) { + KLOGE("write time stamp string buffer fail errno %d\n", errno); + } + ret = write(g_logI.slogFd, buf, strlen(buf)); + if (ret < 0) { + KLOGE("write time stamp %s fail errno %d\n", buf, errno); + } + free(buf); + return ret; +} + +void SlogExit(void) +{ + if (g_logI.slogFd < 0) { + return; + } + + if (g_logI.needTruncate && g_logI.slogOffset > 0) { + if (ftruncate(g_logI.slogFd, g_logI.slogOffset)) { + KLOGE("ftruncate slog fail\n"); + } + } + + if (fsync(g_logI.slogFd) < 0) { + KLOGE("fsync slog fail\n"); + } + + if (g_logI.logDir) + free(g_logI.logDir); + if (g_logI.slogFile) + free(g_logI.slogFile); + if (g_logI.slogFileBak) + free(g_logI.slogFileBak); + if (g_logI.slogFd) + close(g_logI.slogFd); + + KLOGI("Logging done\n"); +} + +int SlogInitBuffer(char **buffer, int *bufferSize, const char *fmt, va_list args) +{ + char *buf = NULL; + int size = LOG_BUF_INITIAL_SIZE; + int ret = 0; + + buf = malloc(size); + if (!buf) { + KLOGE("SlogFixSize fail to malloc buffer, size: %d\n", size); + return LOG_ERR; + } + + while (1) { + ret = memset_s(buf, size, 0, size); + if (ret != 0) { + KLOGE("SlogFixSize fail to initialize buffer.\n"); + free(buf); + return LOG_ERR; + } + + ret = vsnprintf_s(buf, size, size - 1, fmt, args); + /* when buf is truncated but return -1 */ + if (ret < 0 && strlen(buf) == size - 1) { + ret = size - 1; + } + if (ret < 0) { + KLOGE("SlogFixSize fail to format buffer: %d\n", ret); + free(buf); + return LOG_ERR; + } + + if (ret < size - 1 || size + size > LOG_BUF_SIZE_PROTECT_THRESHOLD) { + /* The buffer size should not exceed the protect threshold. + * The log output is going to be left as truncated by vsnprintf_s. + */ + /* we get the whole fmt... */ + size = ret; + break; + } + + size += size; + free(buf); + buf = malloc(size); + if (!buf) { + KLOGE("SlogFixSize fail to malloc buffer, size: %d\n", size); + return LOG_ERR; + } + } + + *bufferSize = size; + *buffer = buf; + return LOG_OK; +} + +int SlogWrite(const char *fmt, ...) +{ + va_list args; + char *buf = NULL; + int size = 0; + int ret = 0; + + if (g_logI.slogFd < 0) { + return 0; + } + + va_start(args, fmt); + ret = SlogInitBuffer(&buf, &size, fmt, args); + va_end(args); + if (ret < 0) { + KLOGE("SlogInitBuffer fail\n"); + return ret; + } + + ret = SlogFixSize(size); + if (ret < 0) { + KLOGE("SlogFixSize fail\n"); + free(buf); + return ret; + } + + ret = write(g_logI.slogFd, buf, size); + if (ret < 0) { + KLOGE("write to slog file fail errno %d\n", errno); + } + free(buf); + return ret; +} diff --git a/lib/libf2fs_zoned.c b/lib/libf2fs_zoned.c index 8d8f2a9bc2f8a42500e3f8904dc8f95b54de299b..10d16aec3e28f157b61ae114daacbb672f64b01f 100644 --- a/lib/libf2fs_zoned.c +++ b/lib/libf2fs_zoned.c @@ -352,7 +352,7 @@ int f2fs_check_zones(int j) } if (blk_zone_conv(blkz)) { - DBG(2, "Zone %05u: Conventional, cond 0x%x (%s), sector %llu, %llu sectors\n", + DBG(2, "Zone %05u: Conventional, cond 0x%x (%s), sector %llu, %llu sectors\n", n, blk_zone_cond(blkz), blk_zone_cond_str(blkz), blk_zone_sector(blkz), diff --git a/mkfs/BUILD.gn b/mkfs/BUILD.gn index ff5a429589b437917f177f7a8067097c33f0a7e6..0234422f222dceb7ee9a63ead193502febb3e943 100644 --- a/mkfs/BUILD.gn +++ b/mkfs/BUILD.gn @@ -20,6 +20,7 @@ config("f2fs-defaults") { ohos_executable("mkfs.f2fs") { configs = [ ":f2fs-defaults" ] sources = [ + "../lib/extra_fsck.c", "f2fs_format.c", "f2fs_format_main.c", "f2fs_format_utils.c", @@ -28,13 +29,16 @@ ohos_executable("mkfs.f2fs") { include_dirs = [ ".", "//third_party/f2fs-tools", - "//third_party/f2fs-tools/include", "//third_party/f2fs-tools/lib", + "//third_party/f2fs-tools/include", ] deps = [ "//third_party/f2fs-tools/lib:libf2fs" ] - public_external_deps = [ "e2fsprogs:libext2_uuid" ] + external_deps = [ + "e2fsprogs:libext2_uuid", + "e2fsprogs:libdacconfig" + ] defines = [ "HAVE_CONFIG_H" ] diff --git a/mkfs/f2fs_format.c b/mkfs/f2fs_format.c index 879991bb98f937eb3e1377033137e6e9ab0e8d78..ca953f88d1c3133ef93fc60dfe42698ab18ad23c 100644 --- a/mkfs/f2fs_format.c +++ b/mkfs/f2fs_format.c @@ -26,15 +26,16 @@ #include #ifdef HAVE_UUID_UUID_H -#include +#include #endif #ifndef HAVE_LIBUUID #define uuid_parse(a, b) -1 #define uuid_generate(a) #endif -#include "quota.h" +#include "extra_fsck.h" #include "f2fs_format_utils.h" +#include "quota.h" extern struct f2fs_configuration c; struct f2fs_super_block raw_sb; @@ -280,6 +281,7 @@ static int f2fs_prepare_super_block(void) set_sb(segment0_blkaddr, zone_align_start_offset / blk_size_bytes); sb->cp_blkaddr = sb->segment0_blkaddr; + ClearExtraFlag(sb, EXTRA_NEED_FSCK_FLAG); MSG(0, "Info: zone aligned segment0 blkaddr: %u\n", get_sb(segment0_blkaddr)); @@ -693,8 +695,6 @@ static int f2fs_write_check_point_pack(void) struct f2fs_summary_block *sum = NULL; struct f2fs_journal *journal; uint32_t blk_size_bytes; - uint32_t nat_bits_bytes, nat_bits_blocks; - unsigned char *nat_bits = NULL, *empty_nat_bits; uint64_t cp_seg_blk = 0; uint32_t crc = 0, flags; unsigned int i; @@ -724,19 +724,10 @@ static int f2fs_write_check_point_pack(void) } sum_compact_p = sum_compact; - nat_bits_bytes = get_sb(segment_count_nat) << 5; - nat_bits_blocks = F2FS_BYTES_TO_BLK((nat_bits_bytes << 1) + 8 + - F2FS_BLKSIZE - 1); - nat_bits = calloc(F2FS_BLKSIZE, nat_bits_blocks); - if (nat_bits == NULL) { - MSG(1, "\tError: Calloc failed for nat bits buffer!!!\n"); - goto free_sum_compact; - } - cp_payload = calloc(F2FS_BLKSIZE, 1); if (cp_payload == NULL) { MSG(1, "\tError: Calloc failed for cp_payload!!!\n"); - goto free_nat_bits; + goto free_sum_compact; } /* 1. cp page 1 of checkpoint pack 1 */ @@ -791,9 +782,6 @@ static int f2fs_write_check_point_pack(void) /* cp page (2), data summaries (1), node summaries (3) */ set_cp(cp_pack_total_block_count, 6 + get_sb(cp_payload)); flags = CP_UMOUNT_FLAG | CP_COMPACT_SUM_FLAG; - if (get_cp(cp_pack_total_block_count) <= - (1 << get_sb(log_blocks_per_seg)) - nat_bits_blocks) - flags |= CP_NAT_BITS_FLAG; if (c.trimmed) flags |= CP_TRIMMED_FLAG; @@ -1052,31 +1040,6 @@ static int f2fs_write_check_point_pack(void) goto free_cp_payload; } - /* write NAT bits, if possible */ - if (flags & CP_NAT_BITS_FLAG) { - uint32_t i; - - *(__le64 *)nat_bits = get_cp_crc(cp); - empty_nat_bits = nat_bits + 8 + nat_bits_bytes; - memset(empty_nat_bits, 0xff, nat_bits_bytes); - test_and_clear_bit_le(0, empty_nat_bits); - - /* write the last blocks in cp pack */ - cp_seg_blk = get_sb(segment0_blkaddr) + (1 << - get_sb(log_blocks_per_seg)) - nat_bits_blocks; - - DBG(1, "\tWriting NAT bits pages, at offset 0x%08"PRIx64"\n", - cp_seg_blk); - - for (i = 0; i < nat_bits_blocks; i++) { - if (dev_write_block(nat_bits + i * - F2FS_BLKSIZE, cp_seg_blk + i)) { - MSG(1, "\tError: write NAT bits to disk!!!\n"); - goto free_cp_payload; - } - } - } - /* cp page 1 of check point pack 2 * Initialize other checkpoint pack with version zero */ @@ -1116,8 +1079,6 @@ static int f2fs_write_check_point_pack(void) free_cp_payload: free(cp_payload); -free_nat_bits: - free(nat_bits); free_sum_compact: free(sum_compact); free_sum: @@ -1272,6 +1233,12 @@ static int f2fs_write_root_inode(void) raw_node->i.i_padding = 0; } + if (c.feature & cpu_to_le32(F2FS_FEATURE_DEDUP)) { + raw_node->i.i_inner_ino = 0; + raw_node->i.i_dedup_flags = 0; + raw_node->i.i_dedup_rsvd = 0; + } + data_blk_nor = get_sb(main_blkaddr) + c.cur_seg[CURSEG_HOT_DATA] * c.blks_per_seg; raw_node->i.i_addr[get_extra_isize(raw_node)] = cpu_to_le32(data_blk_nor); @@ -1594,6 +1561,12 @@ static int f2fs_write_lpf_inode(void) raw_node->i.i_padding = 0; } + if (c.feature & cpu_to_le32(F2FS_FEATURE_DEDUP)) { + raw_node->i.i_inner_ino = 0; + raw_node->i.i_dedup_flags = 0; + raw_node->i.i_dedup_rsvd = 0; + } + data_blk_nor = f2fs_add_default_dentry_lpf(); if (data_blk_nor == 0) { MSG(1, "\tError: Failed to add default dentries for lost+found!!!\n"); diff --git a/mkfs/f2fs_format_main.c b/mkfs/f2fs_format_main.c index 07084b3fdb07ff28e585fec337de52550f1abc55..a89acba1abb39b5d2e270dfb986fe5aca913c422 100644 --- a/mkfs/f2fs_format_main.c +++ b/mkfs/f2fs_format_main.c @@ -30,7 +30,7 @@ #include #endif #ifdef HAVE_UUID_UUID_H -#include +#include #endif #include "quota.h" @@ -110,6 +110,9 @@ static void f2fs_show_info() if (c.feature & le32_to_cpu(F2FS_FEATURE_COMPRESSION)) MSG(0, "Info: Enable Compression\n"); + + if (c.feature & le32_to_cpu(F2FS_FEATURE_DEDUP)) + MSG(0, "Info: Enable Dedup\n"); } #if defined(ANDROID_TARGET) && defined(HAVE_SYS_UTSNAME_H) @@ -332,6 +335,11 @@ static void f2fs_parse_options(int argc, char *argv[]) "enabled with extra attr feature\n"); exit(1); } + if (c.feature & cpu_to_le32(F2FS_FEATURE_DEDUP)) { + MSG(0, "\tInfo: dedup feature should always be " + "enabled with extra attr feature\n"); + exit(1); + } } if (optind >= argc) { @@ -449,6 +457,11 @@ int main(int argc, char *argv[]) if (f2fs_get_device_info() < 0) return -1; + unsigned long long total_size = (c.total_sectors * c.sector_size) >> F2FS_GB_SHIFT; + if (total_size > HMFS_LARGE_NAT_BITMAP_MIN_SIZE) { + c.large_nat_bitmap = 1; + } + if (f2fs_check_overwrite()) { char *zero_buf = NULL; int i; diff --git a/tools/BUILD.gn b/tools/BUILD.gn index be1cbfd463fab74e4ea6d6776752cff1a8176506..9dc44bff589ad72d95f664b1d9b68b14698570b4 100644 --- a/tools/BUILD.gn +++ b/tools/BUILD.gn @@ -26,11 +26,10 @@ ohos_executable("f2fscrypt") { ".", "//third_party/f2fs-tools", "//third_party/f2fs-tools/include", - "//third_party/e2fsprogs/e2fsprogs/lib/uuid", ] cflags = [ "-Wno-error=format-extra-args" ] - deps = [ "//third_party/e2fsprogs:libext2_uuid" ] + external_deps = [ "e2fsprogs:libext2_uuid" ] install_enable = true subsystem_name = "thirdparty" diff --git a/tools/debug_tools/fsck_debug.c b/tools/debug_tools/fsck_debug.c index 33ffb216f91ffa6f77871612850086ce38a8986b..304709040eb621a0f0e01793e8b2bcbecc53992e 100644 --- a/tools/debug_tools/fsck_debug.c +++ b/tools/debug_tools/fsck_debug.c @@ -38,6 +38,7 @@ void dump_sbi_info(struct f2fs_sb_info *sbi) #define HEX_SHIFT_8 8 #define HEX_SHIFT_4 4 #define HEX_MASK 0x0F +#define U32_PER_SEG 64 void hex_info_dump(const char *prompts, const unsigned char *buf, unsigned int len) { @@ -66,4 +67,53 @@ void hex_info_dump(const char *prompts, const unsigned char *buf, MSG(0, "%s\n", line); } MSG(0, "===HEX DUMP END===\n"); +} + +static void dump_one_segment(const char *bitmap, unsigned int segno) +{ + for (u32 i = 0; i < U32_PER_SEG; i++) { + MSG(0, " %02x", *(bitmap + i + segno * U32_PER_SEG)); + + if ((i + 1) % LINE_MAX_INTS == 0) { + MSG(0, "\n"); + } + } +} + +static bool has_diff_segment(const char *sit_area_bitmap, const char *main_area_bitmap, unsigned int segno) +{ + for (u32 i = 0; i < U32_PER_SEG; i++) { + if (*(main_area_bitmap + i + segno * U32_PER_SEG) != *(sit_area_bitmap + i + segno * U32_PER_SEG)) { + return true; + } + } + return false; +} + +void dump_bitmap_diff(struct f2fs_sb_info *sbi, const char *sit_area_bitmap, const char *main_area_bitmap) +{ + struct seg_entry *se; + unsigned int segno; + int i; + u32 main_segments = SM_I(sbi)->main_segments; + + if (sit_area_bitmap == NULL || main_area_bitmap == NULL) { + return; + } + + for (segno = 0; segno < main_segments; segno++) { + se = get_seg_entry(sbi, segno); + if (se->valid_blocks == 0x0) { + continue; + } + + if (has_diff_segment(sit_area_bitmap, main_area_bitmap, segno)) { + MSG(0, "segno: %u vblocks: %u seg_type: %d\n", segno, se->valid_blocks, se->type); + MSG(0, "=Dump main bitmap=\n"); + dump_one_segment(main_area_bitmap, segno); + MSG(0, "=Dump sit bitmap=\n"); + dump_one_segment(sit_area_bitmap, segno); + return; + } + } } \ No newline at end of file diff --git a/tools/debug_tools/fsck_debug.h b/tools/debug_tools/fsck_debug.h index 603ef5d40fabee6e94d1434867b8677d53da2fdb..21748f0e34e61152bdf293a136ec3f0268256e5e 100644 --- a/tools/debug_tools/fsck_debug.h +++ b/tools/debug_tools/fsck_debug.h @@ -13,8 +13,10 @@ #include "f2fs.h" void dump_sbi_info(struct f2fs_sb_info *); -void hex_info_dump(const char *, const unsigned char *, - unsigned int); +void hex_info_dump(const char *prompts, const unsigned char *buf, + unsigned int len); +void dump_bitmap_diff(struct f2fs_sb_info *, const char *, const char *); +extern struct seg_entry *get_seg_entry(struct f2fs_sb_info *, unsigned int); static inline unsigned int total_segments(struct f2fs_sb_info *sbi) { diff --git a/tools/hmfs_tools/hmfs_tools.c b/tools/hmfs_tools/hmfs_tools.c new file mode 100644 index 0000000000000000000000000000000000000000..e36d1251b44ba40c8c98281382e9463a05bd5c07 --- /dev/null +++ b/tools/hmfs_tools/hmfs_tools.c @@ -0,0 +1,33 @@ +/* + * hmfs_tools.c + * + * Copyright (C) 2024 Huawei Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "hmfs_tools.h" +#include "../../fsck/f2fs.h" +#include "../../fsck/fsck.h" + +void hmfs_enable_large_nat_bitmap(struct f2fs_sb_info *sbi, unsigned long long total_size, unsigned long long cur_size) +{ + /* has large_nat_bitmap before */ + if (sbi != NULL) { + struct f2fs_checkpoint *cp = F2FS_CKPT(sbi); + u32 flags = get_cp(ckpt_flags); + if (flags & CP_LARGE_NAT_BITMAP_FLAG) { + c.large_nat_bitmap = 1; + } + } + /* when in first resize */ + if (cur_size > (total_size >> 1)) { + return; + } + + if (total_size > HMFS_LARGE_NAT_BITMAP_MIN_SIZE) { + c.large_nat_bitmap = 1; + } +} \ No newline at end of file diff --git a/tools/hmfs_tools/hmfs_tools.h b/tools/hmfs_tools/hmfs_tools.h new file mode 100644 index 0000000000000000000000000000000000000000..3b1edceedabe7561bb00530bf07ec73f2b06f745 --- /dev/null +++ b/tools/hmfs_tools/hmfs_tools.h @@ -0,0 +1,23 @@ +/** + * hmfs_tools.h + * + * Copyright (C) 2024 Huawei Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef _HMFS_TOOLS_H_ +#define _HMFS_TOOLS_H_ + +#include + +enum compress_algorithm_type { + COMPRESS_ALGO_LZO, + COMPRESS_ALGO_LZ4, + COMPRESS_ALGO_ZSTD, + COMPRESS_ALGO_LZORLE, + COMPRESS_ALGO_MAX, +}; + +#endif /* _HMFS_TOOLS_H_ */ \ No newline at end of file