f2fs: support recording errors into superblock
authorChao Yu <chao@kernel.org>
Wed, 28 Sep 2022 15:38:54 +0000 (23:38 +0800)
committerJaegeuk Kim <jaegeuk@kernel.org>
Tue, 4 Oct 2022 20:31:45 +0000 (13:31 -0700)
This patch supports to record detail reason of FSCORRUPTED error into
f2fs_super_block.s_errors[].

Signed-off-by: Chao Yu <chao@kernel.org>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
16 files changed:
fs/f2fs/compress.c
fs/f2fs/data.c
fs/f2fs/dir.c
fs/f2fs/f2fs.h
fs/f2fs/file.c
fs/f2fs/gc.c
fs/f2fs/inline.c
fs/f2fs/inode.c
fs/f2fs/node.c
fs/f2fs/recovery.c
fs/f2fs/segment.c
fs/f2fs/segment.h
fs/f2fs/super.c
fs/f2fs/verity.c
fs/f2fs/xattr.c
include/linux/f2fs_fs.h

index c16bab5bd6000d4265dc5b6c7e4259f4b0b83cf8..d315c2de136f26f544a5518f49cdade81e693898 100644 (file)
@@ -762,6 +762,7 @@ void f2fs_decompress_cluster(struct decompress_io_ctx *dic, bool in_task)
 
        if (dic->clen > PAGE_SIZE * dic->nr_cpages - COMPRESS_HEADER_SIZE) {
                ret = -EFSCORRUPTED;
+               f2fs_handle_error(sbi, ERROR_FAIL_DECOMPRESSION);
                goto out_release;
        }
 
@@ -950,6 +951,7 @@ static int __f2fs_cluster_blocks(struct inode *inode,
 
        if (f2fs_sanity_check_cluster(&dn)) {
                ret = -EFSCORRUPTED;
+               f2fs_handle_error(F2FS_I_SB(inode), ERROR_CORRUPTED_CLUSTER);
                goto fail;
        }
 
index 3f2210e54577f01188725ecfc1bf1254f34821d2..1c82a4a4e8616d4f2c2e2b237fbb286485bb3900 100644 (file)
@@ -705,8 +705,10 @@ int f2fs_submit_page_bio(struct f2fs_io_info *fio)
 
        if (!f2fs_is_valid_blkaddr(fio->sbi, fio->new_blkaddr,
                        fio->is_por ? META_POR : (__is_meta_io(fio) ?
-                       META_GENERIC : DATA_GENERIC_ENHANCE)))
+                       META_GENERIC : DATA_GENERIC_ENHANCE))) {
+               f2fs_handle_error(fio->sbi, ERROR_INVALID_BLKADDR);
                return -EFSCORRUPTED;
+       }
 
        trace_f2fs_submit_page_bio(page, fio);
 
@@ -906,8 +908,10 @@ int f2fs_merge_page_bio(struct f2fs_io_info *fio)
                        fio->encrypted_page : fio->page;
 
        if (!f2fs_is_valid_blkaddr(fio->sbi, fio->new_blkaddr,
-                       __is_meta_io(fio) ? META_GENERIC : DATA_GENERIC))
+                       __is_meta_io(fio) ? META_GENERIC : DATA_GENERIC)) {
+               f2fs_handle_error(fio->sbi, ERROR_INVALID_BLKADDR);
                return -EFSCORRUPTED;
+       }
 
        trace_f2fs_submit_page_bio(page, fio);
 
@@ -1217,6 +1221,8 @@ struct page *f2fs_get_read_data_page(struct inode *inode, pgoff_t index,
                if (!f2fs_is_valid_blkaddr(F2FS_I_SB(inode), dn.data_blkaddr,
                                                DATA_GENERIC_ENHANCE_READ)) {
                        err = -EFSCORRUPTED;
+                       f2fs_handle_error(F2FS_I_SB(inode),
+                                               ERROR_INVALID_BLKADDR);
                        goto put_err;
                }
                goto got_it;
@@ -1237,6 +1243,8 @@ struct page *f2fs_get_read_data_page(struct inode *inode, pgoff_t index,
                                                dn.data_blkaddr,
                                                DATA_GENERIC_ENHANCE)) {
                err = -EFSCORRUPTED;
+               f2fs_handle_error(F2FS_I_SB(inode),
+                                       ERROR_INVALID_BLKADDR);
                goto put_err;
        }
 got_it:
