bcachefs: rename keeps inheritable inode opts consistent
authorKent Overstreet <kent.overstreet@gmail.com>
Mon, 17 Dec 2018 10:31:49 +0000 (05:31 -0500)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:08:13 +0000 (17:08 -0400)
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/fs.c
fs/bcachefs/fs.h

index d22b9e7e208295b12b239662248cfd6bf419338a..033582a878521978f95670a7fa2f109e2a77eef2 100644 (file)
@@ -282,6 +282,32 @@ int bch2_fs_quota_transfer(struct bch_fs *c,
        return ret;
 }
 
+int bch2_reinherit_attrs_fn(struct bch_inode_info *inode,
+                           struct bch_inode_unpacked *bi,
+                           void *p)
+{
+       struct bch_inode_info *dir = p;
+       u64 src, dst;
+       unsigned id;
+       int ret = 1;
+
+       for (id = 0; id < Inode_opt_nr; id++) {
+               if (bi->bi_fields_set & (1 << id))
+                       continue;
+
+               src = bch2_inode_opt_get(&dir->ei_inode, id);
+               dst = bch2_inode_opt_get(bi, id);
+
+               if (src == dst)
+                       continue;
+
+               bch2_inode_opt_set(bi, id, src);
+               ret = 0;
+       }
+
+       return ret;
+}
+
 static struct inode *bch2_vfs_inode_get(struct bch_fs *c, u64 inum)
 {
        struct bch_inode_unpacked inode_u;
@@ -765,6 +791,7 @@ static int inode_update_for_rename_fn(struct bch_inode_info *inode,
                                      void *p)
 {
        struct rename_info *info = p;
+       int ret;
 
        if (inode == info->src_dir) {
                bi->bi_nlink -= S_ISDIR(info->src_inode->v.i_mode);
@@ -779,6 +806,19 @@ static int inode_update_for_rename_fn(struct bch_inode_info *inode,
                        S_ISDIR(info->dst_inode->v.i_mode);
        }
 
+       if (inode == info->src_inode) {
+               ret = bch2_reinherit_attrs_fn(inode, bi, info->dst_dir);
+
+               BUG_ON(!ret && S_ISDIR(info->src_inode->v.i_mode));
+       }
+
+       if (inode == info->dst_inode &&
+           info->mode == BCH_RENAME_EXCHANGE) {
+               ret = bch2_reinherit_attrs_fn(inode, bi, info->src_dir);
+
+               BUG_ON(!ret && S_ISDIR(info->dst_inode->v.i_mode));
+       }
+
        if (inode == info->dst_inode &&
            info->mode == BCH_RENAME_OVERWRITE) {
                BUG_ON(bi->bi_nlink &&
@@ -844,6 +884,39 @@ static int bch2_rename2(struct mnt_idmap *idmap,
                         i.dst_inode);
 
        bch2_trans_init(&trans, c);
+
+       if (S_ISDIR(i.src_inode->v.i_mode) &&
+           inode_attrs_changing(i.dst_dir, i.src_inode)) {
+               ret = -EXDEV;
+               goto err;
+       }
+
+       if (i.mode == BCH_RENAME_EXCHANGE &&
+           S_ISDIR(i.dst_inode->v.i_mode) &&
+           inode_attrs_changing(i.src_dir, i.dst_inode)) {
+               ret = -EXDEV;
+               goto err;
+       }
+
+       if (inode_attr_changing(i.dst_dir, i.src_inode, Inode_opt_project)) {
+               ret = bch2_fs_quota_transfer(c, i.src_inode,
+                                            i.dst_dir->ei_qid,
+                                            1 << QTYP_PRJ,
+                                            KEY_TYPE_QUOTA_PREALLOC);
+               if (ret)
+                       goto err;
+       }
+
+       if (i.mode == BCH_RENAME_EXCHANGE &&
+           inode_attr_changing(i.src_dir, i.dst_inode, Inode_opt_project)) {
+               ret = bch2_fs_quota_transfer(c, i.dst_inode,
+                                            i.src_dir->ei_qid,
+                                            1 << QTYP_PRJ,
+                                            KEY_TYPE_QUOTA_PREALLOC);
+               if (ret)
+                       goto err;
+       }
+
 retry:
        bch2_trans_begin(&trans);
        i.now = bch2_current_time(c);
@@ -894,6 +967,17 @@ retry:
                                              ATTR_CTIME);
 err:
        bch2_trans_exit(&trans);
+
+       bch2_fs_quota_transfer(c, i.src_inode,
+                              bch_qid(&i.src_inode->ei_inode),
+                              1 << QTYP_PRJ,
+                              KEY_TYPE_QUOTA_NOCHECK);
+       if (i.dst_inode)
+               bch2_fs_quota_transfer(c, i.dst_inode,
+                                      bch_qid(&i.dst_inode->ei_inode),
+                                      1 << QTYP_PRJ,
+                                      KEY_TYPE_QUOTA_NOCHECK);
+
        bch2_unlock_inodes(i.src_dir,
                           i.dst_dir,
                           i.src_inode,
index fbb31976bc559aa52bf7b0a2906727fb836acf68..18e41609c89de76cbfd3dc963f8cd0dc59ad7a28 100644 (file)
@@ -66,6 +66,27 @@ static inline unsigned nlink_bias(umode_t mode)
        return S_ISDIR(mode) ? 2 : 1;
 }
 
+static inline bool inode_attr_changing(struct bch_inode_info *dir,
+                               struct bch_inode_info *inode,
+                               enum inode_opt_id id)
+{
+       return !(inode->ei_inode.bi_fields_set & (1 << id)) &&
+               bch2_inode_opt_get(&dir->ei_inode, id) !=
+               bch2_inode_opt_get(&inode->ei_inode, id);
+}
+
+static inline bool inode_attrs_changing(struct bch_inode_info *dir,
+                                struct bch_inode_info *inode)
+{
+       unsigned id;
+
+       for (id = 0; id < Inode_opt_nr; id++)
+               if (inode_attr_changing(dir, inode, id))
+                       return true;
+
+       return false;
+}
+
 struct bch_inode_unpacked;
 
 #ifndef NO_BCACHEFS_FS
@@ -91,6 +112,10 @@ int __must_check bch2_write_inode_trans(struct btree_trans *,
 int __must_check bch2_write_inode(struct bch_fs *, struct bch_inode_info *,
                                  inode_set_fn, void *, unsigned);
 
+int bch2_reinherit_attrs_fn(struct bch_inode_info *,
+                           struct bch_inode_unpacked *,
+                           void *);
+
 void bch2_vfs_exit(void);
 int bch2_vfs_init(void);