diff options
author | Kent Overstreet <kent.overstreet@gmail.com> | 2018-12-17 05:31:09 -0500 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@linux.dev> | 2023-10-22 17:08:13 -0400 |
commit | 4d269918ed502cba80ddad998bdb087a633c63ab (patch) | |
tree | 4ab21b50871475d87b25fe852cccf4822893e6c8 /fs | |
parent | 721d4ad8eb55bf66ef55b31438b6c8361acf283f (diff) | |
download | lwn-4d269918ed502cba80ddad998bdb087a633c63ab.tar.gz lwn-4d269918ed502cba80ddad998bdb087a633c63ab.zip |
bcachefs: add bcachefs_effective xattrs
Allows seeing xattrs that were inherited, not explicitly set
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/bcachefs/inode.c | 7 | ||||
-rw-r--r-- | fs/bcachefs/inode.h | 34 | ||||
-rw-r--r-- | fs/bcachefs/xattr.c | 203 |
3 files changed, 180 insertions, 64 deletions
diff --git a/fs/bcachefs/inode.c b/fs/bcachefs/inode.c index a85b7a683a3a..23d3668b4567 100644 --- a/fs/bcachefs/inode.c +++ b/fs/bcachefs/inode.c @@ -13,7 +13,12 @@ #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] = { diff --git a/fs/bcachefs/inode.h b/fs/bcachefs/inode.h index 7bf95f889d35..07d7020f230d 100644 --- a/fs/bcachefs/inode.h +++ b/fs/bcachefs/inode.h @@ -6,6 +6,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); @@ -73,17 +75,13 @@ static inline struct bch_io_opts bch2_inode_opts_get(struct bch_inode_unpacked * 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 x(_name, ...) \ - case Opt_##_name: \ +#define x(_name, ...) \ + case Inode_opt_##_name: \ inode->bi_##_name = v; \ - if (v) \ - inode->bi_fields_set |= 1U << Inode_opt_##_name;\ - else \ - inode->bi_fields_set &= ~(1U << Inode_opt_##_name);\ break; BCH_INODE_OPTS() #undef x @@ -92,16 +90,18 @@ static inline void __bch2_inode_opt_set(struct bch_inode_unpacked *inode, } } -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 diff --git a/fs/bcachefs/xattr.c b/fs/bcachefs/xattr.c index ff2d59ee1658..dfb5c385e8c3 100644 --- a/fs/bcachefs/xattr.c +++ b/fs/bcachefs/xattr.c @@ -198,40 +198,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); @@ -242,23 +285,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; } @@ -318,27 +363,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) { @@ -359,6 +425,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; @@ -372,9 +446,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; } @@ -389,33 +466,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; } @@ -424,8 +519,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; @@ -437,6 +532,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[] = { @@ -447,6 +557,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 }; |