@@ -1550,6 +1558,7 @@ next_block:
        if (__is_valid_data_blkaddr(blkaddr) &&
                !f2fs_is_valid_blkaddr(sbi, blkaddr, DATA_GENERIC_ENHANCE)) {
                err = -EFSCORRUPTED;
+               f2fs_handle_error(sbi, ERROR_INVALID_BLKADDR);
                goto sync_out;
        }
 
@@ -1595,6 +1604,8 @@ next_block:
                                        (flag != F2FS_GET_BLOCK_FIEMAP ||
                                        IS_ENABLED(CONFIG_F2FS_CHECK_FS))) {
                                err = -EFSCORRUPTED;
+                               f2fs_handle_error(sbi,
+                                               ERROR_CORRUPTED_CLUSTER);
                                goto sync_out;
                        }
                        if (flag == F2FS_GET_BLOCK_BMAP) {
@@ -2076,6 +2087,8 @@ got_it:
                if (!f2fs_is_valid_blkaddr(F2FS_I_SB(inode), block_nr,
                                                DATA_GENERIC_ENHANCE_READ)) {
                        ret = -EFSCORRUPTED;
+                       f2fs_handle_error(F2FS_I_SB(inode),
+                                               ERROR_INVALID_BLKADDR);
                        goto out;
                }
        } else {
@@ -2619,8 +2632,11 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio)
                fio->old_blkaddr = ei.blk + page->index - ei.fofs;
 
                if (!f2fs_is_valid_blkaddr(fio->sbi, fio->old_blkaddr,
-                                               DATA_GENERIC_ENHANCE))
+                                               DATA_GENERIC_ENHANCE)) {
+                       f2fs_handle_error(fio->sbi,
+                                               ERROR_INVALID_BLKADDR);
                        return -EFSCORRUPTED;
+               }
 
                ipu_force = true;
                fio->need_lock = LOCK_DONE;
@@ -2648,6 +2664,7 @@ got_it:
                !f2fs_is_valid_blkaddr(fio->sbi, fio->old_blkaddr,
                                                DATA_GENERIC_ENHANCE)) {
                err = -EFSCORRUPTED;
+               f2fs_handle_error(fio->sbi, ERROR_INVALID_BLKADDR);
                goto out_writepage;
        }
 
@@ -3561,6 +3578,7 @@ repeat:
                if (!f2fs_is_valid_blkaddr(sbi, blkaddr,
                                DATA_GENERIC_ENHANCE_READ)) {
                        err = -EFSCORRUPTED;
+                       f2fs_handle_error(sbi, ERROR_INVALID_BLKADDR);
                        goto fail;
                }
                err = f2fs_submit_page_read(inode, page, blkaddr, 0, true);
index d5bd7932fb642c7e5fa74ac0d77dc0d80c0206f8..21960a899b6adaf9adb26d8e07ac59d27cfdc330 100644 (file)
@@ -1041,6 +1041,7 @@ int f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d,
                                  __func__, le16_to_cpu(de->name_len));
                        set_sbi_flag(sbi, SBI_NEED_FSCK);
                        err = -EFSCORRUPTED;
+                       f2fs_handle_error(sbi, ERROR_CORRUPTED_DIRENT);
                        goto out;
                }
 
index 7d56948273bef8b20880e6a57b431e4e5961d9e0..b63b482c35a8549f4c34d58e8117fce2c3eacb43 100644 (file)
@@ -1815,6 +1815,10 @@ struct f2fs_sb_info {
 
        struct workqueue_struct *post_read_wq;  /* post read workqueue */
 
+       unsigned char errors[MAX_F2FS_ERRORS];  /* error flags */
+       spinlock_t error_lock;                  /* protect errors array */
+       bool error_dirty;                       /* errors of sb is dirty */
+
        struct kmem_cache *inline_xattr_slab;   /* inline xattr entry */
        unsigned int inline_xattr_slab_size;    /* default inline xattr slab size */
 
@@ -3557,6 +3561,7 @@ int f2fs_quota_sync(struct super_block *sb, int type);
 loff_t max_file_blocks(struct inode *inode);
 void f2fs_quota_off_umount(struct super_block *sb);
 void f2fs_handle_stop(struct f2fs_sb_info *sbi, unsigned char reason);
+void f2fs_handle_error(struct f2fs_sb_info *sbi, unsigned char error);
 int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover);
 int f2fs_sync_fs(struct super_block *sb, int sync);
 int f2fs_sanity_check_ckpt(struct f2fs_sb_info *sbi);
