f2fs: checkpoint disabling
[sfrench/cifs-2.6.git] / fs / f2fs / inode.c
index f121c864f4c0d9cfe5f7c6601d23b8af4fe88501..4ee9d6c4b71916afab0200297400ed44701d1011 100644 (file)
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * fs/f2fs/inode.c
  *
  * Copyright (c) 2012 Samsung Electronics Co., Ltd.
  *             http://www.samsung.com/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
  */
 #include <linux/fs.h>
 #include <linux/f2fs_fs.h>
@@ -68,13 +65,16 @@ static void __get_inode_rdev(struct inode *inode, struct f2fs_inode *ri)
        }
 }
 
-static bool __written_first_block(struct f2fs_inode *ri)
+static int __written_first_block(struct f2fs_sb_info *sbi,
+                                       struct f2fs_inode *ri)
 {
        block_t addr = le32_to_cpu(ri->i_addr[offset_in_addr(ri)]);
 
-       if (is_valid_blkaddr(addr))
-               return true;
-       return false;
+       if (!__is_valid_data_blkaddr(addr))
+               return 1;
+       if (!f2fs_is_valid_blkaddr(sbi, addr, DATA_GENERIC))
+               return -EFAULT;
+       return 0;
 }
 
 static void __set_inode_rdev(struct inode *inode, struct f2fs_inode *ri)
@@ -121,7 +121,7 @@ static bool f2fs_enable_inode_chksum(struct f2fs_sb_info *sbi, struct page *page
        if (!f2fs_sb_has_inode_chksum(sbi->sb))
                return false;
 
-       if (!RAW_IS_INODE(F2FS_NODE(page)) || !(ri->i_inline & F2FS_EXTRA_ATTR))
+       if (!IS_INODE(page) || !(ri->i_inline & F2FS_EXTRA_ATTR))
                return false;
 
        if (!F2FS_FITS_IN_INODE(ri, le16_to_cpu(ri->i_extra_isize),
@@ -159,8 +159,15 @@ bool f2fs_inode_chksum_verify(struct f2fs_sb_info *sbi, struct page *page)
        struct f2fs_inode *ri;
        __u32 provided, calculated;
 
+       if (unlikely(is_sbi_flag_set(sbi, SBI_IS_SHUTDOWN)))
+               return true;
+
+#ifdef CONFIG_F2FS_CHECK_FS
+       if (!f2fs_enable_inode_chksum(sbi, page))
+#else
        if (!f2fs_enable_inode_chksum(sbi, page) ||
                        PageDirty(page) || PageWriteback(page))
+#endif
                return true;
 
        ri = &F2FS_NODE(page)->i;
@@ -185,9 +192,31 @@ void f2fs_inode_chksum_set(struct f2fs_sb_info *sbi, struct page *page)
        ri->i_inode_checksum = cpu_to_le32(f2fs_inode_chksum(sbi, page));
 }
 
-static bool sanity_check_inode(struct inode *inode)
+static bool sanity_check_inode(struct inode *inode, struct page *node_page)
 {
        struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+       struct f2fs_inode_info *fi = F2FS_I(inode);
+       unsigned long long iblocks;
+
+       iblocks = le64_to_cpu(F2FS_INODE(node_page)->i_blocks);
+       if (!iblocks) {
+               set_sbi_flag(sbi, SBI_NEED_FSCK);
+               f2fs_msg(sbi->sb, KERN_WARNING,
+                       "%s: corrupted inode i_blocks i_ino=%lx iblocks=%llu, "
+                       "run fsck to fix.",
+                       __func__, inode->i_ino, iblocks);
+               return false;
+       }
+
+       if (ino_of_node(node_page) != nid_of_node(node_page)) {
+               set_sbi_flag(sbi, SBI_NEED_FSCK);
+               f2fs_msg(sbi->sb, KERN_WARNING,
+                       "%s: corrupted inode footer i_ino=%lx, ino,nid: "
+                       "[%u, %u] run fsck to fix.",
+                       __func__, inode->i_ino,
+                       ino_of_node(node_page), nid_of_node(node_page));
+               return false;
+       }
 
        if (f2fs_sb_has_flexible_inline_xattr(sbi->sb)
                        && !f2fs_has_extra_attr(inode)) {
@@ -197,6 +226,64 @@ static bool sanity_check_inode(struct inode *inode)
                        __func__, inode->i_ino);
                return false;
        }
+
+       if (f2fs_has_extra_attr(inode) &&
+                       !f2fs_sb_has_extra_attr(sbi->sb)) {
+               set_sbi_flag(sbi, SBI_NEED_FSCK);
+               f2fs_msg(sbi->sb, KERN_WARNING,
+                       "%s: inode (ino=%lx) is with extra_attr, "
+                       "but extra_attr feature is off",
+                       __func__, inode->i_ino);
+               return false;
+       }
+
+       if (fi->i_extra_isize > F2FS_TOTAL_EXTRA_ATTR_SIZE ||
+                       fi->i_extra_isize % sizeof(__le32)) {
+               set_sbi_flag(sbi, SBI_NEED_FSCK);
+               f2fs_msg(sbi->sb, KERN_WARNING,
+                       "%s: inode (ino=%lx) has corrupted i_extra_isize: %d, "
+                       "max: %zu",
+                       __func__, inode->i_ino, fi->i_extra_isize,
+                       F2FS_TOTAL_EXTRA_ATTR_SIZE);
+               return false;
+       }
+
+       if (F2FS_I(inode)->extent_tree) {
+               struct extent_info *ei = &F2FS_I(inode)->extent_tree->largest;
+
+               if (ei->len &&
+                       (!f2fs_is_valid_blkaddr(sbi, ei->blk, DATA_GENERIC) ||
+                       !f2fs_is_valid_blkaddr(sbi, ei->blk + ei->len - 1,
+                                                       DATA_GENERIC))) {
+                       set_sbi_flag(sbi, SBI_NEED_FSCK);
+                       f2fs_msg(sbi->sb, KERN_WARNING,
+                               "%s: inode (ino=%lx) extent info [%u, %u, %u] "
+                               "is incorrect, run fsck to fix",
+                               __func__, inode->i_ino,
+                               ei->blk, ei->fofs, ei->len);
+                       return false;
+               }
+       }
+
+       if (f2fs_has_inline_data(inode) &&
+                       (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode))) {
+               set_sbi_flag(sbi, SBI_NEED_FSCK);
+               f2fs_msg(sbi->sb, KERN_WARNING,
+                       "%s: inode (ino=%lx, mode=%u) should not have "
+                       "inline_data, run fsck to fix",
+                       __func__, inode->i_ino, inode->i_mode);
+               return false;
+       }
+
+       if (f2fs_has_inline_dentry(inode) && !S_ISDIR(inode->i_mode)) {
+               set_sbi_flag(sbi, SBI_NEED_FSCK);
+               f2fs_msg(sbi->sb, KERN_WARNING,
+                       "%s: inode (ino=%lx, mode=%u) should not have "
+                       "inline_dentry, run fsck to fix",
+                       __func__, inode->i_ino, inode->i_mode);
+               return false;
+       }
+
        return true;
 }
 
