]> git.sesse.net Git - bcachefs-tools-debian/commitdiff
Update bcachefs sources to 57ea7773cb bcachefs: Fix duplicate ioctl nr
authorKent Overstreet <kent.overstreet@gmail.com>
Mon, 17 Dec 2018 14:26:29 +0000 (09:26 -0500)
committerKent Overstreet <kent.overstreet@gmail.com>
Mon, 17 Dec 2018 14:26:29 +0000 (09:26 -0500)
14 files changed:
.bcachefs_revision
libbcachefs/bcachefs_format.h
libbcachefs/bcachefs_ioctl.h
libbcachefs/fs-ioctl.c
libbcachefs/fs.c
libbcachefs/fs.h
libbcachefs/inode.c
libbcachefs/inode.h
libbcachefs/opts.c
libbcachefs/opts.h
libbcachefs/quota.c
libbcachefs/quota.h
libbcachefs/quota_types.h
libbcachefs/xattr.c

index 0779c530a475f25412aec3a0e2ea4a97c2fc1c6a..d41e3b03d22ef1cc9536f4784814bdff8452f73d 100644 (file)
@@ -1 +1 @@
-f7670cba39ead5fcc99da93b46024bd6355c0663
+57ea7773cb4368dc98ec68385c3d0941c246ad70
index efda901efae1bbd9f8d535c3fe6fcd7de45c4ce4..9245465da1fd08bee32f774c8343bb9196356078 100644 (file)
@@ -689,38 +689,48 @@ struct bch_inode_generation {
        __le32                  pad;
 } __attribute__((packed, aligned(8)));
 
