]>
Commit | Line | Data |
---|---|---|
1c6fdbd8 KO |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | ||
3 | #include "bcachefs.h" | |
a9a7bbab | 4 | #include "acl.h" |
1c6fdbd8 KO |
5 | #include "bkey_methods.h" |
6 | #include "btree_update.h" | |
1c6fdbd8 KO |
7 | #include "extents.h" |
8 | #include "fs.h" | |
9 | #include "rebalance.h" | |
10 | #include "str_hash.h" | |
11 | #include "xattr.h" | |
12 | ||
13 | #include <linux/dcache.h> | |
14 | #include <linux/posix_acl_xattr.h> | |
15 | #include <linux/xattr.h> | |
16 | ||
17 | static const struct xattr_handler *bch2_xattr_type_to_handler(unsigned); | |
18 | ||
19 | static u64 bch2_xattr_hash(const struct bch_hash_info *info, | |
20 | const struct xattr_search_key *key) | |
21 | { | |
22 | struct bch_str_hash_ctx ctx; | |
23 | ||
24 | bch2_str_hash_init(&ctx, info); | |
25 | bch2_str_hash_update(&ctx, info, &key->type, sizeof(key->type)); | |
26 | bch2_str_hash_update(&ctx, info, key->name.name, key->name.len); | |
27 | ||
28 | return bch2_str_hash_end(&ctx, info); | |
29 | } | |
30 | ||
31 | static u64 xattr_hash_key(const struct bch_hash_info *info, const void *key) | |
32 | { | |
33 | return bch2_xattr_hash(info, key); | |
34 | } | |
35 | ||
36 | static u64 xattr_hash_bkey(const struct bch_hash_info *info, struct bkey_s_c k) | |
37 | { | |
38 | struct bkey_s_c_xattr x = bkey_s_c_to_xattr(k); | |
39 | ||
40 | return bch2_xattr_hash(info, | |
41 | &X_SEARCH(x.v->x_type, x.v->x_name, x.v->x_name_len)); | |
42 | } | |
43 | ||
44 | static bool xattr_cmp_key(struct bkey_s_c _l, const void *_r) | |
45 | { | |
46 | struct bkey_s_c_xattr l = bkey_s_c_to_xattr(_l); | |
47 | const struct xattr_search_key *r = _r; | |
48 | ||
49 | return l.v->x_type != r->type || | |
50 | l.v->x_name_len != r->name.len || | |
51 | memcmp(l.v->x_name, r->name.name, r->name.len); | |
52 | } | |
53 | ||
54 | static bool xattr_cmp_bkey(struct bkey_s_c _l, struct bkey_s_c _r) | |
55 | { | |
56 | struct bkey_s_c_xattr l = bkey_s_c_to_xattr(_l); | |
57 | struct bkey_s_c_xattr r = bkey_s_c_to_xattr(_r); | |
58 | ||
59 | return l.v->x_type != r.v->x_type || | |
60 | l.v->x_name_len != r.v->x_name_len || | |
61 | memcmp(l.v->x_name, r.v->x_name, r.v->x_name_len); | |
62 | } | |
63 | ||
64 | const struct bch_hash_desc bch2_xattr_hash_desc = { | |
41f8b09e | 65 | .btree_id = BTREE_ID_xattrs, |
26609b61 | 66 | .key_type = KEY_TYPE_xattr, |
1c6fdbd8 KO |
67 | .hash_key = xattr_hash_key, |
68 | .hash_bkey = xattr_hash_bkey, | |
69 | .cmp_key = xattr_cmp_key, | |
70 | .cmp_bkey = xattr_cmp_bkey, | |
71 | }; | |
72 | ||
b65db750 | 73 | int bch2_xattr_invalid(struct bch_fs *c, struct bkey_s_c k, |
8726dc93 KO |
74 | enum bkey_invalid_flags flags, |
75 | struct printbuf *err) | |
1c6fdbd8 | 76 | { |
26609b61 | 77 | struct bkey_s_c_xattr xattr = bkey_s_c_to_xattr(k); |
b65db750 KO |
78 | unsigned val_u64s = xattr_val_u64s(xattr.v->x_name_len, |
79 | le16_to_cpu(xattr.v->x_val_len)); | |
80 | int ret = 0; | |
1c6fdbd8 | 81 | |
b65db750 KO |
82 | bkey_fsck_err_on(bkey_val_u64s(k.k) < val_u64s, c, err, |
83 | xattr_val_size_too_small, | |
84 | "value too small (%zu < %u)", | |
85 | bkey_val_u64s(k.k), val_u64s); | |
f0ac7df2 KO |
86 | |
87 | /* XXX why +4 ? */ | |
b65db750 KO |
88 | val_u64s = xattr_val_u64s(xattr.v->x_name_len, |
89 | le16_to_cpu(xattr.v->x_val_len) + 4); | |
90 | ||
91 | bkey_fsck_err_on(bkey_val_u64s(k.k) > val_u64s, c, err, | |
92 | xattr_val_size_too_big, | |
93 | "value too big (%zu > %u)", | |
94 | bkey_val_u64s(k.k), val_u64s); | |
95 | ||
96 | bkey_fsck_err_on(!bch2_xattr_type_to_handler(xattr.v->x_type), c, err, | |
97 | xattr_invalid_type, | |
98 | "invalid type (%u)", xattr.v->x_type); | |
99 | ||
100 | bkey_fsck_err_on(memchr(xattr.v->x_name, '\0', xattr.v->x_name_len), c, err, | |
101 | xattr_name_invalid_chars, | |
102 | "xattr name has invalid characters"); | |
103 | fsck_err: | |
104 | return ret; | |
1c6fdbd8 KO |
105 | } |
106 | ||
319f9ac3 KO |
107 | void bch2_xattr_to_text(struct printbuf *out, struct bch_fs *c, |
108 | struct bkey_s_c k) | |
1c6fdbd8 KO |
109 | { |
110 | const struct xattr_handler *handler; | |
26609b61 | 111 | struct bkey_s_c_xattr xattr = bkey_s_c_to_xattr(k); |
1c6fdbd8 | 112 | |
26609b61 KO |
113 | handler = bch2_xattr_type_to_handler(xattr.v->x_type); |
114 | if (handler && handler->prefix) | |
401ec4db | 115 | prt_printf(out, "%s", handler->prefix); |
26609b61 | 116 | else if (handler) |
401ec4db | 117 | prt_printf(out, "(type %u)", xattr.v->x_type); |
26609b61 | 118 | else |
401ec4db | 119 | prt_printf(out, "(unknown type %u)", xattr.v->x_type); |
1c6fdbd8 | 120 | |
401ec4db | 121 | prt_printf(out, "%.*s:%.*s", |
d4b69152 KO |
122 | xattr.v->x_name_len, |
123 | xattr.v->x_name, | |
124 | le16_to_cpu(xattr.v->x_val_len), | |
125 | (char *) xattr_val(xattr.v)); | |
a9a7bbab KO |
126 | |
127 | if (xattr.v->x_type == KEY_TYPE_XATTR_INDEX_POSIX_ACL_ACCESS || | |
128 | xattr.v->x_type == KEY_TYPE_XATTR_INDEX_POSIX_ACL_DEFAULT) { | |
129 | prt_char(out, ' '); | |
130 | bch2_acl_to_text(out, xattr_val(xattr.v), | |
131 | le16_to_cpu(xattr.v->x_val_len)); | |
132 | } | |
1c6fdbd8 KO |
133 | } |
134 | ||
4909fe50 KO |
135 | static int bch2_xattr_get_trans(struct btree_trans *trans, struct bch_inode_info *inode, |
136 | const char *name, void *buffer, size_t size, int type) | |
1c6fdbd8 | 137 | { |
4909fe50 | 138 | struct bch_hash_info hash = bch2_hash_info_init(trans->c, &inode->ei_inode); |
73bd774d | 139 | struct xattr_search_key search = X_SEARCH(type, name, strlen(name)); |
67e0dd8f | 140 | struct btree_iter iter; |
1c6fdbd8 | 141 | struct bkey_s_c_xattr xattr; |
8b3e9bd6 | 142 | struct bkey_s_c k; |
1c6fdbd8 KO |
143 | int ret; |
144 | ||
67e0dd8f | 145 | ret = bch2_hash_lookup(trans, &iter, bch2_xattr_hash_desc, &hash, |
73bd774d | 146 | inode_inum(inode), &search, 0); |
50dc0f69 | 147 | if (ret) |
67e0dd8f | 148 | goto err1; |
1c6fdbd8 | 149 | |
67e0dd8f | 150 | k = bch2_btree_iter_peek_slot(&iter); |
8b3e9bd6 KO |
151 | ret = bkey_err(k); |
152 | if (ret) | |
67e0dd8f | 153 | goto err2; |
8b3e9bd6 KO |
154 | |
155 | xattr = bkey_s_c_to_xattr(k); | |
1c6fdbd8 KO |
156 | ret = le16_to_cpu(xattr.v->x_val_len); |
157 | if (buffer) { | |
158 | if (ret > size) | |
159 | ret = -ERANGE; | |
160 | else | |
161 | memcpy(buffer, xattr_val(xattr.v), ret); | |
162 | } | |
67e0dd8f KO |
163 | err2: |
164 | bch2_trans_iter_exit(trans, &iter); | |
165 | err1: | |
e47a390a | 166 | return ret < 0 && bch2_err_matches(ret, ENOENT) ? -ENODATA : ret; |
1c6fdbd8 KO |
167 | } |
168 | ||
6fed42bb | 169 | int bch2_xattr_set(struct btree_trans *trans, subvol_inum inum, |
07f293c8 | 170 | struct bch_inode_unpacked *inode_u, |
1c6fdbd8 KO |
171 | const struct bch_hash_info *hash_info, |
172 | const char *name, const void *value, size_t size, | |
173 | int type, int flags) | |
174 | { | |
07f293c8 | 175 | struct bch_fs *c = trans->c; |
68a2054d | 176 | struct btree_iter inode_iter = { NULL }; |
1c6fdbd8 KO |
177 | int ret; |
178 | ||
07f293c8 KO |
179 | ret = bch2_inode_peek(trans, &inode_iter, inode_u, inum, BTREE_ITER_INTENT); |
180 | if (ret) | |
181 | return ret; | |
68a2054d | 182 | |
07f293c8 KO |
183 | inode_u->bi_ctime = bch2_current_time(c); |
184 | ||
185 | ret = bch2_inode_write(trans, &inode_iter, inode_u); | |
68a2054d KO |
186 | bch2_trans_iter_exit(trans, &inode_iter); |
187 | ||
188 | if (ret) | |
189 | return ret; | |
190 | ||
1c6fdbd8 KO |
191 | if (value) { |
192 | struct bkey_i_xattr *xattr; | |
193 | unsigned namelen = strlen(name); | |
194 | unsigned u64s = BKEY_U64s + | |
195 | xattr_val_u64s(namelen, size); | |
196 | ||
197 | if (u64s > U8_MAX) | |
198 | return -ERANGE; | |
199 | ||
200 | xattr = bch2_trans_kmalloc(trans, u64s * sizeof(u64)); | |
201 | if (IS_ERR(xattr)) | |
202 | return PTR_ERR(xattr); | |
203 | ||
204 | bkey_xattr_init(&xattr->k_i); | |
205 | xattr->k.u64s = u64s; | |
206 | xattr->v.x_type = type; | |
207 | xattr->v.x_name_len = namelen; | |
208 | xattr->v.x_val_len = cpu_to_le16(size); | |
209 | memcpy(xattr->v.x_name, name, namelen); | |
210 | memcpy(xattr_val(&xattr->v), value, size); | |
211 | ||
0564b167 | 212 | ret = bch2_hash_set(trans, bch2_xattr_hash_desc, hash_info, |
1c6fdbd8 KO |
213 | inum, &xattr->k_i, |
214 | (flags & XATTR_CREATE ? BCH_HASH_SET_MUST_CREATE : 0)| | |
215 | (flags & XATTR_REPLACE ? BCH_HASH_SET_MUST_REPLACE : 0)); | |
216 | } else { | |
217 | struct xattr_search_key search = | |
218 | X_SEARCH(type, name, strlen(name)); | |
219 | ||
220 | ret = bch2_hash_delete(trans, bch2_xattr_hash_desc, | |
221 | hash_info, inum, &search); | |
222 | } | |
223 | ||
e47a390a | 224 | if (bch2_err_matches(ret, ENOENT)) |
1c6fdbd8 KO |
225 | ret = flags & XATTR_REPLACE ? -ENODATA : 0; |
226 | ||
227 | return ret; | |
228 | } | |
229 | ||
94f651e2 KO |
230 | struct xattr_buf { |
231 | char *buf; | |
232 | size_t len; | |
233 | size_t used; | |
234 | }; | |
235 | ||
236 | static int __bch2_xattr_emit(const char *prefix, | |
237 | const char *name, size_t name_len, | |
238 | struct xattr_buf *buf) | |
4d269918 KO |
239 | { |
240 | const size_t prefix_len = strlen(prefix); | |
241 | const size_t total_len = prefix_len + name_len + 1; | |
242 | ||
94f651e2 KO |
243 | if (buf->buf) { |
244 | if (buf->used + total_len > buf->len) | |
245 | return -ERANGE; | |
4d269918 | 246 | |
94f651e2 KO |
247 | memcpy(buf->buf + buf->used, prefix, prefix_len); |
248 | memcpy(buf->buf + buf->used + prefix_len, | |
4d269918 | 249 | name, name_len); |
94f651e2 | 250 | buf->buf[buf->used + prefix_len + name_len] = '\0'; |
4d269918 KO |
251 | } |
252 | ||
94f651e2 KO |
253 | buf->used += total_len; |
254 | return 0; | |
4d269918 KO |
255 | } |
256 | ||
94f651e2 | 257 | static int bch2_xattr_emit(struct dentry *dentry, |
4d269918 | 258 | const struct bch_xattr *xattr, |
94f651e2 | 259 | struct xattr_buf *buf) |
1c6fdbd8 KO |
260 | { |
261 | const struct xattr_handler *handler = | |
262 | bch2_xattr_type_to_handler(xattr->x_type); | |
263 | ||
94f651e2 KO |
264 | return handler && (!handler->list || handler->list(dentry)) |
265 | ? __bch2_xattr_emit(handler->prefix ?: handler->name, | |
266 | xattr->x_name, xattr->x_name_len, buf) | |
267 | : 0; | |
4d269918 | 268 | } |
1c6fdbd8 | 269 | |
94f651e2 | 270 | static int bch2_xattr_list_bcachefs(struct bch_fs *c, |
07bca3bd | 271 | struct bch_inode_unpacked *inode, |
94f651e2 KO |
272 | struct xattr_buf *buf, |
273 | bool all) | |
4d269918 KO |
274 | { |
275 | const char *prefix = all ? "bcachefs_effective." : "bcachefs."; | |
276 | unsigned id; | |
94f651e2 | 277 | int ret = 0; |
4d269918 | 278 | u64 v; |
1c6fdbd8 | 279 | |
4d269918 | 280 | for (id = 0; id < Inode_opt_nr; id++) { |
07bca3bd | 281 | v = bch2_inode_opt_get(inode, id); |
4d269918 KO |
282 | if (!v) |
283 | continue; | |
284 | ||
285 | if (!all && | |
07bca3bd | 286 | !(inode->bi_fields_set & (1 << id))) |
4d269918 KO |
287 | continue; |
288 | ||
94f651e2 KO |
289 | ret = __bch2_xattr_emit(prefix, bch2_inode_opts[id], |
290 | strlen(bch2_inode_opts[id]), buf); | |
291 | if (ret) | |
4d269918 | 292 | break; |
1c6fdbd8 | 293 | } |
94f651e2 KO |
294 | |
295 | return ret; | |
1c6fdbd8 KO |
296 | } |
297 | ||
298 | ssize_t bch2_xattr_list(struct dentry *dentry, char *buffer, size_t buffer_size) | |
299 | { | |
300 | struct bch_fs *c = dentry->d_sb->s_fs_info; | |
4d269918 | 301 | struct bch_inode_info *inode = to_bch_ei(dentry->d_inode); |
6bd68ec2 | 302 | struct btree_trans *trans = bch2_trans_get(c); |
67e0dd8f | 303 | struct btree_iter iter; |
1c6fdbd8 | 304 | struct bkey_s_c k; |
94f651e2 | 305 | struct xattr_buf buf = { .buf = buffer, .len = buffer_size }; |
6fed42bb KO |
306 | u64 offset = 0, inum = inode->ei_inode.bi_inum; |
307 | u32 snapshot; | |
94f651e2 | 308 | int ret; |
6fed42bb | 309 | retry: |
6bd68ec2 | 310 | bch2_trans_begin(trans); |
6fed42bb KO |
311 | iter = (struct btree_iter) { NULL }; |
312 | ||
6bd68ec2 | 313 | ret = bch2_subvolume_get_snapshot(trans, inode->ei_subvol, &snapshot); |
6fed42bb KO |
314 | if (ret) |
315 | goto err; | |
424eb881 | 316 | |
6bd68ec2 | 317 | for_each_btree_key_upto_norestart(trans, iter, BTREE_ID_xattrs, |
85d8cf16 KO |
318 | SPOS(inum, offset, snapshot), |
319 | POS(inum, U64_MAX), 0, k, ret) { | |
26609b61 | 320 | if (k.k->type != KEY_TYPE_xattr) |
1c6fdbd8 KO |
321 | continue; |
322 | ||
94f651e2 KO |
323 | ret = bch2_xattr_emit(dentry, bkey_s_c_to_xattr(k).v, &buf); |
324 | if (ret) | |
4d269918 KO |
325 | break; |
326 | } | |
6fed42bb KO |
327 | |
328 | offset = iter.pos.offset; | |
6bd68ec2 | 329 | bch2_trans_iter_exit(trans, &iter); |
6fed42bb | 330 | err: |
549d173c | 331 | if (bch2_err_matches(ret, BCH_ERR_transaction_restart)) |
6fed42bb | 332 | goto retry; |
50dc0f69 | 333 | |
6bd68ec2 | 334 | bch2_trans_put(trans); |
1c6fdbd8 | 335 | |
94f651e2 | 336 | if (ret) |
5c1ef830 | 337 | goto out; |
1c6fdbd8 | 338 | |
07bca3bd | 339 | ret = bch2_xattr_list_bcachefs(c, &inode->ei_inode, &buf, false); |
94f651e2 | 340 | if (ret) |
5c1ef830 | 341 | goto out; |
1c6fdbd8 | 342 | |
07bca3bd | 343 | ret = bch2_xattr_list_bcachefs(c, &inode->ei_inode, &buf, true); |
94f651e2 | 344 | if (ret) |
5c1ef830 | 345 | goto out; |
1c6fdbd8 | 346 | |
94f651e2 | 347 | return buf.used; |
5c1ef830 KO |
348 | out: |
349 | return bch2_err_class(ret); | |
1c6fdbd8 KO |
350 | } |
351 | ||
352 | static int bch2_xattr_get_handler(const struct xattr_handler *handler, | |
353 | struct dentry *dentry, struct inode *vinode, | |
354 | const char *name, void *buffer, size_t size) | |
355 | { | |
356 | struct bch_inode_info *inode = to_bch_ei(vinode); | |
357 | struct bch_fs *c = inode->v.i_sb->s_fs_info; | |
ca630f1d | 358 | int ret = bch2_trans_do(c, NULL, NULL, 0, |
6bd68ec2 | 359 | bch2_xattr_get_trans(trans, inode, name, buffer, size, handler->flags)); |
1c6fdbd8 | 360 | |
5c1ef830 | 361 | return bch2_err_class(ret); |
1c6fdbd8 KO |
362 | } |
363 | ||
364 | static int bch2_xattr_set_handler(const struct xattr_handler *handler, | |
365 | struct mnt_idmap *idmap, | |
366 | struct dentry *dentry, struct inode *vinode, | |
367 | const char *name, const void *value, | |
368 | size_t size, int flags) | |
369 | { | |
370 | struct bch_inode_info *inode = to_bch_ei(vinode); | |
371 | struct bch_fs *c = inode->v.i_sb->s_fs_info; | |
07bca3bd | 372 | struct bch_hash_info hash = bch2_hash_info_init(c, &inode->ei_inode); |
07f293c8 | 373 | struct bch_inode_unpacked inode_u; |
5c1ef830 | 374 | int ret; |
1c6fdbd8 | 375 | |
6bd68ec2 KO |
376 | ret = bch2_trans_run(c, |
377 | commit_do(trans, NULL, NULL, 0, | |
378 | bch2_xattr_set(trans, inode_inum(inode), &inode_u, | |
07f293c8 | 379 | &hash, name, value, size, |
6bd68ec2 KO |
380 | handler->flags, flags)) ?: |
381 | (bch2_inode_update_after_write(trans, inode, &inode_u, ATTR_CTIME), 0)); | |
07f293c8 | 382 | |
5c1ef830 | 383 | return bch2_err_class(ret); |
1c6fdbd8 KO |
384 | } |
385 | ||
386 | static const struct xattr_handler bch_xattr_user_handler = { | |
387 | .prefix = XATTR_USER_PREFIX, | |
388 | .get = bch2_xattr_get_handler, | |
389 | .set = bch2_xattr_set_handler, | |
26609b61 | 390 | .flags = KEY_TYPE_XATTR_INDEX_USER, |
1c6fdbd8 KO |
391 | }; |
392 | ||
393 | static bool bch2_xattr_trusted_list(struct dentry *dentry) | |
394 | { | |
395 | return capable(CAP_SYS_ADMIN); | |
396 | } | |
397 | ||
398 | static const struct xattr_handler bch_xattr_trusted_handler = { | |
399 | .prefix = XATTR_TRUSTED_PREFIX, | |
400 | .list = bch2_xattr_trusted_list, | |
401 | .get = bch2_xattr_get_handler, | |
402 | .set = bch2_xattr_set_handler, | |
26609b61 | 403 | .flags = KEY_TYPE_XATTR_INDEX_TRUSTED, |
1c6fdbd8 KO |
404 | }; |
405 | ||
406 | static const struct xattr_handler bch_xattr_security_handler = { | |
407 | .prefix = XATTR_SECURITY_PREFIX, | |
408 | .get = bch2_xattr_get_handler, | |
409 | .set = bch2_xattr_set_handler, | |
26609b61 | 410 | .flags = KEY_TYPE_XATTR_INDEX_SECURITY, |
1c6fdbd8 KO |
411 | }; |
412 | ||
413 | #ifndef NO_BCACHEFS_FS | |
414 | ||
4d269918 KO |
415 | static int opt_to_inode_opt(int id) |
416 | { | |
417 | switch (id) { | |
418 | #define x(name, ...) \ | |
419 | case Opt_##name: return Inode_opt_##name; | |
420 | BCH_INODE_OPTS() | |
421 | #undef x | |
422 | default: | |
423 | return -1; | |
424 | } | |
425 | } | |
426 | ||
427 | static int __bch2_xattr_bcachefs_get(const struct xattr_handler *handler, | |
428 | struct dentry *dentry, struct inode *vinode, | |
429 | const char *name, void *buffer, size_t size, | |
430 | bool all) | |
1c6fdbd8 KO |
431 | { |
432 | struct bch_inode_info *inode = to_bch_ei(vinode); | |
433 | struct bch_fs *c = inode->v.i_sb->s_fs_info; | |
434 | struct bch_opts opts = | |
abb936fb | 435 | bch2_inode_opts_to_opts(&inode->ei_inode); |
1c6fdbd8 | 436 | const struct bch_option *opt; |
4d269918 | 437 | int id, inode_opt_id; |
fa8e94fa KO |
438 | struct printbuf out = PRINTBUF; |
439 | int ret; | |
1c6fdbd8 KO |
440 | u64 v; |
441 | ||
442 | id = bch2_opt_lookup(name); | |
443 | if (id < 0 || !bch2_opt_is_inode_opt(id)) | |
444 | return -EINVAL; | |
445 | ||
4d269918 KO |
446 | inode_opt_id = opt_to_inode_opt(id); |
447 | if (inode_opt_id < 0) | |
448 | return -EINVAL; | |
449 | ||
1c6fdbd8 KO |
450 | opt = bch2_opt_table + id; |
451 | ||
452 | if (!bch2_opt_defined_by_id(&opts, id)) | |
453 | return -ENODATA; | |
454 | ||
4d269918 KO |
455 | if (!all && |
456 | !(inode->ei_inode.bi_fields_set & (1 << inode_opt_id))) | |
457 | return -ENODATA; | |
458 | ||
1c6fdbd8 | 459 | v = bch2_opt_get_by_id(&opts, id); |
5521b1df | 460 | bch2_opt_to_text(&out, c, c->disk_sb.sb, opt, v, 0); |
1c6fdbd8 | 461 | |
fa8e94fa | 462 | ret = out.pos; |
1c6fdbd8 | 463 | |
fa8e94fa KO |
464 | if (out.allocation_failure) { |
465 | ret = -ENOMEM; | |
466 | } else if (buffer) { | |
467 | if (out.pos > size) | |
468 | ret = -ERANGE; | |
469 | else | |
470 | memcpy(buffer, out.buf, out.pos); | |
471 | } | |
319f9ac3 | 472 | |
fa8e94fa KO |
473 | printbuf_exit(&out); |
474 | return ret; | |
1c6fdbd8 KO |
475 | } |
476 | ||
4d269918 KO |
477 | static int bch2_xattr_bcachefs_get(const struct xattr_handler *handler, |
478 | struct dentry *dentry, struct inode *vinode, | |
479 | const char *name, void *buffer, size_t size) | |
480 | { | |
481 | return __bch2_xattr_bcachefs_get(handler, dentry, vinode, | |
482 | name, buffer, size, false); | |
483 | } | |
484 | ||
1c6fdbd8 KO |
485 | struct inode_opt_set { |
486 | int id; | |
487 | u64 v; | |
488 | bool defined; | |
489 | }; | |
490 | ||
791236b8 JA |
491 | static int inode_opt_set_fn(struct btree_trans *trans, |
492 | struct bch_inode_info *inode, | |
1c6fdbd8 KO |
493 | struct bch_inode_unpacked *bi, |
494 | void *p) | |
495 | { | |
496 | struct inode_opt_set *s = p; | |
497 | ||
498 | if (s->defined) | |
4d269918 | 499 | bi->bi_fields_set |= 1U << s->id; |
1c6fdbd8 | 500 | else |
4d269918 KO |
501 | bi->bi_fields_set &= ~(1U << s->id); |
502 | ||
503 | bch2_inode_opt_set(bi, s->id, s->v); | |
504 | ||
1c6fdbd8 KO |
505 | return 0; |
506 | } | |
507 | ||
508 | static int bch2_xattr_bcachefs_set(const struct xattr_handler *handler, | |
509 | struct mnt_idmap *idmap, | |
510 | struct dentry *dentry, struct inode *vinode, | |
511 | const char *name, const void *value, | |
512 | size_t size, int flags) | |
513 | { | |
514 | struct bch_inode_info *inode = to_bch_ei(vinode); | |
515 | struct bch_fs *c = inode->v.i_sb->s_fs_info; | |
516 | const struct bch_option *opt; | |
517 | char *buf; | |
518 | struct inode_opt_set s; | |
4d269918 KO |
519 | int opt_id, inode_opt_id, ret; |
520 | ||
521 | opt_id = bch2_opt_lookup(name); | |
522 | if (opt_id < 0) | |
523 | return -EINVAL; | |
1c6fdbd8 | 524 | |
4d269918 KO |
525 | opt = bch2_opt_table + opt_id; |
526 | ||
527 | inode_opt_id = opt_to_inode_opt(opt_id); | |
528 | if (inode_opt_id < 0) | |
1c6fdbd8 KO |
529 | return -EINVAL; |
530 | ||
4d269918 | 531 | s.id = inode_opt_id; |
1c6fdbd8 KO |
532 | |
533 | if (value) { | |
4d269918 KO |
534 | u64 v = 0; |
535 | ||
1c6fdbd8 KO |
536 | buf = kmalloc(size + 1, GFP_KERNEL); |
537 | if (!buf) | |
538 | return -ENOMEM; | |
539 | memcpy(buf, value, size); | |
540 | buf[size] = '\0'; | |
541 | ||
63c4b254 | 542 | ret = bch2_opt_parse(c, opt, buf, &v, NULL); |
1c6fdbd8 KO |
543 | kfree(buf); |
544 | ||
545 | if (ret < 0) | |
546 | return ret; | |
547 | ||
4d269918 | 548 | ret = bch2_opt_check_may_set(c, opt_id, v); |
c258f28e KO |
549 | if (ret < 0) |
550 | return ret; | |
1c6fdbd8 | 551 | |
4d269918 | 552 | s.v = v + 1; |
1c6fdbd8 KO |
553 | s.defined = true; |
554 | } else { | |
ba276ce5 KO |
555 | /* |
556 | * Check if this option was set on the parent - if so, switched | |
557 | * back to inheriting from the parent: | |
558 | * | |
559 | * rename() also has to deal with keeping inherited options up | |
560 | * to date - see bch2_reinherit_attrs() | |
561 | */ | |
562 | spin_lock(&dentry->d_lock); | |
4d269918 KO |
563 | if (!IS_ROOT(dentry)) { |
564 | struct bch_inode_info *dir = | |
565 | to_bch_ei(d_inode(dentry->d_parent)); | |
566 | ||
567 | s.v = bch2_inode_opt_get(&dir->ei_inode, inode_opt_id); | |
568 | } else { | |
569 | s.v = 0; | |
570 | } | |
ba276ce5 | 571 | spin_unlock(&dentry->d_lock); |
4d269918 | 572 | |
1c6fdbd8 KO |
573 | s.defined = false; |
574 | } | |
575 | ||
576 | mutex_lock(&inode->ei_update_lock); | |
2fab25cd | 577 | if (inode_opt_id == Inode_opt_project) { |
7af0cec3 KO |
578 | /* |
579 | * inode fields accessible via the xattr interface are stored | |
580 | * with a +1 bias, so that 0 means unset: | |
581 | */ | |
582 | ret = bch2_set_projid(c, inode, s.v ? s.v - 1 : 0); | |
2fab25cd KO |
583 | if (ret) |
584 | goto err; | |
585 | } | |
586 | ||
2ea90048 | 587 | ret = bch2_write_inode(c, inode, inode_opt_set_fn, &s, 0); |
2fab25cd | 588 | err: |
1c6fdbd8 KO |
589 | mutex_unlock(&inode->ei_update_lock); |
590 | ||
591 | if (value && | |
4d269918 KO |
592 | (opt_id == Opt_background_compression || |
593 | opt_id == Opt_background_target)) | |
fb3f57bb | 594 | bch2_set_rebalance_needs_scan(c, inode->ei_inode.bi_inum); |
1c6fdbd8 | 595 | |
e47a390a | 596 | return bch2_err_class(ret); |
1c6fdbd8 KO |
597 | } |
598 | ||
599 | static const struct xattr_handler bch_xattr_bcachefs_handler = { | |
600 | .prefix = "bcachefs.", | |
601 | .get = bch2_xattr_bcachefs_get, | |
602 | .set = bch2_xattr_bcachefs_set, | |
603 | }; | |
604 | ||
4d269918 KO |
605 | static int bch2_xattr_bcachefs_get_effective( |
606 | const struct xattr_handler *handler, | |
607 | struct dentry *dentry, struct inode *vinode, | |
608 | const char *name, void *buffer, size_t size) | |
609 | { | |
610 | return __bch2_xattr_bcachefs_get(handler, dentry, vinode, | |
611 | name, buffer, size, true); | |
612 | } | |
613 | ||
614 | static const struct xattr_handler bch_xattr_bcachefs_effective_handler = { | |
615 | .prefix = "bcachefs_effective.", | |
616 | .get = bch2_xattr_bcachefs_get_effective, | |
617 | .set = bch2_xattr_bcachefs_set, | |
618 | }; | |
619 | ||
1c6fdbd8 KO |
620 | #endif /* NO_BCACHEFS_FS */ |
621 | ||
622 | const struct xattr_handler *bch2_xattr_handlers[] = { | |
623 | &bch_xattr_user_handler, | |
59e2480f | 624 | #ifdef CONFIG_BCACHEFS_POSIX_ACL |
1c6fdbd8 KO |
625 | &nop_posix_acl_access, |
626 | &nop_posix_acl_default, | |
59e2480f | 627 | #endif |
1c6fdbd8 KO |
628 | &bch_xattr_trusted_handler, |
629 | &bch_xattr_security_handler, | |
630 | #ifndef NO_BCACHEFS_FS | |
631 | &bch_xattr_bcachefs_handler, | |
4d269918 | 632 | &bch_xattr_bcachefs_effective_handler, |
1c6fdbd8 KO |
633 | #endif |
634 | NULL | |
635 | }; | |
636 | ||
637 | static const struct xattr_handler *bch_xattr_handler_map[] = { | |
26609b61 KO |
638 | [KEY_TYPE_XATTR_INDEX_USER] = &bch_xattr_user_handler, |
639 | [KEY_TYPE_XATTR_INDEX_POSIX_ACL_ACCESS] = | |
1c6fdbd8 | 640 | &nop_posix_acl_access, |
26609b61 | 641 | [KEY_TYPE_XATTR_INDEX_POSIX_ACL_DEFAULT] = |
1c6fdbd8 | 642 | &nop_posix_acl_default, |
26609b61 KO |
643 | [KEY_TYPE_XATTR_INDEX_TRUSTED] = &bch_xattr_trusted_handler, |
644 | [KEY_TYPE_XATTR_INDEX_SECURITY] = &bch_xattr_security_handler, | |
1c6fdbd8 KO |
645 | }; |
646 | ||
647 | static const struct xattr_handler *bch2_xattr_type_to_handler(unsigned type) | |
648 | { | |
649 | return type < ARRAY_SIZE(bch_xattr_handler_map) | |
650 | ? bch_xattr_handler_map[type] | |
651 | : NULL; | |
652 | } |