fsck.f2fs: check sanity of superblock and fix any misalignment
This patch detects any corrupted superblock and fix misalignment when it finds,
which is synced with the f2fs kernel module.
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
diff --git a/fsck/mount.c b/fsck/mount.c
index 37a0025..67c681e 100644
--- a/fsck/mount.c
+++ b/fsck/mount.c
@@ -265,7 +265,90 @@
MSG(0, "\n");
}
-int sanity_check_raw_super(struct f2fs_super_block *sb)
+static inline int sanity_check_area_boundary(struct f2fs_super_block *sb,
+ u64 offset)
+{
+ u32 segment0_blkaddr = get_sb(segment0_blkaddr);
+ u32 cp_blkaddr = get_sb(cp_blkaddr);
+ u32 sit_blkaddr = get_sb(sit_blkaddr);
+ u32 nat_blkaddr = get_sb(nat_blkaddr);
+ u32 ssa_blkaddr = get_sb(ssa_blkaddr);
+ u32 main_blkaddr = get_sb(main_blkaddr);
+ u32 segment_count_ckpt = get_sb(segment_count_ckpt);
+ u32 segment_count_sit = get_sb(segment_count_sit);
+ u32 segment_count_nat = get_sb(segment_count_nat);
+ u32 segment_count_ssa = get_sb(segment_count_ssa);
+ u32 segment_count_main = get_sb(segment_count_main);
+ u32 segment_count = get_sb(segment_count);
+ u32 log_blocks_per_seg = get_sb(log_blocks_per_seg);
+ u64 main_end_blkaddr = main_blkaddr +
+ (segment_count_main << log_blocks_per_seg);
+ u64 seg_end_blkaddr = segment0_blkaddr +
+ (segment_count << log_blocks_per_seg);
+
+ if (segment0_blkaddr != cp_blkaddr) {
+ MSG(0, "\tMismatch segment0(%u) cp_blkaddr(%u)\n",
+ segment0_blkaddr, cp_blkaddr);
+ return -1;
+ }
+
+ if (cp_blkaddr + (segment_count_ckpt << log_blocks_per_seg) !=
+ sit_blkaddr) {
+ MSG(0, "\tWrong CP boundary, start(%u) end(%u) blocks(%u)\n",
+ cp_blkaddr, sit_blkaddr,
+ segment_count_ckpt << log_blocks_per_seg);
+ return -1;
+ }
+
+ if (sit_blkaddr + (segment_count_sit << log_blocks_per_seg) !=
+ nat_blkaddr) {
+ MSG(0, "\tWrong SIT boundary, start(%u) end(%u) blocks(%u)\n",
+ sit_blkaddr, nat_blkaddr,
+ segment_count_sit << log_blocks_per_seg);
+ return -1;
+ }
+
+ if (nat_blkaddr + (segment_count_nat << log_blocks_per_seg) !=
+ ssa_blkaddr) {
+ MSG(0, "\tWrong NAT boundary, start(%u) end(%u) blocks(%u)\n",
+ nat_blkaddr, ssa_blkaddr,
+ segment_count_nat << log_blocks_per_seg);
+ return -1;
+ }
+
+ if (ssa_blkaddr + (segment_count_ssa << log_blocks_per_seg) !=
+ main_blkaddr) {
+ MSG(0, "\tWrong SSA boundary, start(%u) end(%u) blocks(%u)\n",
+ ssa_blkaddr, main_blkaddr,
+ segment_count_ssa << log_blocks_per_seg);
+ return -1;
+ }
+
+ if (main_end_blkaddr > seg_end_blkaddr) {
+ MSG(0, "\tWrong MAIN_AREA, start(%u) end(%u) block(%u)\n",
+ main_blkaddr,
+ segment0_blkaddr +
+ (segment_count << log_blocks_per_seg),
+ segment_count_main << log_blocks_per_seg);
+ return -1;
+ } else if (main_end_blkaddr < seg_end_blkaddr) {
+ int err;
+
+ set_sb(segment_count, (main_end_blkaddr -
+ segment0_blkaddr) >> log_blocks_per_seg);
+
+ err = dev_write(sb, offset, sizeof(struct f2fs_super_block));
+ MSG(0, "Info: Fix alignment: %s, start(%u) end(%u) block(%u)\n",
+ err ? "failed": "done",
+ main_blkaddr,
+ segment0_blkaddr +
+ (segment_count << log_blocks_per_seg),
+ segment_count_main << log_blocks_per_seg);
+ }
+ return 0;
+}
+
+int sanity_check_raw_super(struct f2fs_super_block *sb, u64 offset)
{
unsigned int blocksize;
@@ -279,6 +362,15 @@
if (F2FS_BLKSIZE != blocksize)
return -1;
+ /* check log blocks per segment */
+ if (get_sb(log_blocks_per_seg) != 9)
+ return -1;
+
+ if (get_sb(log_sectorsize) > F2FS_MAX_LOG_SECTOR_SIZE ||
+ get_sb(log_sectorsize) < F2FS_MIN_LOG_SECTOR_SIZE)
+ return -1;
+
+ /* Currently, support 512/1024/2048/4096 bytes sector size */
if (get_sb(log_sectorsize) > F2FS_MAX_LOG_SECTOR_SIZE ||
get_sb(log_sectorsize) < F2FS_MIN_LOG_SECTOR_SIZE)
return -1;
@@ -287,6 +379,13 @@
F2FS_MAX_LOG_SECTOR_SIZE)
return -1;
+ /* check reserved ino info */
+ if (get_sb(node_ino) != 1 || get_sb(meta_ino) != 2 ||
+ get_sb(root_ino) != 3)
+ return -1;
+
+ if (sanity_check_area_boundary(sb, offset))
+ return -1;
return 0;
}
@@ -304,7 +403,7 @@
if (dev_read(sbi->raw_super, offset, sizeof(struct f2fs_super_block)))
return -1;
- if (!sanity_check_raw_super(sbi->raw_super)) {
+ if (!sanity_check_raw_super(sbi->raw_super, offset)) {
/* get kernel version */
if (config.kd >= 0) {
dev_read_version(config.version, 0, VERSION_LEN);