f2fs: support inode checksum
authorChao Yu <yuchao0@huawei.com>
Mon, 31 Jul 2017 12:19:09 +0000 (20:19 +0800)
committerJaegeuk Kim <jaegeuk@kernel.org>
Fri, 4 Aug 2017 02:09:26 +0000 (19:09 -0700)
This patch adds to support inode checksum in f2fs.

Signed-off-by: Chao Yu <yuchao0@huawei.com>
[Jaegeuk Kim: fix verification flow]
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
fs/f2fs/f2fs.h
fs/f2fs/inode.c
fs/f2fs/node.c
fs/f2fs/segment.c
fs/f2fs/super.c
include/linux/f2fs_fs.h

index 1c31e4e..7c1244b 100644 (file)
@@ -116,6 +116,7 @@ struct f2fs_mount_info {
 #define F2FS_FEATURE_ATOMIC_WRITE      0x0004
 #define F2FS_FEATURE_EXTRA_ATTR                0x0008
 #define F2FS_FEATURE_PRJQUOTA          0x0010
+#define F2FS_FEATURE_INODE_CHKSUM      0x0020
 
 #define F2FS_HAS_FEATURE(sb, mask)                                     \
        ((F2FS_SB(sb)->raw_super->feature & cpu_to_le32(mask)) != 0)
@@ -1085,6 +1086,9 @@ struct f2fs_sb_info {
        /* Reference to checksum algorithm driver via cryptoapi */
        struct crypto_shash *s_chksum_driver;
 
+       /* Precomputed FS UUID checksum for seeding other checksums */
+       __u32 s_chksum_seed;
+
        /* For fault injection */
 #ifdef CONFIG_F2FS_FAULT_INJECTION
        struct f2fs_fault_info fault_info;
@@ -1176,6 +1180,27 @@ static inline bool f2fs_crc_valid(struct f2fs_sb_info *sbi, __u32 blk_crc,
        return f2fs_crc32(sbi, buf, buf_size) == blk_crc;
 }
 
+static inline u32 f2fs_chksum(struct f2fs_sb_info *sbi, u32 crc,
+                             const void *address, unsigned int length)
+{
+       struct {
+               struct shash_desc shash;
+               char ctx[4];
+       } desc;
+       int err;
+
+       BUG_ON(crypto_shash_descsize(sbi->s_chksum_driver) != sizeof(desc.ctx));
+
+       desc.shash.tfm = sbi->s_chksum_driver;
+       desc.shash.flags = 0;
+       *(u32 *)desc.ctx = crc;
+
+       err = crypto_shash_update(&desc.shash, address, length);
+       BUG_ON(err);
+
+       return *(u32 *)desc.ctx;
+}
+
 static inline struct f2fs_inode_info *F2FS_I(struct inode *inode)
 {
        return container_of(inode, struct f2fs_inode_info, vfs_inode);
@@ -2285,6 +2310,8 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
  * inode.c
  */
 void f2fs_set_inode_flags(struct inode *inode);
+bool f2fs_inode_chksum_verify(struct f2fs_sb_info *sbi, struct page *page);
+void f2fs_inode_chksum_set(struct f2fs_sb_info *sbi, struct page *page);
 struct inode *f2fs_iget(struct super_block *sb, unsigned long ino);
 struct inode *f2fs_iget_retry(struct super_block *sb, unsigned long ino);
 int try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink);
@@ -2869,6 +2896,11 @@ static inline int f2fs_sb_has_project_quota(struct super_block *sb)
        return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_PRJQUOTA);
 }
 
+static inline int f2fs_sb_has_inode_chksum(struct super_block *sb)
+{
+       return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_INODE_CHKSUM);
+}
+
 #ifdef CONFIG_BLK_DEV_ZONED
 static inline int get_blkz_type(struct f2fs_sb_info *sbi,
                        struct block_device *bdev, block_t blkaddr)
index f15e663..b4c401d 100644 (file)
@@ -108,6 +108,76 @@ static void __recover_inline_status(struct inode *inode, struct page *ipage)
        return;
 }
 
