X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libbcachefs%2Fxattr.c;h=c89c7200a1b483a33cfc40b8b5d77c9a7aab6382;hb=ff86d4722124c300c40b85b6eb8ef2d410ab303c;hp=488d5369e1f55d4830cd8f165d7b626fe7d94f45;hpb=c0ad33c126300a51721a4f0ec8c0d757647e9cbe;p=bcachefs-tools-debian diff --git a/libbcachefs/xattr.c b/libbcachefs/xattr.c index 488d536..c89c720 100644 --- a/libbcachefs/xattr.c +++ b/libbcachefs/xattr.c @@ -2,8 +2,10 @@ #include "bcachefs.h" #include "bkey_methods.h" #include "btree_update.h" +#include "compress.h" #include "extents.h" #include "fs.h" +#include "rebalance.h" #include "str_hash.h" #include "xattr.h" @@ -11,6 +13,16 @@ #include #include +static unsigned xattr_val_u64s(unsigned name_len, unsigned val_len) +{ + return DIV_ROUND_UP(sizeof(struct bch_xattr) + + name_len + val_len, sizeof(u64)); +} + +#define xattr_val(_xattr) ((_xattr)->x_name + (_xattr)->x_name_len) + +static const struct xattr_handler *bch2_xattr_type_to_handler(unsigned); + struct xattr_search_key { u8 type; struct qstr name; @@ -31,8 +43,6 @@ static u64 bch2_xattr_hash(const struct bch_hash_info *info, return bch2_str_hash_end(&ctx, info); } -#define xattr_val(_xattr) ((_xattr)->x_name + (_xattr)->x_name_len) - static u64 xattr_hash_key(const struct bch_hash_info *info, const void *key) { return bch2_xattr_hash(info, key); @@ -66,7 +76,7 @@ static bool xattr_cmp_bkey(struct bkey_s_c _l, struct bkey_s_c _r) memcmp(l.v->x_name, r.v->x_name, r.v->x_name_len); } -static const struct bch_hash_desc xattr_hash_desc = { +const struct bch_hash_desc bch2_xattr_hash_desc = { .btree_id = BTREE_ID_XATTRS, .key_type = BCH_XATTR, .whiteout_type = BCH_XATTR_WHITEOUT, @@ -76,15 +86,35 @@ static const struct bch_hash_desc xattr_hash_desc = { .cmp_bkey = xattr_cmp_bkey, }; -static const char *bch2_xattr_invalid(const struct bch_fs *c, - struct bkey_s_c k) +const char *bch2_xattr_invalid(const struct bch_fs *c, struct bkey_s_c k) { + const struct xattr_handler *handler; + struct bkey_s_c_xattr xattr; + unsigned u64s; + switch (k.k->type) { case BCH_XATTR: - return bkey_val_bytes(k.k) < sizeof(struct bch_xattr) - ? "value too small" - : NULL; + if (bkey_val_bytes(k.k) < sizeof(struct bch_xattr)) + return "value too small"; + + xattr = bkey_s_c_to_xattr(k); + u64s = xattr_val_u64s(xattr.v->x_name_len, + le16_to_cpu(xattr.v->x_val_len)); + + if (bkey_val_u64s(k.k) < u64s) + return "value too small"; + + if (bkey_val_u64s(k.k) > u64s) + return "value too big"; + handler = bch2_xattr_type_to_handler(xattr.v->x_type); + if (!handler) + return "invalid type"; + + if (memchr(xattr.v->x_name, '\0', xattr.v->x_name_len)) + return "xattr name has invalid characters"; + + return NULL; case BCH_XATTR_WHITEOUT: return bkey_val_bytes(k.k) != 0 ? "value size should be zero" @@ -95,37 +125,32 @@ static const char *bch2_xattr_invalid(const struct bch_fs *c, } } -static void bch2_xattr_to_text(struct bch_fs *c, char *buf, - size_t size, struct bkey_s_c k) +void bch2_xattr_to_text(struct bch_fs *c, char *buf, + size_t size, struct bkey_s_c k) { + const struct xattr_handler *handler; struct bkey_s_c_xattr xattr; - int n; + size_t n = 0; switch (k.k->type) { case BCH_XATTR: xattr = bkey_s_c_to_xattr(k); - if (size) { - n = min_t(unsigned, size, xattr.v->x_name_len); - memcpy(buf, xattr.v->x_name, n); - buf[size - 1] = '\0'; - buf += n; - size -= n; - } - - n = scnprintf(buf, size, " -> "); - buf += n; - size -= n; - - if (size) { - n = min_t(unsigned, size, - le16_to_cpu(xattr.v->x_val_len)); - memcpy(buf, xattr_val(xattr.v), n); - buf[size - 1] = '\0'; - buf += n; - size -= n; - } - + handler = bch2_xattr_type_to_handler(xattr.v->x_type); + if (handler && handler->prefix) + n += scnprintf(buf + n, size - n, "%s", handler->prefix); + else if (handler) + n += scnprintf(buf + n, size - n, "(type %u)", + xattr.v->x_type); + else + n += scnprintf(buf + n, size - n, "(unknown type %u)", + xattr.v->x_type); + + n += bch_scnmemcpy(buf + n, size - n, xattr.v->x_name, + xattr.v->x_name_len); + n += scnprintf(buf + n, size - n, ":"); + n += bch_scnmemcpy(buf + n, size - n, xattr_val(xattr.v), + le16_to_cpu(xattr.v->x_val_len)); break; case BCH_XATTR_WHITEOUT: scnprintf(buf, size, "whiteout"); @@ -133,23 +158,17 @@ static void bch2_xattr_to_text(struct bch_fs *c, char *buf, } } -const struct bkey_ops bch2_bkey_xattr_ops = { - .key_invalid = bch2_xattr_invalid, - .val_to_text = bch2_xattr_to_text, -}; - -int bch2_xattr_get(struct bch_fs *c, struct inode *inode, +int bch2_xattr_get(struct bch_fs *c, struct bch_inode_info *inode, const char *name, void *buffer, size_t size, int type) { - struct bch_inode_info *ei = to_bch_ei(inode); struct btree_iter iter; struct bkey_s_c k; struct bkey_s_c_xattr xattr; int ret; - k = bch2_hash_lookup(xattr_hash_desc, &ei->str_hash, c, - ei->vfs_inode.i_ino, &iter, - &X_SEARCH(type, name, strlen(name))); + k = bch2_hash_lookup(bch2_xattr_hash_desc, &inode->ei_str_hash, c, + inode->v.i_ino, &iter, + &X_SEARCH(type, name, strlen(name))); if (IS_ERR(k.k)) return bch2_btree_iter_unlock(&iter) ?: -ENODATA; @@ -175,15 +194,13 @@ int __bch2_xattr_set(struct bch_fs *c, u64 inum, int ret; if (!value) { - ret = bch2_hash_delete(xattr_hash_desc, hash_info, + ret = bch2_hash_delete(bch2_xattr_hash_desc, hash_info, c, inum, journal_seq, &search); } else { struct bkey_i_xattr *xattr; unsigned u64s = BKEY_U64s + - DIV_ROUND_UP(sizeof(struct bch_xattr) + - search.name.len + size, - sizeof(u64)); + xattr_val_u64s(search.name.len, size); if (u64s > U8_MAX) return -ERANGE; @@ -200,7 +217,7 @@ int __bch2_xattr_set(struct bch_fs *c, u64 inum, memcpy(xattr->v.x_name, search.name.name, search.name.len); memcpy(xattr_val(&xattr->v), value, size); - ret = bch2_hash_set(xattr_hash_desc, hash_info, c, + ret = bch2_hash_set(bch2_xattr_hash_desc, hash_info, c, inum, journal_seq, &xattr->k_i, (flags & XATTR_CREATE ? BCH_HASH_SET_MUST_CREATE : 0)| @@ -214,19 +231,15 @@ int __bch2_xattr_set(struct bch_fs *c, u64 inum, return ret; } -int bch2_xattr_set(struct bch_fs *c, struct inode *inode, - const char *name, const void *value, size_t size, - int flags, int type) +int bch2_xattr_set(struct bch_fs *c, struct bch_inode_info *inode, + const char *name, const void *value, size_t size, + int flags, int type) { - struct bch_inode_info *ei = to_bch_ei(inode); - - return __bch2_xattr_set(c, inode->i_ino, &ei->str_hash, - name, value, size, flags, type, - &ei->journal_seq); + return __bch2_xattr_set(c, inode->v.i_ino, &inode->ei_str_hash, + name, value, size, flags, type, + &inode->ei_journal_seq); } -static const struct xattr_handler *bch2_xattr_type_to_handler(unsigned); - static size_t bch2_xattr_emit(struct dentry *dentry, const struct bch_xattr *xattr, char *buffer, size_t buffer_size) @@ -262,7 +275,7 @@ ssize_t bch2_xattr_list(struct dentry *dentry, char *buffer, size_t buffer_size) ssize_t ret = 0; size_t len; - for_each_btree_key(&iter, c, BTREE_ID_XATTRS, POS(inum, 0), k) { + for_each_btree_key(&iter, c, BTREE_ID_XATTRS, POS(inum, 0), 0, k) { BUG_ON(k.k->p.inode < inum); if (k.k->p.inode > inum) @@ -293,23 +306,25 @@ ssize_t bch2_xattr_list(struct dentry *dentry, char *buffer, size_t buffer_size) } static int bch2_xattr_get_handler(const struct xattr_handler *handler, - struct dentry *dentry, struct inode *inode, - const char *name, void *buffer, size_t size) + struct dentry *dentry, struct inode *vinode, + const char *name, void *buffer, size_t size) { - struct bch_fs *c = inode->i_sb->s_fs_info; + struct bch_inode_info *inode = to_bch_ei(vinode); + struct bch_fs *c = inode->v.i_sb->s_fs_info; return bch2_xattr_get(c, inode, name, buffer, size, handler->flags); } static int bch2_xattr_set_handler(const struct xattr_handler *handler, - struct dentry *dentry, struct inode *inode, - const char *name, const void *value, - size_t size, int flags) + struct dentry *dentry, struct inode *vinode, + const char *name, const void *value, + size_t size, int flags) { - struct bch_fs *c = inode->i_sb->s_fs_info; + struct bch_inode_info *inode = to_bch_ei(vinode); + struct bch_fs *c = inode->v.i_sb->s_fs_info; return bch2_xattr_set(c, inode, name, value, size, flags, - handler->flags); + handler->flags); } static const struct xattr_handler bch_xattr_user_handler = { @@ -339,25 +354,140 @@ static const struct xattr_handler bch_xattr_security_handler = { .flags = BCH_XATTR_INDEX_SECURITY, }; -static const struct xattr_handler *bch_xattr_handler_map[] = { - [BCH_XATTR_INDEX_USER] = &bch_xattr_user_handler, - [BCH_XATTR_INDEX_POSIX_ACL_ACCESS] = - &posix_acl_access_xattr_handler, - [BCH_XATTR_INDEX_POSIX_ACL_DEFAULT] = - &posix_acl_default_xattr_handler, - [BCH_XATTR_INDEX_TRUSTED] = &bch_xattr_trusted_handler, - [BCH_XATTR_INDEX_SECURITY] = &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) +{ + 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 ret, id; + u64 v; + + id = bch2_opt_lookup(name); + if (id < 0 || !bch2_opt_is_inode_opt(id)) + return -EINVAL; + + opt = bch2_opt_table + id; + + if (!bch2_opt_defined_by_id(&opts, id)) + return -ENODATA; + + v = bch2_opt_get_by_id(&opts, id); + + ret = bch2_opt_to_text(c, buffer, size, opt, v, 0); + + return ret < size || !buffer ? ret : -ERANGE; +} + +struct inode_opt_set { + int id; + u64 v; + bool defined; }; +static int inode_opt_set_fn(struct bch_inode_info *inode, + struct bch_inode_unpacked *bi, + void *p) +{ + struct inode_opt_set *s = p; + + if (s->defined) + bch2_inode_opt_set(bi, s->id, s->v); + else + bch2_inode_opt_clear(bi, s->id); + return 0; +} + +static int bch2_xattr_bcachefs_set(const struct xattr_handler *handler, + struct dentry *dentry, struct inode *vinode, + const char *name, const void *value, + size_t size, int flags) +{ + struct bch_inode_info *inode = to_bch_ei(vinode); + struct bch_fs *c = inode->v.i_sb->s_fs_info; + const struct bch_option *opt; + char *buf; + struct inode_opt_set s; + int ret; + + s.id = bch2_opt_lookup(name); + if (s.id < 0 || !bch2_opt_is_inode_opt(s.id)) + return -EINVAL; + + opt = bch2_opt_table + s.id; + + if (value) { + 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); + kfree(buf); + + if (ret < 0) + return ret; + + if (s.id == Opt_compression || + s.id == Opt_background_compression) { + ret = bch2_check_set_has_compressed_data(c, s.v); + if (ret) + return ret; + } + + s.defined = true; + } else { + s.defined = false; + } + + mutex_lock(&inode->ei_update_lock); + ret = __bch2_write_inode(c, inode, inode_opt_set_fn, &s); + mutex_unlock(&inode->ei_update_lock); + + if (value && + (s.id == Opt_background_compression || + s.id == Opt_background_target)) + bch2_rebalance_add_work(c, inode->v.i_blocks); + + return ret; +} + +static const struct xattr_handler bch_xattr_bcachefs_handler = { + .prefix = "bcachefs.", + .get = bch2_xattr_bcachefs_get, + .set = bch2_xattr_bcachefs_set, +}; + +#endif /* NO_BCACHEFS_FS */ + const struct xattr_handler *bch2_xattr_handlers[] = { &bch_xattr_user_handler, &posix_acl_access_xattr_handler, &posix_acl_default_xattr_handler, &bch_xattr_trusted_handler, &bch_xattr_security_handler, +#ifndef NO_BCACHEFS_FS + &bch_xattr_bcachefs_handler, +#endif NULL }; +static const struct xattr_handler *bch_xattr_handler_map[] = { + [BCH_XATTR_INDEX_USER] = &bch_xattr_user_handler, + [BCH_XATTR_INDEX_POSIX_ACL_ACCESS] = + &posix_acl_access_xattr_handler, + [BCH_XATTR_INDEX_POSIX_ACL_DEFAULT] = + &posix_acl_default_xattr_handler, + [BCH_XATTR_INDEX_TRUSTED] = &bch_xattr_trusted_handler, + [BCH_XATTR_INDEX_SECURITY] = &bch_xattr_security_handler, +}; + static const struct xattr_handler *bch2_xattr_type_to_handler(unsigned type) { return type < ARRAY_SIZE(bch_xattr_handler_map)