diff options
author | JP Abgrall <jpa@google.com> | 2014-11-07 17:08:09 -0800 |
---|---|---|
committer | JP Abgrall <jpa@google.com> | 2014-11-07 17:09:13 -0800 |
commit | 3f4408b13665bd82dd7edf5c76ed9dccc3a3fce7 (patch) | |
tree | 62901f86d5f7470134b57b0b6dfd69b47a909b23 | |
parent | fc24ac8347630a14d62ffafa93a27ec3b81cc44b (diff) | |
parent | 100ea9f321b92fa1e15889ec0ef72b6b8929459d (diff) | |
download | f2fs-tools-lollipop-mr1-cts-release.tar.gz |
Update fsck to latest version (1.2 -> 1.4.0++ with better fix support)android-wear-5.1.1_r1android-wear-5.1.0_r1android-cts-5.1_r9android-cts-5.1_r8android-cts-5.1_r7android-cts-5.1_r6android-cts-5.1_r5android-cts-5.1_r4android-cts-5.1_r3android-cts-5.1_r28android-cts-5.1_r27android-cts-5.1_r26android-cts-5.1_r25android-cts-5.1_r24android-cts-5.1_r23android-cts-5.1_r22android-cts-5.1_r21android-cts-5.1_r20android-cts-5.1_r2android-cts-5.1_r19android-cts-5.1_r18android-cts-5.1_r17android-cts-5.1_r16android-cts-5.1_r15android-cts-5.1_r14android-cts-5.1_r13android-cts-5.1_r10android-cts-5.1_r1android-5.1.1_r9android-5.1.1_r8android-5.1.1_r7android-5.1.1_r6android-5.1.1_r5android-5.1.1_r4android-5.1.1_r38android-5.1.1_r37android-5.1.1_r36android-5.1.1_r35android-5.1.1_r34android-5.1.1_r33android-5.1.1_r30android-5.1.1_r3android-5.1.1_r29android-5.1.1_r28android-5.1.1_r26android-5.1.1_r25android-5.1.1_r24android-5.1.1_r23android-5.1.1_r22android-5.1.1_r20android-5.1.1_r2android-5.1.1_r19android-5.1.1_r18android-5.1.1_r17android-5.1.1_r16android-5.1.1_r15android-5.1.1_r14android-5.1.1_r13android-5.1.1_r12android-5.1.1_r10android-5.1.1_r1android-5.1.0_r5android-5.1.0_r4android-5.1.0_r3android-5.1.0_r1lollipop-mr1-wfc-releaselollipop-mr1-releaselollipop-mr1-fi-releaselollipop-mr1-devlollipop-mr1-cts-release
Merge remote-tracking branch 'goog/lmp-mr1-dev-plus-aosp' into update_to_140
* goog/lmp-mr1-dev-plus-aosp: (45 commits)
fsck.f2fs: actually flag the fixed dentries as fixed
f2fs: use last_blk for print dentries
fsck.f2fs: fix DATA_EXIST flag for old partition
fsck.f2fs: attempt to identify bad dentries
Update build version from 1.2.0 to 1.4.0 after merge.
fsck.f2fs: show inline status of inode
fsck.f2fs: fix wrongly allocated 0'th block for inline_data
fsck.f2fs: fix link count correctly
fsck.f2fs: fix wrong hash_code made by previous buggy code
fsck.f2fs: support inline_dentry
fsck.f2fs: fix superblock offset
mkfs.f2fs: avoid build warning
fsck.f2fs: fix wrong block addres of nids
mkfs.f2fs: possible endianes bug in mkfs.f2fs roll-forward speed
f2fs-tools: fix for build big-endian processors
f2fs-tools: release 1.4.0
f2fstat: enhance readability of output
tracepoint.sh: update latest tracepoints
f2fs: rearrange options to remove redundant check
fsck.f2fs: add auto_fix feature
...
Bug: 17640053
Bug: 18292088
Change-Id: Idab448af4fccd7033ca5e3e55e3f3472673df622
Signed-off-by: JP Abgrall <jpa@google.com>
-rw-r--r-- | Android.mk | 2 | ||||
-rw-r--r-- | VERSION | 4 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | fsck/dump.c | 170 | ||||
-rw-r--r-- | fsck/f2fs.h | 63 | ||||
-rw-r--r-- | fsck/fsck.c | 981 | ||||
-rw-r--r-- | fsck/fsck.h | 112 | ||||
-rw-r--r-- | fsck/main.c | 190 | ||||
-rw-r--r-- | fsck/mount.c | 492 | ||||
-rw-r--r-- | include/f2fs_fs.h | 190 | ||||
-rw-r--r-- | lib/libf2fs.c | 47 | ||||
-rw-r--r-- | lib/libf2fs_io.c | 14 | ||||
-rw-r--r-- | mkfs/Makefile.am | 2 | ||||
-rw-r--r-- | mkfs/f2fs_format.c | 81 | ||||
-rw-r--r-- | mkfs/f2fs_format_main.c | 3 | ||||
-rw-r--r-- | mkfs/f2fs_format_utils.c | 6 | ||||
-rwxr-xr-x | scripts/tracepoint.sh | 24 | ||||
-rw-r--r-- | tools/f2fstat.c | 83 |
18 files changed, 1649 insertions, 817 deletions
@@ -4,7 +4,7 @@ LOCAL_PATH:= $(call my-dir) ifeq ($(HOST_OS),linux) # The versions depend on $(LOCAL_PATH)/VERSION -version_CFLAGS := -DF2FS_MAJOR_VERSION=1 -DF2FS_MINOR_VERSION=2 -DF2FS_TOOLS_VERSION=\"1.2.0\" -DF2FS_TOOLS_DATE=\"2013-10-25\" +version_CFLAGS := -DF2FS_MAJOR_VERSION=1 -DF2FS_MINOR_VERSION=4 -DF2FS_TOOLS_VERSION=\"1.4.0\" -DF2FS_TOOLS_DATE=\"2014-10-18\" # external/e2fsprogs/lib is needed for uuid/uuid.h common_C_INCLUDES := $(LOCAL_PATH)/include external/e2fsprogs/lib/ @@ -1,2 +1,2 @@ -1.2.0 -2013-10-25 +1.4.0 +2014-09-18 diff --git a/configure.ac b/configure.ac index c2dafb0..0111e72 100644 --- a/configure.ac +++ b/configure.ac @@ -56,7 +56,7 @@ AC_PATH_PROG([LDCONFIG], [ldconfig], PKG_CHECK_MODULES([libuuid], [uuid]) # Checks for header files. -AC_CHECK_HEADERS([fcntl.h mntent.h stdlib.h string.h \ +AC_CHECK_HEADERS([linux/fs.h fcntl.h mntent.h stdlib.h string.h \ sys/ioctl.h sys/mount.h unistd.h]) # Checks for typedefs, structures, and compiler characteristics. diff --git a/fsck/dump.c b/fsck/dump.c index 765e9db..4bb906f 100644 --- a/fsck/dump.c +++ b/fsck/dump.c @@ -8,6 +8,8 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include <inttypes.h> + #include "fsck.h" #define BUF_SZ 80 @@ -57,7 +59,7 @@ void sit_dump(struct f2fs_sb_info *sbi, int start_sit, int end_sit) ASSERT(ret >= 0); close(fd); - DBG(1, "Blocks [0x%lx] Free Segs [0x%x]\n", valid_blocks, free_segs); + DBG(1, "Blocks [0x%" PRIx64 "] Free Segs [0x%x]\n", valid_blocks, free_segs); } void ssa_dump(struct f2fs_sb_info *sbi, int start_ssa, int end_ssa) @@ -113,15 +115,164 @@ void ssa_dump(struct f2fs_sb_info *sbi, int start_ssa, int end_ssa) close(fd); } -int dump_node(struct f2fs_sb_info *sbi, nid_t nid) +static void dump_data_blk(__u64 offset, u32 blkaddr) +{ + char buf[F2FS_BLKSIZE]; + + if (blkaddr == NULL_ADDR) + return; + + /* get data */ + if (blkaddr == NEW_ADDR) { + 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); +} + +static void dump_node_blk(struct f2fs_sb_info *sbi, int ntype, + u32 nid, u64 *ofs) { struct node_info ni; struct f2fs_node *node_blk; + u32 skip = 0; + u32 i, idx; + + switch (ntype) { + case TYPE_DIRECT_NODE: + skip = idx = ADDRS_PER_BLOCK; + break; + case TYPE_INDIRECT_NODE: + idx = NIDS_PER_BLOCK; + skip = idx * ADDRS_PER_BLOCK; + break; + case TYPE_DOUBLE_INDIRECT_NODE: + skip = 0; + idx = NIDS_PER_BLOCK; + break; + } + + if (nid == 0) { + *ofs += skip; + return; + } + + get_node_info(sbi, nid, &ni); + + node_blk = calloc(BLOCK_SZ, 1); + dev_read_block(node_blk, ni.blk_addr); + + for (i = 0; i < idx; i++, (*ofs)++) { + switch (ntype) { + case TYPE_DIRECT_NODE: + dump_data_blk(*ofs * F2FS_BLKSIZE, + le32_to_cpu(node_blk->dn.addr[i])); + break; + case TYPE_INDIRECT_NODE: + dump_node_blk(sbi, TYPE_DIRECT_NODE, + le32_to_cpu(node_blk->in.nid[i]), ofs); + break; + case TYPE_DOUBLE_INDIRECT_NODE: + dump_node_blk(sbi, TYPE_INDIRECT_NODE, + le32_to_cpu(node_blk->in.nid[i]), ofs); + break; + } + } + free(node_blk); +} + +static void dump_inode_blk(struct f2fs_sb_info *sbi, u32 nid, + struct f2fs_node *node_blk) +{ + u32 i = 0; + u64 ofs = 0; + + /* TODO: need to dump xattr */ + + if((node_blk->i.i_inline & F2FS_INLINE_DATA)){ + DBG(3, "ino[0x%x] has inline data!\n", nid); + /* recover from inline data */ + dev_write_dump(((unsigned char *)node_blk) + INLINE_DATA_OFFSET, + 0, MAX_INLINE_DATA); + return; + } + + /* check data blocks in inode */ + for (i = 0; i < ADDRS_PER_INODE(&node_blk->i); i++, ofs++) + dump_data_blk(ofs * F2FS_BLKSIZE, + le32_to_cpu(node_blk->i.i_addr[i])); + + /* check node blocks in inode */ + for (i = 0; i < 5; i++) { + if (i == 0 || i == 1) + dump_node_blk(sbi, TYPE_DIRECT_NODE, + node_blk->i.i_nid[i], &ofs); + else if (i == 2 || i == 3) + dump_node_blk(sbi, TYPE_INDIRECT_NODE, + node_blk->i.i_nid[i], &ofs); + else if (i == 4) + dump_node_blk(sbi, TYPE_DOUBLE_INDIRECT_NODE, + node_blk->i.i_nid[i], &ofs); + else + ASSERT(0); + } +} + +void dump_file(struct f2fs_sb_info *sbi, struct node_info *ni, + struct f2fs_node *node_blk) +{ + struct f2fs_inode *inode = &node_blk->i; + u32 imode = le32_to_cpu(inode->i_mode); + char name[255] = {0}; + char path[1024] = {0}; + char ans[255] = {0}; int ret; - ret = get_node_info(sbi, nid, &ni); + if (!S_ISREG(imode)) { + MSG(0, "Not a regular file\n\n"); + return; + } + + printf("Do you want to dump this file into ./lost_found/? [Y/N] "); + ret = scanf("%s", ans); ASSERT(ret >= 0); + if (!strcasecmp(ans, "y")) { + ret = system("mkdir -p ./lost_found"); + ASSERT(ret >= 0); + + /* make a file */ + strncpy(name, (const char *)inode->i_name, + le32_to_cpu(inode->i_namelen)); + name[le32_to_cpu(inode->i_namelen)] = 0; + sprintf(path, "./lost_found/%s", name); + + config.dump_fd = open(path, O_TRUNC|O_CREAT|O_RDWR, 0666); + ASSERT(config.dump_fd >= 0); + + /* dump file's data */ + dump_inode_blk(sbi, ni->ino, node_blk); + + /* adjust file size */ + ret = ftruncate(config.dump_fd, le32_to_cpu(inode->i_size)); + ASSERT(ret >= 0); + + close(config.dump_fd); + } +} + +void dump_node(struct f2fs_sb_info *sbi, nid_t nid) +{ + struct node_info ni; + struct f2fs_node *node_blk; + + get_node_info(sbi, nid, &ni); + node_blk = calloc(BLOCK_SZ, 1); dev_read_block(node_blk, ni.blk_addr); @@ -130,9 +281,8 @@ int dump_node(struct f2fs_sb_info *sbi, nid_t nid) DBG(1, "nat_entry.version [0x%x]\n", ni.version); DBG(1, "nat_entry.ino [0x%x]\n", ni.ino); - if (ni.blk_addr == 0x0) { + if (ni.blk_addr == 0x0) MSG(0, "Invalid nat entry\n\n"); - } DBG(1, "node_blk.footer.ino [0x%x]\n", le32_to_cpu(node_blk->footer.ino)); DBG(1, "node_blk.footer.nid [0x%x]\n", le32_to_cpu(node_blk->footer.nid)); @@ -140,12 +290,12 @@ int dump_node(struct f2fs_sb_info *sbi, nid_t nid) if (le32_to_cpu(node_blk->footer.ino) == ni.ino && le32_to_cpu(node_blk->footer.nid) == ni.nid) { print_node_info(node_blk); + dump_file(sbi, &ni, node_blk); } else { MSG(0, "Invalid node block\n\n"); } free(node_blk); - return 0; } int dump_inode_from_blkaddr(struct f2fs_sb_info *sbi, u32 blk_addr) @@ -159,8 +309,7 @@ int dump_inode_from_blkaddr(struct f2fs_sb_info *sbi, u32 blk_addr) type = get_sum_entry(sbi, blk_addr, &sum_entry); nid = le32_to_cpu(sum_entry.nid); - ret = get_node_info(sbi, nid, &ni); - ASSERT(ret >= 0); + get_node_info(sbi, nid, &ni); DBG(1, "Note: blkaddr = main_blkaddr + segno * 512 + offset\n"); DBG(1, "Block_addr [0x%x]\n", blk_addr); @@ -176,7 +325,8 @@ int dump_inode_from_blkaddr(struct f2fs_sb_info *sbi, u32 blk_addr) node_blk = calloc(BLOCK_SZ, 1); read_node_blk: - dev_read_block(node_blk, blk_addr); + ret = dev_read_block(node_blk, blk_addr); + ASSERT(ret >= 0); ino = le32_to_cpu(node_blk->footer.ino); nid = le32_to_cpu(node_blk->footer.nid); @@ -184,7 +334,7 @@ read_node_blk: if (ino == nid) { print_node_info(node_blk); } else { - ret = get_node_info(sbi, ino, &ni); + get_node_info(sbi, ino, &ni); goto read_node_blk; } diff --git a/fsck/f2fs.h b/fsck/f2fs.h index 427a733..47f785d 100644 --- a/fsck/f2fs.h +++ b/fsck/f2fs.h @@ -26,7 +26,6 @@ #include <sys/mount.h> #include <assert.h> -#include <list.h> #include <f2fs_fs.h> #define EXIT_ERR_CODE (-1) @@ -34,6 +33,10 @@ typecheck(unsigned long long, b) && \ ((long long)((a) - (b)) > 0)) +struct list_head { + struct list_head *next, *prev; +}; + enum { NAT_BITMAP, SIT_BITMAP @@ -126,6 +129,7 @@ struct f2fs_sb_info { struct f2fs_nm_info *nm_info; struct f2fs_sm_info *sm_info; struct f2fs_checkpoint *ckpt; + int cur_cp; struct list_head orphan_inode_list; unsigned int n_orphans; @@ -187,6 +191,11 @@ static inline struct sit_info *SIT_I(struct f2fs_sb_info *sbi) return (struct sit_info *)(SM_I(sbi)->sit_info); } +static inline void *inline_data_addr(struct f2fs_node *node_blk) +{ + return (void *)&(node_blk->i.i_addr[1]); +} + static inline unsigned long __bitmap_size(struct f2fs_sb_info *sbi, int flag) { struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); @@ -265,10 +274,15 @@ static inline block_t __start_sum_addr(struct f2fs_sb_info *sbi) #define GET_SEGNO_FROM_SEG0(sbi, blk_addr) \ (GET_SEGOFF_FROM_SEG0(sbi, blk_addr) >> sbi->log_blocks_per_seg) -#define FREE_I_START_SEGNO(sbi) GET_SEGNO_FROM_SEG0(sbi, SM_I(sbi)->main_blkaddr) +#define GET_BLKOFF_FROM_SEG0(sbi, blk_addr) \ + (GET_SEGOFF_FROM_SEG0(sbi, blk_addr) & (sbi->blocks_per_seg - 1)) + +#define FREE_I_START_SEGNO(sbi) \ + GET_SEGNO_FROM_SEG0(sbi, SM_I(sbi)->main_blkaddr) #define GET_R2L_SEGNO(sbi, segno) (segno + FREE_I_START_SEGNO(sbi)) -#define START_BLOCK(sbi, segno) (SM_I(sbi)->main_blkaddr + (segno << sbi->log_blocks_per_seg)) +#define START_BLOCK(sbi, segno) (SM_I(sbi)->main_blkaddr + \ + (segno << sbi->log_blocks_per_seg)) static inline struct curseg_info *CURSEG_I(struct f2fs_sb_info *sbi, int type) { @@ -301,23 +315,32 @@ static inline block_t sum_blk_addr(struct f2fs_sb_info *sbi, int base, int type) (segno / SIT_ENTRY_PER_BLOCK) #define TOTAL_SEGS(sbi) (SM_I(sbi)->main_segments) -#define IS_VALID_NID(sbi, nid) \ - do { \ - ASSERT(nid <= (NAT_ENTRY_PER_BLOCK * \ - F2FS_RAW_SUPER(sbi)->segment_count_nat \ - << (sbi->log_blocks_per_seg - 1))); \ - } while (0); - -#define IS_VALID_BLK_ADDR(sbi, addr) \ - do { \ - if (addr >= F2FS_RAW_SUPER(sbi)->block_count || \ - addr < SM_I(sbi)->main_blkaddr) \ - { \ - DBG(0, "block addr [0x%x]\n", addr); \ - ASSERT(addr < F2FS_RAW_SUPER(sbi)->block_count); \ - ASSERT(addr >= SM_I(sbi)->main_blkaddr); \ - } \ - } while (0); +static inline bool IS_VALID_NID(struct f2fs_sb_info *sbi, u32 nid) +{ + return (nid <= (NAT_ENTRY_PER_BLOCK * + F2FS_RAW_SUPER(sbi)->segment_count_nat + << (sbi->log_blocks_per_seg - 1))); +} + +static inline bool IS_VALID_BLK_ADDR(struct f2fs_sb_info *sbi, u32 addr) +{ + int i; + + if (addr >= F2FS_RAW_SUPER(sbi)->block_count || + addr < SM_I(sbi)->main_blkaddr) { + ASSERT_MSG("block addr [0x%x]\n", addr); + return 0; + } + + for (i = 0; i < NO_CHECK_TYPE; i++) { + struct curseg_info *curseg = CURSEG_I(sbi, i); + + if (START_BLOCK(sbi, curseg->segno) + + curseg->next_blkoff == addr) + return 0; + } + return 1; +} static inline u64 BLKOFF_FROM_MAIN(struct f2fs_sb_info *sbi, u64 blk_addr) { diff --git a/fsck/fsck.c b/fsck/fsck.c index 0f48918..f6039b8 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -13,7 +13,30 @@ char *tree_mark; uint32_t tree_mark_size = 256; -static int add_into_hard_link_list(struct f2fs_sb_info *sbi, u32 nid, u32 link_cnt) +static inline int f2fs_set_main_bitmap(struct f2fs_sb_info *sbi, u32 blk) +{ + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + + 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) +{ + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + + return f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, blk), + fsck->main_area_bitmap); +} + +static inline int f2fs_test_sit_bitmap(struct f2fs_sb_info *sbi, u32 blk) +{ + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + + return f2fs_test_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) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct hard_link_node *node = NULL, *tmp = NULL, *prev = NULL; @@ -57,10 +80,8 @@ static int find_and_dec_hard_link_list(struct f2fs_sb_info *sbi, u32 nid) struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct hard_link_node *node = NULL, *prev = NULL; - if (fsck->hard_link_list_head == NULL) { - ASSERT(0); - return -1; - } + if (fsck->hard_link_list_head == NULL) + return -EINVAL; node = fsck->hard_link_list_head; @@ -69,10 +90,8 @@ static int find_and_dec_hard_link_list(struct f2fs_sb_info *sbi, u32 nid) node = node->next; } - if (node == NULL || (nid != node->nid)) { - ASSERT(0); - return -1; - } + if (node == NULL || (nid != node->nid)) + return -EINVAL; /* Decrease link count */ node->links = node->links - 1; @@ -85,40 +104,38 @@ static int find_and_dec_hard_link_list(struct f2fs_sb_info *sbi, u32 nid) prev->next = node->next; free(node); } - return 0; - } -static int is_valid_ssa_node_blk(struct f2fs_sb_info *sbi, u32 nid, u32 blk_addr) +static int is_valid_ssa_node_blk(struct f2fs_sb_info *sbi, u32 nid, + u32 blk_addr) { int ret = 0; struct f2fs_summary sum_entry; ret = get_sum_entry(sbi, blk_addr, &sum_entry); - ASSERT(ret >= 0); - if (ret == SEG_TYPE_DATA || ret == SEG_TYPE_CUR_DATA) { - ASSERT_MSG(0, "Summary footer is not a node segment summary\n");; - } else if (ret == SEG_TYPE_NODE) { - if (le32_to_cpu(sum_entry.nid) != nid) { - DBG(0, "nid [0x%x]\n", nid); - DBG(0, "target blk_addr [0x%x]\n", blk_addr); - DBG(0, "summary blk_addr [0x%x]\n", - GET_SUM_BLKADDR(sbi, GET_SEGNO(sbi, blk_addr))); - DBG(0, "seg no / offset [0x%x / 0x%x]\n", - GET_SEGNO(sbi, blk_addr), OFFSET_IN_SEG(sbi, blk_addr)); - DBG(0, "summary_entry.nid [0x%x]\n", le32_to_cpu(sum_entry.nid)); - DBG(0, "--> node block's nid [0x%x]\n", nid); - ASSERT_MSG(0, "Invalid node seg summary\n"); - } - } else if (ret == SEG_TYPE_CUR_NODE) { - /* current node segment has no ssa */ - } else { - ASSERT_MSG(0, "Invalid return value of 'get_sum_entry'"); + if (ret != SEG_TYPE_NODE && ret != SEG_TYPE_CUR_NODE) { + ASSERT_MSG("Summary footer is not for node segment"); + return -EINVAL; } - return 1; + if (le32_to_cpu(sum_entry.nid) != nid) { + DBG(0, "nid [0x%x]\n", nid); + DBG(0, "target blk_addr [0x%x]\n", blk_addr); + DBG(0, "summary blk_addr [0x%x]\n", + GET_SUM_BLKADDR(sbi, + GET_SEGNO(sbi, blk_addr))); + DBG(0, "seg no / offset [0x%x / 0x%x]\n", + GET_SEGNO(sbi, blk_addr), + OFFSET_IN_SEG(sbi, blk_addr)); + DBG(0, "summary_entry.nid [0x%x]\n", + le32_to_cpu(sum_entry.nid)); + DBG(0, "--> node block's nid [0x%x]\n", nid); + ASSERT_MSG("Invalid node seg summary\n"); + return -EINVAL; + } + return 0; } static int is_valid_ssa_data_blk(struct f2fs_sb_info *sbi, u32 blk_addr, @@ -128,143 +145,200 @@ static int is_valid_ssa_data_blk(struct f2fs_sb_info *sbi, u32 blk_addr, struct f2fs_summary sum_entry; ret = get_sum_entry(sbi, blk_addr, &sum_entry); - ASSERT(ret == SEG_TYPE_DATA || ret == SEG_TYPE_CUR_DATA); + + if (ret != SEG_TYPE_DATA && ret != SEG_TYPE_CUR_DATA) { + ASSERT_MSG("Summary footer is not for data segment"); + return -EINVAL; + } if (le32_to_cpu(sum_entry.nid) != parent_nid || sum_entry.version != version || le16_to_cpu(sum_entry.ofs_in_node) != idx_in_node) { - DBG(0, "summary_entry.nid [0x%x]\n", le32_to_cpu(sum_entry.nid)); - DBG(0, "summary_entry.version [0x%x]\n", sum_entry.version); - DBG(0, "summary_entry.ofs_in_node [0x%x]\n", le16_to_cpu(sum_entry.ofs_in_node)); - + DBG(0, "summary_entry.nid [0x%x]\n", + le32_to_cpu(sum_entry.nid)); + DBG(0, "summary_entry.version [0x%x]\n", + sum_entry.version); + DBG(0, "summary_entry.ofs_in_node [0x%x]\n", + le16_to_cpu(sum_entry.ofs_in_node)); DBG(0, "parent nid [0x%x]\n", parent_nid); DBG(0, "version from nat [0x%x]\n", version); DBG(0, "idx in parent node [0x%x]\n", idx_in_node); DBG(0, "Target data block addr [0x%x]\n", blk_addr); - ASSERT_MSG(0, "Invalid data seg summary\n"); + ASSERT_MSG("Invalid data seg summary\n"); + return -EINVAL; } - - return 1; + return 0; } -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) +static 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); - struct node_info ni; - struct f2fs_node *node_blk = NULL; - int ret = 0; + int ret; + + if (!IS_VALID_NID(sbi, nid)) { + ASSERT_MSG("nid is not valid. [0x%x]", nid); + return -EINVAL; + } - IS_VALID_NID(sbi, nid); + get_node_info(sbi, nid, ni); + if (ni->blk_addr == NEW_ADDR) { + ASSERT_MSG("nid is NEW_ADDR. [0x%x]", nid); + return -EINVAL; + } - if (ftype != F2FS_FT_ORPHAN || - f2fs_test_bit(nid, fsck->nat_area_bitmap) != 0x0) - f2fs_clear_bit(nid, fsck->nat_area_bitmap); - else - ASSERT_MSG(0, "nid duplicated [0x%x]\n", nid); + if (!IS_VALID_BLK_ADDR(sbi, ni->blk_addr)) { + ASSERT_MSG("blkaddres is not valid. [0x%x]", ni->blk_addr); + return -EINVAL; + } + + if (is_valid_ssa_node_blk(sbi, nid, ni->blk_addr)) { + ASSERT_MSG("summary node block is not valid. [0x%x]", nid); + return -EINVAL; + } - ret = get_node_info(sbi, nid, &ni); + ret = dev_read_block(node_blk, ni->blk_addr); ASSERT(ret >= 0); - /* Is it reserved block? - * if block addresss was 0xffff,ffff,ffff,ffff - * it means that block was already allocated, but not stored in disk - */ - if (ni.blk_addr == NEW_ADDR) { - fsck->chk.valid_blk_cnt++; - fsck->chk.valid_node_cnt++; - if (ntype == TYPE_INODE) - fsck->chk.valid_inode_cnt++; - return 0; + if (ntype == TYPE_INODE && + node_blk->footer.nid != node_blk->footer.ino) { + 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 (ntype != TYPE_INODE && + node_blk->footer.nid == node_blk->footer.ino) { + 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; } - IS_VALID_BLK_ADDR(sbi, ni.blk_addr); + if (le32_to_cpu(node_blk->footer.nid) != 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)); + return -EINVAL; + } - is_valid_ssa_node_blk(sbi, nid, ni.blk_addr); + if (ntype == TYPE_XATTR) { + u32 flag = le32_to_cpu(node_blk->footer.flag); - if (f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, ni.blk_addr), fsck->sit_area_bitmap) == 0x0) { - DBG(0, "SIT bitmap is 0x0. blk_addr[0x%x]\n", ni.blk_addr); - ASSERT(0); + if ((flag >> OFFSET_BIT_SHIFT) != XATTR_NODE_OFFSET) { + ASSERT_MSG("xnid[0x%x] has wrong ofs:[0x%x]", + nid, flag); + return -EINVAL; + } } - if (f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, ni.blk_addr), fsck->main_area_bitmap) == 0x0) { + if ((ntype == TYPE_INODE && ftype == F2FS_FT_DIR) || + (ntype == TYPE_XATTR && ftype == F2FS_FT_XATTR)) { + /* not included '.' & '..' */ + if (f2fs_test_main_bitmap(sbi, ni->blk_addr) != 0) { + ASSERT_MSG("Duplicated node blk. nid[0x%x][0x%x]\n", + nid, ni->blk_addr); + return -EINVAL; + } + } + + /* workaround to fix later */ + if (ftype != F2FS_FT_ORPHAN || + f2fs_test_bit(nid, fsck->nat_area_bitmap) != 0) + f2fs_clear_bit(nid, fsck->nat_area_bitmap); + else + ASSERT_MSG("orphan or xattr nid is duplicated [0x%x]\n", + nid); + + if (f2fs_test_sit_bitmap(sbi, ni->blk_addr) == 0) + ASSERT_MSG("SIT bitmap is 0x0. blk_addr[0x%x]", + ni->blk_addr); + + if (f2fs_test_main_bitmap(sbi, ni->blk_addr) == 0) { fsck->chk.valid_blk_cnt++; fsck->chk.valid_node_cnt++; } + return 0; +} + +static int fsck_chk_xattr_blk(struct f2fs_sb_info *sbi, u32 ino, + u32 x_nid, u32 *blk_cnt) +{ + struct f2fs_node *node_blk = NULL; + struct node_info ni; + int ret = 0; + + if (x_nid == 0x0) + return 0; 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); + /* Sanity check */ + if (sanity_check_nid(sbi, x_nid, node_blk, + F2FS_FT_XATTR, TYPE_XATTR, &ni)) { + ret = -EINVAL; + goto out; + } + + *blk_cnt = *blk_cnt + 1; + f2fs_set_main_bitmap(sbi, ni.blk_addr); + DBG(2, "ino[0x%x] x_nid[0x%x]\n", ino, x_nid); +out: + free(node_blk); + return ret; +} - ASSERT_MSG(nid == le32_to_cpu(node_blk->footer.nid), - "nid[0x%x] blk_addr[0x%x] footer.nid[0x%x]\n", - nid, ni.blk_addr, le32_to_cpu(node_blk->footer.nid)); +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 node_info ni; + struct f2fs_node *node_blk = NULL; + + node_blk = (struct f2fs_node *)calloc(BLOCK_SZ, 1); + ASSERT(node_blk != NULL); + + if (sanity_check_nid(sbi, nid, node_blk, ftype, ntype, &ni)) + goto err; if (ntype == TYPE_INODE) { - ret = fsck_chk_inode_blk(sbi, - nid, - ftype, - node_blk, - blk_cnt, - &ni); + fsck_chk_inode_blk(sbi, nid, ftype, node_blk, blk_cnt, &ni); } else { - /* it's not inode */ - ASSERT(node_blk->footer.nid != node_blk->footer.ino); - - if (f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, ni.blk_addr), fsck->main_area_bitmap) != 0) { - DBG(0, "Duplicated node block. ino[0x%x][0x%x]\n", nid, ni.blk_addr); - ASSERT(0); - } - f2fs_set_bit(BLKOFF_FROM_MAIN(sbi, ni.blk_addr), fsck->main_area_bitmap); + f2fs_set_main_bitmap(sbi, ni.blk_addr); switch (ntype) { case TYPE_DIRECT_NODE: - ret = fsck_chk_dnode_blk(sbi, - inode, - nid, - ftype, - node_blk, - blk_cnt, - &ni); + fsck_chk_dnode_blk(sbi, inode, nid, ftype, node_blk, + blk_cnt, &ni); break; case TYPE_INDIRECT_NODE: - ret = fsck_chk_idnode_blk(sbi, - inode, - ftype, - node_blk, + fsck_chk_idnode_blk(sbi, inode, ftype, node_blk, blk_cnt); break; case TYPE_DOUBLE_INDIRECT_NODE: - ret = fsck_chk_didnode_blk(sbi, - inode, - ftype, - node_blk, + fsck_chk_didnode_blk(sbi, inode, ftype, node_blk, blk_cnt); break; default: ASSERT(0); } } - ASSERT(ret >= 0); - free(node_blk); return 0; +err: + free(node_blk); + return -EINVAL; } -int fsck_chk_inode_blk(struct f2fs_sb_info *sbi, - u32 nid, - enum FILE_TYPE ftype, - struct f2fs_node *node_blk, - u32 *blk_cnt, - struct node_info *ni) +/* start with valid nid and blkaddr */ +void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid, + enum FILE_TYPE ftype, struct f2fs_node *node_blk, + u32 *blk_cnt, struct node_info *ni) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); u32 child_cnt = 0, child_files = 0; @@ -272,81 +346,104 @@ int fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 i_links = le32_to_cpu(node_blk->i.i_links); u64 i_blocks = le64_to_cpu(node_blk->i.i_blocks); unsigned int idx = 0; - int ret = 0; - - ASSERT(node_blk->footer.nid == node_blk->footer.ino); - ASSERT(le32_to_cpu(node_blk->footer.nid) == nid); + int need_fix = 0; + int ret; - if (f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, ni->blk_addr), fsck->main_area_bitmap) == 0x0) + if (f2fs_test_main_bitmap(sbi, ni->blk_addr) == 0) fsck->chk.valid_inode_cnt++; - /* Orphan node. i_links should be 0 */ - if (ftype == F2FS_FT_ORPHAN) { - ASSERT(i_links == 0); - } else { - ASSERT(i_links > 0); - } - if (ftype == F2FS_FT_DIR) { - - /* not included '.' & '..' */ - if (f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, ni->blk_addr), fsck->main_area_bitmap) != 0) { - DBG(0, "Duplicated inode blk. ino[0x%x][0x%x]\n", nid, ni->blk_addr); - ASSERT(0); - } - f2fs_set_bit(BLKOFF_FROM_MAIN(sbi, ni->blk_addr), fsck->main_area_bitmap); - + f2fs_set_main_bitmap(sbi, ni->blk_addr); } else { - - if (f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, ni->blk_addr), fsck->main_area_bitmap) == 0x0) { - f2fs_set_bit(BLKOFF_FROM_MAIN(sbi, ni->blk_addr), fsck->main_area_bitmap); + if (f2fs_test_main_bitmap(sbi, ni->blk_addr) == 0) { + f2fs_set_main_bitmap(sbi, ni->blk_addr); if (i_links > 1) { /* First time. Create new hard link node */ add_into_hard_link_list(sbi, nid, i_links); fsck->chk.multi_hard_link_files++; } } else { - if (i_links <= 1) { - DBG(0, "Error. Node ID [0x%x]." - " There are one more hard links." - " But i_links is [0x%x]\n", + DBG(3, "[0x%x] has hard links [0x%x]\n", nid, i_links); + if (find_and_dec_hard_link_list(sbi, nid)) { + ASSERT_MSG("[0x%x] needs more i_links=0x%x", nid, i_links); - ASSERT(0); + if (config.fix_on) { + node_blk->i.i_links = + cpu_to_le32(i_links + 1); + need_fix = 1; + FIX_MSG("File: 0x%x " + "i_links= 0x%x -> 0x%x", + nid, i_links, i_links + 1); + } + goto check; } - - DBG(3, "ino[0x%x] has hard links [0x%x]\n", nid, i_links); - ret = find_and_dec_hard_link_list(sbi, nid); - ASSERT(ret >= 0); - /* No need to go deep into the node */ - goto out; + return; } } - fsck_chk_xattr_blk(sbi, nid, le32_to_cpu(node_blk->i.i_xattr_nid), blk_cnt); + if (fsck_chk_xattr_blk(sbi, nid, + le32_to_cpu(node_blk->i.i_xattr_nid), blk_cnt) && + config.fix_on) { + node_blk->i.i_xattr_nid = 0; + need_fix = 1; + FIX_MSG("Remove xattr block: 0x%x, x_nid = 0x%x", + nid, le32_to_cpu(node_blk->i.i_xattr_nid)); + } if (ftype == F2FS_FT_CHRDEV || ftype == F2FS_FT_BLKDEV || ftype == F2FS_FT_FIFO || ftype == F2FS_FT_SOCK) goto check; - if((node_blk->i.i_inline & F2FS_INLINE_DATA)){ + + if((node_blk->i.i_inline & F2FS_INLINE_DATA)) { + if (le32_to_cpu(node_blk->i.i_addr[0]) != 0) { + /* should fix this bug all the time */ + FIX_MSG("inline_data has wrong 0'th block = %x", + le32_to_cpu(node_blk->i.i_addr[0])); + node_blk->i.i_addr[0] = 0; + node_blk->i.i_blocks = cpu_to_le64(*blk_cnt); + need_fix = 1; + } + if (!(node_blk->i.i_inline & F2FS_DATA_EXIST)) { + char buf[MAX_INLINE_DATA]; + memset(buf, 0, MAX_INLINE_DATA); + + if (memcmp(buf, &node_blk->i.i_addr[1], + MAX_INLINE_DATA)) { + FIX_MSG("inline_data has DATA_EXIST"); + node_blk->i.i_inline |= F2FS_DATA_EXIST; + need_fix = 1; + } + } DBG(3, "ino[0x%x] has inline data!\n", nid); goto check; } + 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_cnt, &child_files); + if (ret < 0) { + /* should fix this bug all the time */ + need_fix = 1; + } + goto check; + } /* check data blocks in inode */ for (idx = 0; idx < ADDRS_PER_INODE(&node_blk->i); idx++) { if (le32_to_cpu(node_blk->i.i_addr[idx]) != 0) { - *blk_cnt = *blk_cnt + 1; ret = fsck_chk_data_blk(sbi, le32_to_cpu(node_blk->i.i_addr[idx]), - &child_cnt, - &child_files, + &child_cnt, &child_files, (i_blocks == *blk_cnt), - ftype, - nid, - idx, - ni->version); - ASSERT(ret >= 0); + ftype, nid, idx, ni->version); + if (!ret) { + *blk_cnt = *blk_cnt + 1; + } else if (config.fix_on) { + node_blk->i.i_addr[idx] = 0; + need_fix = 1; + FIX_MSG("[0x%x] i_addr[%d] = 0", nid, idx); + } } } @@ -362,116 +459,127 @@ int fsck_chk_inode_blk(struct f2fs_sb_info *sbi, ASSERT(0); if (le32_to_cpu(node_blk->i.i_nid[idx]) != 0) { - *blk_cnt = *blk_cnt + 1; - ret = fsck_chk_node_blk(sbi, - &node_blk->i, + ret = fsck_chk_node_blk(sbi, &node_blk->i, le32_to_cpu(node_blk->i.i_nid[idx]), - ftype, - ntype, - blk_cnt); - ASSERT(ret >= 0); + ftype, ntype, blk_cnt); + if (!ret) { + *blk_cnt = *blk_cnt + 1; + } else if (config.fix_on) { + node_blk->i.i_nid[idx] = 0; + need_fix = 1; + FIX_MSG("[0x%x] i_nid[%d] = 0", nid, idx); + } } } check: if (ftype == F2FS_FT_DIR) - DBG(1, "Directory Inode: ino: %x name: %s depth: %d child files: %d\n\n", - le32_to_cpu(node_blk->footer.ino), node_blk->i.i_name, - le32_to_cpu(node_blk->i.i_current_depth), child_files); + DBG(1, "Directory Inode: 0x%x [%s] depth: %d has %d files\n\n", + le32_to_cpu(node_blk->footer.ino), + node_blk->i.i_name, + le32_to_cpu(node_blk->i.i_current_depth), + child_files); if (ftype == F2FS_FT_ORPHAN) - DBG(1, "Orphan Inode: ino: %x name: %s i_blocks: %u\n\n", - le32_to_cpu(node_blk->footer.ino), node_blk->i.i_name, + DBG(1, "Orphan Inode: 0x%x [%s] i_blocks: %u\n\n", + le32_to_cpu(node_blk->footer.ino), + node_blk->i.i_name, (u32)i_blocks); - if ((ftype == F2FS_FT_DIR && i_links != child_cnt) || - (i_blocks != *blk_cnt)) { - print_node_info(node_blk); - DBG(1, "blk cnt [0x%x]\n", *blk_cnt); - DBG(1, "child cnt [0x%x]\n", child_cnt); + + if (i_blocks != *blk_cnt) { + ASSERT_MSG("ino: 0x%x has i_blocks: %08"PRIx64", " + "but has %u blocks", + nid, i_blocks, *blk_cnt); + if (config.fix_on) { + node_blk->i.i_blocks = cpu_to_le64(*blk_cnt); + need_fix = 1; + FIX_MSG("[0x%x] i_blocks=0x%08"PRIx64" -> 0x%x", + nid, i_blocks, *blk_cnt); + } + } + if (ftype == F2FS_FT_DIR && i_links != child_cnt) { + ASSERT_MSG("ino: 0x%x has i_links: %u but real links: %u", + nid, i_links, child_cnt); + if (config.fix_on) { + node_blk->i.i_links = cpu_to_le32(child_cnt); + need_fix = 1; + FIX_MSG("Dir: 0x%x i_links= 0x%x -> 0x%x", + nid, i_links, child_cnt); + } } - ASSERT(i_blocks == *blk_cnt); - if (ftype == F2FS_FT_DIR) - ASSERT(i_links == child_cnt); -out: - return 0; + if (ftype == F2FS_FT_ORPHAN && i_links) + ASSERT_MSG("ino: 0x%x is orphan inode, but has i_links: %u", + nid, i_links); + if (need_fix) { + ret = dev_write_block(node_blk, ni->blk_addr); + ASSERT(ret >= 0); + } } -int fsck_chk_dnode_blk(struct f2fs_sb_info *sbi, - struct f2fs_inode *inode, - u32 nid, - enum FILE_TYPE ftype, - struct f2fs_node *node_blk, - u32 *blk_cnt, - struct node_info *ni) +int fsck_chk_dnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode, + u32 nid, enum FILE_TYPE ftype, struct f2fs_node *node_blk, + u32 *blk_cnt, struct node_info *ni) { - int idx; + int idx, ret; u32 child_cnt = 0, child_files = 0; for (idx = 0; idx < ADDRS_PER_BLOCK; idx++) { if (le32_to_cpu(node_blk->dn.addr[idx]) == 0x0) continue; - *blk_cnt = *blk_cnt + 1; - fsck_chk_data_blk(sbi, - le32_to_cpu(node_blk->dn.addr[idx]), - &child_cnt, - &child_files, - le64_to_cpu(inode->i_blocks) == *blk_cnt, - ftype, - nid, - idx, - ni->version); + ret = fsck_chk_data_blk(sbi, + le32_to_cpu(node_blk->dn.addr[idx]), + &child_cnt, &child_files, + le64_to_cpu(inode->i_blocks) == *blk_cnt, ftype, + nid, idx, ni->version); + if (!ret) + *blk_cnt = *blk_cnt + 1; } - return 0; } -int fsck_chk_idnode_blk(struct f2fs_sb_info *sbi, - struct f2fs_inode *inode, - enum FILE_TYPE ftype, - struct f2fs_node *node_blk, - u32 *blk_cnt) +int fsck_chk_idnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode, + enum FILE_TYPE ftype, struct f2fs_node *node_blk, u32 *blk_cnt) { + int ret; int i = 0; for (i = 0 ; i < NIDS_PER_BLOCK; i++) { if (le32_to_cpu(node_blk->in.nid[i]) == 0x0) continue; - *blk_cnt = *blk_cnt + 1; - fsck_chk_node_blk(sbi, - inode, + ret = fsck_chk_node_blk(sbi, inode, le32_to_cpu(node_blk->in.nid[i]), - ftype, - TYPE_DIRECT_NODE, - blk_cnt); + ftype, TYPE_DIRECT_NODE, blk_cnt); + if (!ret) + *blk_cnt = *blk_cnt + 1; + else if (ret == -EINVAL) + printf("delete in.nid[i] = 0;\n"); } - return 0; } -int fsck_chk_didnode_blk(struct f2fs_sb_info *sbi, - struct f2fs_inode *inode, - enum FILE_TYPE ftype, - struct f2fs_node *node_blk, - u32 *blk_cnt) +int fsck_chk_didnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode, + enum FILE_TYPE ftype, struct f2fs_node *node_blk, u32 *blk_cnt) { int i = 0; + int ret = 0; for (i = 0; i < NIDS_PER_BLOCK; i++) { if (le32_to_cpu(node_blk->in.nid[i]) == 0x0) continue; - *blk_cnt = *blk_cnt + 1; - fsck_chk_node_blk(sbi, - inode, + ret = fsck_chk_node_blk(sbi, inode, le32_to_cpu(node_blk->in.nid[i]), - ftype, - TYPE_INDIRECT_NODE, - blk_cnt); + ftype, TYPE_INDIRECT_NODE, blk_cnt); + if (!ret) + *blk_cnt = *blk_cnt + 1; + else if (ret == -EINVAL) + printf("delete in.nid[i] = 0;\n"); } - return 0; } static void print_dentry(__u32 depth, __u8 *name, - struct f2fs_dentry_block *de_blk, int idx, int last_blk) + unsigned long *bitmap, + struct f2fs_dir_entry *dentry, + int max, int idx, int last_blk) { int last_de = 0; int next_idx = 0; @@ -482,12 +590,11 @@ static void print_dentry(__u32 depth, __u8 *name, if (config.dbg_lv != -1) return; - name_len = le16_to_cpu(de_blk->dentry[idx].name_len); + name_len = le16_to_cpu(dentry[idx].name_len); next_idx = idx + (name_len + F2FS_SLOT_LEN - 1) / F2FS_SLOT_LEN; - bit_offset = find_next_bit((unsigned long *)de_blk->dentry_bitmap, - NR_DENTRY_IN_BLOCK, next_idx); - if (bit_offset >= NR_DENTRY_IN_BLOCK && last_blk) + bit_offset = find_next_bit(bitmap, max, next_idx); + if (bit_offset >= max && last_blk) last_de = 1; if (tree_mark_size <= depth) { @@ -506,101 +613,185 @@ static void print_dentry(__u32 depth, __u8 *name, for (i = 1; i < depth; i++) printf("%c ", tree_mark[i]); - printf("%c-- %s\n", last_de ? '`' : '|', name); + printf("%c-- %s 0x%x\n", last_de ? '`' : '|', + name, le32_to_cpu(dentry[idx].ino)); } -int fsck_chk_dentry_blk(struct f2fs_sb_info *sbi, - u32 blk_addr, - u32 *child_cnt, - u32 *child_files, - int last_blk) +static int __chk_dentries(struct f2fs_sb_info *sbi, u32 *child_cnt, + u32* child_files, + unsigned long *bitmap, + struct f2fs_dir_entry *dentry, + __u8 (*filenames)[F2FS_SLOT_LEN], + int max, int last_blk) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); - int i; - int ret = 0; + enum FILE_TYPE ftype; int dentries = 0; + u32 blk_cnt; u8 *name; u32 hash_code; - u32 blk_cnt; u16 name_len;; + int ret = 0; + int fixed = 0; + int i; - enum FILE_TYPE ftype; - struct f2fs_dentry_block *de_blk; - - de_blk = (struct f2fs_dentry_block *)calloc(BLOCK_SZ, 1); - ASSERT(de_blk != NULL); - - ret = dev_read_block(de_blk, blk_addr); - ASSERT(ret >= 0); - - fsck->dentry_depth++; - - for (i = 0; i < NR_DENTRY_IN_BLOCK;) { - if (test_bit(i, (unsigned long *)de_blk->dentry_bitmap) == 0x0) { + for (i = 0; i < max;) { + if (test_bit(i, bitmap) == 0) { i++; continue; } - - name_len = le32_to_cpu(de_blk->dentry[i].name_len); + if (!IS_VALID_NID(sbi, le32_to_cpu(dentry[i].ino))) { + DBG(1, "Bad dentry 0x%x with invalid NID/ino 0x%x", + i, le32_to_cpu(dentry[i].ino)); + if (config.fix_on) { + FIX_MSG("Clear bad dentry 0x%x with bad ino 0x%x", + i, le32_to_cpu(dentry[i].ino)); + clear_bit(i, bitmap); + i++; + fixed = 1; + continue; + } + } + ftype = dentry[i].file_type; + if ((ftype <= F2FS_FT_UNKNOWN || ftype > F2FS_FT_LAST_FILE_TYPE) && config.fix_on) { + DBG(1, "Bad dentry 0x%x with unexpected ftype 0x%x", + i, ftype); + if (config.fix_on) { + FIX_MSG("Clear bad dentry 0x%x with bad ftype 0x%x", + i, ftype); + clear_bit(i, bitmap); + i++; + fixed = 1; + continue; + } + } + name_len = le16_to_cpu(dentry[i].name_len); name = calloc(name_len + 1, 1); - memcpy(name, de_blk->filename[i], name_len); - - hash_code = f2fs_dentry_hash((const char *)name, name_len); - ASSERT(le32_to_cpu(de_blk->dentry[i].hash_code) == hash_code); - - ftype = de_blk->dentry[i].file_type; + memcpy(name, filenames[i], name_len); + hash_code = f2fs_dentry_hash((const unsigned char *)name, + name_len); + + /* fix hash_code made by old buggy code */ + if (le32_to_cpu(dentry[i].hash_code) != hash_code) { + dentry[i].hash_code = hash_code; + fixed = 1; + FIX_MSG("hash_code[%d] of %s", i, name); + } /* Becareful. 'dentry.file_type' is not imode. */ if (ftype == F2FS_FT_DIR) { *child_cnt = *child_cnt + 1; - if ((name[0] == '.' && name[1] == '.' && name_len == 2) || - (name[0] == '.' && name_len == 1)) { + if ((name[0] == '.' && name_len == 1) || + (name[0] == '.' && name[1] == '.' && + name_len == 2)) { i++; free(name); continue; } } - DBG(2, "[%3u] - no[0x%x] name[%s] len[0x%x] ino[0x%x] type[0x%x]\n", + DBG(1, "[%3u]-[0x%x] name[%s] len[0x%x] ino[0x%x] type[0x%x]\n", fsck->dentry_depth, i, name, name_len, - le32_to_cpu(de_blk->dentry[i].ino), - de_blk->dentry[i].file_type); + le32_to_cpu(dentry[i].ino), + dentry[i].file_type); - print_dentry(fsck->dentry_depth, name, de_blk, i, last_blk); + print_dentry(fsck->dentry_depth, name, bitmap, + dentry, max, i, last_blk); blk_cnt = 1; ret = fsck_chk_node_blk(sbi, - NULL, - le32_to_cpu(de_blk->dentry[i].ino), - ftype, - TYPE_INODE, - &blk_cnt); - - ASSERT(ret >= 0); + NULL, le32_to_cpu(dentry[i].ino), + ftype, TYPE_INODE, &blk_cnt); + + if (ret && config.fix_on) { + int j; + int slots = (name_len + F2FS_SLOT_LEN - 1) / + F2FS_SLOT_LEN; + for (j = 0; j < slots; j++) + clear_bit(i + j, bitmap); + FIX_MSG("Unlink [0x%x] - %s len[0x%x], type[0x%x]", + le32_to_cpu(dentry[i].ino), + name, name_len, + dentry[i].file_type); + i += slots; + free(name); + continue; + } i += (name_len + F2FS_SLOT_LEN - 1) / F2FS_SLOT_LEN; dentries++; *child_files = *child_files + 1; free(name); } + return fixed ? -1 : dentries; +} + +int fsck_chk_inline_dentries(struct f2fs_sb_info *sbi, + struct f2fs_node *node_blk, u32 *child_cnt, u32 *child_files) +{ + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + struct f2fs_inline_dentry *de_blk; + int dentries; - DBG(1, "[%3d] Dentry Block [0x%x] Done : dentries:%d in %d slots (len:%d)\n\n", - fsck->dentry_depth, blk_addr, dentries, NR_DENTRY_IN_BLOCK, F2FS_NAME_LEN); + de_blk = inline_data_addr(node_blk); + ASSERT(de_blk != NULL); + + fsck->dentry_depth++; + dentries = __chk_dentries(sbi, child_cnt, child_files, + (unsigned long *)de_blk->dentry_bitmap, + de_blk->dentry, de_blk->filename, + NR_INLINE_DENTRY, 1); + if (dentries < 0) { + DBG(1, "[%3d] Inline Dentry Block Fixed hash_codes\n\n", + fsck->dentry_depth); + } else { + DBG(1, "[%3d] Inline Dentry Block Done : " + "dentries:%d in %d slots (len:%d)\n\n", + fsck->dentry_depth, dentries, + (int)NR_INLINE_DENTRY, F2FS_NAME_LEN); + } fsck->dentry_depth--; + return dentries; +} + +int fsck_chk_dentry_blk(struct f2fs_sb_info *sbi, u32 blk_addr, + u32 *child_cnt, u32 *child_files, int last_blk) +{ + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + struct f2fs_dentry_block *de_blk; + int dentries, ret; + + de_blk = (struct f2fs_dentry_block *)calloc(BLOCK_SZ, 1); + ASSERT(de_blk != NULL); + + ret = dev_read_block(de_blk, blk_addr); + ASSERT(ret >= 0); + + fsck->dentry_depth++; + dentries = __chk_dentries(sbi, child_cnt, child_files, + (unsigned long *)de_blk->dentry_bitmap, + de_blk->dentry, de_blk->filename, + NR_DENTRY_IN_BLOCK, last_blk); + if (dentries < 0) { + ret = dev_write_block(de_blk, blk_addr); + ASSERT(ret >= 0); + DBG(1, "[%3d] Dentry Block [0x%x] Fixed hash_codes\n\n", + fsck->dentry_depth, blk_addr); + } else { + DBG(1, "[%3d] Dentry Block [0x%x] Done : " + "dentries:%d in %d slots (len:%d)\n\n", + fsck->dentry_depth, blk_addr, dentries, + NR_DENTRY_IN_BLOCK, F2FS_NAME_LEN); + } + fsck->dentry_depth--; free(de_blk); return 0; } -int fsck_chk_data_blk(struct f2fs_sb_info *sbi, - u32 blk_addr, - u32 *child_cnt, - u32 *child_files, - int last_blk, - enum FILE_TYPE ftype, - u32 parent_nid, - u16 idx_in_node, - u8 ver) +int fsck_chk_data_blk(struct f2fs_sb_info *sbi, u32 blk_addr, + u32 *child_cnt, u32 *child_files, int last_blk, + enum FILE_TYPE ftype, u32 parent_nid, u16 idx_in_node, u8 ver) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); @@ -610,114 +801,80 @@ int fsck_chk_data_blk(struct f2fs_sb_info *sbi, return 0; } - IS_VALID_BLK_ADDR(sbi, blk_addr); - - is_valid_ssa_data_blk(sbi, blk_addr, parent_nid, idx_in_node, ver); - - if (f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, blk_addr), fsck->sit_area_bitmap) == 0x0) { - ASSERT_MSG(0, "SIT bitmap is 0x0. blk_addr[0x%x]\n", blk_addr); + if (!IS_VALID_BLK_ADDR(sbi, blk_addr)) { + ASSERT_MSG("blkaddres is not valid. [0x%x]", blk_addr); + return -EINVAL; } - if (f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, blk_addr), fsck->main_area_bitmap) != 0) { - ASSERT_MSG(0, "Duplicated data block. pnid[0x%x] idx[0x%x] blk_addr[0x%x]\n", - parent_nid, idx_in_node, blk_addr); + if (is_valid_ssa_data_blk(sbi, blk_addr, parent_nid, + idx_in_node, ver)) { + ASSERT_MSG("summary data block is not valid. [0x%x]", + parent_nid); + return -EINVAL; } - f2fs_set_bit(BLKOFF_FROM_MAIN(sbi, blk_addr), fsck->main_area_bitmap); - fsck->chk.valid_blk_cnt++; + if (f2fs_test_sit_bitmap(sbi, blk_addr) == 0) + ASSERT_MSG("SIT bitmap is 0x0. blk_addr[0x%x]", blk_addr); - if (ftype == F2FS_FT_DIR) { - fsck_chk_dentry_blk(sbi, - blk_addr, - child_cnt, - child_files, - last_blk); - } + if (f2fs_test_main_bitmap(sbi, blk_addr) != 0) + ASSERT_MSG("Duplicated data [0x%x]. pnid[0x%x] idx[0x%x]", + blk_addr, parent_nid, idx_in_node); + f2fs_set_main_bitmap(sbi, blk_addr); + + fsck->chk.valid_blk_cnt++; + + if (ftype == F2FS_FT_DIR) + return fsck_chk_dentry_blk(sbi, blk_addr, child_cnt, + child_files, last_blk); return 0; } -int fsck_chk_orphan_node(struct f2fs_sb_info *sbi) +void fsck_chk_orphan_node(struct f2fs_sb_info *sbi) { - int ret = 0; u32 blk_cnt = 0; - block_t start_blk, orphan_blkaddr, i, j; struct f2fs_orphan_block *orphan_blk; struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); if (!is_set_ckpt_flags(ckpt, CP_ORPHAN_PRESENT_FLAG)) - return 0; + return; + + if (config.fix_on) + return; start_blk = __start_cp_addr(sbi) + 1 + le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_payload); - orphan_blkaddr = __start_sum_addr(sbi) - 1; - orphan_blk = calloc(BLOCK_SZ, 1); for (i = 0; i < orphan_blkaddr; i++) { - dev_read_block(orphan_blk, start_blk + i); + int ret = dev_read_block(orphan_blk, start_blk + i); + + ASSERT(ret >= 0); for (j = 0; j < le32_to_cpu(orphan_blk->entry_count); j++) { nid_t ino = le32_to_cpu(orphan_blk->ino[j]); DBG(1, "[%3d] ino [0x%x]\n", i, ino); blk_cnt = 1; - ret = fsck_chk_node_blk(sbi, - NULL, - ino, - F2FS_FT_ORPHAN, - TYPE_INODE, - &blk_cnt); - ASSERT(ret >= 0); + fsck_chk_node_blk(sbi, NULL, ino, + F2FS_FT_ORPHAN, TYPE_INODE, &blk_cnt); } memset(orphan_blk, 0, BLOCK_SZ); } free(orphan_blk); - - - return 0; } -int fsck_chk_xattr_blk(struct f2fs_sb_info *sbi, u32 ino, u32 x_nid, u32 *blk_cnt) -{ - struct f2fs_fsck *fsck = F2FS_FSCK(sbi); - struct node_info ni; - - if (x_nid == 0x0) - return 0; - - if (f2fs_test_bit(x_nid, fsck->nat_area_bitmap) != 0x0) { - f2fs_clear_bit(x_nid, fsck->nat_area_bitmap); - } else { - ASSERT_MSG(0, "xattr_nid duplicated [0x%x]\n", x_nid); - } - - *blk_cnt = *blk_cnt + 1; - fsck->chk.valid_blk_cnt++; - fsck->chk.valid_node_cnt++; - - ASSERT(get_node_info(sbi, x_nid, &ni) >= 0); - - if (f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, ni.blk_addr), fsck->main_area_bitmap) != 0) { - ASSERT_MSG(0, "Duplicated node block for x_attr. " - "x_nid[0x%x] block addr[0x%x]\n", - x_nid, ni.blk_addr); - } - f2fs_set_bit(BLKOFF_FROM_MAIN(sbi, ni.blk_addr), fsck->main_area_bitmap); - - DBG(2, "ino[0x%x] x_nid[0x%x]\n", ino, x_nid); - return 0; -} - -int fsck_init(struct f2fs_sb_info *sbi) +void fsck_init(struct f2fs_sb_info *sbi) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct f2fs_sm_info *sm_i = SM_I(sbi); /* - * We build three bitmap for main/sit/nat so that may check consistency of filesystem. - * 1. main_area_bitmap will be used to check whether all blocks of main area is used or not. + * We build three bitmap for main/sit/nat so that may check consistency + * of filesystem. + * 1. main_area_bitmap will be used to check whether all blocks of main + * area is used or not. * 2. nat_area_bitmap has bitmap information of used nid in NAT. * 3. sit_area_bitmap has bitmap information of used main block. * At Last sequence, we compare main_area_bitmap with sit_area_bitmap. @@ -732,6 +889,83 @@ int fsck_init(struct f2fs_sb_info *sbi) build_sit_area_bitmap(sbi); tree_mark = calloc(tree_mark_size, 1); + ASSERT(tree_mark != NULL); +} + +static void fix_nat_entries(struct f2fs_sb_info *sbi) +{ + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + u32 i; + + for (i = 0; i < fsck->nr_nat_entries; i++) + if (f2fs_test_bit(i, fsck->nat_area_bitmap) != 0) + nullify_nat_entry(sbi, i); +} + +static void fix_checkpoint(struct f2fs_sb_info *sbi) +{ + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + struct f2fs_super_block *raw_sb = sbi->raw_super; + struct f2fs_checkpoint *ckp = F2FS_CKPT(sbi); + unsigned long long cp_blk_no; + u32 i; + int ret; + u_int32_t crc = 0; + + ckp->ckpt_flags = cpu_to_le32(CP_UMOUNT_FLAG); + ckp->cp_pack_total_block_count = + cpu_to_le32(8 + le32_to_cpu(raw_sb->cp_payload)); + ckp->cp_pack_start_sum = cpu_to_le32(1 + + le32_to_cpu(raw_sb->cp_payload)); + + ckp->free_segment_count = cpu_to_le32(fsck->chk.free_segs); + ckp->valid_block_count = cpu_to_le32(fsck->chk.valid_blk_cnt); + ckp->valid_node_count = cpu_to_le32(fsck->chk.valid_node_cnt); + ckp->valid_inode_count = cpu_to_le32(fsck->chk.valid_inode_cnt); + + crc = f2fs_cal_crc32(F2FS_SUPER_MAGIC, ckp, CHECKSUM_OFFSET); + *((__le32 *)((unsigned char *)ckp + CHECKSUM_OFFSET)) = + cpu_to_le32(crc); + + cp_blk_no = le32_to_cpu(raw_sb->cp_blkaddr); + if (sbi->cur_cp == 2) + cp_blk_no += 1 << le32_to_cpu(raw_sb->log_blocks_per_seg); + + ret = dev_write_block(ckp, cp_blk_no++); + ASSERT(ret >= 0); + + for (i = 0; i < le32_to_cpu(raw_sb->cp_payload); i++) { + ret = dev_write_block(((unsigned char *)ckp) + i * F2FS_BLKSIZE, + cp_blk_no++); + ASSERT(ret >= 0); + } + + for (i = 0; i < NO_CHECK_TYPE; i++) { + struct curseg_info *curseg = CURSEG_I(sbi, i); + + ret = dev_write_block(curseg->sum_blk, cp_blk_no++); + ASSERT(ret >= 0); + } + + ret = dev_write_block(ckp, cp_blk_no++); + ASSERT(ret >= 0); +} + +int check_curseg_offset(struct f2fs_sb_info *sbi) +{ + int i; + + for (i = 0; i < NO_CHECK_TYPE; i++) { + struct curseg_info *curseg = CURSEG_I(sbi, i); + struct seg_entry *se; + + se = get_seg_entry(sbi, curseg->segno); + if (f2fs_test_bit(curseg->next_blkoff, + (const char *)se->cur_valid_map) == 1) { + ASSERT_MSG("Next block offset is not free, type:%d", i); + return -EINVAL; + } + } return 0; } @@ -759,6 +993,7 @@ int fsck_verify(struct f2fs_sb_info *sbi) node->nid, node->links); node = node->next; } + config.bug_on = 1; } printf("[FSCK] Unreachable nat entries "); @@ -767,14 +1002,17 @@ int fsck_verify(struct f2fs_sb_info *sbi) } else { printf(" [Fail] [0x%x]\n", nr_unref_nid); ret = EXIT_ERR_CODE; + config.bug_on = 1; } printf("[FSCK] SIT valid block bitmap checking "); - if (memcmp(fsck->sit_area_bitmap, fsck->main_area_bitmap, fsck->sit_area_bitmap_sz) == 0x0) { + if (memcmp(fsck->sit_area_bitmap, fsck->main_area_bitmap, + fsck->sit_area_bitmap_sz) == 0x0) { printf("[Ok..]\n"); } else { printf("[Fail]\n"); ret = EXIT_ERR_CODE; + config.bug_on = 1; } printf("[FSCK] Hard link checking for regular file "); @@ -783,6 +1021,7 @@ int fsck_verify(struct f2fs_sb_info *sbi) } else { printf(" [Fail] [0x%x]\n", fsck->chk.multi_hard_link_files); ret = EXIT_ERR_CODE; + config.bug_on = 1; } printf("[FSCK] valid_block_count matching with CP "); @@ -791,6 +1030,7 @@ int fsck_verify(struct f2fs_sb_info *sbi) } else { printf(" [Fail] [0x%x]\n", (u32)fsck->chk.valid_blk_cnt); ret = EXIT_ERR_CODE; + config.bug_on = 1; } printf("[FSCK] valid_node_count matcing with CP (de lookup) "); @@ -799,6 +1039,7 @@ int fsck_verify(struct f2fs_sb_info *sbi) } else { printf(" [Fail] [0x%x]\n", fsck->chk.valid_node_cnt); ret = EXIT_ERR_CODE; + config.bug_on = 1; } printf("[FSCK] valid_node_count matcing with CP (nat lookup) "); @@ -807,6 +1048,7 @@ int fsck_verify(struct f2fs_sb_info *sbi) } else { printf(" [Fail] [0x%x]\n", fsck->chk.valid_nat_entry_cnt); ret = EXIT_ERR_CODE; + config.bug_on = 1; } printf("[FSCK] valid_inode_count matched with CP "); @@ -815,8 +1057,43 @@ int fsck_verify(struct f2fs_sb_info *sbi) } else { printf(" [Fail] [0x%x]\n", fsck->chk.valid_inode_cnt); ret = EXIT_ERR_CODE; + config.bug_on = 1; } + printf("[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); + } else { + printf(" [Fail] [0x%x]\n", fsck->chk.sit_free_segs); + ret = EXIT_ERR_CODE; + config.bug_on = 1; + } + + printf("[FSCK] next block offset is free "); + if (check_curseg_offset(sbi) == 0) { + printf(" [Ok..]\n"); + } else { + printf(" [Fail]\n"); + ret = EXIT_ERR_CODE; + config.bug_on = 1; + } + + printf("[FSCK] other corrupted bugs "); + if (config.bug_on == 0) { + printf(" [Ok..]\n"); + } else { + printf(" [Fail]\n"); + ret = EXIT_ERR_CODE; + config.bug_on = 1; + } + + /* fix global metadata */ + if (config.bug_on && config.fix_on) { + fix_nat_entries(sbi); + rewrite_sit_area_bitmap(sbi); + fix_checkpoint(sbi); + } return ret; } diff --git a/fsck/fsck.h b/fsck/fsck.h index e5a3841..49d6d1d 100644 --- a/fsck/fsck.h +++ b/fsck/fsck.h @@ -31,6 +31,7 @@ struct f2fs_fsck { u32 multi_hard_link_files; u64 sit_valid_blocks; u32 sit_free_segs; + u32 free_segs; } chk; struct hard_link_node *hard_link_list_head; @@ -59,7 +60,8 @@ enum NODE_TYPE { TYPE_INODE = 37, TYPE_DIRECT_NODE = 43, TYPE_INDIRECT_NODE = 53, - TYPE_DOUBLE_INDIRECT_NODE = 67 + TYPE_DOUBLE_INDIRECT_NODE = 67, + TYPE_XATTR = 77 }; struct hard_link_node { @@ -76,72 +78,40 @@ enum seg_type { SEG_TYPE_MAX, }; -extern int fsck_chk_xattr_blk(struct f2fs_sb_info *sbi, u32 ino, u32 x_nid, u32 *blk_cnt); -extern int fsck_chk_orphan_node(struct f2fs_sb_info *sbi); - -extern 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); - -extern int fsck_chk_inode_blk(struct f2fs_sb_info *sbi, - u32 nid, - enum FILE_TYPE ftype, - struct f2fs_node *node_blk, - u32 *blk_cnt, - struct node_info *ni); - -extern int fsck_chk_dnode_blk(struct f2fs_sb_info *sbi, - struct f2fs_inode *inode, - u32 nid, - enum FILE_TYPE ftype, - struct f2fs_node *node_blk, - u32 *blk_cnt, - struct node_info *ni); - -extern int fsck_chk_idnode_blk(struct f2fs_sb_info *sbi, - struct f2fs_inode *inode, - enum FILE_TYPE ftype, - struct f2fs_node *node_blk, - u32 *blk_cnt); - -extern int fsck_chk_didnode_blk(struct f2fs_sb_info *sbi, - struct f2fs_inode *inode, - enum FILE_TYPE ftype, - struct f2fs_node *node_blk, - u32 *blk_cnt); - -extern int fsck_chk_data_blk(struct f2fs_sb_info *sbi, - u32 blk_addr, - u32 *child_cnt, - u32 *child_files, - int last_blk, - enum FILE_TYPE ftype, - u32 parent_nid, - u16 idx_in_node, - u8 ver); - -extern int fsck_chk_dentry_blk(struct f2fs_sb_info *sbi, - u32 blk_addr, - u32 *child_cnt, - u32 *child_files, - int last_blk); - -extern void print_node_info(struct f2fs_node *node_block); -extern void print_inode_info(struct f2fs_inode *inode); -extern struct seg_entry *get_seg_entry(struct f2fs_sb_info *sbi, unsigned int segno); -extern int get_sum_block(struct f2fs_sb_info *sbi, unsigned int segno, struct f2fs_summary_block *sum_blk); -extern int get_sum_entry(struct f2fs_sb_info *sbi, u32 blk_addr, struct f2fs_summary *sum_entry); -extern int get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni); -extern void build_nat_area_bitmap(struct f2fs_sb_info *sbi); -extern int build_sit_area_bitmap(struct f2fs_sb_info *sbi); -extern int fsck_init(struct f2fs_sb_info *sbi); -extern int fsck_verify(struct f2fs_sb_info *sbi); -extern void fsck_free(struct f2fs_sb_info *sbi); -extern int f2fs_do_mount(struct f2fs_sb_info *sbi); -extern void f2fs_do_umount(struct f2fs_sb_info *sbi); +extern void fsck_chk_orphan_node(struct f2fs_sb_info *); +extern int fsck_chk_node_blk(struct f2fs_sb_info *, struct f2fs_inode *, u32, + enum FILE_TYPE, enum NODE_TYPE, u32 *); +extern void fsck_chk_inode_blk(struct f2fs_sb_info *, u32, enum FILE_TYPE, + struct f2fs_node *, u32 *, struct node_info *); +extern int fsck_chk_dnode_blk(struct f2fs_sb_info *, struct f2fs_inode *, + u32, enum FILE_TYPE, struct f2fs_node *, u32 *, + struct node_info *); +extern int fsck_chk_idnode_blk(struct f2fs_sb_info *, struct f2fs_inode *, + enum FILE_TYPE, struct f2fs_node *, u32 *); +extern int fsck_chk_didnode_blk(struct f2fs_sb_info *, struct f2fs_inode *, + enum FILE_TYPE, struct f2fs_node *, u32 *); +extern int fsck_chk_data_blk(struct f2fs_sb_info *sbi, u32, u32 *, u32 *, + int, enum FILE_TYPE, u32, u16, u8); +extern int fsck_chk_dentry_blk(struct f2fs_sb_info *, u32, u32 *, u32 *, int); +int fsck_chk_inline_dentries(struct f2fs_sb_info *, struct f2fs_node *, + u32 *, u32 *); + +extern void print_node_info(struct f2fs_node *); +extern void print_inode_info(struct f2fs_inode *); +extern struct seg_entry *get_seg_entry(struct f2fs_sb_info *, unsigned int); +extern int get_sum_block(struct f2fs_sb_info *, unsigned int, + struct f2fs_summary_block *); +extern int get_sum_entry(struct f2fs_sb_info *, u32, struct f2fs_summary *); +extern void get_node_info(struct f2fs_sb_info *, nid_t, struct node_info *); +extern void nullify_nat_entry(struct f2fs_sb_info *, u32); +extern void rewrite_sit_area_bitmap(struct f2fs_sb_info *); +extern void build_nat_area_bitmap(struct f2fs_sb_info *); +extern void build_sit_area_bitmap(struct f2fs_sb_info *); +extern void fsck_init(struct f2fs_sb_info *); +extern int fsck_verify(struct f2fs_sb_info *); +extern void fsck_free(struct f2fs_sb_info *); +extern int f2fs_do_mount(struct f2fs_sb_info *); +extern void f2fs_do_umount(struct f2fs_sb_info *); /* dump.c */ struct dump_option { @@ -153,9 +123,9 @@ struct dump_option { int32_t blk_addr; }; -extern void sit_dump(struct f2fs_sb_info *sbi, int start_sit, int end_sit); -extern void ssa_dump(struct f2fs_sb_info *sbi, int start_ssa, int end_ssa); -extern int dump_node(struct f2fs_sb_info *sbi, nid_t nid); -extern int dump_inode_from_blkaddr(struct f2fs_sb_info *sbi, u32 blk_addr); +extern void sit_dump(struct f2fs_sb_info *, int, int); +extern void ssa_dump(struct f2fs_sb_info *, int, int); +extern void dump_node(struct f2fs_sb_info *, nid_t); +extern int dump_inode_from_blkaddr(struct f2fs_sb_info *, u32); #endif /* _FSCK_H_ */ diff --git a/fsck/main.c b/fsck/main.c index 46f5d04..2af3daf 100644 --- a/fsck/main.c +++ b/fsck/main.c @@ -11,15 +11,16 @@ #include "fsck.h" #include <libgen.h> -struct f2fs_fsck gfsck = { - .sbi = { .fsck = &gfsck, }, -}; +struct f2fs_fsck gfsck; void fsck_usage() { MSG(0, "\nUsage: fsck.f2fs [options] device\n"); MSG(0, "[options]:\n"); + MSG(0, " -a check/fix potential corruption, reported by f2fs\n"); MSG(0, " -d debug level [default:0]\n"); + MSG(0, " -f check/fix entire partition\n"); + MSG(0, " -t show directory tree [-d -1]\n"); exit(1); } @@ -42,22 +43,31 @@ void f2fs_parse_options(int argc, char *argv[]) char *prog = basename(argv[0]); if (!strcmp("fsck.f2fs", prog)) { - const char *option_string = "d:t"; + const char *option_string = "ad:ft"; config.func = FSCK; while ((option = getopt(argc, argv, option_string)) != EOF) { switch (option) { - case 'd': - config.dbg_lv = atoi(optarg); - MSG(0, "Info: Debug level = %d\n", config.dbg_lv); - break; - case 't': - config.dbg_lv = -1; - break; - default: - MSG(0, "\tError: Unknown option %c\n",option); - fsck_usage(); - break; + case 'a': + config.auto_fix = 1; + MSG(0, "Info: Fix the reported corruption.\n"); + break; + case 'd': + config.dbg_lv = atoi(optarg); + MSG(0, "Info: Debug level = %d\n", + config.dbg_lv); + break; + case 'f': + config.fix_on = 1; + MSG(0, "Info: Force to fix corruption\n"); + break; + case 't': + config.dbg_lv = -1; + break; + default: + MSG(0, "\tError: Unknown option %c\n", option); + fsck_usage(); + break; } } } else if (!strcmp("dump.f2fs", prog)) { @@ -73,34 +83,46 @@ void f2fs_parse_options(int argc, char *argv[]) config.func = DUMP; while ((option = getopt(argc, argv, option_string)) != EOF) { + int ret = 0; + switch (option) { - case 'd': - config.dbg_lv = atoi(optarg); - MSG(0, "Info: Debug level = %d\n", config.dbg_lv); - break; - case 'i': - if (strncmp(optarg, "0x", 2)) - sscanf(optarg, "%d", &dump_opt.nid); - else - sscanf(optarg, "%x", &dump_opt.nid); - break; - case 's': - sscanf(optarg, "%d~%d", &dump_opt.start_sit, &dump_opt.end_sit); - break; - case 'a': - sscanf(optarg, "%d~%d", &dump_opt.start_ssa, &dump_opt.end_ssa); - break; - case 'b': - if (strncmp(optarg, "0x", 2)) - sscanf(optarg, "%d", &dump_opt.blk_addr); - else - sscanf(optarg, "%x", &dump_opt.blk_addr); - break; - default: - MSG(0, "\tError: Unknown option %c\n", option); - dump_usage(); - break; + case 'd': + config.dbg_lv = atoi(optarg); + MSG(0, "Info: Debug level = %d\n", + config.dbg_lv); + break; + case 'i': + if (strncmp(optarg, "0x", 2)) + ret = sscanf(optarg, "%d", + &dump_opt.nid); + else + ret = sscanf(optarg, "%x", + &dump_opt.nid); + break; + case 's': + ret = sscanf(optarg, "%d~%d", + &dump_opt.start_sit, + &dump_opt.end_sit); + break; + case 'a': + ret = sscanf(optarg, "%d~%d", + &dump_opt.start_ssa, + &dump_opt.end_ssa); + break; + case 'b': + if (strncmp(optarg, "0x", 2)) + ret = sscanf(optarg, "%d", + &dump_opt.blk_addr); + else + ret = sscanf(optarg, "%x", + &dump_opt.blk_addr); + break; + default: + MSG(0, "\tError: Unknown option %c\n", option); + dump_usage(); + break; } + ASSERT(ret >= 0); } config.private = &dump_opt; @@ -116,43 +138,27 @@ void f2fs_parse_options(int argc, char *argv[]) config.device_name = argv[optind]; } -int do_fsck(struct f2fs_sb_info *sbi) +static void do_fsck(struct f2fs_sb_info *sbi) { u32 blk_cnt; - int ret; - ret = fsck_init(sbi); - if (ret < 0) - return ret; + fsck_init(sbi); fsck_chk_orphan_node(sbi); - /* Travses all block recursively from root inode */ + /* Traverse all block recursively from root inode */ blk_cnt = 1; - ret = fsck_chk_node_blk(sbi, - NULL, - sbi->root_ino_num, - F2FS_FT_DIR, - TYPE_INODE, - &blk_cnt); - if (ret < 0) - goto out1; - - ret = fsck_verify(sbi); - -out1: + fsck_chk_node_blk(sbi, NULL, sbi->root_ino_num, + F2FS_FT_DIR, TYPE_INODE, &blk_cnt); + fsck_verify(sbi); fsck_free(sbi); - return ret; } -int do_dump(struct f2fs_sb_info *sbi) +static void do_dump(struct f2fs_sb_info *sbi) { struct dump_option *opt = (struct dump_option *)config.private; - int ret; - ret = fsck_init(sbi); - if (ret < 0) - return ret; + fsck_init(sbi); if (opt->end_sit == -1) opt->end_sit = SM_I(sbi)->main_segments; @@ -166,17 +172,14 @@ int do_dump(struct f2fs_sb_info *sbi) dump_inode_from_blkaddr(sbi, opt->blk_addr); goto cleanup; } - dump_node(sbi, opt->nid); - cleanup: fsck_free(sbi); - return 0; } -int main (int argc, char **argv) +int main(int argc, char **argv) { - struct f2fs_sb_info *sbi = &gfsck.sbi; + struct f2fs_sb_info *sbi; int ret = 0; f2fs_init_configuration(&config); @@ -189,23 +192,50 @@ int main (int argc, char **argv) /* Get device */ if (f2fs_get_device_info(&config) < 0) return -1; - - if (f2fs_do_mount(sbi) < 0) +fsck_again: + memset(&gfsck, 0, sizeof(gfsck)); + gfsck.sbi.fsck = &gfsck; + sbi = &gfsck.sbi; + + ret = f2fs_do_mount(sbi); + if (ret == 1) { + free(sbi->ckpt); + free(sbi->raw_super); + goto out; + } else if (ret < 0) return -1; switch (config.func) { - case FSCK: - ret = do_fsck(sbi); - break; - case DUMP: - ret = do_dump(sbi); - break; + case FSCK: + do_fsck(sbi); + break; + case DUMP: + do_dump(sbi); + break; } f2fs_do_umount(sbi); - +out: + if (config.func == FSCK && config.bug_on) { + if (config.fix_on == 0 && config.auto_fix == 0) { + char ans[255] = {0}; +retry: + printf("Do you want to fix this partition? [Y/N] "); + ret = scanf("%s", ans); + ASSERT(ret >= 0); + if (!strcasecmp(ans, "y")) + config.fix_on = 1; + else if (!strcasecmp(ans, "n")) + config.fix_on = 0; + else + goto retry; + + if (config.fix_on) + goto fsck_again; + } + } f2fs_finalize_device(&config); printf("\nDone.\n"); - return ret; + return 0; } diff --git a/fsck/mount.c b/fsck/mount.c index 5d3231f..9ec6004 100644 --- a/fsck/mount.c +++ b/fsck/mount.c @@ -33,6 +33,7 @@ void print_inode_info(struct f2fs_inode *inode) DISP_u32(inode, i_current_depth); DISP_u32(inode, i_xattr_nid); DISP_u32(inode, i_flags); + DISP_u32(inode, i_inline); DISP_u32(inode, i_pino); if (namelen) { @@ -53,7 +54,7 @@ void print_inode_info(struct f2fs_inode *inode) for (i = 4; i < ADDRS_PER_INODE(inode); i++) { if (inode->i_addr[i] != 0x0) { - printf("i_addr[0x%x] points data block\r\t\t\t\t[0x%4x]\n", + printf("i_addr[0x%x] points data block\r\t\t[0x%4x]\n", i, inode->i_addr[i]); break; } @@ -79,9 +80,11 @@ void print_node_info(struct f2fs_node *node_block) } else { int i; u32 *dump_blk = (u32 *)node_block; - DBG(0, "Node ID [0x%x:%u] is direct node or indirect node.\n", nid, nid); + DBG(0, "Node ID [0x%x:%u] is direct node or indirect node.\n", + nid, nid); for (i = 0; i <= 10; i++) - MSG(0, "[%d]\t\t\t[0x%8x : %d]\n", i, dump_blk[i], dump_blk[i]); + MSG(0, "[%d]\t\t\t[0x%8x : %d]\n", + i, dump_blk[i], dump_blk[i]); } } @@ -211,7 +214,8 @@ int sanity_check_raw_super(struct f2fs_super_block *raw_super) return -1; } - if (F2FS_LOG_SECTORS_PER_BLOCK != le32_to_cpu(raw_super->log_sectors_per_block)) { + if (F2FS_LOG_SECTORS_PER_BLOCK != + le32_to_cpu(raw_super->log_sectors_per_block)) { return -1; } @@ -220,9 +224,14 @@ int sanity_check_raw_super(struct f2fs_super_block *raw_super) int validate_super_block(struct f2fs_sb_info *sbi, int block) { - u64 offset = (block + 1) * F2FS_SUPER_OFFSET; + u64 offset; sbi->raw_super = malloc(sizeof(struct f2fs_super_block)); + if (block == 0) + offset = F2FS_SUPER_OFFSET; + else + offset = F2FS_BLKSIZE + F2FS_SUPER_OFFSET; + if (dev_read(sbi->raw_super, offset, sizeof(struct f2fs_super_block))) return -1; @@ -230,7 +239,7 @@ int validate_super_block(struct f2fs_sb_info *sbi, int block) return 0; free(sbi->raw_super); - MSG(0, "\tCan't find a valid F2FS filesystem in %d superblock\n", block); + MSG(0, "\tCan't find a valid F2FS superblock at 0x%x\n", block); return -EINVAL; } @@ -258,7 +267,8 @@ int init_sb_info(struct f2fs_sb_info *sbi) return 0; } -void *validate_checkpoint(struct f2fs_sb_info *sbi, block_t cp_addr, unsigned long long *version) +void *validate_checkpoint(struct f2fs_sb_info *sbi, block_t cp_addr, + unsigned long long *version) { void *cp_page_1, *cp_page_2; struct f2fs_checkpoint *cp_block; @@ -322,6 +332,7 @@ int get_valid_checkpoint(struct f2fs_sb_info *sbi) unsigned long long cp1_version = 0, cp2_version = 0; unsigned long long cp_start_blk_no; unsigned int cp_blks = 1 + le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_payload); + int ret; sbi->ckpt = malloc(cp_blks * blk_size); if (!sbi->ckpt) @@ -338,14 +349,19 @@ int get_valid_checkpoint(struct f2fs_sb_info *sbi) cp2 = validate_checkpoint(sbi, cp_start_blk_no, &cp2_version); if (cp1 && cp2) { - if (ver_after(cp2_version, cp1_version)) + if (ver_after(cp2_version, cp1_version)) { cur_page = cp2; - else + sbi->cur_cp = 2; + } else { cur_page = cp1; + sbi->cur_cp = 1; + } } else if (cp1) { cur_page = cp1; + sbi->cur_cp = 1; } else if (cp2) { cur_page = cp2; + sbi->cur_cp = 2; } else { free(cp1); free(cp2); @@ -355,16 +371,18 @@ int get_valid_checkpoint(struct f2fs_sb_info *sbi) memcpy(sbi->ckpt, cur_page, blk_size); if (cp_blks > 1) { - int i; + unsigned int i; unsigned long long cp_blk_no; cp_blk_no = le32_to_cpu(raw_sb->cp_blkaddr); if (cur_page == cp2) - cp_blk_no += 1 << le32_to_cpu(raw_sb->log_blocks_per_seg); + cp_blk_no += 1 << + le32_to_cpu(raw_sb->log_blocks_per_seg); /* copy sit bitmap */ for (i = 1; i < cp_blks; i++) { unsigned char *ckpt = (unsigned char *)sbi->ckpt; - dev_read_block(cur_page, cp_blk_no + i); + ret = dev_read_block(cur_page, cp_blk_no + i); + ASSERT(ret >= 0); memcpy(ckpt + i * blk_size, cur_page, blk_size); } } @@ -490,196 +508,178 @@ int build_sit_info(struct f2fs_sb_info *sbi) void reset_curseg(struct f2fs_sb_info *sbi, int type) { struct curseg_info *curseg = CURSEG_I(sbi, type); + struct summary_footer *sum_footer; + struct seg_entry *se; - curseg->segno = curseg->next_segno; - curseg->zone = GET_ZONENO_FROM_SEGNO(sbi, curseg->segno); - curseg->next_blkoff = 0; - curseg->next_segno = NULL_SEGNO; - + sum_footer = &(curseg->sum_blk->footer); + memset(sum_footer, 0, sizeof(struct summary_footer)); + if (IS_DATASEG(type)) + SET_SUM_TYPE(sum_footer, SUM_TYPE_DATA); + if (IS_NODESEG(type)) + SET_SUM_TYPE(sum_footer, SUM_TYPE_NODE); + se = get_seg_entry(sbi, curseg->segno); + se->type = type; } -int read_compacted_summaries(struct f2fs_sb_info *sbi) +static void read_compacted_summaries(struct f2fs_sb_info *sbi) { - struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); struct curseg_info *curseg; + unsigned int i, j, offset; block_t start; char *kaddr; - unsigned int i, j, offset; + int ret; start = start_sum_block(sbi); kaddr = (char *)malloc(PAGE_SIZE); - dev_read_block(kaddr, start++); + ret = dev_read_block(kaddr, start++); + ASSERT(ret >= 0); curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); memcpy(&curseg->sum_blk->n_nats, kaddr, SUM_JOURNAL_SIZE); curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); - memcpy(&curseg->sum_blk->n_sits, kaddr + SUM_JOURNAL_SIZE, SUM_JOURNAL_SIZE); + memcpy(&curseg->sum_blk->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; - unsigned int segno; + struct curseg_info *curseg = CURSEG_I(sbi, i); - curseg = CURSEG_I(sbi, i); - segno = le32_to_cpu(ckpt->cur_data_segno[i]); - blk_off = le16_to_cpu(ckpt->cur_data_blkoff[i]); - curseg->next_segno = segno; reset_curseg(sbi, i); - curseg->alloc_type = ckpt->alloc_type[i]; - curseg->next_blkoff = blk_off; if (curseg->alloc_type == SSR) blk_off = sbi->blocks_per_seg; + else + blk_off = curseg->next_blkoff; for (j = 0; j < blk_off; j++) { struct f2fs_summary *s; s = (struct f2fs_summary *)(kaddr + offset); curseg->sum_blk->entries[j] = *s; offset += SUMMARY_SIZE; - if (offset + SUMMARY_SIZE <= PAGE_CACHE_SIZE - SUM_FOOTER_SIZE) + if (offset + SUMMARY_SIZE <= + PAGE_CACHE_SIZE - SUM_FOOTER_SIZE) continue; memset(kaddr, 0, PAGE_SIZE); - dev_read_block(kaddr, start++); + ret = dev_read_block(kaddr, start++); + ASSERT(ret >= 0); offset = 0; } } - free(kaddr); - return 0; } -int restore_node_summary(struct f2fs_sb_info *sbi, +static void restore_node_summary(struct f2fs_sb_info *sbi, unsigned int segno, struct f2fs_summary_block *sum_blk) { struct f2fs_node *node_blk; struct f2fs_summary *sum_entry; - void *page; block_t addr; unsigned int i; + int ret; - page = malloc(PAGE_SIZE); - if (!page) - return -ENOMEM; + node_blk = malloc(F2FS_BLKSIZE); + ASSERT(node_blk); /* scan the node segment */ addr = START_BLOCK(sbi, segno); sum_entry = &sum_blk->entries[0]; for (i = 0; i < sbi->blocks_per_seg; i++, sum_entry++) { - if (dev_read_block(page, addr)) - goto out; - - node_blk = (struct f2fs_node *)page; + ret = dev_read_block(node_blk, addr); + ASSERT(ret >= 0); sum_entry->nid = node_blk->footer.nid; - /* do not change original value */ -#if 0 - sum_entry->version = 0; - sum_entry->ofs_in_node = 0; -#endif addr++; - } -out: - free(page); - return 0; + free(node_blk); } -int read_normal_summaries(struct f2fs_sb_info *sbi, int type) +static void read_normal_summaries(struct f2fs_sb_info *sbi, int type) { struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); struct f2fs_summary_block *sum_blk; struct curseg_info *curseg; - unsigned short blk_off; unsigned int segno = 0; block_t blk_addr = 0; + int ret; if (IS_DATASEG(type)) { segno = le32_to_cpu(ckpt->cur_data_segno[type]); - blk_off = le16_to_cpu(ckpt->cur_data_blkoff[type - CURSEG_HOT_DATA]); - if (is_set_ckpt_flags(ckpt, CP_UMOUNT_FLAG)) blk_addr = sum_blk_addr(sbi, NR_CURSEG_TYPE, type); else blk_addr = sum_blk_addr(sbi, NR_CURSEG_DATA_TYPE, type); } else { - segno = le32_to_cpu(ckpt->cur_node_segno[type - CURSEG_HOT_NODE]); - blk_off = le16_to_cpu(ckpt->cur_node_blkoff[type - CURSEG_HOT_NODE]); - + segno = le32_to_cpu(ckpt->cur_node_segno[type - + CURSEG_HOT_NODE]); if (is_set_ckpt_flags(ckpt, CP_UMOUNT_FLAG)) - blk_addr = sum_blk_addr(sbi, NR_CURSEG_NODE_TYPE, type - CURSEG_HOT_NODE); + blk_addr = sum_blk_addr(sbi, NR_CURSEG_NODE_TYPE, + type - CURSEG_HOT_NODE); else blk_addr = GET_SUM_BLKADDR(sbi, segno); } sum_blk = (struct f2fs_summary_block *)malloc(PAGE_SIZE); - dev_read_block(sum_blk, blk_addr); - - if (IS_NODESEG(type)) { - if (is_set_ckpt_flags(ckpt, CP_UMOUNT_FLAG)) { - struct f2fs_summary *sum_entry = &sum_blk->entries[0]; - unsigned int i; - for (i = 0; i < sbi->blocks_per_seg; i++, sum_entry++) { - /* do not change original value */ -#if 0 - sum_entry->version = 0; - sum_entry->ofs_in_node = 0; -#endif - } - } else { - if (restore_node_summary(sbi, segno, sum_blk)) { - free(sum_blk); - return -EINVAL; - } - } - } + ret = dev_read_block(sum_blk, blk_addr); + ASSERT(ret >= 0); + + if (IS_NODESEG(type) && !is_set_ckpt_flags(ckpt, CP_UMOUNT_FLAG)) + restore_node_summary(sbi, segno, sum_blk); curseg = CURSEG_I(sbi, type); memcpy(curseg->sum_blk, sum_blk, PAGE_CACHE_SIZE); - curseg->next_segno = segno; reset_curseg(sbi, type); - curseg->alloc_type = ckpt->alloc_type[type]; - curseg->next_blkoff = blk_off; free(sum_blk); - - return 0; } -int restore_curseg_summaries(struct f2fs_sb_info *sbi) +static void restore_curseg_summaries(struct f2fs_sb_info *sbi) { int type = CURSEG_HOT_DATA; if (is_set_ckpt_flags(F2FS_CKPT(sbi), CP_COMPACT_SUM_FLAG)) { - if (read_compacted_summaries(sbi)) - return -EINVAL; + read_compacted_summaries(sbi); type = CURSEG_HOT_NODE; } - for (; type <= CURSEG_COLD_NODE; type++) { - if (read_normal_summaries(sbi, type)) - return -EINVAL; - } - return 0; + for (; type <= CURSEG_COLD_NODE; type++) + read_normal_summaries(sbi, type); } -int build_curseg(struct f2fs_sb_info *sbi) +static void build_curseg(struct f2fs_sb_info *sbi) { + struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); struct curseg_info *array; + unsigned short blk_off; + unsigned int segno; int i; array = malloc(sizeof(*array) * NR_CURSEG_TYPE); + ASSERT(array); SM_I(sbi)->curseg_array = array; for (i = 0; i < NR_CURSEG_TYPE; i++) { array[i].sum_blk = malloc(PAGE_CACHE_SIZE); - if (!array[i].sum_blk) - return -ENOMEM; - array[i].segno = NULL_SEGNO; - array[i].next_blkoff = 0; + ASSERT(array[i].sum_blk); + if (i <= CURSEG_COLD_DATA) { + blk_off = le16_to_cpu(ckpt->cur_data_blkoff[i]); + segno = le32_to_cpu(ckpt->cur_data_segno[i]); + } + if (i > CURSEG_COLD_DATA) { + blk_off = le16_to_cpu(ckpt->cur_node_blkoff[i - + CURSEG_HOT_NODE]); + segno = le32_to_cpu(ckpt->cur_node_segno[i - + CURSEG_HOT_NODE]); + } + array[i].segno = segno; + array[i].zone = GET_ZONENO_FROM_SEGNO(sbi, segno); + array[i].next_segno = NULL_SEGNO; + array[i].next_blkoff = blk_off; + array[i].alloc_type = ckpt->alloc_type[i]; } - return restore_curseg_summaries(sbi); + restore_curseg_summaries(sbi); } inline void check_seg_range(struct f2fs_sb_info *sbi, unsigned int segno) @@ -688,12 +688,14 @@ inline void check_seg_range(struct f2fs_sb_info *sbi, unsigned int segno) ASSERT(segno <= end_segno); } -struct f2fs_sit_block *get_current_sit_page(struct f2fs_sb_info *sbi, unsigned int segno) +static struct f2fs_sit_block *get_current_sit_page(struct f2fs_sb_info *sbi, + unsigned int segno) { struct sit_info *sit_i = SIT_I(sbi); unsigned int offset = SIT_BLOCK_OFFSET(sit_i, segno); block_t blk_addr = sit_i->sit_base_addr + offset; struct f2fs_sit_block *sit_blk = calloc(BLOCK_SZ, 1); + int ret; check_seg_range(sbi, segno); @@ -701,11 +703,28 @@ struct f2fs_sit_block *get_current_sit_page(struct f2fs_sb_info *sbi, unsigned i if (f2fs_test_bit(offset, sit_i->sit_bitmap)) blk_addr += sit_i->sit_blocks; - dev_read_block(sit_blk, blk_addr); + ret = dev_read_block(sit_blk, blk_addr); + ASSERT(ret >= 0); return sit_blk; } +void rewrite_current_sit_page(struct f2fs_sb_info *sbi, + unsigned int segno, struct f2fs_sit_block *sit_blk) +{ + struct sit_info *sit_i = SIT_I(sbi); + unsigned int offset = SIT_BLOCK_OFFSET(sit_i, segno); + block_t blk_addr = sit_i->sit_base_addr + offset; + int ret; + + /* calculate sit block address */ + if (f2fs_test_bit(offset, sit_i->sit_bitmap)) + blk_addr += sit_i->sit_blocks; + + ret = dev_write_block(sit_blk, blk_addr); + ASSERT(ret >= 0); +} + void check_block_count(struct f2fs_sb_info *sbi, unsigned int segno, struct f2fs_sit_entry *raw_sit) { @@ -714,18 +733,26 @@ void check_block_count(struct f2fs_sb_info *sbi, int valid_blocks = 0; unsigned int i; - /* check segment usage */ - ASSERT(GET_SIT_VBLOCKS(raw_sit) <= sbi->blocks_per_seg); + if (GET_SIT_VBLOCKS(raw_sit) > sbi->blocks_per_seg) + ASSERT_MSG("Invalid SIT vblocks: segno=0x%x, %u", + segno, GET_SIT_VBLOCKS(raw_sit)); /* check boundary of a given segment number */ - ASSERT(segno <= end_segno); + if (segno > end_segno) + ASSERT_MSG("Invalid SEGNO: 0x%x", segno); /* check bitmap with valid block count */ - for (i = 0; i < sbi->blocks_per_seg; i++) - if (f2fs_test_bit(i, (char *)raw_sit->valid_map)) - valid_blocks++; - ASSERT(GET_SIT_VBLOCKS(raw_sit) == valid_blocks); + 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) + 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) + 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, @@ -746,7 +773,8 @@ struct seg_entry *get_seg_entry(struct f2fs_sb_info *sbi, return &sit_i->sentries[segno]; } -int get_sum_block(struct f2fs_sb_info *sbi, unsigned int segno, struct f2fs_summary_block *sum_blk) +int get_sum_block(struct f2fs_sb_info *sbi, unsigned int segno, + struct f2fs_summary_block *sum_blk) { struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); struct curseg_info *curseg; @@ -757,18 +785,30 @@ int get_sum_block(struct f2fs_sb_info *sbi, unsigned int segno, struct f2fs_summ for (type = 0; type < NR_CURSEG_NODE_TYPE; type++) { if (segno == ckpt->cur_node_segno[type]) { curseg = CURSEG_I(sbi, CURSEG_HOT_NODE + type); + if (!IS_SUM_NODE_SEG(curseg->sum_blk->footer)) { + ASSERT_MSG("segno [0x%x] indicates a data " + "segment, but should be node", + segno); + return -EINVAL; + } memcpy(sum_blk, curseg->sum_blk, BLOCK_SZ); - return SEG_TYPE_CUR_NODE; /* current node seg was not stored */ + return SEG_TYPE_CUR_NODE; } } for (type = 0; type < NR_CURSEG_DATA_TYPE; type++) { if (segno == ckpt->cur_data_segno[type]) { curseg = CURSEG_I(sbi, type); + if (IS_SUM_NODE_SEG(curseg->sum_blk->footer)) { + ASSERT_MSG("segno [0x%x] indicates a node " + "segment, but should be data", + segno); + return -EINVAL; + } + DBG(2, "segno [0x%x] is current data seg[0x%x]\n", + segno, type); memcpy(sum_blk, curseg->sum_blk, BLOCK_SZ); - ASSERT(!IS_SUM_NODE_SEG(sum_blk->footer)); - DBG(2, "segno [0x%x] is current data seg[0x%x]\n", segno, type); - return SEG_TYPE_CUR_DATA; /* current data seg was not stored */ + return SEG_TYPE_CUR_DATA; } } @@ -782,7 +822,8 @@ int get_sum_block(struct f2fs_sb_info *sbi, unsigned int segno, struct f2fs_summ } -int get_sum_entry(struct f2fs_sb_info *sbi, u32 blk_addr, struct f2fs_summary *sum_entry) +int get_sum_entry(struct f2fs_sb_info *sbi, u32 blk_addr, + struct f2fs_summary *sum_entry) { struct f2fs_summary_block *sum_blk; u32 segno, offset; @@ -794,16 +835,15 @@ int get_sum_entry(struct f2fs_sb_info *sbi, u32 blk_addr, struct f2fs_summary *s sum_blk = calloc(BLOCK_SZ, 1); ret = get_sum_block(sbi, segno, sum_blk); - - memcpy(sum_entry, &(sum_blk->entries[offset]), sizeof(struct f2fs_summary)); - + memcpy(sum_entry, &(sum_blk->entries[offset]), + sizeof(struct f2fs_summary)); free(sum_blk); return ret; } -int get_nat_entry(struct f2fs_sb_info *sbi, nid_t nid, struct f2fs_nat_entry *raw_nat) +static void get_nat_entry(struct f2fs_sb_info *sbi, nid_t nid, + struct f2fs_nat_entry *raw_nat) { - struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct f2fs_nm_info *nm_i = NM_I(sbi); struct f2fs_nat_block *nat_block; pgoff_t block_off; @@ -811,13 +851,8 @@ int get_nat_entry(struct f2fs_sb_info *sbi, nid_t nid, struct f2fs_nat_entry *ra int seg_off, entry_off; int ret; - if ((nid / NAT_ENTRY_PER_BLOCK) > fsck->nr_nat_entries) { - DBG(0, "nid is over max nid\n"); - return -EINVAL; - } - if (lookup_nat_in_journal(sbi, nid, raw_nat) >= 0) - return 0; + return; nat_block = (struct f2fs_nat_block *)calloc(BLOCK_SZ, 1); @@ -835,21 +870,17 @@ int get_nat_entry(struct f2fs_sb_info *sbi, nid_t nid, struct f2fs_nat_entry *ra ret = dev_read_block(nat_block, block_addr); ASSERT(ret >= 0); - memcpy(raw_nat, &nat_block->entries[entry_off], sizeof(struct f2fs_nat_entry)); + memcpy(raw_nat, &nat_block->entries[entry_off], + sizeof(struct f2fs_nat_entry)); free(nat_block); - - return 0; } -int get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni) +void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni) { struct f2fs_nat_entry raw_nat; - int ret; - - ret = get_nat_entry(sbi, nid, &raw_nat); + get_nat_entry(sbi, nid, &raw_nat); ni->nid = nid; node_info_from_raw_nat(ni, &raw_nat); - return ret; } void build_sit_entries(struct f2fs_sb_info *sbi) @@ -910,18 +941,14 @@ int build_segment_manager(struct f2fs_sb_info *sbi) return 0; } -int build_sit_area_bitmap(struct f2fs_sb_info *sbi) +void build_sit_area_bitmap(struct f2fs_sb_info *sbi) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct f2fs_sm_info *sm_i = SM_I(sbi); unsigned int segno = 0; - int j = 0; char *ptr = NULL; - u32 sum_vblocks = 0; u32 free_segs = 0; - u32 vblocks = 0; - struct seg_entry *se; fsck->sit_area_bitmap_sz = sm_i->main_segments * SIT_VBLOCK_MAP_SIZE; @@ -930,20 +957,13 @@ int build_sit_area_bitmap(struct f2fs_sb_info *sbi) ASSERT(fsck->sit_area_bitmap_sz == fsck->main_area_bitmap_sz); - for (segno = 0; segno < sm_i->main_segments; segno++) { + for (segno = 0; segno < TOTAL_SEGS(sbi); segno++) { se = get_seg_entry(sbi, segno); memcpy(ptr, se->cur_valid_map, SIT_VBLOCK_MAP_SIZE); ptr += SIT_VBLOCK_MAP_SIZE; - vblocks = 0; - for (j = 0; j < SIT_VBLOCK_MAP_SIZE; j++) { - vblocks += get_bits_in_byte(se->cur_valid_map[j]); - } - ASSERT(vblocks == se->valid_blocks); - if (se->valid_blocks == 0x0) { - if (sbi->ckpt->cur_node_segno[0] == segno || sbi->ckpt->cur_data_segno[0] == segno || sbi->ckpt->cur_node_segno[1] == segno || @@ -954,22 +974,76 @@ int build_sit_area_bitmap(struct f2fs_sb_info *sbi) } else { free_segs++; } - } else { - ASSERT(se->valid_blocks <= 512); sum_vblocks += se->valid_blocks; } } - fsck->chk.sit_valid_blocks = sum_vblocks; fsck->chk.sit_free_segs = free_segs; - DBG(1, "Blocks [0x%x : %d] Free Segs [0x%x : %d]\n\n", sum_vblocks, sum_vblocks, + DBG(1, "Blocks [0x%x : %d] Free Segs [0x%x : %d]\n\n", + sum_vblocks, sum_vblocks, free_segs, free_segs); - return 0; } -int lookup_nat_in_journal(struct f2fs_sb_info *sbi, u32 nid, struct f2fs_nat_entry *raw_nat) +void rewrite_sit_area_bitmap(struct f2fs_sb_info *sbi) +{ + struct f2fs_fsck *fsck = F2FS_FSCK(sbi); + struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); + struct sit_info *sit_i = SIT_I(sbi); + unsigned int segno = 0; + struct f2fs_summary_block *sum = curseg->sum_blk; + char *ptr = NULL; + + /* remove sit journal */ + sum->n_sits = 0; + + fsck->chk.free_segs = 0; + + ptr = fsck->main_area_bitmap; + + for (segno = 0; segno < TOTAL_SEGS(sbi); segno++) { + struct f2fs_sit_block *sit_blk; + struct f2fs_sit_entry *sit; + struct seg_entry *se; + u16 valid_blocks = 0; + u16 type; + int i; + + sit_blk = get_current_sit_page(sbi, segno); + sit = &sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, segno)]; + memcpy(sit->valid_map, ptr, SIT_VBLOCK_MAP_SIZE); + + /* update valid block count */ + for (i = 0; i < SIT_VBLOCK_MAP_SIZE; i++) + valid_blocks += get_bits_in_byte(sit->valid_map[i]); + + se = get_seg_entry(sbi, segno); + type = se->type; + if (type >= NO_CHECK_TYPE) { + ASSERT(valid_blocks); + type = 0; + } + sit->vblocks = cpu_to_le16((type << SIT_VBLOCKS_SHIFT) | + valid_blocks); + rewrite_current_sit_page(sbi, segno, sit_blk); + free(sit_blk); + + if (valid_blocks == 0 && + sbi->ckpt->cur_node_segno[0] != segno && + sbi->ckpt->cur_data_segno[0] != segno && + sbi->ckpt->cur_node_segno[1] != segno && + sbi->ckpt->cur_data_segno[1] != segno && + sbi->ckpt->cur_node_segno[2] != segno && + sbi->ckpt->cur_data_segno[2] != segno) + fsck->chk.free_segs++; + + ptr += SIT_VBLOCK_MAP_SIZE; + } +} + +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_summary_block *sum = curseg->sum_blk; @@ -977,7 +1051,8 @@ int lookup_nat_in_journal(struct f2fs_sb_info *sbi, u32 nid, struct f2fs_nat_ent for (i = 0; i < nats_in_cursum(sum); i++) { if (le32_to_cpu(nid_in_journal(sum, i)) == nid) { - memcpy(raw_nat, &nat_in_journal(sum, i), sizeof(struct f2fs_nat_entry)); + memcpy(raw_nat, &nat_in_journal(sum, i), + sizeof(struct f2fs_nat_entry)); DBG(3, "==> Found nid [0x%x] in nat cache\n", nid); return i; } @@ -985,6 +1060,51 @@ int lookup_nat_in_journal(struct f2fs_sb_info *sbi, u32 nid, struct f2fs_nat_ent return -1; } +void nullify_nat_entry(struct f2fs_sb_info *sbi, u32 nid) +{ + struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); + struct f2fs_summary_block *sum = curseg->sum_blk; + struct f2fs_nm_info *nm_i = NM_I(sbi); + struct f2fs_nat_block *nat_block; + pgoff_t block_off; + pgoff_t block_addr; + int seg_off, entry_off; + int ret; + int i = 0; + + /* check in journal */ + for (i = 0; i < nats_in_cursum(sum); i++) { + if (le32_to_cpu(nid_in_journal(sum, i)) == nid) { + memset(&nat_in_journal(sum, i), 0, + sizeof(struct f2fs_nat_entry)); + FIX_MSG("Remove nid [0x%x] in nat journal\n", nid); + return; + } + } + nat_block = (struct f2fs_nat_block *)calloc(BLOCK_SZ, 1); + + block_off = nid / NAT_ENTRY_PER_BLOCK; + entry_off = nid % NAT_ENTRY_PER_BLOCK; + + seg_off = block_off >> sbi->log_blocks_per_seg; + block_addr = (pgoff_t)(nm_i->nat_blkaddr + + (seg_off << sbi->log_blocks_per_seg << 1) + + (block_off & ((1 << sbi->log_blocks_per_seg) - 1))); + + if (f2fs_test_bit(block_off, nm_i->nat_bitmap)) + block_addr += sbi->blocks_per_seg; + + ret = dev_read_block(nat_block, block_addr); + ASSERT(ret >= 0); + + memset(&nat_block->entries[entry_off], 0, + sizeof(struct f2fs_nat_entry)); + + ret = dev_write_block(nat_block, block_addr); + ASSERT(ret >= 0); + free(nat_block); +} + void build_nat_area_bitmap(struct f2fs_sb_info *sbi) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); @@ -992,18 +1112,18 @@ void build_nat_area_bitmap(struct f2fs_sb_info *sbi) struct f2fs_nm_info *nm_i = NM_I(sbi); struct f2fs_nat_block *nat_block; u32 nid, nr_nat_blks; - pgoff_t block_off; pgoff_t block_addr; int seg_off; int ret; unsigned int i; - nat_block = (struct f2fs_nat_block *)calloc(BLOCK_SZ, 1); + ASSERT(nat_block); /* Alloc & build nat entry bitmap */ - nr_nat_blks = (le32_to_cpu(raw_sb->segment_count_nat) / 2) << sbi->log_blocks_per_seg; + nr_nat_blks = (le32_to_cpu(raw_sb->segment_count_nat) / 2) << + sbi->log_blocks_per_seg; fsck->nr_nat_entries = nr_nat_blks * NAT_ENTRY_PER_BLOCK; fsck->nat_area_bitmap_sz = (fsck->nr_nat_entries + 7) / 8; @@ -1014,8 +1134,8 @@ void build_nat_area_bitmap(struct f2fs_sb_info *sbi) seg_off = block_off >> sbi->log_blocks_per_seg; block_addr = (pgoff_t)(nm_i->nat_blkaddr + - (seg_off << sbi->log_blocks_per_seg << 1) + - (block_off & ((1 << sbi->log_blocks_per_seg) - 1))); + (seg_off << sbi->log_blocks_per_seg << 1) + + (block_off & ((1 << sbi->log_blocks_per_seg) - 1))); if (f2fs_test_bit(block_off, nm_i->nat_bitmap)) block_addr += sbi->blocks_per_seg; @@ -1029,44 +1149,47 @@ void build_nat_area_bitmap(struct f2fs_sb_info *sbi) struct node_info ni; ni.nid = nid + i; - if ((nid + i) == F2FS_NODE_INO(sbi) || (nid + i) == F2FS_META_INO(sbi)) { + if ((nid + i) == F2FS_NODE_INO(sbi) || + (nid + i) == F2FS_META_INO(sbi)) { ASSERT(nat_block->entries[i].block_addr != 0x0); continue; } - if (lookup_nat_in_journal(sbi, nid + i, &raw_nat) >= 0) { + if (lookup_nat_in_journal(sbi, nid + i, + &raw_nat) >= 0) { node_info_from_raw_nat(&ni, &raw_nat); if (ni.blk_addr != 0x0) { - f2fs_set_bit(nid + i, fsck->nat_area_bitmap); + f2fs_set_bit(nid + i, + fsck->nat_area_bitmap); fsck->chk.valid_nat_entry_cnt++; - DBG(3, "nid[0x%x] in nat cache\n", nid + i); + DBG(3, "nid[0x%x] in nat cache\n", + nid + i); } } else { - node_info_from_raw_nat(&ni, &nat_block->entries[i]); - if (ni.blk_addr != 0) { - ASSERT(nid + i != 0x0); - - DBG(3, "nid[0x%8x] in nat entry [0x%16x] [0x%8x]\n", - nid + i, - ni.blk_addr, - ni.ino); - - f2fs_set_bit(nid + i, fsck->nat_area_bitmap); - fsck->chk.valid_nat_entry_cnt++; - } + node_info_from_raw_nat(&ni, + &nat_block->entries[i]); + if (ni.blk_addr == 0) + continue; + ASSERT(nid + i != 0x0); + + DBG(3, "nid[0x%8x] addr[0x%16x] ino[0x%8x]\n", + nid + i, ni.blk_addr, ni.ino); + f2fs_set_bit(nid + i, fsck->nat_area_bitmap); + fsck->chk.valid_nat_entry_cnt++; } } } free(nat_block); DBG(1, "valid nat entries (block_addr != 0x0) [0x%8x : %u]\n", - fsck->chk.valid_nat_entry_cnt, fsck->chk.valid_nat_entry_cnt); - + fsck->chk.valid_nat_entry_cnt, + fsck->chk.valid_nat_entry_cnt); } int f2fs_do_mount(struct f2fs_sb_info *sbi) { int ret; + sbi->active_logs = NR_CURSEG_TYPE; ret = validate_super_block(sbi, 0); if (ret) { @@ -1092,10 +1215,23 @@ int f2fs_do_mount(struct f2fs_sb_info *sbi) print_ckpt_info(sbi); + if (config.auto_fix) { + u32 flag = le32_to_cpu(sbi->ckpt->ckpt_flags); + + if (flag & CP_FSCK_FLAG) + config.fix_on = 1; + else + return 1; + } + + config.bug_on = 0; + sbi->total_valid_node_count = le32_to_cpu(sbi->ckpt->valid_node_count); - sbi->total_valid_inode_count = le32_to_cpu(sbi->ckpt->valid_inode_count); + sbi->total_valid_inode_count = + le32_to_cpu(sbi->ckpt->valid_inode_count); sbi->user_block_count = le64_to_cpu(sbi->ckpt->user_block_count); - sbi->total_valid_block_count = le64_to_cpu(sbi->ckpt->valid_block_count); + sbi->total_valid_block_count = + le64_to_cpu(sbi->ckpt->valid_block_count); sbi->last_valid_block_count = sbi->total_valid_block_count; sbi->alloc_valid_block_count = 0; @@ -1109,7 +1245,7 @@ int f2fs_do_mount(struct f2fs_sb_info *sbi) return -1; } - return ret; + return 0; } void f2fs_do_umount(struct f2fs_sb_info *sbi) diff --git a/include/f2fs_fs.h b/include/f2fs_fs.h index 53b8cb9..b02002c 100644 --- a/include/f2fs_fs.h +++ b/include/f2fs_fs.h @@ -5,6 +5,9 @@ * http://www.samsung.com/ * * Dual licensed under the GPL or LGPL version 2 licenses. + * + * The byteswap codes are copied from: + * samba_3_master/lib/ccan/endian/endian.h under LGPL 2.1 */ #ifndef __F2FS_FS_H__ #define __F2FS_FS_H__ @@ -26,6 +29,63 @@ typedef u32 nid_t; typedef u8 bool; typedef unsigned long pgoff_t; +#if HAVE_BYTESWAP_H +#include <byteswap.h> +#else +/** + * bswap_16 - reverse bytes in a uint16_t value. + * @val: value whose bytes to swap. + * + * Example: + * // Output contains "1024 is 4 as two bytes reversed" + * printf("1024 is %u as two bytes reversed\n", bswap_16(1024)); + */ +static inline uint16_t bswap_16(uint16_t val) +{ + return ((val & (uint16_t)0x00ffU) << 8) + | ((val & (uint16_t)0xff00U) >> 8); +} + +/** + * bswap_32 - reverse bytes in a uint32_t value. + * @val: value whose bytes to swap. + * + * Example: + * // Output contains "1024 is 262144 as four bytes reversed" + * printf("1024 is %u as four bytes reversed\n", bswap_32(1024)); + */ +static inline uint32_t bswap_32(uint32_t val) +{ + return ((val & (uint32_t)0x000000ffUL) << 24) + | ((val & (uint32_t)0x0000ff00UL) << 8) + | ((val & (uint32_t)0x00ff0000UL) >> 8) + | ((val & (uint32_t)0xff000000UL) >> 24); +} +#endif /* !HAVE_BYTESWAP_H */ + +#if !HAVE_BSWAP_64 +/** + * bswap_64 - reverse bytes in a uint64_t value. + * @val: value whose bytes to swap. + * + * Example: + * // Output contains "1024 is 1125899906842624 as eight bytes reversed" + * printf("1024 is %llu as eight bytes reversed\n", + * (unsigned long long)bswap_64(1024)); + */ +static inline uint64_t bswap_64(uint64_t val) +{ + return ((val & (uint64_t)0x00000000000000ffULL) << 56) + | ((val & (uint64_t)0x000000000000ff00ULL) << 40) + | ((val & (uint64_t)0x0000000000ff0000ULL) << 24) + | ((val & (uint64_t)0x00000000ff000000ULL) << 8) + | ((val & (uint64_t)0x000000ff00000000ULL) >> 8) + | ((val & (uint64_t)0x0000ff0000000000ULL) >> 24) + | ((val & (uint64_t)0x00ff000000000000ULL) >> 40) + | ((val & (uint64_t)0xff00000000000000ULL) >> 56); +} +#endif + #if __BYTE_ORDER == __LITTLE_ENDIAN #define le16_to_cpu(x) ((__u16)(x)) #define le32_to_cpu(x) ((__u32)(x)) @@ -54,37 +114,39 @@ typedef unsigned long pgoff_t; /* * Debugging interfaces */ -#define ASSERT_MSG(exp, fmt, ...) \ +#define FIX_MSG(fmt, ...) \ do { \ - if (!(exp)) { \ - printf("\nAssertion failed!\n"); \ - printf("[%s:%4d] " #exp, __func__, __LINE__); \ - printf("\n --> "fmt, ##__VA_ARGS__); \ - exit(-1); \ - } \ - } while (0); + printf("[FIX] (%s:%4d) ", __func__, __LINE__); \ + printf(" --> "fmt"\n", ##__VA_ARGS__); \ + } while (0) + +#define ASSERT_MSG(fmt, ...) \ + do { \ + printf("[ASSERT] (%s:%4d) ", __func__, __LINE__); \ + printf(" --> "fmt"\n", ##__VA_ARGS__); \ + config.bug_on = 1; \ + } while (0) #define ASSERT(exp) \ do { \ if (!(exp)) { \ - printf("\nAssertion failed!\n"); \ - printf("[%s:%4d] " #exp"\n", __func__, __LINE__);\ + printf("[ASSERT] (%s:%4d) " #exp"\n", \ + __func__, __LINE__); \ exit(-1); \ } \ - } while (0); + } while (0) #define ERR_MSG(fmt, ...) \ do { \ - printf("[%s:%d] " fmt, __func__, __LINE__, ##__VA_ARGS__); \ - } while (0); - + printf("[%s:%d] " fmt, __func__, __LINE__, ##__VA_ARGS__); \ + } while (0) #define MSG(n, fmt, ...) \ do { \ if (config.dbg_lv >= n) { \ printf(fmt, ##__VA_ARGS__); \ } \ - } while (0); + } while (0) #define DBG(n, fmt, ...) \ do { \ @@ -92,52 +154,52 @@ typedef unsigned long pgoff_t; printf("[%s:%4d] " fmt, \ __func__, __LINE__, ##__VA_ARGS__); \ } \ - } while (0); + } while (0) /* Display on console */ #define DISP(fmt, ptr, member) \ do { \ printf("%-30s" fmt, #member, ((ptr)->member)); \ - } while (0); + } while (0) #define DISP_u32(ptr, member) \ do { \ assert(sizeof((ptr)->member) <= 4); \ printf("%-30s" "\t\t[0x%8x : %u]\n", \ - #member, ((ptr)->member), ((ptr)->member) ); \ - } while (0); + #member, ((ptr)->member), ((ptr)->member)); \ + } while (0) #define DISP_u64(ptr, member) \ do { \ assert(sizeof((ptr)->member) == 8); \ printf("%-30s" "\t\t[0x%8llx : %llu]\n", \ - #member, ((ptr)->member), ((ptr)->member) ); \ - } while (0); + #member, ((ptr)->member), ((ptr)->member)); \ + } while (0) #define DISP_utf(ptr, member) \ do { \ - printf("%-30s" "\t\t[%s]\n", #member, ((ptr)->member) ); \ - } while (0); + printf("%-30s" "\t\t[%s]\n", #member, ((ptr)->member)); \ + } while (0) /* Display to buffer */ -#define BUF_DISP_u32(buf, data, len, ptr, member) \ - do { \ - assert(sizeof((ptr)->member) <= 4); \ - snprintf(buf, len, #member); \ - snprintf(data, len, "0x%x : %u", ((ptr)->member), ((ptr)->member)); \ - } while (0); - -#define BUF_DISP_u64(buf, data, len, ptr, member) \ - do { \ - assert(sizeof((ptr)->member) == 8); \ - snprintf(buf, len, #member); \ - snprintf(data, len, "0x%llx : %llu", ((ptr)->member), ((ptr)->member)); \ - } while (0); - -#define BUF_DISP_utf(buf, data, len, ptr, member) \ - do { \ - snprintf(buf, len, #member); \ - } while (0); +#define BUF_DISP_u32(buf, data, len, ptr, member) \ + do { \ + assert(sizeof((ptr)->member) <= 4); \ + snprintf(buf, len, #member); \ + snprintf(data, len, "0x%x : %u", ((ptr)->member), \ + ((ptr)->member)); \ + } while (0) + +#define BUF_DISP_u64(buf, data, len, ptr, member) \ + do { \ + assert(sizeof((ptr)->member) == 8); \ + snprintf(buf, len, #member); \ + snprintf(data, len, "0x%llx : %llu", ((ptr)->member), \ + ((ptr)->member)); \ + } while (0) + +#define BUF_DISP_utf(buf, data, len, ptr, member) \ + snprintf(buf, len, #member) /* these are defined in kernel */ #define PAGE_SIZE 4096 @@ -173,12 +235,16 @@ struct f2fs_configuration { char *vol_label; int heap; int32_t fd; + int32_t dump_fd; char *device_name; char *extension_list; int dbg_lv; int trim; int func; void *private; + int fix_on; + int bug_on; + int auto_fix; } __attribute__((packed)); #ifdef CONFIG_64BIT @@ -221,6 +287,7 @@ enum { #define F2FS_LOG_SECTORS_PER_BLOCK 3 /* 4KB: F2FS_BLKSIZE */ #define F2FS_BLKSIZE 4096 /* support only 4KB block */ #define F2FS_MAX_EXTENSION 64 /* # of extension entries */ +#define F2FS_BLK_ALIGN(x) (((x) + F2FS_BLKSIZE - 1) / F2FS_BLKSIZE) #define NULL_ADDR 0x0U #define NEW_ADDR -1U @@ -283,6 +350,7 @@ struct f2fs_super_block { /* * For checkpoint */ +#define CP_FSCK_FLAG 0x00000010 #define CP_ERROR_FLAG 0x00000008 #define CP_COMPACT_SUM_FLAG 0x00000004 #define CP_ORPHAN_PRESENT_FLAG 0x00000002 @@ -357,6 +425,9 @@ struct f2fs_extent { #define F2FS_INLINE_XATTR 0x01 /* file inline xattr flag */ #define F2FS_INLINE_DATA 0x02 /* file inline data flag */ +#define F2FS_INLINE_DENTRY 0x04 /* file inline dentry flag */ +#define F2FS_DATA_EXIST 0x08 /* file inline data exist flag */ + #define MAX_INLINE_DATA (sizeof(__le32) * (DEF_ADDRS_PER_INODE - \ F2FS_INLINE_XATTR_ADDRS - 1)) @@ -412,6 +483,9 @@ enum { OFFSET_BIT_SHIFT }; +#define XATTR_NODE_OFFSET ((((unsigned int)-1) << OFFSET_BIT_SHIFT) \ + >> OFFSET_BIT_SHIFT) + struct node_footer { __le32 nid; /* node id */ __le32 ino; /* inode nunmber */ @@ -456,6 +530,13 @@ struct f2fs_nat_block { #define SIT_ENTRY_PER_BLOCK (PAGE_CACHE_SIZE / sizeof(struct f2fs_sit_entry)) /* + * F2FS uses 4 bytes to represent block address. As a result, supported size of + * disk is 16 TB and it equals to 16 * 1024 * 1024 / 2 segments. + */ +#define F2FS_MAX_SEGMENT ((16 * 1024 * 1024) / 2) +#define MAX_SIT_BITMAP_SIZE ((F2FS_MAX_SEGMENT / SIT_ENTRY_PER_BLOCK) / 8) + +/* * Note that f2fs_sit_entry->vblocks has the following bit-field information. * [15:10] : allocation type such as CURSEG_XXXX_TYPE * [9:0] : valid block count @@ -619,6 +700,24 @@ struct f2fs_dentry_block { __u8 filename[NR_DENTRY_IN_BLOCK][F2FS_SLOT_LEN]; } __attribute__((packed)); +/* for inline dir */ +#define NR_INLINE_DENTRY (MAX_INLINE_DATA * BITS_PER_BYTE / \ + ((SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * \ + BITS_PER_BYTE + 1)) +#define INLINE_DENTRY_BITMAP_SIZE ((NR_INLINE_DENTRY + \ + BITS_PER_BYTE - 1) / BITS_PER_BYTE) +#define INLINE_RESERVED_SIZE (MAX_INLINE_DATA - \ + ((SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * \ + NR_INLINE_DENTRY + INLINE_DENTRY_BITMAP_SIZE)) + +/* inline directory entry structure */ +struct f2fs_inline_dentry { + __u8 dentry_bitmap[INLINE_DENTRY_BITMAP_SIZE]; + __u8 reserved[INLINE_RESERVED_SIZE]; + struct f2fs_dir_entry dentry[NR_INLINE_DENTRY]; + __u8 filename[NR_INLINE_DENTRY][F2FS_SLOT_LEN]; +} __packed; + /* file types used in inode_info->flags */ enum FILE_TYPE { F2FS_FT_UNKNOWN, @@ -632,6 +731,8 @@ enum FILE_TYPE { F2FS_FT_MAX, /* added for fsck */ F2FS_FT_ORPHAN, + F2FS_FT_XATTR, + F2FS_FT_LAST_FILE_TYPE = F2FS_FT_XATTR, }; /* from f2fs/segment.h */ @@ -651,7 +752,8 @@ extern int test_bit(unsigned int nr, const void * addr); extern int f2fs_test_bit(unsigned int, const char *); extern int f2fs_set_bit(unsigned int, char *); extern int f2fs_clear_bit(unsigned int, char *); -extern unsigned long find_next_bit(const unsigned long *, unsigned long, unsigned long); +extern unsigned long find_next_bit(const unsigned long *, + unsigned long, unsigned long); extern u_int32_t f2fs_cal_crc32(u_int32_t, void *, int); extern int f2fs_crc_valid(u_int32_t blk_crc, void *buf, int len); @@ -663,13 +765,15 @@ extern void f2fs_finalize_device(struct f2fs_configuration *); extern int dev_read(void *, __u64, size_t); extern int dev_write(void *, __u64, size_t); +extern int dev_write_block(void *, __u64); +extern int dev_write_dump(void *, __u64, size_t); /* All bytes in the buffer must be 0 use dev_fill(). */ extern int dev_fill(void *, __u64, size_t); extern int dev_read_block(void *, __u64); extern int dev_read_blocks(void *, __u64, __u32 ); -f2fs_hash_t f2fs_dentry_hash(const char *, int); +f2fs_hash_t f2fs_dentry_hash(const unsigned char *, int); extern struct f2fs_configuration config; diff --git a/lib/libf2fs.c b/lib/libf2fs.c index 6168c5c..73c551b 100644 --- a/lib/libf2fs.c +++ b/lib/libf2fs.c @@ -235,7 +235,8 @@ static void TEA_transform(unsigned int buf[4], unsigned int const in[]) } -static void str2hashbuf(const char *msg, int len, unsigned int *buf, int num) +static void str2hashbuf(const unsigned char *msg, int len, + unsigned int *buf, int num) { unsigned pad, val; int i; @@ -269,24 +270,17 @@ static void str2hashbuf(const char *msg, int len, unsigned int *buf, int num) * @param len name lenth * @return return on success hash value, errno on failure */ -f2fs_hash_t f2fs_dentry_hash(const char *name, int len) +f2fs_hash_t f2fs_dentry_hash(const unsigned char *name, int len) { __u32 hash; f2fs_hash_t f2fs_hash; - const char *p; + const unsigned char *p; __u32 in[8], buf[4]; /* special hash codes for special dentries */ - if (name[0] == '.') { - if (name[1] == '\0') { - f2fs_hash = F2FS_DOT_HASH; - goto exit; - } - if (name[1] == '.' && name[2] == '\0') { - f2fs_hash = F2FS_DDOT_HASH; - goto exit; - } - } + if ((len <= 2) && (name[0] == '.') && + (name[1] == '.' || name[1] == '\0')) + return 0; /* Initialize the default seed for the hash checksum functions */ buf[0] = 0x67452301; @@ -295,18 +289,17 @@ f2fs_hash_t f2fs_dentry_hash(const char *name, int len) buf[3] = 0x10325476; p = name; - while (len > 0) { + while (1) { str2hashbuf(p, len, in, 4); TEA_transform(buf, in); - len -= 16; p += 16; + if (len <= 16) + break; + len -= 16; } hash = buf[0]; - f2fs_hash = hash; -exit: - f2fs_hash &= ~F2FS_HASH_COL_BIT; - + f2fs_hash = cpu_to_le32(hash & ~F2FS_HASH_COL_BIT); return f2fs_hash; } @@ -429,6 +422,9 @@ int f2fs_get_device_info(struct f2fs_configuration *c) { int32_t fd = 0; uint32_t sector_size; +#ifndef BLKGETSIZE64 + uint32_t total_sectors; +#endif struct stat stat_buf; struct hd_geometry geom; u_int64_t wanted_total_sectors = c->total_sectors; @@ -461,11 +457,20 @@ int f2fs_get_device_info(struct f2fs_configuration *c) } } - if (ioctl(fd, BLKGETSIZE, &c->total_sectors) < 0) { +#ifdef BLKGETSIZE64 + if (ioctl(fd, BLKGETSIZE64, &c->total_sectors) < 0) { MSG(0, "\tError: Cannot get the device size\n"); return -1; } - + c->total_sectors /= c->sector_size; +#else + if (ioctl(fd, BLKGETSIZE, &total_sectors) < 0) { + MSG(0, "\tError: Cannot get the device size\n"); + return -1; + } + total_sectors /= c->sector_size; + c->total_sectors = total_sectors; +#endif if (ioctl(fd, HDIO_GETGEO, &geom) < 0) c->start_sector = 0; else diff --git a/lib/libf2fs_io.c b/lib/libf2fs_io.c index 5d9b68d..0c89ee4 100644 --- a/lib/libf2fs_io.c +++ b/lib/libf2fs_io.c @@ -46,6 +46,20 @@ int dev_write(void *buf, __u64 offset, size_t len) return 0; } +int dev_write_block(void *buf, __u64 blk_addr) +{ + return dev_write(buf, blk_addr * F2FS_BLKSIZE, F2FS_BLKSIZE); +} + +int dev_write_dump(void *buf, __u64 offset, size_t len) +{ + if (lseek64(config.dump_fd, (off64_t)offset, SEEK_SET) < 0) + return -1; + if (write(config.dump_fd, buf, len) < 0) + return -1; + return 0; +} + int dev_fill(void *buf, __u64 offset, size_t len) { /* Only allow fill to zero */ diff --git a/mkfs/Makefile.am b/mkfs/Makefile.am index ff136a7..fa48699 100644 --- a/mkfs/Makefile.am +++ b/mkfs/Makefile.am @@ -1,7 +1,7 @@ ## Makefile.am AM_CPPFLAGS = ${libuuid_CFLAGS} -I$(top_srcdir)/include -AM_CFLAGS = -Wall -DBLKDISCARD +AM_CFLAGS = -Wall -DWITH_BLKDISCARD sbin_PROGRAMS = mkfs.f2fs mkfs_f2fs_SOURCES = f2fs_format_main.c f2fs_format.c f2fs_format_utils.c mkfs_f2fs_LDADD = ${libuuid_LIBS} $(top_builddir)/lib/libf2fs.la diff --git a/mkfs/f2fs_format.c b/mkfs/f2fs_format.c index 1568545..e300731 100644 --- a/mkfs/f2fs_format.c +++ b/mkfs/f2fs_format.c @@ -101,7 +101,8 @@ static int f2fs_prepare_super_block(void) u_int32_t blocks_for_sit, blocks_for_nat, blocks_for_ssa; u_int32_t total_valid_blks_available; u_int64_t zone_align_start_offset, diff, total_meta_segments; - u_int32_t sit_bitmap_size, max_nat_bitmap_size, max_nat_segments; + u_int32_t sit_bitmap_size, max_sit_bitmap_size; + u_int32_t max_nat_bitmap_size, max_nat_segments; u_int32_t total_zones; super_block.magic = cpu_to_le32(F2FS_SUPER_MAGIC); @@ -197,8 +198,26 @@ static int f2fs_prepare_super_block(void) */ sit_bitmap_size = ((le32_to_cpu(super_block.segment_count_sit) / 2) << log_blks_per_seg) / 8; - max_nat_bitmap_size = CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 1 - - sit_bitmap_size; + + if (sit_bitmap_size > MAX_SIT_BITMAP_SIZE) + max_sit_bitmap_size = MAX_SIT_BITMAP_SIZE; + else + max_sit_bitmap_size = sit_bitmap_size; + + /* + * It should be reserved minimum 1 segment for nat. + * When sit is too large, we should expand cp area. It requires more pages for cp. + */ + if (max_sit_bitmap_size > + (CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 65)) { + max_nat_bitmap_size = CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 1; + super_block.cp_payload = F2FS_BLK_ALIGN(max_sit_bitmap_size); + } else { + max_nat_bitmap_size = CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 1 + - max_sit_bitmap_size; + super_block.cp_payload = 0; + } + max_nat_segments = (max_nat_bitmap_size * 8) >> log_blks_per_seg; if (le32_to_cpu(super_block.segment_count_nat) > max_nat_segments) @@ -413,7 +432,8 @@ static int f2fs_write_check_point_pack(void) u_int32_t blk_size_bytes; u_int64_t cp_seg_blk_offset = 0; u_int32_t crc = 0; - int i; + unsigned int i; + char *cp_payload = NULL; ckp = calloc(F2FS_BLKSIZE, 1); if (ckp == NULL) { @@ -427,6 +447,12 @@ static int f2fs_write_check_point_pack(void) return -1; } + cp_payload = calloc(F2FS_BLKSIZE, 1); + if (cp_payload == NULL) { + MSG(1, "\tError: Calloc Failed for cp_payload!!!\n"); + return -1; + } + /* 1. cp page 1 of checkpoint pack 1 */ ckp->checkpoint_ver = cpu_to_le64(1); ckp->cur_node_segno[0] = @@ -465,9 +491,10 @@ static int f2fs_write_check_point_pack(void) ((le32_to_cpu(ckp->free_segment_count) + 6 - le32_to_cpu(ckp->overprov_segment_count)) * config.blks_per_seg)); - ckp->cp_pack_total_block_count = cpu_to_le32(8); + ckp->cp_pack_total_block_count = + cpu_to_le32(8 + le32_to_cpu(super_block.cp_payload)); ckp->ckpt_flags = cpu_to_le32(CP_UMOUNT_FLAG); - ckp->cp_pack_start_sum = cpu_to_le32(1); + ckp->cp_pack_start_sum = cpu_to_le32(1 + le32_to_cpu(super_block.cp_payload)); ckp->valid_node_count = cpu_to_le32(1); ckp->valid_inode_count = cpu_to_le32(1); ckp->next_free_nid = cpu_to_le32( @@ -491,11 +518,20 @@ static int f2fs_write_check_point_pack(void) cp_seg_blk_offset *= blk_size_bytes; DBG(1, "\tWriting main segments, ckp at offset 0x%08"PRIx64"\n", cp_seg_blk_offset); - if (dev_write(ckp, cp_seg_blk_offset, F2FS_BLKSIZE)) { + if (dev_write(ckp, cp_seg_blk_offset, blk_size_bytes)) { MSG(1, "\tError: While writing the ckp to disk!!!\n"); return -1; } + for (i = 0; i < le32_to_cpu(super_block.cp_payload); i++) { + cp_seg_blk_offset += blk_size_bytes; + if (dev_fill(cp_payload, cp_seg_blk_offset, blk_size_bytes)) { + MSG(1, "\tError: While zeroing out the sit bitmap area \ + on disk!!!\n"); + return -1; + } + } + /* 2. Prepare and write Segment summary for data blocks */ memset(sum, 0, sizeof(struct f2fs_summary_block)); SET_SUM_TYPE((&sum->footer), SUM_TYPE_DATA); @@ -505,7 +541,7 @@ static int f2fs_write_check_point_pack(void) cp_seg_blk_offset += blk_size_bytes; DBG(1, "\tWriting segment summary for data, ckp at offset 0x%08"PRIx64"\n", cp_seg_blk_offset); - if (dev_write(sum, cp_seg_blk_offset, F2FS_BLKSIZE)) { + if (dev_write(sum, cp_seg_blk_offset, blk_size_bytes)) { MSG(1, "\tError: While writing the sum_blk to disk!!!\n"); return -1; } @@ -516,7 +552,7 @@ static int f2fs_write_check_point_pack(void) cp_seg_blk_offset += blk_size_bytes; DBG(1, "\tWriting segment summary, ckp at offset 0x%08"PRIx64"\n", cp_seg_blk_offset); - if (dev_write(sum, cp_seg_blk_offset, F2FS_BLKSIZE)) { + if (dev_write(sum, cp_seg_blk_offset, blk_size_bytes)) { MSG(1, "\tError: While writing the sum_blk to disk!!!\n"); return -1; } @@ -546,7 +582,7 @@ static int f2fs_write_check_point_pack(void) cp_seg_blk_offset += blk_size_bytes; DBG(1, "\tWriting data sit for root, at offset 0x%08"PRIx64"\n", cp_seg_blk_offset); - if (dev_write(sum, cp_seg_blk_offset, F2FS_BLKSIZE)) { + if (dev_write(sum, cp_seg_blk_offset, blk_size_bytes)) { MSG(1, "\tError: While writing the sum_blk to disk!!!\n"); return -1; } @@ -560,7 +596,7 @@ static int f2fs_write_check_point_pack(void) cp_seg_blk_offset += blk_size_bytes; DBG(1, "\tWriting Segment summary for node blocks, at offset 0x%08"PRIx64"\n", cp_seg_blk_offset); - if (dev_write(sum, cp_seg_blk_offset, F2FS_BLKSIZE)) { + if (dev_write(sum, cp_seg_blk_offset, blk_size_bytes)) { MSG(1, "\tError: While writing the sum_blk to disk!!!\n"); return -1; } @@ -571,7 +607,7 @@ static int f2fs_write_check_point_pack(void) cp_seg_blk_offset += blk_size_bytes; DBG(1, "\tWriting Segment summary for data block (1/2), at offset 0x%08"PRIx64"\n", cp_seg_blk_offset); - if (dev_write(sum, cp_seg_blk_offset, F2FS_BLKSIZE)) { + if (dev_write(sum, cp_seg_blk_offset, blk_size_bytes)) { MSG(1, "\tError: While writing the sum_blk to disk!!!\n"); return -1; } @@ -581,7 +617,7 @@ static int f2fs_write_check_point_pack(void) SET_SUM_TYPE((&sum->footer), SUM_TYPE_NODE); cp_seg_blk_offset += blk_size_bytes; DBG(1, "\tWriting Segment summary for data block (2/2), at offset 0x%08"PRIx64"\n", cp_seg_blk_offset); - if (dev_write(sum, cp_seg_blk_offset, F2FS_BLKSIZE)) { + if (dev_write(sum, cp_seg_blk_offset, blk_size_bytes)) { MSG(1, "\tError: While writing the sum_blk to disk!!!\n"); return -1; } @@ -589,7 +625,7 @@ static int f2fs_write_check_point_pack(void) /* 8. cp page2 */ cp_seg_blk_offset += blk_size_bytes; DBG(1, "\tWriting cp page2, at offset 0x%08"PRIx64"\n", cp_seg_blk_offset); - if (dev_write(ckp, cp_seg_blk_offset, F2FS_BLKSIZE)) { + if (dev_write(ckp, cp_seg_blk_offset, blk_size_bytes)) { MSG(1, "\tError: While writing the ckp to disk!!!\n"); return -1; } @@ -606,21 +642,32 @@ static int f2fs_write_check_point_pack(void) config.blks_per_seg) * blk_size_bytes; DBG(1, "\tWriting cp page 1 of checkpoint pack 2, at offset 0x%08"PRIx64"\n", cp_seg_blk_offset); - if (dev_write(ckp, cp_seg_blk_offset, F2FS_BLKSIZE)) { + if (dev_write(ckp, cp_seg_blk_offset, blk_size_bytes)) { MSG(1, "\tError: While writing the ckp to disk!!!\n"); return -1; } + for (i = 0; i < le32_to_cpu(super_block.cp_payload); i++) { + cp_seg_blk_offset += blk_size_bytes; + if (dev_fill(cp_payload, cp_seg_blk_offset, blk_size_bytes)) { + MSG(1, "\tError: While zeroing out the sit bitmap area \ + on disk!!!\n"); + return -1; + } + } + /* 10. cp page 2 of check point pack 2 */ - cp_seg_blk_offset += blk_size_bytes * (le32_to_cpu(ckp->cp_pack_total_block_count) - 1); + cp_seg_blk_offset += blk_size_bytes * (le32_to_cpu(ckp->cp_pack_total_block_count) + - le32_to_cpu(super_block.cp_payload) - 1); DBG(1, "\tWriting cp page 2 of checkpoint pack 2, at offset 0x%08"PRIx64"\n", cp_seg_blk_offset); - if (dev_write(ckp, cp_seg_blk_offset, F2FS_BLKSIZE)) { + if (dev_write(ckp, cp_seg_blk_offset, blk_size_bytes)) { MSG(1, "\tError: While writing the ckp to disk!!!\n"); return -1; } free(sum) ; free(ckp) ; + free(cp_payload); return 0; } diff --git a/mkfs/f2fs_format_main.c b/mkfs/f2fs_format_main.c index 19c52e4..e1f7c69 100644 --- a/mkfs/f2fs_format_main.c +++ b/mkfs/f2fs_format_main.c @@ -103,7 +103,8 @@ static void f2fs_parse_options(int argc, char *argv[]) if ((optind + 1) < argc) { /* We have a sector count. */ config.total_sectors = atoll(argv[optind+1]); - MSG(0, "\ttotal_sectors=%lu (%s bytes)\n", config.total_sectors, argv[optind+1]); + MSG(0, "\ttotal_sectors=%08"PRIx64" (%s bytes)\n", + config.total_sectors, argv[optind+1]); } config.reserved_segments = diff --git a/mkfs/f2fs_format_utils.c b/mkfs/f2fs_format_utils.c index 8f7e094..9892a8f 100644 --- a/mkfs/f2fs_format_utils.c +++ b/mkfs/f2fs_format_utils.c @@ -15,6 +15,10 @@ #include "f2fs_fs.h" +#ifdef HAVE_LINUX_FS_H +#include <linux/fs.h> +#endif + int f2fs_trim_device() { unsigned long long range[2]; @@ -31,7 +35,7 @@ int f2fs_trim_device() return -1; } -#if defined(BLKDISCARD) +#if defined(WITH_BLKDISCARD) && defined(BLKDISCARD) MSG(0, "Info: Discarding device\n"); if (S_ISREG(stat_buf.st_mode)) return 0; diff --git a/scripts/tracepoint.sh b/scripts/tracepoint.sh index b0d2fcb..15588d7 100755 --- a/scripts/tracepoint.sh +++ b/scripts/tracepoint.sh @@ -1,13 +1,16 @@ -#!/bin/bash +#!/system/bin/sh TRACE=/sys/kernel/debug/tracing/ dev=$(((8<<20) + 17)) # sdb1 (8,17) -echo 1 > tracing_on +echo 1 > $TRACE/tracing_on + +# mmc tracepoints +echo 0 > $TRACE/events/mmc/enable # block tracepoints -echo "dev == $dev" > $TRACE/events/block/block_rq_complete/filter -echo 1 > $TRACE/events/block/block_rq_complete/enable +#echo "dev == $dev" > $TRACE/events/block/block_rq_complete/filter +echo 0 > $TRACE/events/block/block_rq_complete/enable echo 0 > $TRACE/events/block/block_bio_complete/enable # GC @@ -41,9 +44,18 @@ echo $S > $TRACE/events/f2fs/f2fs_get_data_block/enable # IOs R=0 W=0 -echo $W > $TRACE/events/f2fs/f2fs_submit_write_page/enable -echo $W > $TRACE/events/f2fs/f2fs_do_submit_bio/enable echo $R > $TRACE/events/f2fs/f2fs_readpage/enable +echo $W > $TRACE/events/f2fs/f2fs_writepage/enable +echo $W > $TRACE/events/f2fs/f2fs_write_begin/enable +echo $W > $TRACE/events/f2fs/f2fs_write_end/enable + +echo 0 > $TRACE/events/f2fs/f2fs_submit_page_bio/enable +echo 0 > $TRACE/events/f2fs/f2fs_submit_page_mbio/enable +echo $R > $TRACE/events/f2fs/f2fs_submit_read_bio/enable +echo $W > $TRACE/events/f2fs/f2fs_submit_write_bio/enable + +echo 0 > $TRACE/events/f2fs/f2fs_issue_discard/enable +echo 0 > $TRACE/events/f2fs/f2fs_issue_flush/enable # VFS interfaces V=0 diff --git a/tools/f2fstat.c b/tools/f2fstat.c index c9c1d30..8643797 100644 --- a/tools/f2fstat.c +++ b/tools/f2fstat.c @@ -212,15 +212,72 @@ void parse_option(int argc, char *argv[], struct options *opt) } } -void print_head(void) +void __make_head(char *head, int index, int i, int len) { - fprintf(stderr, "---utilization--- -----------main area-------- ---------balancing async------- ---gc--- ---alloc--- -----memory-----\n"); - fprintf(stderr, "util node data free valid dirty prefree node dent meta sit nat fnid cp gc ssr lfs total node meta\n"); + char name_h[5][20] = {"main segments", "page/slab caches", "cp/gc", "blks", "memory"}; + int half = (len - strlen(name_h[i])) / 2; + + *(head + index) = '|'; + index++; + memset(head + index, '-', half); + index += half; + strcpy(head + index, name_h[i]); + index += strlen(name_h[i]); + memset(head + index, '-', half); +} + +void print_head(char *res) +{ + char *ptr, *ptr_buf; + char buf[1024], head[1024]; + char name[20][10] = {"util", "node", "data", "free", "valid", "dirty", "prefree", "node", "dent", "meta", + "sit", "nat", "fnid", "cp", "gc", "ssr", "lfs", "total", "node", "meta"}; + int i, len, prev_index = 0; + + ptr_buf = buf; + memset(buf, ' ', 1024); + memset(head, ' ', 1024); + + for (i = 0; i < 20; i++) { + ptr = (i == 0) ? strtok(res, " ") : strtok(NULL, " "); + strncpy(ptr_buf, name[i], strlen(name[i])); + if (i == 1) { + prev_index = ptr_buf - buf - 1; + } else if (i == 7) { + len = (ptr_buf - buf) - 1 - prev_index; + __make_head(head, prev_index, 0, len); + prev_index = ptr_buf - buf - 1; + } else if (i == 13) { + len = (ptr_buf - buf) - 1 - prev_index; + __make_head(head, prev_index, 1, len); + prev_index = ptr_buf - buf - 1; + } else if (i == 15) { + len = (ptr_buf - buf) - 1 - prev_index; + __make_head(head, prev_index, 2, len); + prev_index = ptr_buf - buf - 1; + } else if (i == 17) { + len = (ptr_buf - buf) - 1 - prev_index; + __make_head(head, prev_index, 3, len); + prev_index = ptr_buf - buf - 1; + } + + len = strlen(ptr); + ptr_buf += (len > strlen(name[i]) ? len : strlen(name[i])) + 1; + } + + len = (ptr_buf - buf) - 1 - prev_index; + __make_head(head, prev_index, 4, len); + + *ptr_buf = 0; + *(head + (ptr_buf - buf - 1)) = '|'; + *(head + (ptr_buf - buf)) = 0; + fprintf(stderr, "%s\n%s\n", head, buf); } int main(int argc, char *argv[]) { - char format[] = "%3ld %6ld %6ld %6ld %6ld %6ld %6ld %5ld %5ld %3ld %5ld %5ld %3ld %3ld %3ld %6ld %6ld %6ld %6ld %6ld\n"; + char format[] = "%4ld %4ld %4ld %4ld %5ld %5ld %7ld %4ld %4ld %4ld %3ld %3ld %4ld %2ld %2ld %3ld %3ld %5ld %4ld %4ld"; + char buf[1024], tmp[1024]; int head_interval; struct options opt = { .delay = 1, @@ -231,20 +288,22 @@ int main(int argc, char *argv[]) parse_option(argc, argv, &opt); head_interval = opt.interval; - print_head(); while (1) { - if (head_interval-- == 0) { - print_head(); - head_interval = opt.interval; - } - + memset(buf, 0, 1024); f2fstat(&opt); - - fprintf(stderr, format, util, used_node_blks, used_data_blks, + sprintf(buf, format, util, used_node_blks, used_data_blks, free_segs, valid_segs, dirty_segs, prefree_segs, dirty_node, dirty_dents, dirty_meta, dirty_sit, nat_caches, free_nids, cp, gc, ssr_blks, lfs_blks, memory_kb, node_kb, meta_kb); + strcpy(tmp, buf); + if (head_interval == opt.interval) + print_head(tmp); + if (head_interval-- == 0) + head_interval = opt.interval; + + fprintf(stderr, "%s\n", buf); + sleep(opt.delay); } |