-#define BCH_INODE_FIELDS()                                     \
-       BCH_INODE_FIELD(bi_atime,                       64)     \
-       BCH_INODE_FIELD(bi_ctime,                       64)     \
-       BCH_INODE_FIELD(bi_mtime,                       64)     \
-       BCH_INODE_FIELD(bi_otime,                       64)     \
-       BCH_INODE_FIELD(bi_size,                        64)     \
-       BCH_INODE_FIELD(bi_sectors,                     64)     \
-       BCH_INODE_FIELD(bi_uid,                         32)     \
-       BCH_INODE_FIELD(bi_gid,                         32)     \
-       BCH_INODE_FIELD(bi_nlink,                       32)     \
-       BCH_INODE_FIELD(bi_generation,                  32)     \
-       BCH_INODE_FIELD(bi_dev,                         32)     \
-       BCH_INODE_FIELD(bi_data_checksum,               8)      \
-       BCH_INODE_FIELD(bi_compression,                 8)      \
-       BCH_INODE_FIELD(bi_project,                     32)     \
-       BCH_INODE_FIELD(bi_background_compression,      8)      \
-       BCH_INODE_FIELD(bi_data_replicas,               8)      \
-       BCH_INODE_FIELD(bi_promote_target,              16)     \
-       BCH_INODE_FIELD(bi_foreground_target,           16)     \
-       BCH_INODE_FIELD(bi_background_target,           16)     \
-       BCH_INODE_FIELD(bi_erasure_code,                16)
-
-#define BCH_INODE_FIELDS_INHERIT()                             \
-       BCH_INODE_FIELD(bi_data_checksum)                       \
-       BCH_INODE_FIELD(bi_compression)                         \
-       BCH_INODE_FIELD(bi_project)                             \
-       BCH_INODE_FIELD(bi_background_compression)              \
-       BCH_INODE_FIELD(bi_data_replicas)                       \
-       BCH_INODE_FIELD(bi_promote_target)                      \
-       BCH_INODE_FIELD(bi_foreground_target)                   \
-       BCH_INODE_FIELD(bi_background_target)                   \
-       BCH_INODE_FIELD(bi_erasure_code)
+#define BCH_INODE_FIELDS()                     \
+       x(bi_atime,                     64)     \
+       x(bi_ctime,                     64)     \
+       x(bi_mtime,                     64)     \
+       x(bi_otime,                     64)     \
+       x(bi_size,                      64)     \
+       x(bi_sectors,                   64)     \
+       x(bi_uid,                       32)     \
+       x(bi_gid,                       32)     \
+       x(bi_nlink,                     32)     \
+       x(bi_generation,                32)     \
+       x(bi_dev,                       32)     \
+       x(bi_data_checksum,             8)      \
+       x(bi_compression,               8)      \
+       x(bi_project,                   32)     \
+       x(bi_background_compression,    8)      \
+       x(bi_data_replicas,             8)      \
+       x(bi_promote_target,            16)     \
+       x(bi_foreground_target,         16)     \
+       x(bi_background_target,         16)     \
+       x(bi_erasure_code,              16)     \
+       x(bi_fields_set,                16)
+
+/* subset of BCH_INODE_FIELDS */
+#define BCH_INODE_OPTS()                       \
+       x(data_checksum,                8)      \
+       x(compression,                  8)      \
+       x(project,                      32)     \
+       x(background_compression,       8)      \
+       x(data_replicas,                8)      \
+       x(promote_target,               16)     \
+       x(foreground_target,            16)     \
+       x(background_target,            16)     \
+       x(erasure_code,                 16)
+
+enum inode_opt_id {
+#define x(name, ...)                           \
+       Inode_opt_##name,
+       BCH_INODE_OPTS()
+#undef  x
+       Inode_opt_nr,
+};
 
 enum {
        /*
index 73e5d887ccd8bfcc6cf87e8b7800566ac0903209..fb595dffbcc372e5908a7ed05c20e51e320430ec 100644 (file)
@@ -70,7 +70,11 @@ struct bch_ioctl_incremental {
 #define BCH_IOCTL_USAGE                _IOWR(0xbc,     11, struct bch_ioctl_usage)
 #define BCH_IOCTL_READ_SUPER   _IOW(0xbc,      12, struct bch_ioctl_read_super)
 #define BCH_IOCTL_DISK_GET_IDX _IOW(0xbc,      13,  struct bch_ioctl_disk_get_idx)
-#define BCH_IOCTL_DISK_RESIZE  _IOW(0xbc,      13,  struct bch_ioctl_disk_resize)
+#define BCH_IOCTL_DISK_RESIZE  _IOW(0xbc,      14,  struct bch_ioctl_disk_resize)
+
+/* ioctl below act on a particular file, not the filesystem as a whole: */
+
+#define BCHFS_IOC_REINHERIT_ATTRS      _IOR(0xbc, 64, const char __user *)
 
 /*
  * BCH_IOCTL_QUERY_UUID: get filesystem UUID
index 0eb0a0112a84f8c8a4daf7e5d42a4eba46acb924..7b9acafb949194c1444bebec6333c92f110dda56 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "bcachefs.h"
 #include "chardev.h"
+#include "dirent.h"
 #include "fs.h"
 #include "fs-ioctl.h"
 #include "quota.h"
@@ -107,21 +108,12 @@ static int bch2_set_projid(struct bch_fs *c,
                           u32 projid)
 {
        struct bch_qid qid = inode->ei_qid;
-       int ret;
-
-       if (projid == inode->ei_qid.q[QTYP_PRJ])
-               return 0;
 
        qid.q[QTYP_PRJ] = projid;
 
-       return bch2_quota_transfer(c, 1 << QTYP_PRJ, qid, inode->ei_qid,
-                                  inode->v.i_blocks +
-                                  inode->ei_quota_reserved);
-       if (ret)
-               return ret;
-
-       inode->ei_qid.q[QTYP_PRJ] = projid;
-       return 0;
+       return bch2_fs_quota_transfer(c, inode, qid,
+                                     1 << QTYP_PRJ,
+                                     KEY_TYPE_QUOTA_PREALLOC);
 }
 
 static int fssetxattr_inode_update_fn(struct bch_inode_info *inode,
@@ -130,7 +122,14 @@ static int fssetxattr_inode_update_fn(struct bch_inode_info *inode,
 {
        struct flags_set *s = p;
 
-       bi->bi_project = s->projid;
+       if (s->projid != bi->bi_project) {
+               if (s->projid)
+                       bi->bi_fields_set |= 1U << Inode_opt_project;
+               else
+                       bi->bi_fields_set &= ~(1U << Inode_opt_project);
+
+               bi->bi_project = s->projid;
+       }
 
        return bch2_inode_flags_set(inode, bi, p);
 }
@@ -178,6 +177,75 @@ err:
        return ret;
 }
 
+static int bch2_ioc_reinherit_attrs(struct bch_fs *c,
+                                   struct file *file,
+                                   struct bch_inode_info *src,
+                                   const char __user *name)
+{
+       struct bch_inode_info *dst;
+       struct inode *vinode = NULL;
+       char *kname = NULL;
+       struct qstr qstr;
+       int ret = 0;
+       u64 inum;
+
+       kname = kmalloc(BCH_NAME_MAX + 1, GFP_KERNEL);
+       if (!kname)
+               return -ENOMEM;
+
+       ret = strncpy_from_user(kname, name, BCH_NAME_MAX);
+       if (unlikely(ret < 0))
+               goto err1;
+
+       qstr.hash_len   = ret;
+       qstr.name       = kname;
+
+       ret = -ENOENT;
+       inum = bch2_dirent_lookup(c, src->v.i_ino,
+                                 &src->ei_str_hash,
+                                 &qstr);
+       if (!inum)
+               goto err1;
+
+       vinode = bch2_vfs_inode_get(c, inum);
+       ret = PTR_ERR_OR_ZERO(vinode);
+       if (ret)
+               goto err1;
+
+       dst = to_bch_ei(vinode);
+
+       ret = mnt_want_write_file(file);
+       if (ret)
+               goto err2;
+
+       bch2_lock_inodes(src, dst);
+
+       if (inode_attr_changing(src, dst, Inode_opt_project)) {
+               ret = bch2_fs_quota_transfer(c, dst,
+                                            src->ei_qid,
+                                            1 << QTYP_PRJ,
+                                            KEY_TYPE_QUOTA_PREALLOC);
+               if (ret)
+                       goto err3;
+       }
+
+       ret = bch2_write_inode(c, dst, bch2_reinherit_attrs_fn, src, 0);
+err3:
+       bch2_unlock_inodes(src, dst);
+
+       /* return true if we did work */
+       if (ret >= 0)
+               ret = !ret;
+
+       mnt_drop_write_file(file);
+err2:
+       iput(vinode);
+err1:
+       kfree(kname);
+
+       return ret;
+}
+
 long bch2_fs_file_ioctl(struct file *file, unsigned cmd, unsigned long arg)
 {
        struct bch_inode_info *inode = file_bch_inode(file);
@@ -194,7 +262,12 @@ long bch2_fs_file_ioctl(struct file *file, unsigned cmd, unsigned long arg)
        case FS_IOC_FSGETXATTR:
                return bch2_ioc_fsgetxattr(inode, (void __user *) arg);
        case FS_IOC_FSSETXATTR:
-               return bch2_ioc_fssetxattr(c, file, inode, (void __user *) arg);
+               return bch2_ioc_fssetxattr(c, file, inode,
+                                          (void __user *) arg);
+
+       case BCHFS_IOC_REINHERIT_ATTRS:
+               return bch2_ioc_reinherit_attrs(c, file, inode,
+                                               (void __user *) arg);
 
        case FS_IOC_GETVERSION:
                return -ENOTTY;
index 13670a638e0a3c7abfde55bf7e9a1843fec66db7..300348b6648b59d46ae10f7176d518711bb19901 100644 (file)
@@ -47,30 +47,6 @@ static void journal_seq_copy(struct bch_inode_info *dst,
        } while ((v = cmpxchg(&dst->ei_journal_seq, old, journal_seq)) != old);
 }
 
