bcachefs: fix truncate with ATTR_MODE
authorKent Overstreet <kent.overstreet@gmail.com>
Tue, 15 Jun 2021 02:29:54 +0000 (22:29 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:09:07 +0000 (17:09 -0400)
After the v5.12 rebase, we started oopsing when truncate was passed
ATTR_MODE, due to not passing mnt_userns to setattr_copy(). This
refactors things so that truncate/extend finish by using
bch2_setattr_nonsize(), which solves the problem.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
fs/bcachefs/fs-io.c
fs/bcachefs/fs-io.h
fs/bcachefs/fs.c
fs/bcachefs/fs.h

index 0ffc3971d1b208c45a4878e2ece92b56897e2c6f..a25c3b70ef7407f733689aec717a9fdaa7d10fc3 100644 (file)
@@ -2252,11 +2252,11 @@ static int bch2_truncate_page(struct bch_inode_info *inode, loff_t from)
                                    from, round_up(from, PAGE_SIZE));
 }
 
-static int bch2_extend(struct bch_inode_info *inode,
+static int bch2_extend(struct mnt_idmap *idmap,
+                      struct bch_inode_info *inode,
                       struct bch_inode_unpacked *inode_u,
                       struct iattr *iattr)
 {
-       struct bch_fs *c = inode->v.i_sb->s_fs_info;
        struct address_space *mapping = inode->v.i_mapping;
        int ret;
 
@@ -2270,25 +2270,15 @@ static int bch2_extend(struct bch_inode_info *inode,
                return ret;
 
        truncate_setsize(&inode->v, iattr->ia_size);
-       /* ATTR_MODE will never be set here, ns argument isn't needed: */
-       setattr_copy(NULL, &inode->v, iattr);
-
-       mutex_lock(&inode->ei_update_lock);
-       ret = bch2_write_inode_size(c, inode, inode->v.i_size,
-                                   ATTR_MTIME|ATTR_CTIME);
-       mutex_unlock(&inode->ei_update_lock);
 
-       return ret;
+       return bch2_setattr_nonsize(idmap, inode, iattr);
 }
 
 static int bch2_truncate_finish_fn(struct bch_inode_info *inode,
                                   struct bch_inode_unpacked *bi,
                                   void *p)
 {
-       struct bch_fs *c = inode->v.i_sb->s_fs_info;
-
        bi->bi_flags &= ~BCH_INODE_I_SIZE_DIRTY;
-       bi->bi_mtime = bi->bi_ctime = bch2_current_time(c);
        return 0;
 }
 
@@ -2302,7 +2292,8 @@ static int bch2_truncate_start_fn(struct bch_inode_info *inode,
        return 0;
 }
 
-int bch2_truncate(struct bch_inode_info *inode, struct iattr *iattr)
+int bch2_truncate(struct mnt_idmap *idmap,
+                 struct bch_inode_info *inode, struct iattr *iattr)
 {
        struct bch_fs *c = inode->v.i_sb->s_fs_info;
        struct address_space *mapping = inode->v.i_mapping;
@@ -2313,6 +2304,18 @@ int bch2_truncate(struct bch_inode_info *inode, struct iattr *iattr)
        s64 i_sectors_delta = 0;
        int ret = 0;
 
+       /*
+        * Don't update timestamps if we're not doing anything:
+        */
+       if (iattr->ia_size == inode->v.i_size)
+               return 0;
+
+       if (!(iattr->ia_valid & ATTR_MTIME))
+               ktime_get_coarse_real_ts64(&iattr->ia_mtime);
+       if (!(iattr->ia_valid & ATTR_CTIME))
+               ktime_get_coarse_real_ts64(&iattr->ia_ctime);
+       iattr->ia_valid |= ATTR_MTIME|ATTR_CTIME;
+
        inode_dio_wait(&inode->v);
        bch2_pagecache_block_get(&inode->ei_pagecache_lock);
 
@@ -2342,10 +2345,12 @@ int bch2_truncate(struct bch_inode_info *inode, struct iattr *iattr)
                inode->v.i_size < inode_u.bi_size);
 
        if (iattr->ia_size > inode->v.i_size) {
-               ret = bch2_extend(inode, &inode_u, iattr);
+               ret = bch2_extend(idmap, inode, &inode_u, iattr);
                goto err;
        }
 
+       iattr->ia_valid &= ~ATTR_SIZE;
+
        ret = bch2_truncate_page(inode, iattr->ia_size);
        if (unlikely(ret))
                goto err;
@@ -2389,13 +2394,11 @@ int bch2_truncate(struct bch_inode_info *inode, struct iattr *iattr)
        if (unlikely(ret))
                goto err;
 
-       /* ATTR_MODE will never be set here, ns argument isn't needed: */
-       setattr_copy(NULL, &inode->v, iattr);
-
        mutex_lock(&inode->ei_update_lock);
-       ret = bch2_write_inode(c, inode, bch2_truncate_finish_fn, NULL,
-                              ATTR_MTIME|ATTR_CTIME);
+       ret = bch2_write_inode(c, inode, bch2_truncate_finish_fn, NULL, 0);
        mutex_unlock(&inode->ei_update_lock);
+
+       ret = bch2_setattr_nonsize(idmap, inode, iattr);
 err:
        bch2_pagecache_block_put(&inode->ei_pagecache_lock);
        return ret;
index 2a2df58a46bbaf41c11144dae25e6fcc583a0474..64b16b44e25ae57e733306a57c297893d635390d 100644 (file)
@@ -31,7 +31,8 @@ ssize_t bch2_write_iter(struct kiocb *, struct iov_iter *);
 
 int bch2_fsync(struct file *, loff_t, loff_t, int);
 
-int bch2_truncate(struct bch_inode_info *, struct iattr *);
+int bch2_truncate(struct mnt_idmap *,
+                 struct bch_inode_info *, struct iattr *);
 long bch2_fallocate_dispatch(struct file *, int, loff_t, loff_t);
 
 loff_t bch2_remap_file_range(struct file *, loff_t, struct file *,
index efb4673167566ebe408635d3b150d692363721dd..71e738b9896725dd4132bdd89b95488fa62570af 100644 (file)
@@ -662,6 +662,9 @@ static void bch2_setattr_copy(struct mnt_idmap *idmap,
        if (ia_valid & ATTR_GID)
                bi->bi_gid = from_kgid(i_user_ns(&inode->v), attr->ia_gid);
 
+       if (ia_valid & ATTR_SIZE)
+               bi->bi_size = attr->ia_size;
+
        if (ia_valid & ATTR_ATIME)
                bi->bi_atime = timespec_to_bch2_time(c, attr->ia_atime);
        if (ia_valid & ATTR_MTIME)
@@ -682,9 +685,9 @@ static void bch2_setattr_copy(struct mnt_idmap *idmap,
        }
 }
 
-static int bch2_setattr_nonsize(struct mnt_idmap *idmap,
-                               struct bch_inode_info *inode,
-                               struct iattr *attr)
+int bch2_setattr_nonsize(struct mnt_idmap *idmap,
+                        struct bch_inode_info *inode,
+                        struct iattr *attr)
 {
        struct bch_fs *c = inode->v.i_sb->s_fs_info;
        struct bch_qid qid;
@@ -808,7 +811,7 @@ static int bch2_setattr(struct mnt_idmap *idmap,
                return ret;
 
        return iattr->ia_valid & ATTR_SIZE
-               ? bch2_truncate(inode, iattr)
+               ? bch2_truncate(idmap, inode, iattr)
                : bch2_setattr_nonsize(idmap, inode, iattr);
 }
 
index f3072780af51762f58b88697c158ba2f9a931fd6..c08a828d66cd0443b76fbee14e79abcda3e2597e 100644 (file)
@@ -166,6 +166,10 @@ void bch2_inode_update_after_write(struct bch_fs *,
 int __must_check bch2_write_inode(struct bch_fs *, struct bch_inode_info *,
                                  inode_set_fn, void *, unsigned);
 
+int bch2_setattr_nonsize(struct mnt_idmap *,
+                        struct bch_inode_info *,
+                        struct iattr *);
+
 void bch2_vfs_exit(void);
 int bch2_vfs_init(void);