index c86e5e1601c98ac023401d6bd3196424a487f940..7b3ed4a9bb46e0a3a2bd28a2f819c42a83113dc9 100644 (file)
@@ -1156,6 +1156,7 @@ next_dnode:
                        !f2fs_is_valid_blkaddr(sbi, *blkaddr,
                                        DATA_GENERIC_ENHANCE)) {
                        f2fs_put_dnode(&dn);
+                       f2fs_handle_error(sbi, ERROR_INVALID_BLKADDR);
                        return -EFSCORRUPTED;
                }
 
@@ -1440,6 +1441,7 @@ static int f2fs_do_zero_range(struct dnode_of_data *dn, pgoff_t start,
                if (!f2fs_is_valid_blkaddr(sbi, dn->data_blkaddr,
                                        DATA_GENERIC_ENHANCE)) {
                        ret = -EFSCORRUPTED;
+                       f2fs_handle_error(sbi, ERROR_INVALID_BLKADDR);
                        break;
                }
 
@@ -3323,8 +3325,10 @@ static int release_compress_blocks(struct dnode_of_data *dn, pgoff_t count)
                if (!__is_valid_data_blkaddr(blkaddr))
                        continue;
                if (unlikely(!f2fs_is_valid_blkaddr(sbi, blkaddr,
-                                       DATA_GENERIC_ENHANCE)))
+                                       DATA_GENERIC_ENHANCE))) {
+                       f2fs_handle_error(sbi, ERROR_INVALID_BLKADDR);
                        return -EFSCORRUPTED;
+               }
        }
 
        while (count) {
@@ -3485,8 +3489,10 @@ static int reserve_compress_blocks(struct dnode_of_data *dn, pgoff_t count)
                if (!__is_valid_data_blkaddr(blkaddr))
                        continue;
                if (unlikely(!f2fs_is_valid_blkaddr(sbi, blkaddr,
-                                       DATA_GENERIC_ENHANCE)))
+                                       DATA_GENERIC_ENHANCE))) {
+                       f2fs_handle_error(sbi, ERROR_INVALID_BLKADDR);
                        return -EFSCORRUPTED;
+               }
        }
 
        while (count) {
@@ -3758,6 +3764,8 @@ static int f2fs_sec_trim_file(struct file *filp, unsigned long arg)
                                                DATA_GENERIC_ENHANCE)) {
                                ret = -EFSCORRUPTED;
                                f2fs_put_dnode(&dn);
+                               f2fs_handle_error(sbi,
+                                               ERROR_INVALID_BLKADDR);
                                goto out;
                        }
 
index 6e42dad0ac2d0e155d914337697468783de41015..d36bcb23ccfec78e651df64fe3f199dad56c7941 100644 (file)
@@ -1164,6 +1164,7 @@ static int ra_data_block(struct inode *inode, pgoff_t index)
                if (unlikely(!f2fs_is_valid_blkaddr(sbi, dn.data_blkaddr,
                                                DATA_GENERIC_ENHANCE_READ))) {
                        err = -EFSCORRUPTED;
+                       f2fs_handle_error(sbi, ERROR_INVALID_BLKADDR);
                        goto put_page;
                }
                goto got_it;
@@ -1182,6 +1183,7 @@ static int ra_data_block(struct inode *inode, pgoff_t index)
        if (unlikely(!f2fs_is_valid_blkaddr(sbi, dn.data_blkaddr,
                                                DATA_GENERIC_ENHANCE))) {
                err = -EFSCORRUPTED;
+               f2fs_handle_error(sbi, ERROR_INVALID_BLKADDR);
                goto put_page;
        }
 got_it:
index 73da9331803696e04828058f1a128fc9caba6e54..21a495234ffd7f22bc63f8bc05b02acd6e4ee4f7 100644 (file)
@@ -160,6 +160,7 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page)
                set_sbi_flag(fio.sbi, SBI_NEED_FSCK);
                f2fs_warn(fio.sbi, "%s: corrupted inline inode ino=%lx, i_addr[0]:0x%x, run fsck to fix.",
                          __func__, dn->inode->i_ino, dn->data_blkaddr);
+               f2fs_handle_error(fio.sbi, ERROR_INVALID_BLKADDR);
                return -EFSCORRUPTED;
        }
 