@@ -207,6 +294,7 @@ static int do_read_inode(struct inode *inode)
        struct page *node_page;
        struct f2fs_inode *ri;
        projid_t i_projid;
+       int err;
 
        /* Check if ino is within scope */
        if (f2fs_check_nid_range(sbi, inode->i_ino))
@@ -268,6 +356,11 @@ static int do_read_inode(struct inode *inode)
                fi->i_inline_xattr_size = 0;
        }
 
+       if (!sanity_check_inode(inode, node_page)) {
+               f2fs_put_page(node_page, 1);
+               return -EINVAL;
+       }
+
        /* check data exist */
        if (f2fs_has_inline_data(inode) && !f2fs_exist_data(inode))
                __recover_inline_status(inode, node_page);
@@ -275,8 +368,15 @@ static int do_read_inode(struct inode *inode)
        /* get rdev by using inline_info */
        __get_inode_rdev(inode, ri);
 
-       if (__written_first_block(ri))
-               set_inode_flag(inode, FI_FIRST_BLOCK_WRITTEN);
+       if (S_ISREG(inode->i_mode)) {
+               err = __written_first_block(sbi, ri);
+               if (err < 0) {
+                       f2fs_put_page(node_page, 1);
+                       return err;
+               }
+               if (!err)
+                       set_inode_flag(inode, FI_FIRST_BLOCK_WRITTEN);
+       }
 
        if (!f2fs_need_inode_block_update(sbi, inode->i_ino))
                fi->last_disk_size = inode->i_size;
@@ -297,9 +397,9 @@ static int do_read_inode(struct inode *inode)
                fi->i_crtime.tv_nsec = le32_to_cpu(ri->i_crtime_nsec);
        }
 
-       F2FS_I(inode)->i_disk_time[0] = timespec64_to_timespec(inode->i_atime);
-       F2FS_I(inode)->i_disk_time[1] = timespec64_to_timespec(inode->i_ctime);
-       F2FS_I(inode)->i_disk_time[2] = timespec64_to_timespec(inode->i_mtime);
+       F2FS_I(inode)->i_disk_time[0] = inode->i_atime;
+       F2FS_I(inode)->i_disk_time[1] = inode->i_ctime;
+       F2FS_I(inode)->i_disk_time[2] = inode->i_mtime;
        F2FS_I(inode)->i_disk_time[3] = F2FS_I(inode)->i_crtime;
        f2fs_put_page(node_page, 1);
 