-static inline int ptrcmp(void *l, void *r)
-{
-       return (l > r) - (l < r);
-}
-
-#define __bch2_lock_inodes(_lock, ...)                                 \
-do {                                                                   \
-       struct bch_inode_info *a[] = { NULL, __VA_ARGS__ };             \
-       unsigned i;                                                     \
-                                                                       \
-       bubble_sort(&a[1], ARRAY_SIZE(a) - 1 , ptrcmp);                 \
-                                                                       \
-       for (i = ARRAY_SIZE(a) - 1; a[i]; --i)                          \
-               if (a[i] != a[i - 1]) {                                 \
-                       if (_lock)                                      \
-                               mutex_lock_nested(&a[i]->ei_update_lock, i);\
-                       else                                            \
-                               mutex_unlock(&a[i]->ei_update_lock);    \
-               }                                                       \
-} while (0)
-
-#define bch2_lock_inodes(...)  __bch2_lock_inodes(true, __VA_ARGS__)
-#define bch2_unlock_inodes(...)        __bch2_lock_inodes(false, __VA_ARGS__)
-
 /*
  * I_SIZE_DIRTY requires special handling:
  *
@@ -119,7 +95,6 @@ void bch2_inode_update_after_write(struct bch_fs *c,
                inode->v.i_ctime = bch2_time_to_timespec(c, bi->bi_ctime);
 
        inode->ei_inode         = *bi;
-       inode->ei_qid           = bch_qid(bi);
 
        bch2_inode_flags_to_vfs(inode);
 }
@@ -197,7 +172,68 @@ retry:
        return ret < 0 ? ret : 0;
 }
 
-static struct inode *bch2_vfs_inode_get(struct bch_fs *c, u64 inum)
+int bch2_fs_quota_transfer(struct bch_fs *c,
+                          struct bch_inode_info *inode,
+                          struct bch_qid new_qid,
+                          unsigned qtypes,
+                          enum quota_acct_mode mode)
+{
+       unsigned i;
+       int ret;
+
+       qtypes &= enabled_qtypes(c);
+
+       for (i = 0; i < QTYP_NR; i++)
+               if (new_qid.q[i] == inode->ei_qid.q[i])
+                       qtypes &= ~(1U << i);
+
+       if (!qtypes)
+               return 0;
+
+       mutex_lock(&inode->ei_quota_lock);
+
+       ret = bch2_quota_transfer(c, qtypes, new_qid,
+                                 inode->ei_qid,
+                                 inode->v.i_blocks +
+                                 inode->ei_quota_reserved,
+                                 mode);
+       if (!ret)
+               for (i = 0; i < QTYP_NR; i++)
+                       if (qtypes & (1 << i))
+                               inode->ei_qid.q[i] = new_qid.q[i];
+
+       mutex_unlock(&inode->ei_quota_lock);
+
+       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;
+}
+
+struct inode *bch2_vfs_inode_get(struct bch_fs *c, u64 inum)
 {
        struct bch_inode_unpacked inode_u;
        struct bch_inode_info *inode;
@@ -274,14 +310,13 @@ __bch2_create(struct bch_inode_info *dir, struct dentry *dentry,
        bch2_inode_init(c, &inode_u, 0, 0, 0, rdev, &dir->ei_inode);
        bch2_inode_init_owner(&inode_u, &dir->v, mode);
 
-       inode_u.bi_project = dir->ei_qid.q[QTYP_PRJ];
-
        hash_info = bch2_hash_info_init(c, &inode_u);
 
        if (tmpfile)
                inode_u.bi_flags |= BCH_INODE_UNLINKED;
 
-       ret = bch2_quota_acct(c, bch_qid(&inode_u), Q_INO, 1, KEY_TYPE_QUOTA_PREALLOC);
+       ret = bch2_quota_acct(c, bch_qid(&inode_u), Q_INO, 1,
+                             KEY_TYPE_QUOTA_PREALLOC);
        if (ret)
                return ERR_PTR(ret);
 
@@ -664,6 +699,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);
@@ -678,6 +714,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 &&
@@ -742,6 +791,39 @@ static int bch2_rename2(struct inode *src_vdir, struct dentry *src_dentry,
                         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);
@@ -792,6 +874,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,
@@ -838,36 +931,26 @@ static int inode_update_for_setattr_fn(struct bch_inode_info *inode,
 static int bch2_setattr_nonsize(struct bch_inode_info *inode, struct iattr *iattr)
 {
        struct bch_fs *c = inode->v.i_sb->s_fs_info;
-       struct bch_qid qid = inode->ei_qid;
+       struct bch_qid qid;
        struct btree_trans trans;
        struct bch_inode_unpacked inode_u;
        struct posix_acl *acl = NULL;
-       unsigned qtypes = 0;
        int ret;
 
        mutex_lock(&inode->ei_update_lock);
 
-       if (c->opts.usrquota &&
-           (iattr->ia_valid & ATTR_UID) &&
-           !uid_eq(iattr->ia_uid, inode->v.i_uid)) {
-               qid.q[QTYP_USR] = from_kuid(&init_user_ns, iattr->ia_uid),
-               qtypes |= 1 << QTYP_USR;
-       }
+       qid = inode->ei_qid;
 
-       if (c->opts.grpquota &&
-           (iattr->ia_valid & ATTR_GID) &&
-           !gid_eq(iattr->ia_gid, inode->v.i_gid)) {
+       if (iattr->ia_valid & ATTR_UID)
+               qid.q[QTYP_USR] = from_kuid(&init_user_ns, iattr->ia_uid);
+
+       if (iattr->ia_valid & ATTR_GID)
                qid.q[QTYP_GRP] = from_kgid(&init_user_ns, iattr->ia_gid);
-               qtypes |= 1 << QTYP_GRP;
-       }
 
-       if (qtypes) {
-               ret = bch2_quota_transfer(c, qtypes, qid, inode->ei_qid,
-                                         inode->v.i_blocks +
-                                         inode->ei_quota_reserved);
-               if (ret)
-                       goto err;
-       }
+       ret = bch2_fs_quota_transfer(c, inode, qid, ~0,
+                                    KEY_TYPE_QUOTA_PREALLOC);
+       if (ret)
+               goto err;
 
        bch2_trans_init(&trans, c);
 retry:
@@ -1228,6 +1311,7 @@ static void bch2_vfs_inode_init(struct bch_fs *c,
        inode->ei_journal_seq   = 0;
        inode->ei_quota_reserved = 0;
        inode->ei_str_hash      = bch2_hash_info_init(c, bi);
+       inode->ei_qid           = bch_qid(bi);
 
        inode->v.i_mapping->a_ops = &bch_address_space_operations;
 
index a434c757e526f35d3893b083fdc77be98432e6cf..7dc0453af2db42277a478562689ad52554006bff 100644 (file)
@@ -29,6 +29,30 @@ struct bch_inode_info {
 #define to_bch_ei(_inode)                                      \
        container_of_or_null(_inode, struct bch_inode_info, v)
 
+static inline int ptrcmp(void *l, void *r)
+{
+       return (l > r) - (l < r);
+}
+
+#define __bch2_lock_inodes(_lock, ...)                                 \
+do {                                                                   \
+       struct bch_inode_info *a[] = { NULL, __VA_ARGS__ };             \
+       unsigned i;                                                     \
+                                                                       \
+       bubble_sort(&a[1], ARRAY_SIZE(a) - 1 , ptrcmp);                 \
+                                                                       \
+       for (i = ARRAY_SIZE(a) - 1; a[i]; --i)                          \
+               if (a[i] != a[i - 1]) {                                 \
+                       if (_lock)                                      \
+                               mutex_lock_nested(&a[i]->ei_update_lock, i);\
+                       else                                            \
+                               mutex_unlock(&a[i]->ei_update_lock);    \
+               }                                                       \
+} while (0)
+
+#define bch2_lock_inodes(...)  __bch2_lock_inodes(true, __VA_ARGS__)
+#define bch2_unlock_inodes(...)        __bch2_lock_inodes(false, __VA_ARGS__)
+
 static inline struct bch_inode_info *file_bch_inode(struct file *file)
 {
        return to_bch_ei(file_inode(file));
@@ -49,10 +73,39 @@ static inline u64 bch2_current_time(struct bch_fs *c)
        return timespec_to_bch2_time(c, current_kernel_time64());
 }
 
+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
 
+int bch2_fs_quota_transfer(struct bch_fs *,
+                          struct bch_inode_info *,
+                          struct bch_qid,
+                          unsigned,
+                          enum quota_acct_mode);
+
+struct inode *bch2_vfs_inode_get(struct bch_fs *, u64);
+
 /* returns 0 if we want to do the update, or error is passed up */
 typedef int (*inode_set_fn)(struct bch_inode_info *,
                            struct bch_inode_unpacked *, void *);