@@ -412,6 +413,7 @@ static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage,
                set_sbi_flag(F2FS_P_SB(page), SBI_NEED_FSCK);
                f2fs_warn(F2FS_P_SB(page), "%s: corrupted inline inode ino=%lx, i_addr[0]:0x%x, run fsck to fix.",
                          __func__, dir->i_ino, dn.data_blkaddr);
+               f2fs_handle_error(F2FS_P_SB(page), ERROR_INVALID_BLKADDR);
                err = -EFSCORRUPTED;
                goto out;
        }
index c972276027b494b907474211c4d87722683c59fc..9f0d3864d9f13a619285332de1aa307c3776a691 100644 (file)
@@ -81,8 +81,10 @@ static int __written_first_block(struct f2fs_sb_info *sbi,
 
        if (!__is_valid_data_blkaddr(addr))
                return 1;
-       if (!f2fs_is_valid_blkaddr(sbi, addr, DATA_GENERIC_ENHANCE))
+       if (!f2fs_is_valid_blkaddr(sbi, addr, DATA_GENERIC_ENHANCE)) {
+               f2fs_handle_error(sbi, ERROR_INVALID_BLKADDR);
                return -EFSCORRUPTED;
+       }
        return 0;
 }
 
@@ -415,6 +417,7 @@ static int do_read_inode(struct inode *inode)
 
        if (!sanity_check_inode(inode, node_page)) {
                f2fs_put_page(node_page, 1);
+               f2fs_handle_error(sbi, ERROR_CORRUPTED_INODE);
                return -EFSCORRUPTED;
        }
 
@@ -510,6 +513,7 @@ struct inode *f2fs_iget(struct super_block *sb, unsigned long ino)
                        ret = -EFSCORRUPTED;
                        trace_f2fs_iget_exit(inode, ret);
                        iput(inode);
+                       f2fs_handle_error(sbi, ERROR_CORRUPTED_INODE);
                        return ERR_PTR(ret);
                }
 
index 9263bf5f10d376e467c7cdc2da21be9e3e302518..983572f23896901b50fda587aa8782c99e61fdcb 100644 (file)
@@ -36,6 +36,7 @@ int f2fs_check_nid_range(struct f2fs_sb_info *sbi, nid_t nid)
                set_sbi_flag(sbi, SBI_NEED_FSCK);
                f2fs_warn(sbi, "%s: out-of-range nid=%x, run fsck to fix.",
                          __func__, nid);
+               f2fs_handle_error(sbi, ERROR_CORRUPTED_INODE);
                return -EFSCORRUPTED;
        }
        return 0;
@@ -1295,6 +1296,7 @@ struct page *f2fs_new_node_page(struct dnode_of_data *dn, unsigned int ofs)
        if (unlikely(new_ni.blk_addr != NULL_ADDR)) {
                err = -EFSCORRUPTED;
                set_sbi_flag(sbi, SBI_NEED_FSCK);
+               f2fs_handle_error(sbi, ERROR_INVALID_BLKADDR);
                goto fail;
        }
 #endif
index 5c9facec98f69ce303cac5624bc213f51e58f10d..dea95b48b647d61888217a54ad08372d96377fd0 100644 (file)
@@ -507,6 +507,7 @@ got_it:
        if (ofs_in_node >= max_addrs) {
                f2fs_err(sbi, "Inconsistent ofs_in_node:%u in summary, ino:%lu, nid:%u, max:%u",
                        ofs_in_node, dn->inode->i_ino, nid, max_addrs);
+               f2fs_handle_error(sbi, ERROR_INCONSISTENT_SUMMARY);
                return -EFSCORRUPTED;
        }
 
@@ -637,6 +638,7 @@ retry_dn:
                          inode->i_ino, ofs_of_node(dn.node_page),
                          ofs_of_node(page));
                err = -EFSCORRUPTED;
+               f2fs_handle_error(sbi, ERROR_INCONSISTENT_FOOTER);
                goto err;
        }
 
@@ -649,12 +651,14 @@ retry_dn:
                if (__is_valid_data_blkaddr(src) &&
                        !f2fs_is_valid_blkaddr(sbi, src, META_POR)) {
                        err = -EFSCORRUPTED;
+                       f2fs_handle_error(sbi, ERROR_INVALID_BLKADDR);
                        goto err;
                }
 
                if (__is_valid_data_blkaddr(dest) &&
                        !f2fs_is_valid_blkaddr(sbi, dest, META_POR)) {
                        err = -EFSCORRUPTED;
+                       f2fs_handle_error(sbi, ERROR_INVALID_BLKADDR);
                        goto err;
                }
 