@@ -330,10 +430,6 @@ struct inode *f2fs_iget(struct super_block *sb, unsigned long ino)
        ret = do_read_inode(inode);
        if (ret)
                goto bad_inode;
-       if (!sanity_check_inode(inode)) {
-               ret = -EINVAL;
-               goto bad_inode;
-       }
 make_now:
        if (ino == F2FS_NODE_INO(sbi)) {
                inode->i_mapping->a_ops = &f2fs_node_aops;
@@ -470,10 +566,14 @@ void f2fs_update_inode(struct inode *inode, struct page *node_page)
        if (inode->i_nlink == 0)
                clear_inline_node(node_page);
 
-       F2FS_I(inode)->i_disk_time[0] = timespec64_to_timespec(inode->i_atime);
-       F2FS_I(inode)->i_disk_time[1] = timespec64_to_timespec(inode->i_ctime);
-       F2FS_I(inode)->i_disk_time[2] = timespec64_to_timespec(inode->i_mtime);
+       F2FS_I(inode)->i_disk_time[0] = inode->i_atime;
+       F2FS_I(inode)->i_disk_time[1] = inode->i_ctime;
+       F2FS_I(inode)->i_disk_time[2] = inode->i_mtime;
        F2FS_I(inode)->i_disk_time[3] = F2FS_I(inode)->i_crtime;
+
+#ifdef CONFIG_F2FS_CHECK_FS
+       f2fs_inode_chksum_set(F2FS_I_SB(inode), node_page);
+#endif
 }
 
 void f2fs_update_inode_page(struct inode *inode)
@@ -507,6 +607,9 @@ int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc)
        if (!is_inode_flag_set(inode, FI_DIRTY_INODE))
                return 0;
 
+       if (f2fs_is_checkpoint_ready(sbi))
+               return -ENOSPC;
+
        /*
         * We need to balance fs here to prevent from producing dirty node pages
         * during the urgent cleaning time when runing out of free sections.
@@ -558,12 +661,11 @@ retry:
        if (F2FS_HAS_BLOCKS(inode))
                err = f2fs_truncate(inode);
 
-#ifdef CONFIG_F2FS_FAULT_INJECTION
        if (time_to_inject(sbi, FAULT_EVICT_INODE)) {
                f2fs_show_injection_info(FAULT_EVICT_INODE);
                err = -EIO;
        }
-#endif
+
        if (!err) {
                f2fs_lock_op(sbi);
                err = f2fs_remove_inode_page(inode);
@@ -589,7 +691,8 @@ no_delete:
        stat_dec_inline_dir(inode);
        stat_dec_inline_inode(inode);
 
-       if (likely(!is_set_ckpt_flags(sbi, CP_ERROR_FLAG)))
+       if (likely(!is_set_ckpt_flags(sbi, CP_ERROR_FLAG) &&
+                               !is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
                f2fs_bug_on(sbi, is_inode_flag_set(inode, FI_DIRTY_INODE));
        else
                f2fs_inode_synced(inode);
@@ -626,6 +729,7 @@ void f2fs_handle_failed_inode(struct inode *inode)
 {
        struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
        struct node_info ni;
+       int err;
 
        /*
         * clear nlink of inode in order to release resource of inode
@@ -648,10 +752,16 @@ void f2fs_handle_failed_inode(struct inode *inode)
         * so we can prevent losing this orphan when encoutering checkpoint
         * and following suddenly power-off.
         */
-       f2fs_get_node_info(sbi, inode->i_ino, &ni);
+       err = f2fs_get_node_info(sbi, inode->i_ino, &ni);
+       if (err) {
+               set_sbi_flag(sbi, SBI_NEED_FSCK);
+               f2fs_msg(sbi->sb, KERN_WARNING,
+                       "May loss orphan inode, run fsck to fix.");
+               goto out;
+       }
 
        if (ni.blk_addr != NULL_ADDR) {
-               int err = f2fs_acquire_orphan_inode(sbi);
+               err = f2fs_acquire_orphan_inode(sbi);
                if (err) {
                        set_sbi_flag(sbi, SBI_NEED_FSCK);
                        f2fs_msg(sbi->sb, KERN_WARNING,
@@ -664,6 +774,7 @@ void f2fs_handle_failed_inode(struct inode *inode)
                set_inode_flag(inode, FI_FREE_NID);
        }
 
+out:
        f2fs_unlock_op(sbi);
 
        /* iput will drop the inode object */