nilfs2: replace WARN_ONs for invalid DAT metadata block requests
authorRyusuke Konishi <konishi.ryusuke@gmail.com>
Thu, 26 Jan 2023 16:41:14 +0000 (01:41 +0900)
committerAndrew Morton <akpm@linux-foundation.org>
Fri, 3 Feb 2023 06:50:08 +0000 (22:50 -0800)
If DAT metadata file block access fails due to corruption of the DAT file
or abnormal virtual block numbers held by b-trees or inodes, a kernel
warning is generated.

This replaces the WARN_ONs by error output, so that a kernel, booted with
panic_on_warn, does not panic.  This patch also replaces the detected
return code -ENOENT with another internal code -EINVAL to notify the bmap
layer of metadata corruption.  When the bmap layer sees -EINVAL, it
handles the abnormal situation with nilfs_bmap_convert_error() and finally
returns code -EIO as it should.

Link: https://lkml.kernel.org/r/0000000000005cc3d205ea23ddcf@google.com
Link: https://lkml.kernel.org/r/20230126164114.6911-1-konishi.ryusuke@gmail.com
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Reported-by: <syzbot+5d5d25f90f195a3cfcb4@syzkaller.appspotmail.com>
Tested-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
fs/nilfs2/dat.c

index 9930fa901039fcf16fe1cc4c8476499960c74869..1e7f653c1df7e0344a6d9ef3e9b446291fe7f13f 100644 (file)
@@ -40,8 +40,21 @@ static inline struct nilfs_dat_info *NILFS_DAT_I(struct inode *dat)
 static int nilfs_dat_prepare_entry(struct inode *dat,
                                   struct nilfs_palloc_req *req, int create)
 {
-       return nilfs_palloc_get_entry_block(dat, req->pr_entry_nr,
-                                           create, &req->pr_entry_bh);
+       int ret;
+
+       ret = nilfs_palloc_get_entry_block(dat, req->pr_entry_nr,
+                                          create, &req->pr_entry_bh);
+       if (unlikely(ret == -ENOENT)) {
+               nilfs_err(dat->i_sb,
+                         "DAT doesn't have a block to manage vblocknr = %llu",
+                         (unsigned long long)req->pr_entry_nr);
+               /*
+                * Return internal code -EINVAL to notify bmap layer of
+                * metadata corruption.
+                */
+               ret = -EINVAL;
+       }
+       return ret;
 }
 
 static void nilfs_dat_commit_entry(struct inode *dat,
@@ -123,11 +136,7 @@ static void nilfs_dat_commit_free(struct inode *dat,
 
 int nilfs_dat_prepare_start(struct inode *dat, struct nilfs_palloc_req *req)
 {
-       int ret;
-
-       ret = nilfs_dat_prepare_entry(dat, req, 0);
-       WARN_ON(ret == -ENOENT);
-       return ret;
+       return nilfs_dat_prepare_entry(dat, req, 0);
 }
 
 void nilfs_dat_commit_start(struct inode *dat, struct nilfs_palloc_req *req,
@@ -154,10 +163,8 @@ int nilfs_dat_prepare_end(struct inode *dat, struct nilfs_palloc_req *req)
        int ret;
 
        ret = nilfs_dat_prepare_entry(dat, req, 0);
-       if (ret < 0) {
-               WARN_ON(ret == -ENOENT);
+       if (ret < 0)
                return ret;
-       }
 
        kaddr = kmap_atomic(req->pr_entry_bh->b_page);
        entry = nilfs_palloc_block_get_entry(dat, req->pr_entry_nr,