@@ -712,6 +716,8 @@ retry_prev:
                                f2fs_err(sbi, "Inconsistent dest blkaddr:%u, ino:%lu, ofs:%u",
                                        dest, inode->i_ino, dn.ofs_in_node);
                                err = -EFSCORRUPTED;
+                               f2fs_handle_error(sbi,
+                                               ERROR_INVALID_BLKADDR);
                                goto err;
                        }
 
index 54c86a5518597482a2ffb0bb31137e8d2ac0d4be..d7b13127b0b8a5096f1d6da92802b9c31b5191ce 100644 (file)
@@ -312,6 +312,8 @@ static int __f2fs_commit_atomic_write(struct inode *inode)
                                        DATA_GENERIC_ENHANCE)) {
                                f2fs_put_dnode(&dn);
                                ret = -EFSCORRUPTED;
+                               f2fs_handle_error(sbi,
+                                               ERROR_INVALID_BLKADDR);
                                goto out;
                        }
 
@@ -3433,6 +3435,7 @@ int f2fs_inplace_write_data(struct f2fs_io_info *fio)
                f2fs_warn(sbi, "%s: incorrect segment(%u) type, run fsck to fix.",
                          __func__, segno);
                err = -EFSCORRUPTED;
+               f2fs_handle_error(sbi, ERROR_INCONSISTENT_SUM_TYPE);
                goto drop_bio;
        }
 
@@ -4381,6 +4384,8 @@ static int build_sit_entries(struct f2fs_sb_info *sbi)
                        if (se->type >= NR_PERSISTENT_LOG) {
                                f2fs_err(sbi, "Invalid segment type: %u, segno: %u",
                                                        se->type, start);
+                               f2fs_handle_error(sbi,
+                                               ERROR_INCONSISTENT_SUM_TYPE);
                                return -EFSCORRUPTED;
                        }
 
@@ -4417,6 +4422,7 @@ static int build_sit_entries(struct f2fs_sb_info *sbi)
                        f2fs_err(sbi, "Wrong journal entry on segno %u",
                                 start);
                        err = -EFSCORRUPTED;
+                       f2fs_handle_error(sbi, ERROR_CORRUPTED_JOURNAL);
                        break;
                }
 
@@ -4436,6 +4442,7 @@ static int build_sit_entries(struct f2fs_sb_info *sbi)
                        f2fs_err(sbi, "Invalid segment type: %u, segno: %u",
                                                        se->type, start);
                        err = -EFSCORRUPTED;
+                       f2fs_handle_error(sbi, ERROR_INCONSISTENT_SUM_TYPE);
                        break;
                }
 
@@ -4467,6 +4474,7 @@ static int build_sit_entries(struct f2fs_sb_info *sbi)
        if (sit_valid_blocks[NODE] != valid_node_count(sbi)) {
                f2fs_err(sbi, "SIT is corrupted node# %u vs %u",
                         sit_valid_blocks[NODE], valid_node_count(sbi));
+               f2fs_handle_error(sbi, ERROR_INCONSISTENT_NODE_COUNT);
                return -EFSCORRUPTED;
        }
 
@@ -4475,6 +4483,7 @@ static int build_sit_entries(struct f2fs_sb_info *sbi)
                f2fs_err(sbi, "SIT is corrupted data# %u %u vs %u",
                         sit_valid_blocks[DATA], sit_valid_blocks[NODE],
                         valid_user_blocks(sbi));
+               f2fs_handle_error(sbi, ERROR_INCONSISTENT_BLOCK_COUNT);
                return -EFSCORRUPTED;
        }
 
@@ -4625,6 +4634,7 @@ static int sanity_check_curseg(struct f2fs_sb_info *sbi)
                        f2fs_err(sbi,
                                 "Current segment has invalid alloc_type:%d",
                                 curseg->alloc_type);
+                       f2fs_handle_error(sbi, ERROR_INVALID_CURSEG);
                        return -EFSCORRUPTED;
                }
 
@@ -4642,6 +4652,7 @@ out:
                                 "Current segment's next free block offset is inconsistent with bitmap, logtype:%u, segno:%u, type:%u, next_blkoff:%u, blkofs:%u",
                                 i, curseg->segno, curseg->alloc_type,
                                 curseg->next_blkoff, blkofs);
