f2fs: relocate chksum_offset for large_nat_bitmap feature
authorChao Yu <yuchao0@huawei.com>
Mon, 22 Apr 2019 09:33:53 +0000 (17:33 +0800)
committerJaegeuk Kim <jaegeuk@kernel.org>
Thu, 9 May 2019 04:23:11 +0000 (21:23 -0700)
For large_nat_bitmap feature, there is a design flaw:

Previous:

struct f2fs_checkpoint layout:
+--------------------------+  0x0000
| checkpoint_ver           |
| ......                   |
| checksum_offset          |------+
| ......                   |      |
| sit_nat_version_bitmap[] |<-----|-------+
| ......                   |      |       |
| checksum_value           |<-----+       |
+--------------------------+  0x1000      |
|                          |      nat_bitmap + sit_bitmap
| payload blocks           |              |
|                          |              |
+--------------------------|<-------------+

Obviously, if nat_bitmap size + sit_bitmap size is larger than
MAX_BITMAP_SIZE_IN_CKPT, nat_bitmap or sit_bitmap may overlap
checkpoint checksum's position, once checkpoint() is triggered
from kernel, nat or sit bitmap will be damaged by checksum field.

In order to fix this, let's relocate checksum_value's position
to the head of sit_nat_version_bitmap as below, then nat/sit
bitmap and chksum value update will become safe.

After:

struct f2fs_checkpoint layout:
+--------------------------+  0x0000
| checkpoint_ver           |
| ......                   |
| checksum_offset          |------+
| ......                   |      |
| sit_nat_version_bitmap[] |<-----+
| ......                   |<-------------+
|                          |              |
+--------------------------+  0x1000      |
|                          |      nat_bitmap + sit_bitmap
| payload blocks           |              |
|                          |              |
+--------------------------|<-------------+

Related report and discussion:

https://sourceforge.net/p/linux-f2fs/mailman/message/36642346/

Reported-by: Park Ju Hyung <qkrwngud825@gmail.com>
Signed-off-by: Chao Yu <yuchao0@huawei.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
fs/f2fs/checkpoint.c
fs/f2fs/f2fs.h

index af77655e65b2c53098c16a0c8678bf8f60aff066..d52a15799488c8096ce03171fa0f1cd7caf76d5c 100644 (file)
@@ -795,6 +795,17 @@ static int get_checkpoint_version(struct f2fs_sb_info *sbi, block_t cp_addr,
                return -EINVAL;
        }
 
+       if (__is_set_ckpt_flags(*cp_block, CP_LARGE_NAT_BITMAP_FLAG)) {
+               if (crc_offset != CP_MIN_CHKSUM_OFFSET) {
+                       f2fs_put_page(*cp_page, 1);
+                       f2fs_msg(sbi->sb, KERN_WARNING,
+                               "layout of large_nat_bitmap is deprecated, "
+                               "run fsck to repair, chksum_offset: %zu",
+                               crc_offset);
+                       return -EINVAL;
+               }
+       }
+
        crc = f2fs_checkpoint_chksum(sbi, *cp_block);
        if (crc != cur_cp_crc(*cp_block)) {
                f2fs_put_page(*cp_page, 1);
index 9a9dc20e295b6dba0b6c040653ab7c2a9af24eb9..97e4ef71de64491c752efdadac47e60630e0682c 100644 (file)
@@ -1910,7 +1910,11 @@ static inline void *__bitmap_ptr(struct f2fs_sb_info *sbi, int flag)
        if (is_set_ckpt_flags(sbi, CP_LARGE_NAT_BITMAP_FLAG)) {
                offset = (flag == SIT_BITMAP) ?
                        le32_to_cpu(ckpt->nat_ver_bitmap_bytesize) : 0;
-               return &ckpt->sit_nat_version_bitmap + offset;
+               /*
+                * if large_nat_bitmap feature is enabled, leave checksum
+                * protection for all nat/sit bitmaps.
+                */
+               return &ckpt->sit_nat_version_bitmap + offset + sizeof(__le32);
        }
 
        if (__cp_payload(sbi) > 0) {