+// SPDX-License-Identifier: GPL-2.0
#include "bcachefs.h"
#include "bkey_methods.h"
}
const struct bch_hash_desc bch2_xattr_hash_desc = {
- .btree_id = BTREE_ID_XATTRS,
+ .btree_id = BTREE_ID_xattrs,
.key_type = KEY_TYPE_xattr,
.hash_key = xattr_hash_key,
.hash_bkey = xattr_hash_bkey,
.cmp_bkey = xattr_cmp_bkey,
};
-const char *bch2_xattr_invalid(const struct bch_fs *c, struct bkey_s_c k)
+int bch2_xattr_invalid(const struct bch_fs *c, struct bkey_s_c k,
+ unsigned flags, struct printbuf *err)
{
const struct xattr_handler *handler;
struct bkey_s_c_xattr xattr = bkey_s_c_to_xattr(k);
- if (bkey_val_bytes(k.k) < sizeof(struct bch_xattr))
- return "value too small";
-
if (bkey_val_u64s(k.k) <
xattr_val_u64s(xattr.v->x_name_len,
- le16_to_cpu(xattr.v->x_val_len)))
- return "value too small";
+ le16_to_cpu(xattr.v->x_val_len))) {
+ prt_printf(err, "value too small (%zu < %u)",
+ bkey_val_u64s(k.k),
+ xattr_val_u64s(xattr.v->x_name_len,
+ le16_to_cpu(xattr.v->x_val_len)));
+ return -BCH_ERR_invalid_bkey;
+ }
+ /* XXX why +4 ? */
if (bkey_val_u64s(k.k) >
xattr_val_u64s(xattr.v->x_name_len,
- le16_to_cpu(xattr.v->x_val_len) + 4))
- return "value too big";
+ le16_to_cpu(xattr.v->x_val_len) + 4)) {
+ prt_printf(err, "value too big (%zu > %u)",
+ bkey_val_u64s(k.k),
+ xattr_val_u64s(xattr.v->x_name_len,
+ le16_to_cpu(xattr.v->x_val_len) + 4));
+ return -BCH_ERR_invalid_bkey;
+ }
handler = bch2_xattr_type_to_handler(xattr.v->x_type);
- if (!handler)
- return "invalid type";
+ if (!handler) {
+ prt_printf(err, "invalid type (%u)", xattr.v->x_type);
+ return -BCH_ERR_invalid_bkey;
+ }
- if (memchr(xattr.v->x_name, '\0', xattr.v->x_name_len))
- return "xattr name has invalid characters";
+ if (memchr(xattr.v->x_name, '\0', xattr.v->x_name_len)) {
+ prt_printf(err, "xattr name has invalid characters");
+ return -BCH_ERR_invalid_bkey;
+ }
- return NULL;
+ return 0;
}
void bch2_xattr_to_text(struct printbuf *out, struct bch_fs *c,
handler = bch2_xattr_type_to_handler(xattr.v->x_type);
if (handler && handler->prefix)
- pr_buf(out, "%s", handler->prefix);
+ prt_printf(out, "%s", handler->prefix);
else if (handler)
- pr_buf(out, "(type %u)", xattr.v->x_type);
+ prt_printf(out, "(type %u)", xattr.v->x_type);
else
- pr_buf(out, "(unknown type %u)", xattr.v->x_type);
+ prt_printf(out, "(unknown type %u)", xattr.v->x_type);
- bch_scnmemcpy(out, xattr.v->x_name,
- xattr.v->x_name_len);
- pr_buf(out, ":");
- bch_scnmemcpy(out, xattr_val(xattr.v),
- le16_to_cpu(xattr.v->x_val_len));
+ prt_printf(out, "%.*s:%.*s",
+ xattr.v->x_name_len,
+ xattr.v->x_name,
+ le16_to_cpu(xattr.v->x_val_len),
+ (char *) xattr_val(xattr.v));
}
-int bch2_xattr_get(struct bch_fs *c, struct bch_inode_info *inode,
- const char *name, void *buffer, size_t size, int type)
+static int bch2_xattr_get_trans(struct btree_trans *trans, struct bch_inode_info *inode,
+ const char *name, void *buffer, size_t size, int type)
{
- struct btree_trans trans;
- struct btree_iter *iter;
+ struct bch_hash_info hash = bch2_hash_info_init(trans->c, &inode->ei_inode);
+ struct btree_iter iter;
struct bkey_s_c_xattr xattr;
+ struct bkey_s_c k;
int ret;
- bch2_trans_init(&trans, c);
+ ret = bch2_hash_lookup(trans, &iter, bch2_xattr_hash_desc, &hash,
+ inode_inum(inode),
+ &X_SEARCH(type, name, strlen(name)),
+ 0);
+ if (ret)
+ goto err1;
- iter = bch2_hash_lookup(&trans, bch2_xattr_hash_desc,
- &inode->ei_str_hash, inode->v.i_ino,
- &X_SEARCH(type, name, strlen(name)),
- 0);
- if (IS_ERR(iter)) {
- bch2_trans_exit(&trans);
- BUG_ON(PTR_ERR(iter) == -EINTR);
+ k = bch2_btree_iter_peek_slot(&iter);
+ ret = bkey_err(k);
+ if (ret)
+ goto err2;
- return PTR_ERR(iter) == -ENOENT ? -ENODATA : PTR_ERR(iter);
- }
-
- xattr = bkey_s_c_to_xattr(bch2_btree_iter_peek_slot(iter));
+ xattr = bkey_s_c_to_xattr(k);
ret = le16_to_cpu(xattr.v->x_val_len);
if (buffer) {
if (ret > size)
else
memcpy(buffer, xattr_val(xattr.v), ret);
}
+err2:
+ bch2_trans_iter_exit(trans, &iter);
+err1:
+ return ret == -ENOENT ? -ENODATA : ret;
+}
- bch2_trans_exit(&trans);
- return ret;
+int bch2_xattr_get(struct bch_fs *c, struct bch_inode_info *inode,
+ const char *name, void *buffer, size_t size, int type)
+{
+ return bch2_trans_do(c, NULL, NULL, 0,
+ bch2_xattr_get_trans(&trans, inode, name, buffer, size, type));
}
-int bch2_xattr_set(struct btree_trans *trans, u64 inum,
+int bch2_xattr_set(struct btree_trans *trans, subvol_inum inum,
const struct bch_hash_info *hash_info,
const char *name, const void *value, size_t size,
int type, int flags)
{
+ struct btree_iter inode_iter = { NULL };
+ struct bch_inode_unpacked inode_u;
int ret;
+ /*
+ * We need to do an inode update so that bi_journal_sync gets updated
+ * and fsync works:
+ *
+ * Perhaps we should be updating bi_mtime too?
+ */
+
+ ret = bch2_inode_peek(trans, &inode_iter, &inode_u, inum, BTREE_ITER_INTENT) ?:
+ bch2_inode_write(trans, &inode_iter, &inode_u);
+ bch2_trans_iter_exit(trans, &inode_iter);
+
+ if (ret)
+ return ret;
+
if (value) {
struct bkey_i_xattr *xattr;
unsigned namelen = strlen(name);
return ret;
}
-static void __bch2_xattr_emit(const char *prefix,
- const char *name, size_t name_len,
- char **buffer, size_t *buffer_size,
- ssize_t *ret)
+struct xattr_buf {
+ char *buf;
+ size_t len;
+ size_t used;
+};
+
+static int __bch2_xattr_emit(const char *prefix,
+ const char *name, size_t name_len,
+ struct xattr_buf *buf)
{
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;
- }
+ if (buf->buf) {
+ if (buf->used + total_len > buf->len)
+ return -ERANGE;
- memcpy(*buffer, prefix, prefix_len);
- memcpy(*buffer + prefix_len,
+ memcpy(buf->buf + buf->used, prefix, prefix_len);
+ memcpy(buf->buf + buf->used + prefix_len,
name, name_len);
- (*buffer)[prefix_len + name_len] = '\0';
-
- *buffer += total_len;
- *buffer_size -= total_len;
+ buf->buf[buf->used + prefix_len + name_len] = '\0';
}
- *ret += total_len;
+ buf->used += total_len;
+ return 0;
}
-static void bch2_xattr_emit(struct dentry *dentry,
+static int bch2_xattr_emit(struct dentry *dentry,
const struct bch_xattr *xattr,
- char **buffer, size_t *buffer_size,
- ssize_t *ret)
+ struct xattr_buf *buf)
{
const struct xattr_handler *handler =
bch2_xattr_type_to_handler(xattr->x_type);
- 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);
+ return handler && (!handler->list || handler->list(dentry))
+ ? __bch2_xattr_emit(handler->prefix ?: handler->name,
+ xattr->x_name, xattr->x_name_len, buf)
+ : 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)
+static int bch2_xattr_list_bcachefs(struct bch_fs *c,
+ struct bch_inode_unpacked *inode,
+ struct xattr_buf *buf,
+ bool all)
{
const char *prefix = all ? "bcachefs_effective." : "bcachefs.";
unsigned id;
+ int ret = 0;
u64 v;
for (id = 0; id < Inode_opt_nr; id++) {
- v = bch2_inode_opt_get(&inode->ei_inode, id);
+ v = bch2_inode_opt_get(inode, id);
if (!v)
continue;
if (!all &&
- !(inode->ei_inode.bi_fields_set & (1 << id)))
+ !(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)
+ ret = __bch2_xattr_emit(prefix, bch2_inode_opts[id],
+ strlen(bch2_inode_opts[id]), buf);
+ if (ret)
break;
}
+
+ return ret;
}
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_trans trans;
- struct btree_iter *iter;
+ struct btree_iter iter;
struct bkey_s_c k;
- u64 inum = dentry->d_inode->i_ino;
- ssize_t ret = 0;
-
- bch2_trans_init(&trans, c);
+ struct xattr_buf buf = { .buf = buffer, .len = buffer_size };
+ u64 offset = 0, inum = inode->ei_inode.bi_inum;
+ u32 snapshot;
+ int ret;
- for_each_btree_key(&trans, iter, BTREE_ID_XATTRS,
- POS(inum, 0), 0, k) {
- BUG_ON(k.k->p.inode < inum);
+ bch2_trans_init(&trans, c, 0, 0);
+retry:
+ bch2_trans_begin(&trans);
+ iter = (struct btree_iter) { NULL };
- if (k.k->p.inode > inum)
- break;
+ ret = bch2_subvolume_get_snapshot(&trans, inode->ei_subvol, &snapshot);
+ if (ret)
+ goto err;
+ for_each_btree_key_upto_norestart(&trans, iter, BTREE_ID_xattrs,
+ SPOS(inum, offset, snapshot),
+ POS(inum, U64_MAX), 0, k, ret) {
if (k.k->type != KEY_TYPE_xattr)
continue;
- bch2_xattr_emit(dentry, bkey_s_c_to_xattr(k).v,
- &buffer, &buffer_size, &ret);
- if (ret < 0)
+ ret = bch2_xattr_emit(dentry, bkey_s_c_to_xattr(k).v, &buf);
+ if (ret)
break;
}
+
+ offset = iter.pos.offset;
+ bch2_trans_iter_exit(&trans, &iter);
+err:
+ if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
+ goto retry;
+
bch2_trans_exit(&trans);
- if (ret < 0)
- return ret;
+ if (ret)
+ goto out;
- bch2_xattr_list_bcachefs(c, inode, &buffer,
- &buffer_size, &ret, false);
- if (ret < 0)
- return ret;
+ ret = bch2_xattr_list_bcachefs(c, &inode->ei_inode, &buf, false);
+ if (ret)
+ goto out;
- bch2_xattr_list_bcachefs(c, inode, &buffer,
- &buffer_size, &ret, true);
- if (ret < 0)
- return ret;
+ ret = bch2_xattr_list_bcachefs(c, &inode->ei_inode, &buf, true);
+ if (ret)
+ goto out;
- return ret;
+ return buf.used;
+out:
+ return bch2_err_class(ret);
}
static int bch2_xattr_get_handler(const struct xattr_handler *handler,
{
struct bch_inode_info *inode = to_bch_ei(vinode);
struct bch_fs *c = inode->v.i_sb->s_fs_info;
+ int ret;
- return bch2_xattr_get(c, inode, name, buffer, size, handler->flags);
+ ret = bch2_xattr_get(c, inode, name, buffer, size, handler->flags);
+ return bch2_err_class(ret);
}
static int bch2_xattr_set_handler(const struct xattr_handler *handler,
+ struct mnt_idmap *idmap,
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;
+ struct bch_hash_info hash = bch2_hash_info_init(c, &inode->ei_inode);
+ int ret;
- return bch2_trans_do(c, &inode->ei_journal_seq, BTREE_INSERT_ATOMIC,
- bch2_xattr_set(&trans, inode->v.i_ino,
- &inode->ei_str_hash,
+ ret = bch2_trans_do(c, NULL, NULL, 0,
+ bch2_xattr_set(&trans, inode_inum(inode), &hash,
name, value, size,
handler->flags, flags));
+ return bch2_err_class(ret);
}
static const struct xattr_handler bch_xattr_user_handler = {
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));
+ bch2_inode_opts_to_opts(&inode->ei_inode);
const struct bch_option *opt;
int id, inode_opt_id;
+ struct printbuf out = PRINTBUF;
+ int ret;
u64 v;
id = bch2_opt_lookup(name);
return -ENODATA;
v = bch2_opt_get_by_id(&opts, id);
+ bch2_opt_to_text(&out, c, c->disk_sb.sb, opt, v, 0);
- if (!buffer) {
- char buf[512];
- struct printbuf out = PBUF(buf);
-
- bch2_opt_to_text(&out, c, opt, v, 0);
-
- return out.pos - buf;
- } else {
- struct printbuf out = _PBUF(buffer, size);
+ ret = out.pos;
- bch2_opt_to_text(&out, c, opt, v, 0);
-
- return printbuf_remaining(&out)
- ? (void *) out.pos - buffer
- : -ERANGE;
+ if (out.allocation_failure) {
+ ret = -ENOMEM;
+ } else if (buffer) {
+ if (out.pos > size)
+ ret = -ERANGE;
+ else
+ memcpy(buffer, out.buf, out.pos);
}
+
+ printbuf_exit(&out);
+ return ret;
}
static int bch2_xattr_bcachefs_get(const struct xattr_handler *handler,
}
static int bch2_xattr_bcachefs_set(const struct xattr_handler *handler,
+ struct mnt_idmap *idmap,
struct dentry *dentry, struct inode *vinode,
const char *name, const void *value,
size_t size, int flags)
memcpy(buf, value, size);
buf[size] = '\0';
- ret = bch2_opt_parse(c, opt, buf, &v);
+ ret = bch2_opt_parse(c, opt, buf, &v, NULL);
kfree(buf);
if (ret < 0)
mutex_lock(&inode->ei_update_lock);
if (inode_opt_id == Inode_opt_project) {
- ret = bch2_set_projid(c, inode, s.v);
+ /*
+ * inode fields accessible via the xattr interface are stored
+ * with a +1 bias, so that 0 means unset:
+ */
+ ret = bch2_set_projid(c, inode, s.v ? s.v - 1 : 0);
if (ret)
goto err;
}
const struct xattr_handler *bch2_xattr_handlers[] = {
&bch_xattr_user_handler,
+#ifdef CONFIG_BCACHEFS_POSIX_ACL
&posix_acl_access_xattr_handler,
&posix_acl_default_xattr_handler,
+#endif
&bch_xattr_trusted_handler,
&bch_xattr_security_handler,
#ifndef NO_BCACHEFS_FS