+                       f2fs_handle_error(sbi, ERROR_INVALID_CURSEG);
                        return -EFSCORRUPTED;
                }
        }
index d1d63766f2c7e57cded48edcb67798c9217dd69d..be8f2d7d007b9eb54cf1b7fabd60ffc3696cab96 100644 (file)
@@ -753,6 +753,7 @@ static inline int check_block_count(struct f2fs_sb_info *sbi,
                f2fs_err(sbi, "Mismatch valid blocks %d vs. %d",
                         GET_SIT_VBLOCKS(raw_sit), valid_blocks);
                set_sbi_flag(sbi, SBI_NEED_FSCK);
+               f2fs_handle_error(sbi, ERROR_INCONSISTENT_SIT);
                return -EFSCORRUPTED;
        }
 
@@ -767,6 +768,7 @@ static inline int check_block_count(struct f2fs_sb_info *sbi,
                f2fs_err(sbi, "Wrong valid blocks %d or segno %u",
                         GET_SIT_VBLOCKS(raw_sit), segno);
                set_sbi_flag(sbi, SBI_NEED_FSCK);
+               f2fs_handle_error(sbi, ERROR_INCONSISTENT_SIT);
                return -EFSCORRUPTED;
        }
        return 0;
index 2533d309a9240af39571b6c5a14ae28873146fa7..6cf72fbf20541392d808bc15697b32f3e478613a 100644 (file)
@@ -3851,8 +3851,6 @@ void f2fs_handle_stop(struct f2fs_sb_info *sbi, unsigned char reason)
        struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi);
        int err;
 
-       f2fs_bug_on(sbi, reason >= MAX_STOP_REASON);
-
        f2fs_down_write(&sbi->sb_lock);
 
        if (raw_super->s_stop_reason[reason] < ((1 << BITS_PER_BYTE) - 1))
@@ -3862,7 +3860,51 @@ void f2fs_handle_stop(struct f2fs_sb_info *sbi, unsigned char reason)
        if (err)
                f2fs_err(sbi, "f2fs_commit_super fails to record reason:%u err:%d",
                                                                reason, err);
+       f2fs_up_write(&sbi->sb_lock);
+}
+
+static void f2fs_save_errors(struct f2fs_sb_info *sbi, unsigned char flag)
+{
+       spin_lock(&sbi->error_lock);
+       if (!test_bit(flag, (unsigned long *)sbi->errors)) {
+               set_bit(flag, (unsigned long *)sbi->errors);
+               sbi->error_dirty = true;
+       }
+       spin_unlock(&sbi->error_lock);
+}
+
+static bool f2fs_update_errors(struct f2fs_sb_info *sbi)
+{
+       bool need_update = false;
+
+       spin_lock(&sbi->error_lock);
+       if (sbi->error_dirty) {
+               memcpy(F2FS_RAW_SUPER(sbi)->s_errors, sbi->errors,
+                                                       MAX_F2FS_ERRORS);
+               sbi->error_dirty = false;
+               need_update = true;
+       }
+       spin_unlock(&sbi->error_lock);
+
+       return need_update;
+}
 
+void f2fs_handle_error(struct f2fs_sb_info *sbi, unsigned char error)
+{
+       int err;
+
+       f2fs_save_errors(sbi, error);
+
+       f2fs_down_write(&sbi->sb_lock);
+
+       if (!f2fs_update_errors(sbi))
+               goto out_unlock;
+
+       err = f2fs_commit_super(sbi, false);
+       if (err)
+               f2fs_err(sbi, "f2fs_commit_super fails to record errors:%u, err:%d",
+                                                               error, err);
+out_unlock:
        f2fs_up_write(&sbi->sb_lock);
 }
 
@@ -4213,6 +4255,9 @@ try_onemore:
                goto free_devices;
        }
 