@@ -68,6 +121,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);
 
index 8c3d4431de109478905c46a7be67d1962601e4a7..f851e3b73170945154e68b15475ade633c7f3fd8 100644 (file)
 
 #include <asm/unaligned.h>
 
-#define FIELD_BYTES()                                          \
+const char * const bch2_inode_opts[] = {
+#define x(name, ...)   #name,
+       BCH_INODE_OPTS()
+#undef  x
+       NULL,
+};
 
 static const u8 byte_table[8] = { 1, 2, 3, 4, 6, 8, 10, 13 };
 static const u8 bits_table[8] = {
@@ -97,7 +102,7 @@ void bch2_inode_pack(struct bkey_inode_buf *packed,
        packed->inode.v.bi_flags        = cpu_to_le32(inode->bi_flags);
        packed->inode.v.bi_mode         = cpu_to_le16(inode->bi_mode);
 
-#define BCH_INODE_FIELD(_name, _bits)                                  \
+#define x(_name, _bits)                                        \
        out += inode_encode_field(out, end, 0, inode->_name);           \
        nr_fields++;                                                    \
                                                                        \
@@ -107,7 +112,7 @@ void bch2_inode_pack(struct bkey_inode_buf *packed,
        }
 
        BCH_INODE_FIELDS()
-#undef  BCH_INODE_FIELD
+#undef  x
 
        out = last_nonzero_field;
        nr_fields = last_nonzero_fieldnr;
@@ -129,9 +134,9 @@ void bch2_inode_pack(struct bkey_inode_buf *packed,
                BUG_ON(unpacked.bi_hash_seed    != inode->bi_hash_seed);
                BUG_ON(unpacked.bi_mode         != inode->bi_mode);
 
-#define BCH_INODE_FIELD(_name, _bits)  BUG_ON(unpacked._name != inode->_name);
+#define x(_name, _bits)        BUG_ON(unpacked._name != inode->_name);
                BCH_INODE_FIELDS()
-#undef  BCH_INODE_FIELD
+#undef  x
        }
 }
 
@@ -149,7 +154,7 @@ int bch2_inode_unpack(struct bkey_s_c_inode inode,
        unpacked->bi_flags      = le32_to_cpu(inode.v->bi_flags);
        unpacked->bi_mode       = le16_to_cpu(inode.v->bi_mode);
 
-#define BCH_INODE_FIELD(_name, _bits)                                  \
+#define x(_name, _bits)                                        \
        if (fieldnr++ == INODE_NR_FIELDS(inode.v)) {                    \
                memset(&unpacked->_name, 0,                             \
                       sizeof(*unpacked) -                              \
@@ -168,7 +173,7 @@ int bch2_inode_unpack(struct bkey_s_c_inode inode,
        in += ret;
 
        BCH_INODE_FIELDS()
-#undef  BCH_INODE_FIELD
+#undef  x
 
        /* XXX: signal if there were more fields than expected? */
 
@@ -219,10 +224,10 @@ void bch2_inode_to_text(struct printbuf *out, struct bch_fs *c,
                return;
        }
 
-#define BCH_INODE_FIELD(_name, _bits)                                          \
+#define x(_name, _bits)                                                \
        pr_buf(out, #_name ": %llu ", (u64) unpacked._name);
        BCH_INODE_FIELDS()
-#undef  BCH_INODE_FIELD
+#undef  x
 }
 
 const char *bch2_inode_generation_invalid(const struct bch_fs *c,
@@ -254,7 +259,8 @@ void bch2_inode_init(struct bch_fs *c, struct bch_inode_unpacked *inode_u,
 
        /* ick */
        inode_u->bi_flags |= c->opts.str_hash << INODE_STR_HASH_OFFSET;
-       get_random_bytes(&inode_u->bi_hash_seed, sizeof(inode_u->bi_hash_seed));
+       get_random_bytes(&inode_u->bi_hash_seed,
+                        sizeof(inode_u->bi_hash_seed));
 
        inode_u->bi_mode        = mode;
        inode_u->bi_uid         = uid;
@@ -266,9 +272,9 @@ void bch2_inode_init(struct bch_fs *c, struct bch_inode_unpacked *inode_u,
        inode_u->bi_otime       = now;
 
        if (parent) {
-#define BCH_INODE_FIELD(_name) inode_u->_name = parent->_name;
-               BCH_INODE_FIELDS_INHERIT()
-#undef BCH_INODE_FIELD
+#define x(_name, ...)  inode_u->bi_##_name = parent->bi_##_name;
+               BCH_INODE_OPTS()
+#undef x
        }
 }
 
index 44855e1a163e37eed741b6c7216f3c0249d9c373..0d609985d15be9cc87397996e3d018f1e8595a6c 100644 (file)
@@ -5,6 +5,8 @@
 
 #include <linux/math64.h>
 
+extern const char * const bch2_inode_opts[];
+
 const char *bch2_inode_invalid(const struct bch_fs *, struct bkey_s_c);
 void bch2_inode_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
 
@@ -29,17 +31,17 @@ struct bch_inode_unpacked {
        u32                     bi_flags;
        u16                     bi_mode;
 
-#define BCH_INODE_FIELD(_name, _bits)  u##_bits _name;
+#define x(_name, _bits)        u##_bits _name;
        BCH_INODE_FIELDS()
-#undef  BCH_INODE_FIELD
+#undef  x
 };
 
 struct bkey_inode_buf {
        struct bkey_i_inode     inode;
 
-#define BCH_INODE_FIELD(_name, _bits)          + 8 + _bits / 8
+#define x(_name, _bits)                + 8 + _bits / 8
        u8              _pad[0 + BCH_INODE_FIELDS()];
-#undef  BCH_INODE_FIELD
+#undef  x
 } __attribute__((packed, aligned(8)));
 
 void bch2_inode_pack(struct bkey_inode_buf *, const struct bch_inode_unpacked *);
@@ -79,39 +81,41 @@ static inline struct bch_io_opts bch2_inode_opts_get(struct bch_inode_unpacked *
 {
        struct bch_io_opts ret = { 0 };
 
-#define BCH_INODE_OPT(_name, _bits)                                    \
+#define x(_name, _bits)                                        \
        if (inode->bi_##_name)                                          \
                opt_set(ret, _name, inode->bi_##_name - 1);
        BCH_INODE_OPTS()
-#undef BCH_INODE_OPT
+#undef x
        return ret;
 }
 
-static inline void __bch2_inode_opt_set(struct bch_inode_unpacked *inode,
-                                       enum bch_opt_id id, u64 v)
+static inline void bch2_inode_opt_set(struct bch_inode_unpacked *inode,
+                                     enum inode_opt_id id, u64 v)
 {
        switch (id) {
-#define BCH_INODE_OPT(_name, ...)                                      \
-       case Opt_##_name:                                               \
+#define x(_name, ...)                                                  \
+       case Inode_opt_##_name:                                         \
                inode->bi_##_name = v;                                  \
                break;
        BCH_INODE_OPTS()
-#undef BCH_INODE_OPT
+#undef x
        default:
                BUG();
        }
 }
 
-static inline void bch2_inode_opt_set(struct bch_inode_unpacked *inode,
-                                     enum bch_opt_id id, u64 v)
-{
-       return __bch2_inode_opt_set(inode, id, v + 1);
-}
-
-static inline void bch2_inode_opt_clear(struct bch_inode_unpacked *inode,
-                                       enum bch_opt_id id)
+static inline u64 bch2_inode_opt_get(struct bch_inode_unpacked *inode,
+                                    enum inode_opt_id id)
 {
-       return __bch2_inode_opt_set(inode, id, 0);
+       switch (id) {
+#define x(_name, ...)                                                  \
+       case Inode_opt_##_name:                                         \
+               return inode->bi_##_name;
+       BCH_INODE_OPTS()
+#undef x
+       default:
+               BUG();
+       }
 }
 
 #ifdef CONFIG_BCACHEFS_DEBUG
index 449cd5bfcfc7317adc615e72a5f4ad385b6cc811..4739f78262da4a69e818d4a76604da204952057f 100644 (file)
@@ -376,40 +376,40 @@ no_val:
 struct bch_io_opts bch2_opts_to_inode_opts(struct bch_opts src)
 {
        struct bch_io_opts ret = { 0 };
-#define BCH_INODE_OPT(_name, _bits)                                    \
+#define x(_name, _bits)                                        \
        if (opt_defined(src, _name))                                    \
                opt_set(ret, _name, src._name);
        BCH_INODE_OPTS()
-#undef BCH_INODE_OPT
+#undef x
        return ret;
 }
 
 struct bch_opts bch2_inode_opts_to_opts(struct bch_io_opts src)
 {
        struct bch_opts ret = { 0 };
-#define BCH_INODE_OPT(_name, _bits)                                    \
+#define x(_name, _bits)                                        \
        if (opt_defined(src, _name))                                    \
                opt_set(ret, _name, src._name);
        BCH_INODE_OPTS()
-#undef BCH_INODE_OPT
+#undef x
        return ret;
 }
 
 void bch2_io_opts_apply(struct bch_io_opts *dst, struct bch_io_opts src)
 {
-#define BCH_INODE_OPT(_name, _bits)                                    \
+#define x(_name, _bits)                                        \
        if (opt_defined(src, _name))                                    \
                opt_set(*dst, _name, src._name);
        BCH_INODE_OPTS()
-#undef BCH_INODE_OPT
+#undef x
 }
 
 bool bch2_opt_is_inode_opt(enum bch_opt_id id)
 {
        static const enum bch_opt_id inode_opt_list[] = {
-#define BCH_INODE_OPT(_name, _bits)    Opt_##_name,
+#define x(_name, _bits)        Opt_##_name,
        BCH_INODE_OPTS()
-#undef BCH_INODE_OPT
+#undef x
        };
        unsigned i;
 
index 5c74401848a0d9035de50ffd74921b4ce3bc33b6..8a4e2e5072b07cb2c3f28bb14e08971de276067d 100644 (file)
@@ -184,7 +184,10 @@ enum opt_type {
                NO_SB_OPT,                      false)                  \
        BCH_OPT(version_upgrade,        u8,     OPT_MOUNT,              \
                OPT_BOOL(),                                             \
-               NO_SB_OPT,                      false)
+               NO_SB_OPT,                      false)                  \
+       BCH_OPT(project,                u8,     OPT_INTERNAL,           \
+               OPT_BOOL(),                                             \
+               NO_SB_OPT,                      false)                  \
 
 struct bch_opts {
 #define BCH_OPT(_name, _bits, ...)     unsigned _name##_defined:1;
@@ -277,24 +280,14 @@ int bch2_parse_mount_opts(struct bch_opts *, char *);
 
 /* inode opts: */
 
-#define BCH_INODE_OPTS()                                       \
-       BCH_INODE_OPT(data_checksum,                    8)      \
-       BCH_INODE_OPT(compression,                      8)      \
-       BCH_INODE_OPT(background_compression,           8)      \
-       BCH_INODE_OPT(data_replicas,                    8)      \
-       BCH_INODE_OPT(promote_target,                   16)     \
-       BCH_INODE_OPT(foreground_target,                16)     \
-       BCH_INODE_OPT(background_target,                16)     \
-       BCH_INODE_OPT(erasure_code,                     16)
-
 struct bch_io_opts {
-#define BCH_INODE_OPT(_name, _bits)    unsigned _name##_defined:1;
+#define x(_name, _bits)        unsigned _name##_defined:1;
        BCH_INODE_OPTS()
-#undef BCH_INODE_OPT
+#undef x
 
-#define BCH_INODE_OPT(_name, _bits)    u##_bits _name;
+#define x(_name, _bits)        u##_bits _name;
        BCH_INODE_OPTS()
-#undef BCH_INODE_OPT
+#undef x
 };
 
 struct bch_io_opts bch2_opts_to_inode_opts(struct bch_opts);
index 95ff0caea19917a2d69d86eb9ed69daa86c3b3c0..44aacd40bfe2f824686e67e0d08c2f091eebf2b6 100644 (file)
@@ -269,7 +269,8 @@ static void __bch2_quota_transfer(struct bch_memquota *src_q,
 
 int bch2_quota_transfer(struct bch_fs *c, unsigned qtypes,
                        struct bch_qid dst,
-                       struct bch_qid src, u64 space)
+                       struct bch_qid src, u64 space,
+                       enum quota_acct_mode mode)
 {
        struct bch_memquota_type *q;
        struct bch_memquota *src_q[3], *dst_q[3];
@@ -295,13 +296,13 @@ int bch2_quota_transfer(struct bch_fs *c, unsigned qtypes,
 
                ret = bch2_quota_check_limit(c, i, dst_q[i], &msgs, Q_SPC,
                                             dst_q[i]->c[Q_SPC].v + space,
-                                            KEY_TYPE_QUOTA_PREALLOC);
+                                            mode);
                if (ret)
                        goto err;
 
                ret = bch2_quota_check_limit(c, i, dst_q[i], &msgs, Q_INO,
                                             dst_q[i]->c[Q_INO].v + 1,
-                                            KEY_TYPE_QUOTA_PREALLOC);
+                                            mode);
                if (ret)
                        goto err;
        }
index 0c3eb6973de1e8eed3b00fca671d40b6816368dd..eabb94c143a8457e04af7de6bd9583018a6bf412 100644 (file)
@@ -14,12 +14,6 @@ void bch2_quota_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
        .val_to_text    = bch2_quota_to_text,           \
 }
 
-enum quota_acct_mode {
-       KEY_TYPE_QUOTA_PREALLOC,
-       KEY_TYPE_QUOTA_WARN,
-       KEY_TYPE_QUOTA_NOCHECK,
-};
-
 static inline struct bch_qid bch_qid(struct bch_inode_unpacked *u)
 {
        return (struct bch_qid) {
@@ -42,7 +36,7 @@ int bch2_quota_acct(struct bch_fs *, struct bch_qid, enum quota_counters,
                    s64, enum quota_acct_mode);
 
 int bch2_quota_transfer(struct bch_fs *, unsigned, struct bch_qid,
-                       struct bch_qid, u64);
+                       struct bch_qid, u64, enum quota_acct_mode);
 
 void bch2_fs_quota_exit(struct bch_fs *);
 void bch2_fs_quota_init(struct bch_fs *);
index bcaed4ea8345837bd901ef80e943656f508a0651..e978dc54b6aa7c6fb4dc16a6cf53d3c4e0bdee7d 100644 (file)
@@ -7,6 +7,12 @@ struct bch_qid {
        u32             q[QTYP_NR];
 };
 
+enum quota_acct_mode {
+       KEY_TYPE_QUOTA_PREALLOC,
+       KEY_TYPE_QUOTA_WARN,
+       KEY_TYPE_QUOTA_NOCHECK,
+};
+
 struct memquota_counter {
        u64                             v;
        u64                             hardlimit;
index 85d8bdd3a7b5f95d77fbf4f1640aae37625c996c..407177b25e242ebb3f6ceb32a30f477c1c782486 100644 (file)
@@ -197,40 +197,83 @@ int bch2_xattr_set(struct btree_trans *trans, u64 inum,
        return ret;
 }
 
-static size_t bch2_xattr_emit(struct dentry *dentry,
-                            const struct bch_xattr *xattr,
-                            char *buffer, size_t buffer_size)
+static void __bch2_xattr_emit(const char *prefix,
+                             const char *name, size_t name_len,
+                             char **buffer, size_t *buffer_size,
+                             ssize_t *ret)
+{
+       const size_t prefix_len = strlen(prefix);
+       const size_t total_len = prefix_len + name_len + 1;
+
+       if (*buffer) {
+               if (total_len > *buffer_size) {
+                       *ret = -ERANGE;
+                       return;
+               }
+
+               memcpy(*buffer, prefix, prefix_len);
+               memcpy(*buffer + prefix_len,
+                      name, name_len);
+               (*buffer)[prefix_len + name_len] = '\0';
+
+               *buffer         += total_len;
+               *buffer_size    -= total_len;
+       }
+
+       *ret += total_len;
+}
+
+static void bch2_xattr_emit(struct dentry *dentry,
+                           const struct bch_xattr *xattr,
+                           char **buffer, size_t *buffer_size,
+                           ssize_t *ret)
 {
        const struct xattr_handler *handler =
                bch2_xattr_type_to_handler(xattr->x_type);
 
-       if (handler && (!handler->list || handler->list(dentry))) {
-               const char *prefix = handler->prefix ?: handler->name;
-               const size_t prefix_len = strlen(prefix);
-               const size_t total_len = prefix_len + xattr->x_name_len + 1;
+       if (handler && (!handler->list || handler->list(dentry)))
+               __bch2_xattr_emit(handler->prefix ?: handler->name,
+                                 xattr->x_name, xattr->x_name_len,
+                                 buffer, buffer_size, ret);
+}
 
-               if (buffer && total_len <= buffer_size) {
-                       memcpy(buffer, prefix, prefix_len);
-                       memcpy(buffer + prefix_len,
-                              xattr->x_name, xattr->x_name_len);
-                       buffer[prefix_len + xattr->x_name_len] = '\0';
-               }
+static void bch2_xattr_list_bcachefs(struct bch_fs *c,
+                                    struct bch_inode_info *inode,
+                                    char **buffer,
+                                    size_t *buffer_size,
+                                    ssize_t *ret,
+                                    bool all)
+{
+       const char *prefix = all ? "bcachefs_effective." : "bcachefs.";
+       unsigned id;
+       u64 v;
 
-               return total_len;
-       } else {
-               return 0;
+       for (id = 0; id < Inode_opt_nr; id++) {
+               v = bch2_inode_opt_get(&inode->ei_inode, id);
+               if (!v)
+                       continue;
+
+               if (!all &&
+                   !(inode->ei_inode.bi_fields_set & (1 << id)))
+                       continue;
+
+               __bch2_xattr_emit(prefix,
+                                 bch2_inode_opts[id],
+                                 strlen(bch2_inode_opts[id]),
+                                 buffer, buffer_size, ret);
+               if (*ret < 0)
+                       break;
        }
 }
 
 ssize_t bch2_xattr_list(struct dentry *dentry, char *buffer, size_t buffer_size)
 {
        struct bch_fs *c = dentry->d_sb->s_fs_info;
+       struct bch_inode_info *inode = to_bch_ei(dentry->d_inode);
        struct btree_iter iter;
        struct bkey_s_c k;
-       const struct bch_xattr *xattr;
        u64 inum = dentry->d_inode->i_ino;
        ssize_t ret = 0;
-       size_t len;
 
        for_each_btree_key(&iter, c, BTREE_ID_XATTRS, POS(inum, 0), 0, k) {
                BUG_ON(k.k->p.inode < inum);
@@ -241,23 +284,25 @@ ssize_t bch2_xattr_list(struct dentry *dentry, char *buffer, size_t buffer_size)
                if (k.k->type != KEY_TYPE_xattr)
                        continue;
 
-               xattr = bkey_s_c_to_xattr(k).v;
-
-               len = bch2_xattr_emit(dentry, xattr, buffer, buffer_size);
-               if (buffer) {
-                       if (len > buffer_size) {
-                               bch2_btree_iter_unlock(&iter);
-                               return -ERANGE;
-                       }
+               bch2_xattr_emit(dentry, bkey_s_c_to_xattr(k).v,
+                               &buffer, &buffer_size, &ret);
+               if (ret < 0)
+                       break;
+       }
+       bch2_btree_iter_unlock(&iter);
 
-                       buffer += len;
-                       buffer_size -= len;
-               }
+       if (ret < 0)
+               return ret;
 
-               ret += len;
+       bch2_xattr_list_bcachefs(c, inode, &buffer,
+                                &buffer_size, &ret, false);
+       if (ret < 0)
+               return ret;
 
-       }
-       bch2_btree_iter_unlock(&iter);
+       bch2_xattr_list_bcachefs(c, inode, &buffer,
+                                &buffer_size, &ret, true);
+       if (ret < 0)
+               return ret;
 
        return ret;
 }
@@ -316,27 +361,48 @@ static const struct xattr_handler bch_xattr_security_handler = {
 
 #ifndef NO_BCACHEFS_FS
 
-static int bch2_xattr_bcachefs_get(const struct xattr_handler *handler,
-                                  struct dentry *dentry, struct inode *vinode,
-                                  const char *name, void *buffer, size_t size)
+static int opt_to_inode_opt(int id)
+{
+       switch (id) {
+#define x(name, ...)                           \
+       case Opt_##name: return Inode_opt_##name;
+       BCH_INODE_OPTS()
+#undef  x
+       default:
+               return -1;
+       }
+}
+
+static int __bch2_xattr_bcachefs_get(const struct xattr_handler *handler,
+                               struct dentry *dentry, struct inode *vinode,
+                               const char *name, void *buffer, size_t size,
+                               bool all)
 {
        struct bch_inode_info *inode = to_bch_ei(vinode);
        struct bch_fs *c = inode->v.i_sb->s_fs_info;
        struct bch_opts opts =
                bch2_inode_opts_to_opts(bch2_inode_opts_get(&inode->ei_inode));
        const struct bch_option *opt;
-       int id;
+       int id, inode_opt_id;
        u64 v;
 
        id = bch2_opt_lookup(name);
        if (id < 0 || !bch2_opt_is_inode_opt(id))
                return -EINVAL;
 
+       inode_opt_id = opt_to_inode_opt(id);
+       if (inode_opt_id < 0)
+               return -EINVAL;
+
        opt = bch2_opt_table + id;
 
        if (!bch2_opt_defined_by_id(&opts, id))
                return -ENODATA;
 
+       if (!all &&
+           !(inode->ei_inode.bi_fields_set & (1 << inode_opt_id)))
+               return -ENODATA;
+
        v = bch2_opt_get_by_id(&opts, id);
 
        if (!buffer) {
@@ -357,6 +423,14 @@ static int bch2_xattr_bcachefs_get(const struct xattr_handler *handler,
        }
 }
 
+static int bch2_xattr_bcachefs_get(const struct xattr_handler *handler,
+                                  struct dentry *dentry, struct inode *vinode,
+                                  const char *name, void *buffer, size_t size)
+{
+       return __bch2_xattr_bcachefs_get(handler, dentry, vinode,
+                                        name, buffer, size, false);
+}
+
 struct inode_opt_set {
        int                     id;
        u64                     v;
@@ -370,9 +444,12 @@ static int inode_opt_set_fn(struct bch_inode_info *inode,
        struct inode_opt_set *s = p;
 
        if (s->defined)
-               bch2_inode_opt_set(bi, s->id, s->v);
+               bi->bi_fields_set |= 1U << s->id;
        else
-               bch2_inode_opt_clear(bi, s->id);
+               bi->bi_fields_set &= ~(1U << s->id);
+
+       bch2_inode_opt_set(bi, s->id, s->v);
+
        return 0;
 }
 
@@ -386,33 +463,51 @@ static int bch2_xattr_bcachefs_set(const struct xattr_handler *handler,
        const struct bch_option *opt;
        char *buf;
        struct inode_opt_set s;
-       int ret;
+       int opt_id, inode_opt_id, ret;
+
+       opt_id = bch2_opt_lookup(name);
+       if (opt_id < 0)
+               return -EINVAL;
 
-       s.id = bch2_opt_lookup(name);
-       if (s.id < 0 || !bch2_opt_is_inode_opt(s.id))
+       opt = bch2_opt_table + opt_id;
+
+       inode_opt_id = opt_to_inode_opt(opt_id);
+       if (inode_opt_id < 0)
                return -EINVAL;
 
-       opt = bch2_opt_table + s.id;
+       s.id = inode_opt_id;
 
        if (value) {
+               u64 v = 0;
+
                buf = kmalloc(size + 1, GFP_KERNEL);
                if (!buf)
                        return -ENOMEM;
                memcpy(buf, value, size);
                buf[size] = '\0';
 
-               ret = bch2_opt_parse(c, opt, buf, &s.v);
+               ret = bch2_opt_parse(c, opt, buf, &v);
                kfree(buf);
 
                if (ret < 0)
                        return ret;
 
-               ret = bch2_opt_check_may_set(c, s.id, s.v);
+               ret = bch2_opt_check_may_set(c, opt_id, v);
                if (ret < 0)
                        return ret;
 
+               s.v = v + 1;
                s.defined = true;
        } else {
+               if (!IS_ROOT(dentry)) {
+                       struct bch_inode_info *dir =
+                               to_bch_ei(d_inode(dentry->d_parent));
+
+                       s.v = bch2_inode_opt_get(&dir->ei_inode, inode_opt_id);
+               } else {
+                       s.v = 0;
+               }
+
                s.defined = false;
        }
 
@@ -421,8 +516,8 @@ static int bch2_xattr_bcachefs_set(const struct xattr_handler *handler,
        mutex_unlock(&inode->ei_update_lock);
 
        if (value &&
-           (s.id == Opt_background_compression ||
-            s.id == Opt_background_target))
+           (opt_id == Opt_background_compression ||
+            opt_id == Opt_background_target))
                bch2_rebalance_add_work(c, inode->v.i_blocks);
 
        return ret;
@@ -434,6 +529,21 @@ static const struct xattr_handler bch_xattr_bcachefs_handler = {
        .set    = bch2_xattr_bcachefs_set,
 };
 
+static int bch2_xattr_bcachefs_get_effective(
+                               const struct xattr_handler *handler,
+                               struct dentry *dentry, struct inode *vinode,
+                               const char *name, void *buffer, size_t size)
+{
+       return __bch2_xattr_bcachefs_get(handler, dentry, vinode,
+                                        name, buffer, size, true);
+}
+
+static const struct xattr_handler bch_xattr_bcachefs_effective_handler = {
+       .prefix = "bcachefs_effective.",
+       .get    = bch2_xattr_bcachefs_get_effective,
+       .set    = bch2_xattr_bcachefs_set,
+};
+
 #endif /* NO_BCACHEFS_FS */
 
 const struct xattr_handler *bch2_xattr_handlers[] = {
@@ -444,6 +554,7 @@ const struct xattr_handler *bch2_xattr_handlers[] = {
        &bch_xattr_security_handler,
 #ifndef NO_BCACHEFS_FS
        &bch_xattr_bcachefs_handler,
+       &bch_xattr_bcachefs_effective_handler,
 #endif
        NULL
 };