+static bool f2fs_enable_inode_chksum(struct f2fs_sb_info *sbi, struct page *page)
+{
+       struct f2fs_inode *ri = &F2FS_NODE(page)->i;
+       int extra_isize = le32_to_cpu(ri->i_extra_isize);
+
+       if (!f2fs_sb_has_inode_chksum(sbi->sb))
+               return false;
+
+       if (!RAW_IS_INODE(F2FS_NODE(page)) || !(ri->i_inline & F2FS_EXTRA_ATTR))
+               return false;
+
+       if (!F2FS_FITS_IN_INODE(ri, extra_isize, i_inode_checksum))
+               return false;
+
+       return true;
+}
+
+static __u32 f2fs_inode_chksum(struct f2fs_sb_info *sbi, struct page *page)
+{
+       struct f2fs_node *node = F2FS_NODE(page);
+       struct f2fs_inode *ri = &node->i;
+       __le32 ino = node->footer.ino;
+       __le32 gen = ri->i_generation;
+       __u32 chksum, chksum_seed;
+       __u32 dummy_cs = 0;
+       unsigned int offset = offsetof(struct f2fs_inode, i_inode_checksum);
+       unsigned int cs_size = sizeof(dummy_cs);
+
+       chksum = f2fs_chksum(sbi, sbi->s_chksum_seed, (__u8 *)&ino,
+                                                       sizeof(ino));
+       chksum_seed = f2fs_chksum(sbi, chksum, (__u8 *)&gen, sizeof(gen));
+
+       chksum = f2fs_chksum(sbi, chksum_seed, (__u8 *)ri, offset);
+       chksum = f2fs_chksum(sbi, chksum, (__u8 *)&dummy_cs, cs_size);
+       offset += cs_size;
+       chksum = f2fs_chksum(sbi, chksum, (__u8 *)ri + offset,
+                                               F2FS_BLKSIZE - offset);
+       return chksum;
+}
+
+bool f2fs_inode_chksum_verify(struct f2fs_sb_info *sbi, struct page *page)
+{
+       struct f2fs_inode *ri;
+       __u32 provided, calculated;
+
+       if (!f2fs_enable_inode_chksum(sbi, page))
+               return true;
+
+       ri = &F2FS_NODE(page)->i;
+       provided = le32_to_cpu(ri->i_inode_checksum);
+       calculated = f2fs_inode_chksum(sbi, page);
+
+       if (provided != calculated)
+               f2fs_msg(sbi->sb, KERN_WARNING,
+                       "checksum invalid, ino = %x, %x vs. %x",
+                       ino_of_node(page), provided, calculated);
+
+       return provided == calculated;
+}
+
+void f2fs_inode_chksum_set(struct f2fs_sb_info *sbi, struct page *page)
+{
+       struct f2fs_inode *ri = &F2FS_NODE(page)->i;
+
+       if (!f2fs_enable_inode_chksum(sbi, page))
+               return;
+
+       ri->i_inode_checksum = cpu_to_le32(f2fs_inode_chksum(sbi, page));
+}
+
 static int do_read_inode(struct inode *inode)
 {
        struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
index 14b79a3..0176035 100644 (file)
@@ -1171,6 +1171,11 @@ repeat:
                err = -EIO;
                goto out_err;
        }
+
+       if (!f2fs_inode_chksum_verify(sbi, page)) {
+               err = -EBADMSG;
+               goto out_err;
+       }
 page_hit:
        if(unlikely(nid != nid_of_node(page))) {
                f2fs_msg(sbi->sb, KERN_WARNING, "inconsistent node block, "
@@ -2275,6 +2280,8 @@ retry:
                        F2FS_FITS_IN_INODE(src, le16_to_cpu(src->i_extra_isize),
                                                                i_projid))
                        dst->i_projid = src->i_projid;
+
+               f2fs_inode_chksum_set(sbi, ipage);
        }
 
        new_ni = old_ni;
index 2a5672a..8911f50 100644 (file)
@@ -2214,9 +2214,12 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page,
 
        mutex_unlock(&sit_i->sentry_lock);
 
-       if (page && IS_NODESEG(type))
+       if (page && IS_NODESEG(type)) {
                fill_node_footer_blkaddr(page, NEXT_FREE_BLKADDR(sbi, curseg));
 
+               f2fs_inode_chksum_set(sbi, page);
+       }
+
        if (add_list) {
                struct f2fs_bio_info *io;
 
index 4c28576..fc757c8 100644 (file)
@@ -1993,6 +1993,11 @@ try_onemore:
        sb->s_fs_info = sbi;
        sbi->raw_super = raw_super;
 
+       /* precompute checksum seed for metadata */
+       if (f2fs_sb_has_inode_chksum(sb))
+               sbi->s_chksum_seed = f2fs_chksum(sbi, ~0, raw_super->uuid,
+                                               sizeof(raw_super->uuid));
+
        /*
         * The BLKZONED feature indicates that the drive was formatted with
         * zone alignment optimization. This is optional for host-aware
index 50f1766..2a0c453 100644 (file)
@@ -240,6 +240,7 @@ struct f2fs_inode {
                        __le16 i_extra_isize;   /* extra inode attribute size */
                        __le16 i_padding;       /* padding */
                        __le32 i_projid;        /* project id */
+                       __le32 i_inode_checksum;/* inode meta checksum */
                        __le32 i_extra_end[0];  /* for attribute size calculation */
                };
                __le32 i_addr[DEF_ADDRS_PER_INODE];     /* Pointers to data blocks */