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);