+       spin_lock_init(&sbi->error_lock);
+       memcpy(sbi->errors, raw_super->s_errors, MAX_F2FS_ERRORS);
+
        sbi->total_valid_node_count =
                                le32_to_cpu(sbi->ckpt->valid_node_count);
        percpu_counter_set(&sbi->total_valid_inode_count,
index 97ec60f39d6960f82e19474b1b333dc05c55fbfb..f0805e51b3fed784319357121d9986ca9350121e 100644 (file)
@@ -240,6 +240,8 @@ static int f2fs_get_verity_descriptor(struct inode *inode, void *buf,
        if (pos + size < pos || pos + size > inode->i_sb->s_maxbytes ||
            pos < f2fs_verity_metadata_pos(inode) || size > INT_MAX) {
                f2fs_warn(F2FS_I_SB(inode), "invalid verity xattr");
+               f2fs_handle_error(F2FS_I_SB(inode),
+                               ERROR_CORRUPTED_VERITY_XATTR);
                return -EFSCORRUPTED;
        }
        if (buf_size) {
index c76c15086e5f5bdf98227c9421e43a6eb684d394..dc2e8637189e2e2541507888a7ea2858ed4289d5 100644 (file)
@@ -367,6 +367,8 @@ static int lookup_all_xattrs(struct inode *inode, struct page *ipage,
                                                                inode->i_ino);
                set_sbi_flag(F2FS_I_SB(inode), SBI_NEED_FSCK);
                err = -EFSCORRUPTED;
+               f2fs_handle_error(F2FS_I_SB(inode),
+                                       ERROR_CORRUPTED_XATTR);
                goto out;
        }
 check:
@@ -583,6 +585,8 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
                                                inode->i_ino);
                        set_sbi_flag(F2FS_I_SB(inode), SBI_NEED_FSCK);
                        error = -EFSCORRUPTED;
+                       f2fs_handle_error(F2FS_I_SB(inode),
+                                               ERROR_CORRUPTED_XATTR);
                        goto cleanup;
                }
 
@@ -658,6 +662,8 @@ static int __f2fs_setxattr(struct inode *inode, int index,
                                                                inode->i_ino);
                set_sbi_flag(F2FS_I_SB(inode), SBI_NEED_FSCK);
                error = -EFSCORRUPTED;
+               f2fs_handle_error(F2FS_I_SB(inode),
+                                       ERROR_CORRUPTED_XATTR);
                goto exit;
        }
 
@@ -684,6 +690,8 @@ static int __f2fs_setxattr(struct inode *inode, int index,
                                        inode->i_ino, ENTRY_SIZE(last));
                        set_sbi_flag(F2FS_I_SB(inode), SBI_NEED_FSCK);
                        error = -EFSCORRUPTED;
+                       f2fs_handle_error(F2FS_I_SB(inode),
+                                               ERROR_CORRUPTED_XATTR);
                        goto exit;
                }
                last = XATTR_NEXT_ENTRY(last);
index 5dd1e52b8997f30e0b2cc2a2cbdfb667628bc29e..ee0d75d9a302d47649c79c7dcb4c24abd0bbd547 100644 (file)
@@ -87,6 +87,28 @@ enum stop_cp_reason {
 
 #define        MAX_STOP_REASON                 32
 
+/* detail reason for EFSCORRUPTED */
+enum f2fs_error {
+       ERROR_CORRUPTED_CLUSTER,
+       ERROR_FAIL_DECOMPRESSION,
+       ERROR_INVALID_BLKADDR,
+       ERROR_CORRUPTED_DIRENT,
+       ERROR_CORRUPTED_INODE,
+       ERROR_INCONSISTENT_SUMMARY,
+       ERROR_INCONSISTENT_FOOTER,
+       ERROR_INCONSISTENT_SUM_TYPE,
+       ERROR_CORRUPTED_JOURNAL,
+       ERROR_INCONSISTENT_NODE_COUNT,
+       ERROR_INCONSISTENT_BLOCK_COUNT,
+       ERROR_INVALID_CURSEG,
+       ERROR_INCONSISTENT_SIT,
+       ERROR_CORRUPTED_VERITY_XATTR,
+       ERROR_CORRUPTED_XATTR,
+       ERROR_MAX,
+};
+
+#define MAX_F2FS_ERRORS                        16
+
 struct f2fs_super_block {
        __le32 magic;                   /* Magic Number */
        __le16 major_ver;               /* Major Version */
@@ -131,7 +153,8 @@ struct f2fs_super_block {
        __le16  s_encoding;             /* Filename charset encoding */
        __le16  s_encoding_flags;       /* Filename charset encoding flags */
        __u8 s_stop_reason[MAX_STOP_REASON];    /* stop checkpoint reason */
-       __u8 reserved[274];             /* valid reserved region */
+       __u8 s_errors[MAX_F2FS_ERRORS];         /* reason of image corrupts */
+       __u8 reserved[258];             /* valid reserved region */
        __le32 crc;                     /* checksum of superblock */
 } __packed;