1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/ceph/ceph_debug.h> |
3 | #include <linux/ceph/pagelist.h> |
4 | |
5 | #include "super.h" |
6 | #include "mds_client.h" |
7 | |
8 | #include <linux/ceph/decode.h> |
9 | |
10 | #include <linux/xattr.h> |
11 | #include <linux/security.h> |
12 | #include <linux/posix_acl_xattr.h> |
13 | #include <linux/slab.h> |
14 | |
15 | #define XATTR_CEPH_PREFIX "ceph." |
16 | #define XATTR_CEPH_PREFIX_LEN (sizeof (XATTR_CEPH_PREFIX) - 1) |
17 | |
18 | static int __remove_xattr(struct ceph_inode_info *ci, |
19 | struct ceph_inode_xattr *xattr); |
20 | |
21 | static bool ceph_is_valid_xattr(const char *name) |
22 | { |
23 | return !strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) || |
24 | !strncmp(name, XATTR_CEPH_PREFIX, XATTR_CEPH_PREFIX_LEN) || |
25 | !strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) || |
26 | !strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN); |
27 | } |
28 | |
29 | /* |
30 | * These define virtual xattrs exposing the recursive directory |
31 | * statistics and layout metadata. |
32 | */ |
33 | struct ceph_vxattr { |
34 | char *name; |
35 | size_t name_size; /* strlen(name) + 1 (for '\0') */ |
36 | ssize_t (*getxattr_cb)(struct ceph_inode_info *ci, char *val, |
37 | size_t size); |
38 | bool (*exists_cb)(struct ceph_inode_info *ci); |
39 | unsigned int flags; |
40 | }; |
41 | |
42 | #define VXATTR_FLAG_READONLY (1<<0) |
43 | #define VXATTR_FLAG_HIDDEN (1<<1) |
44 | #define VXATTR_FLAG_RSTAT (1<<2) |
45 | #define VXATTR_FLAG_DIRSTAT (1<<3) |
46 | |
47 | /* layouts */ |
48 | |
49 | static bool ceph_vxattrcb_layout_exists(struct ceph_inode_info *ci) |
50 | { |
51 | struct ceph_file_layout *fl = &ci->i_layout; |
52 | return (fl->stripe_unit > 0 || fl->stripe_count > 0 || |
53 | fl->object_size > 0 || fl->pool_id >= 0 || |
54 | rcu_dereference_raw(fl->pool_ns) != NULL); |
55 | } |
56 | |
57 | static ssize_t ceph_vxattrcb_layout(struct ceph_inode_info *ci, char *val, |
58 | size_t size) |
59 | { |
60 | struct ceph_fs_client *fsc = ceph_sb_to_client(sb: ci->netfs.inode.i_sb); |
61 | struct ceph_osd_client *osdc = &fsc->client->osdc; |
62 | struct ceph_string *pool_ns; |
63 | s64 pool = ci->i_layout.pool_id; |
64 | const char *pool_name; |
65 | const char *ns_field = " pool_namespace=" ; |
66 | char buf[128]; |
67 | size_t len, total_len = 0; |
68 | ssize_t ret; |
69 | |
70 | pool_ns = ceph_try_get_string(ci->i_layout.pool_ns); |
71 | |
72 | dout("ceph_vxattrcb_layout %p\n" , &ci->netfs.inode); |
73 | down_read(sem: &osdc->lock); |
74 | pool_name = ceph_pg_pool_name_by_id(map: osdc->osdmap, id: pool); |
75 | if (pool_name) { |
76 | len = snprintf(buf, size: sizeof(buf), |
77 | fmt: "stripe_unit=%u stripe_count=%u object_size=%u pool=" , |
78 | ci->i_layout.stripe_unit, ci->i_layout.stripe_count, |
79 | ci->i_layout.object_size); |
80 | total_len = len + strlen(pool_name); |
81 | } else { |
82 | len = snprintf(buf, size: sizeof(buf), |
83 | fmt: "stripe_unit=%u stripe_count=%u object_size=%u pool=%lld" , |
84 | ci->i_layout.stripe_unit, ci->i_layout.stripe_count, |
85 | ci->i_layout.object_size, pool); |
86 | total_len = len; |
87 | } |
88 | |
89 | if (pool_ns) |
90 | total_len += strlen(ns_field) + pool_ns->len; |
91 | |
92 | ret = total_len; |
93 | if (size >= total_len) { |
94 | memcpy(val, buf, len); |
95 | ret = len; |
96 | if (pool_name) { |
97 | len = strlen(pool_name); |
98 | memcpy(val + ret, pool_name, len); |
99 | ret += len; |
100 | } |
101 | if (pool_ns) { |
102 | len = strlen(ns_field); |
103 | memcpy(val + ret, ns_field, len); |
104 | ret += len; |
105 | memcpy(val + ret, pool_ns->str, pool_ns->len); |
106 | ret += pool_ns->len; |
107 | } |
108 | } |
109 | up_read(sem: &osdc->lock); |
110 | ceph_put_string(str: pool_ns); |
111 | return ret; |
112 | } |
113 | |
114 | /* |
115 | * The convention with strings in xattrs is that they should not be NULL |
116 | * terminated, since we're returning the length with them. snprintf always |
117 | * NULL terminates however, so call it on a temporary buffer and then memcpy |
118 | * the result into place. |
119 | */ |
120 | static __printf(3, 4) |
121 | int ceph_fmt_xattr(char *val, size_t size, const char *fmt, ...) |
122 | { |
123 | int ret; |
124 | va_list args; |
125 | char buf[96]; /* NB: reevaluate size if new vxattrs are added */ |
126 | |
127 | va_start(args, fmt); |
128 | ret = vsnprintf(buf, size: size ? sizeof(buf) : 0, fmt, args); |
129 | va_end(args); |
130 | |
131 | /* Sanity check */ |
132 | if (size && ret + 1 > sizeof(buf)) { |
133 | WARN_ONCE(true, "Returned length too big (%d)" , ret); |
134 | return -E2BIG; |
135 | } |
136 | |
137 | if (ret <= size) |
138 | memcpy(val, buf, ret); |
139 | return ret; |
140 | } |
141 | |
142 | static ssize_t ceph_vxattrcb_layout_stripe_unit(struct ceph_inode_info *ci, |
143 | char *val, size_t size) |
144 | { |
145 | return ceph_fmt_xattr(val, size, fmt: "%u" , ci->i_layout.stripe_unit); |
146 | } |
147 | |
148 | static ssize_t ceph_vxattrcb_layout_stripe_count(struct ceph_inode_info *ci, |
149 | char *val, size_t size) |
150 | { |
151 | return ceph_fmt_xattr(val, size, fmt: "%u" , ci->i_layout.stripe_count); |
152 | } |
153 | |
154 | static ssize_t ceph_vxattrcb_layout_object_size(struct ceph_inode_info *ci, |
155 | char *val, size_t size) |
156 | { |
157 | return ceph_fmt_xattr(val, size, fmt: "%u" , ci->i_layout.object_size); |
158 | } |
159 | |
160 | static ssize_t ceph_vxattrcb_layout_pool(struct ceph_inode_info *ci, |
161 | char *val, size_t size) |
162 | { |
163 | ssize_t ret; |
164 | struct ceph_fs_client *fsc = ceph_sb_to_client(sb: ci->netfs.inode.i_sb); |
165 | struct ceph_osd_client *osdc = &fsc->client->osdc; |
166 | s64 pool = ci->i_layout.pool_id; |
167 | const char *pool_name; |
168 | |
169 | down_read(sem: &osdc->lock); |
170 | pool_name = ceph_pg_pool_name_by_id(map: osdc->osdmap, id: pool); |
171 | if (pool_name) { |
172 | ret = strlen(pool_name); |
173 | if (ret <= size) |
174 | memcpy(val, pool_name, ret); |
175 | } else { |
176 | ret = ceph_fmt_xattr(val, size, fmt: "%lld" , pool); |
177 | } |
178 | up_read(sem: &osdc->lock); |
179 | return ret; |
180 | } |
181 | |
182 | static ssize_t ceph_vxattrcb_layout_pool_namespace(struct ceph_inode_info *ci, |
183 | char *val, size_t size) |
184 | { |
185 | ssize_t ret = 0; |
186 | struct ceph_string *ns = ceph_try_get_string(ci->i_layout.pool_ns); |
187 | |
188 | if (ns) { |
189 | ret = ns->len; |
190 | if (ret <= size) |
191 | memcpy(val, ns->str, ret); |
192 | ceph_put_string(str: ns); |
193 | } |
194 | return ret; |
195 | } |
196 | |
197 | /* directories */ |
198 | |
199 | static ssize_t ceph_vxattrcb_dir_entries(struct ceph_inode_info *ci, char *val, |
200 | size_t size) |
201 | { |
202 | return ceph_fmt_xattr(val, size, fmt: "%lld" , ci->i_files + ci->i_subdirs); |
203 | } |
204 | |
205 | static ssize_t ceph_vxattrcb_dir_files(struct ceph_inode_info *ci, char *val, |
206 | size_t size) |
207 | { |
208 | return ceph_fmt_xattr(val, size, fmt: "%lld" , ci->i_files); |
209 | } |
210 | |
211 | static ssize_t ceph_vxattrcb_dir_subdirs(struct ceph_inode_info *ci, char *val, |
212 | size_t size) |
213 | { |
214 | return ceph_fmt_xattr(val, size, fmt: "%lld" , ci->i_subdirs); |
215 | } |
216 | |
217 | static ssize_t ceph_vxattrcb_dir_rentries(struct ceph_inode_info *ci, char *val, |
218 | size_t size) |
219 | { |
220 | return ceph_fmt_xattr(val, size, fmt: "%lld" , |
221 | ci->i_rfiles + ci->i_rsubdirs); |
222 | } |
223 | |
224 | static ssize_t ceph_vxattrcb_dir_rfiles(struct ceph_inode_info *ci, char *val, |
225 | size_t size) |
226 | { |
227 | return ceph_fmt_xattr(val, size, fmt: "%lld" , ci->i_rfiles); |
228 | } |
229 | |
230 | static ssize_t ceph_vxattrcb_dir_rsubdirs(struct ceph_inode_info *ci, char *val, |
231 | size_t size) |
232 | { |
233 | return ceph_fmt_xattr(val, size, fmt: "%lld" , ci->i_rsubdirs); |
234 | } |
235 | |
236 | static ssize_t ceph_vxattrcb_dir_rsnaps(struct ceph_inode_info *ci, char *val, |
237 | size_t size) |
238 | { |
239 | return ceph_fmt_xattr(val, size, fmt: "%lld" , ci->i_rsnaps); |
240 | } |
241 | |
242 | static ssize_t ceph_vxattrcb_dir_rbytes(struct ceph_inode_info *ci, char *val, |
243 | size_t size) |
244 | { |
245 | return ceph_fmt_xattr(val, size, fmt: "%lld" , ci->i_rbytes); |
246 | } |
247 | |
248 | static ssize_t ceph_vxattrcb_dir_rctime(struct ceph_inode_info *ci, char *val, |
249 | size_t size) |
250 | { |
251 | return ceph_fmt_xattr(val, size, fmt: "%lld.%09ld" , ci->i_rctime.tv_sec, |
252 | ci->i_rctime.tv_nsec); |
253 | } |
254 | |
255 | /* dir pin */ |
256 | static bool ceph_vxattrcb_dir_pin_exists(struct ceph_inode_info *ci) |
257 | { |
258 | return ci->i_dir_pin != -ENODATA; |
259 | } |
260 | |
261 | static ssize_t ceph_vxattrcb_dir_pin(struct ceph_inode_info *ci, char *val, |
262 | size_t size) |
263 | { |
264 | return ceph_fmt_xattr(val, size, fmt: "%d" , (int)ci->i_dir_pin); |
265 | } |
266 | |
267 | /* quotas */ |
268 | static bool ceph_vxattrcb_quota_exists(struct ceph_inode_info *ci) |
269 | { |
270 | bool ret = false; |
271 | spin_lock(lock: &ci->i_ceph_lock); |
272 | if ((ci->i_max_files || ci->i_max_bytes) && |
273 | ci->i_vino.snap == CEPH_NOSNAP && |
274 | ci->i_snap_realm && |
275 | ci->i_snap_realm->ino == ci->i_vino.ino) |
276 | ret = true; |
277 | spin_unlock(lock: &ci->i_ceph_lock); |
278 | return ret; |
279 | } |
280 | |
281 | static ssize_t ceph_vxattrcb_quota(struct ceph_inode_info *ci, char *val, |
282 | size_t size) |
283 | { |
284 | return ceph_fmt_xattr(val, size, fmt: "max_bytes=%llu max_files=%llu" , |
285 | ci->i_max_bytes, ci->i_max_files); |
286 | } |
287 | |
288 | static ssize_t ceph_vxattrcb_quota_max_bytes(struct ceph_inode_info *ci, |
289 | char *val, size_t size) |
290 | { |
291 | return ceph_fmt_xattr(val, size, fmt: "%llu" , ci->i_max_bytes); |
292 | } |
293 | |
294 | static ssize_t ceph_vxattrcb_quota_max_files(struct ceph_inode_info *ci, |
295 | char *val, size_t size) |
296 | { |
297 | return ceph_fmt_xattr(val, size, fmt: "%llu" , ci->i_max_files); |
298 | } |
299 | |
300 | /* snapshots */ |
301 | static bool ceph_vxattrcb_snap_btime_exists(struct ceph_inode_info *ci) |
302 | { |
303 | return (ci->i_snap_btime.tv_sec != 0 || ci->i_snap_btime.tv_nsec != 0); |
304 | } |
305 | |
306 | static ssize_t ceph_vxattrcb_snap_btime(struct ceph_inode_info *ci, char *val, |
307 | size_t size) |
308 | { |
309 | return ceph_fmt_xattr(val, size, fmt: "%lld.%09ld" , ci->i_snap_btime.tv_sec, |
310 | ci->i_snap_btime.tv_nsec); |
311 | } |
312 | |
313 | static ssize_t ceph_vxattrcb_cluster_fsid(struct ceph_inode_info *ci, |
314 | char *val, size_t size) |
315 | { |
316 | struct ceph_fs_client *fsc = ceph_sb_to_client(sb: ci->netfs.inode.i_sb); |
317 | |
318 | return ceph_fmt_xattr(val, size, fmt: "%pU" , &fsc->client->fsid); |
319 | } |
320 | |
321 | static ssize_t ceph_vxattrcb_client_id(struct ceph_inode_info *ci, |
322 | char *val, size_t size) |
323 | { |
324 | struct ceph_fs_client *fsc = ceph_sb_to_client(sb: ci->netfs.inode.i_sb); |
325 | |
326 | return ceph_fmt_xattr(val, size, fmt: "client%lld" , |
327 | ceph_client_gid(client: fsc->client)); |
328 | } |
329 | |
330 | static ssize_t ceph_vxattrcb_caps(struct ceph_inode_info *ci, char *val, |
331 | size_t size) |
332 | { |
333 | int issued; |
334 | |
335 | spin_lock(lock: &ci->i_ceph_lock); |
336 | issued = __ceph_caps_issued(ci, NULL); |
337 | spin_unlock(lock: &ci->i_ceph_lock); |
338 | |
339 | return ceph_fmt_xattr(val, size, fmt: "%s/0x%x" , |
340 | ceph_cap_string(c: issued), issued); |
341 | } |
342 | |
343 | static ssize_t ceph_vxattrcb_auth_mds(struct ceph_inode_info *ci, |
344 | char *val, size_t size) |
345 | { |
346 | int ret; |
347 | |
348 | spin_lock(lock: &ci->i_ceph_lock); |
349 | ret = ceph_fmt_xattr(val, size, fmt: "%d" , |
350 | ci->i_auth_cap ? ci->i_auth_cap->session->s_mds : -1); |
351 | spin_unlock(lock: &ci->i_ceph_lock); |
352 | return ret; |
353 | } |
354 | |
355 | #if IS_ENABLED(CONFIG_FS_ENCRYPTION) |
356 | static bool ceph_vxattrcb_fscrypt_auth_exists(struct ceph_inode_info *ci) |
357 | { |
358 | return ci->fscrypt_auth_len; |
359 | } |
360 | |
361 | static ssize_t ceph_vxattrcb_fscrypt_auth(struct ceph_inode_info *ci, |
362 | char *val, size_t size) |
363 | { |
364 | if (size) { |
365 | if (size < ci->fscrypt_auth_len) |
366 | return -ERANGE; |
367 | memcpy(val, ci->fscrypt_auth, ci->fscrypt_auth_len); |
368 | } |
369 | return ci->fscrypt_auth_len; |
370 | } |
371 | #endif /* CONFIG_FS_ENCRYPTION */ |
372 | |
373 | #define CEPH_XATTR_NAME(_type, _name) XATTR_CEPH_PREFIX #_type "." #_name |
374 | #define CEPH_XATTR_NAME2(_type, _name, _name2) \ |
375 | XATTR_CEPH_PREFIX #_type "." #_name "." #_name2 |
376 | |
377 | #define XATTR_NAME_CEPH(_type, _name, _flags) \ |
378 | { \ |
379 | .name = CEPH_XATTR_NAME(_type, _name), \ |
380 | .name_size = sizeof (CEPH_XATTR_NAME(_type, _name)), \ |
381 | .getxattr_cb = ceph_vxattrcb_ ## _type ## _ ## _name, \ |
382 | .exists_cb = NULL, \ |
383 | .flags = (VXATTR_FLAG_READONLY | _flags), \ |
384 | } |
385 | #define XATTR_RSTAT_FIELD(_type, _name) \ |
386 | XATTR_NAME_CEPH(_type, _name, VXATTR_FLAG_RSTAT) |
387 | #define XATTR_RSTAT_FIELD_UPDATABLE(_type, _name) \ |
388 | { \ |
389 | .name = CEPH_XATTR_NAME(_type, _name), \ |
390 | .name_size = sizeof (CEPH_XATTR_NAME(_type, _name)), \ |
391 | .getxattr_cb = ceph_vxattrcb_ ## _type ## _ ## _name, \ |
392 | .exists_cb = NULL, \ |
393 | .flags = VXATTR_FLAG_RSTAT, \ |
394 | } |
395 | #define XATTR_LAYOUT_FIELD(_type, _name, _field) \ |
396 | { \ |
397 | .name = CEPH_XATTR_NAME2(_type, _name, _field), \ |
398 | .name_size = sizeof (CEPH_XATTR_NAME2(_type, _name, _field)), \ |
399 | .getxattr_cb = ceph_vxattrcb_ ## _name ## _ ## _field, \ |
400 | .exists_cb = ceph_vxattrcb_layout_exists, \ |
401 | .flags = VXATTR_FLAG_HIDDEN, \ |
402 | } |
403 | #define XATTR_QUOTA_FIELD(_type, _name) \ |
404 | { \ |
405 | .name = CEPH_XATTR_NAME(_type, _name), \ |
406 | .name_size = sizeof(CEPH_XATTR_NAME(_type, _name)), \ |
407 | .getxattr_cb = ceph_vxattrcb_ ## _type ## _ ## _name, \ |
408 | .exists_cb = ceph_vxattrcb_quota_exists, \ |
409 | .flags = VXATTR_FLAG_HIDDEN, \ |
410 | } |
411 | |
412 | static struct ceph_vxattr ceph_dir_vxattrs[] = { |
413 | { |
414 | .name = "ceph.dir.layout" , |
415 | .name_size = sizeof("ceph.dir.layout" ), |
416 | .getxattr_cb = ceph_vxattrcb_layout, |
417 | .exists_cb = ceph_vxattrcb_layout_exists, |
418 | .flags = VXATTR_FLAG_HIDDEN, |
419 | }, |
420 | XATTR_LAYOUT_FIELD(dir, layout, stripe_unit), |
421 | XATTR_LAYOUT_FIELD(dir, layout, stripe_count), |
422 | XATTR_LAYOUT_FIELD(dir, layout, object_size), |
423 | XATTR_LAYOUT_FIELD(dir, layout, pool), |
424 | XATTR_LAYOUT_FIELD(dir, layout, pool_namespace), |
425 | XATTR_NAME_CEPH(dir, entries, VXATTR_FLAG_DIRSTAT), |
426 | XATTR_NAME_CEPH(dir, files, VXATTR_FLAG_DIRSTAT), |
427 | XATTR_NAME_CEPH(dir, subdirs, VXATTR_FLAG_DIRSTAT), |
428 | XATTR_RSTAT_FIELD(dir, rentries), |
429 | XATTR_RSTAT_FIELD(dir, rfiles), |
430 | XATTR_RSTAT_FIELD(dir, rsubdirs), |
431 | XATTR_RSTAT_FIELD(dir, rsnaps), |
432 | XATTR_RSTAT_FIELD(dir, rbytes), |
433 | XATTR_RSTAT_FIELD_UPDATABLE(dir, rctime), |
434 | { |
435 | .name = "ceph.dir.pin" , |
436 | .name_size = sizeof("ceph.dir.pin" ), |
437 | .getxattr_cb = ceph_vxattrcb_dir_pin, |
438 | .exists_cb = ceph_vxattrcb_dir_pin_exists, |
439 | .flags = VXATTR_FLAG_HIDDEN, |
440 | }, |
441 | { |
442 | .name = "ceph.quota" , |
443 | .name_size = sizeof("ceph.quota" ), |
444 | .getxattr_cb = ceph_vxattrcb_quota, |
445 | .exists_cb = ceph_vxattrcb_quota_exists, |
446 | .flags = VXATTR_FLAG_HIDDEN, |
447 | }, |
448 | XATTR_QUOTA_FIELD(quota, max_bytes), |
449 | XATTR_QUOTA_FIELD(quota, max_files), |
450 | { |
451 | .name = "ceph.snap.btime" , |
452 | .name_size = sizeof("ceph.snap.btime" ), |
453 | .getxattr_cb = ceph_vxattrcb_snap_btime, |
454 | .exists_cb = ceph_vxattrcb_snap_btime_exists, |
455 | .flags = VXATTR_FLAG_READONLY, |
456 | }, |
457 | { |
458 | .name = "ceph.caps" , |
459 | .name_size = sizeof("ceph.caps" ), |
460 | .getxattr_cb = ceph_vxattrcb_caps, |
461 | .exists_cb = NULL, |
462 | .flags = VXATTR_FLAG_HIDDEN, |
463 | }, |
464 | { .name = NULL, 0 } /* Required table terminator */ |
465 | }; |
466 | |
467 | /* files */ |
468 | |
469 | static struct ceph_vxattr ceph_file_vxattrs[] = { |
470 | { |
471 | .name = "ceph.file.layout" , |
472 | .name_size = sizeof("ceph.file.layout" ), |
473 | .getxattr_cb = ceph_vxattrcb_layout, |
474 | .exists_cb = ceph_vxattrcb_layout_exists, |
475 | .flags = VXATTR_FLAG_HIDDEN, |
476 | }, |
477 | XATTR_LAYOUT_FIELD(file, layout, stripe_unit), |
478 | XATTR_LAYOUT_FIELD(file, layout, stripe_count), |
479 | XATTR_LAYOUT_FIELD(file, layout, object_size), |
480 | XATTR_LAYOUT_FIELD(file, layout, pool), |
481 | XATTR_LAYOUT_FIELD(file, layout, pool_namespace), |
482 | { |
483 | .name = "ceph.snap.btime" , |
484 | .name_size = sizeof("ceph.snap.btime" ), |
485 | .getxattr_cb = ceph_vxattrcb_snap_btime, |
486 | .exists_cb = ceph_vxattrcb_snap_btime_exists, |
487 | .flags = VXATTR_FLAG_READONLY, |
488 | }, |
489 | { |
490 | .name = "ceph.caps" , |
491 | .name_size = sizeof("ceph.caps" ), |
492 | .getxattr_cb = ceph_vxattrcb_caps, |
493 | .exists_cb = NULL, |
494 | .flags = VXATTR_FLAG_HIDDEN, |
495 | }, |
496 | { .name = NULL, 0 } /* Required table terminator */ |
497 | }; |
498 | |
499 | static struct ceph_vxattr ceph_common_vxattrs[] = { |
500 | { |
501 | .name = "ceph.cluster_fsid" , |
502 | .name_size = sizeof("ceph.cluster_fsid" ), |
503 | .getxattr_cb = ceph_vxattrcb_cluster_fsid, |
504 | .exists_cb = NULL, |
505 | .flags = VXATTR_FLAG_READONLY, |
506 | }, |
507 | { |
508 | .name = "ceph.client_id" , |
509 | .name_size = sizeof("ceph.client_id" ), |
510 | .getxattr_cb = ceph_vxattrcb_client_id, |
511 | .exists_cb = NULL, |
512 | .flags = VXATTR_FLAG_READONLY, |
513 | }, |
514 | { |
515 | .name = "ceph.auth_mds" , |
516 | .name_size = sizeof("ceph.auth_mds" ), |
517 | .getxattr_cb = ceph_vxattrcb_auth_mds, |
518 | .exists_cb = NULL, |
519 | .flags = VXATTR_FLAG_READONLY, |
520 | }, |
521 | #if IS_ENABLED(CONFIG_FS_ENCRYPTION) |
522 | { |
523 | .name = "ceph.fscrypt.auth" , |
524 | .name_size = sizeof("ceph.fscrypt.auth" ), |
525 | .getxattr_cb = ceph_vxattrcb_fscrypt_auth, |
526 | .exists_cb = ceph_vxattrcb_fscrypt_auth_exists, |
527 | .flags = VXATTR_FLAG_READONLY, |
528 | }, |
529 | #endif /* CONFIG_FS_ENCRYPTION */ |
530 | { .name = NULL, 0 } /* Required table terminator */ |
531 | }; |
532 | |
533 | static struct ceph_vxattr *ceph_inode_vxattrs(struct inode *inode) |
534 | { |
535 | if (S_ISDIR(inode->i_mode)) |
536 | return ceph_dir_vxattrs; |
537 | else if (S_ISREG(inode->i_mode)) |
538 | return ceph_file_vxattrs; |
539 | return NULL; |
540 | } |
541 | |
542 | static struct ceph_vxattr *ceph_match_vxattr(struct inode *inode, |
543 | const char *name) |
544 | { |
545 | struct ceph_vxattr *vxattr = ceph_inode_vxattrs(inode); |
546 | |
547 | if (vxattr) { |
548 | while (vxattr->name) { |
549 | if (!strcmp(vxattr->name, name)) |
550 | return vxattr; |
551 | vxattr++; |
552 | } |
553 | } |
554 | |
555 | vxattr = ceph_common_vxattrs; |
556 | while (vxattr->name) { |
557 | if (!strcmp(vxattr->name, name)) |
558 | return vxattr; |
559 | vxattr++; |
560 | } |
561 | |
562 | return NULL; |
563 | } |
564 | |
565 | #define MAX_XATTR_VAL_PRINT_LEN 256 |
566 | |
567 | static int __set_xattr(struct ceph_inode_info *ci, |
568 | const char *name, int name_len, |
569 | const char *val, int val_len, |
570 | int flags, int update_xattr, |
571 | struct ceph_inode_xattr **newxattr) |
572 | { |
573 | struct rb_node **p; |
574 | struct rb_node *parent = NULL; |
575 | struct ceph_inode_xattr *xattr = NULL; |
576 | int c; |
577 | int new = 0; |
578 | |
579 | p = &ci->i_xattrs.index.rb_node; |
580 | while (*p) { |
581 | parent = *p; |
582 | xattr = rb_entry(parent, struct ceph_inode_xattr, node); |
583 | c = strncmp(name, xattr->name, min(name_len, xattr->name_len)); |
584 | if (c < 0) |
585 | p = &(*p)->rb_left; |
586 | else if (c > 0) |
587 | p = &(*p)->rb_right; |
588 | else { |
589 | if (name_len == xattr->name_len) |
590 | break; |
591 | else if (name_len < xattr->name_len) |
592 | p = &(*p)->rb_left; |
593 | else |
594 | p = &(*p)->rb_right; |
595 | } |
596 | xattr = NULL; |
597 | } |
598 | |
599 | if (update_xattr) { |
600 | int err = 0; |
601 | |
602 | if (xattr && (flags & XATTR_CREATE)) |
603 | err = -EEXIST; |
604 | else if (!xattr && (flags & XATTR_REPLACE)) |
605 | err = -ENODATA; |
606 | if (err) { |
607 | kfree(objp: name); |
608 | kfree(objp: val); |
609 | kfree(objp: *newxattr); |
610 | return err; |
611 | } |
612 | if (update_xattr < 0) { |
613 | if (xattr) |
614 | __remove_xattr(ci, xattr); |
615 | kfree(objp: name); |
616 | kfree(objp: *newxattr); |
617 | return 0; |
618 | } |
619 | } |
620 | |
621 | if (!xattr) { |
622 | new = 1; |
623 | xattr = *newxattr; |
624 | xattr->name = name; |
625 | xattr->name_len = name_len; |
626 | xattr->should_free_name = update_xattr; |
627 | |
628 | ci->i_xattrs.count++; |
629 | dout("%s count=%d\n" , __func__, ci->i_xattrs.count); |
630 | } else { |
631 | kfree(objp: *newxattr); |
632 | *newxattr = NULL; |
633 | if (xattr->should_free_val) |
634 | kfree(objp: xattr->val); |
635 | |
636 | if (update_xattr) { |
637 | kfree(objp: name); |
638 | name = xattr->name; |
639 | } |
640 | ci->i_xattrs.names_size -= xattr->name_len; |
641 | ci->i_xattrs.vals_size -= xattr->val_len; |
642 | } |
643 | ci->i_xattrs.names_size += name_len; |
644 | ci->i_xattrs.vals_size += val_len; |
645 | if (val) |
646 | xattr->val = val; |
647 | else |
648 | xattr->val = "" ; |
649 | |
650 | xattr->val_len = val_len; |
651 | xattr->dirty = update_xattr; |
652 | xattr->should_free_val = (val && update_xattr); |
653 | |
654 | if (new) { |
655 | rb_link_node(node: &xattr->node, parent, rb_link: p); |
656 | rb_insert_color(&xattr->node, &ci->i_xattrs.index); |
657 | dout("%s p=%p\n" , __func__, p); |
658 | } |
659 | |
660 | dout("%s added %llx.%llx xattr %p %.*s=%.*s%s\n" , __func__, |
661 | ceph_vinop(&ci->netfs.inode), xattr, name_len, name, |
662 | min(val_len, MAX_XATTR_VAL_PRINT_LEN), val, |
663 | val_len > MAX_XATTR_VAL_PRINT_LEN ? "..." : "" ); |
664 | |
665 | return 0; |
666 | } |
667 | |
668 | static struct ceph_inode_xattr *__get_xattr(struct ceph_inode_info *ci, |
669 | const char *name) |
670 | { |
671 | struct rb_node **p; |
672 | struct rb_node *parent = NULL; |
673 | struct ceph_inode_xattr *xattr = NULL; |
674 | int name_len = strlen(name); |
675 | int c; |
676 | |
677 | p = &ci->i_xattrs.index.rb_node; |
678 | while (*p) { |
679 | parent = *p; |
680 | xattr = rb_entry(parent, struct ceph_inode_xattr, node); |
681 | c = strncmp(name, xattr->name, xattr->name_len); |
682 | if (c == 0 && name_len > xattr->name_len) |
683 | c = 1; |
684 | if (c < 0) |
685 | p = &(*p)->rb_left; |
686 | else if (c > 0) |
687 | p = &(*p)->rb_right; |
688 | else { |
689 | int len = min(xattr->val_len, MAX_XATTR_VAL_PRINT_LEN); |
690 | |
691 | dout("%s %s: found %.*s%s\n" , __func__, name, len, |
692 | xattr->val, xattr->val_len > len ? "..." : "" ); |
693 | return xattr; |
694 | } |
695 | } |
696 | |
697 | dout("%s %s: not found\n" , __func__, name); |
698 | |
699 | return NULL; |
700 | } |
701 | |
702 | static void __free_xattr(struct ceph_inode_xattr *xattr) |
703 | { |
704 | BUG_ON(!xattr); |
705 | |
706 | if (xattr->should_free_name) |
707 | kfree(objp: xattr->name); |
708 | if (xattr->should_free_val) |
709 | kfree(objp: xattr->val); |
710 | |
711 | kfree(objp: xattr); |
712 | } |
713 | |
714 | static int __remove_xattr(struct ceph_inode_info *ci, |
715 | struct ceph_inode_xattr *xattr) |
716 | { |
717 | if (!xattr) |
718 | return -ENODATA; |
719 | |
720 | rb_erase(&xattr->node, &ci->i_xattrs.index); |
721 | |
722 | if (xattr->should_free_name) |
723 | kfree(objp: xattr->name); |
724 | if (xattr->should_free_val) |
725 | kfree(objp: xattr->val); |
726 | |
727 | ci->i_xattrs.names_size -= xattr->name_len; |
728 | ci->i_xattrs.vals_size -= xattr->val_len; |
729 | ci->i_xattrs.count--; |
730 | kfree(objp: xattr); |
731 | |
732 | return 0; |
733 | } |
734 | |
735 | static char *__copy_xattr_names(struct ceph_inode_info *ci, |
736 | char *dest) |
737 | { |
738 | struct rb_node *p; |
739 | struct ceph_inode_xattr *xattr = NULL; |
740 | |
741 | p = rb_first(&ci->i_xattrs.index); |
742 | dout("__copy_xattr_names count=%d\n" , ci->i_xattrs.count); |
743 | |
744 | while (p) { |
745 | xattr = rb_entry(p, struct ceph_inode_xattr, node); |
746 | memcpy(dest, xattr->name, xattr->name_len); |
747 | dest[xattr->name_len] = '\0'; |
748 | |
749 | dout("dest=%s %p (%s) (%d/%d)\n" , dest, xattr, xattr->name, |
750 | xattr->name_len, ci->i_xattrs.names_size); |
751 | |
752 | dest += xattr->name_len + 1; |
753 | p = rb_next(p); |
754 | } |
755 | |
756 | return dest; |
757 | } |
758 | |
759 | void __ceph_destroy_xattrs(struct ceph_inode_info *ci) |
760 | { |
761 | struct rb_node *p, *tmp; |
762 | struct ceph_inode_xattr *xattr = NULL; |
763 | |
764 | p = rb_first(&ci->i_xattrs.index); |
765 | |
766 | dout("__ceph_destroy_xattrs p=%p\n" , p); |
767 | |
768 | while (p) { |
769 | xattr = rb_entry(p, struct ceph_inode_xattr, node); |
770 | tmp = p; |
771 | p = rb_next(tmp); |
772 | dout("__ceph_destroy_xattrs next p=%p (%.*s)\n" , p, |
773 | xattr->name_len, xattr->name); |
774 | rb_erase(tmp, &ci->i_xattrs.index); |
775 | |
776 | __free_xattr(xattr); |
777 | } |
778 | |
779 | ci->i_xattrs.names_size = 0; |
780 | ci->i_xattrs.vals_size = 0; |
781 | ci->i_xattrs.index_version = 0; |
782 | ci->i_xattrs.count = 0; |
783 | ci->i_xattrs.index = RB_ROOT; |
784 | } |
785 | |
786 | static int __build_xattrs(struct inode *inode) |
787 | __releases(ci->i_ceph_lock) |
788 | __acquires(ci->i_ceph_lock) |
789 | { |
790 | u32 namelen; |
791 | u32 numattr = 0; |
792 | void *p, *end; |
793 | u32 len; |
794 | const char *name, *val; |
795 | struct ceph_inode_info *ci = ceph_inode(inode); |
796 | u64 xattr_version; |
797 | struct ceph_inode_xattr **xattrs = NULL; |
798 | int err = 0; |
799 | int i; |
800 | |
801 | dout("__build_xattrs() len=%d\n" , |
802 | ci->i_xattrs.blob ? (int)ci->i_xattrs.blob->vec.iov_len : 0); |
803 | |
804 | if (ci->i_xattrs.index_version >= ci->i_xattrs.version) |
805 | return 0; /* already built */ |
806 | |
807 | __ceph_destroy_xattrs(ci); |
808 | |
809 | start: |
810 | /* updated internal xattr rb tree */ |
811 | if (ci->i_xattrs.blob && ci->i_xattrs.blob->vec.iov_len > 4) { |
812 | p = ci->i_xattrs.blob->vec.iov_base; |
813 | end = p + ci->i_xattrs.blob->vec.iov_len; |
814 | ceph_decode_32_safe(&p, end, numattr, bad); |
815 | xattr_version = ci->i_xattrs.version; |
816 | spin_unlock(lock: &ci->i_ceph_lock); |
817 | |
818 | xattrs = kcalloc(n: numattr, size: sizeof(struct ceph_inode_xattr *), |
819 | GFP_NOFS); |
820 | err = -ENOMEM; |
821 | if (!xattrs) |
822 | goto bad_lock; |
823 | |
824 | for (i = 0; i < numattr; i++) { |
825 | xattrs[i] = kmalloc(size: sizeof(struct ceph_inode_xattr), |
826 | GFP_NOFS); |
827 | if (!xattrs[i]) |
828 | goto bad_lock; |
829 | } |
830 | |
831 | spin_lock(lock: &ci->i_ceph_lock); |
832 | if (ci->i_xattrs.version != xattr_version) { |
833 | /* lost a race, retry */ |
834 | for (i = 0; i < numattr; i++) |
835 | kfree(objp: xattrs[i]); |
836 | kfree(objp: xattrs); |
837 | xattrs = NULL; |
838 | goto start; |
839 | } |
840 | err = -EIO; |
841 | while (numattr--) { |
842 | ceph_decode_32_safe(&p, end, len, bad); |
843 | namelen = len; |
844 | name = p; |
845 | p += len; |
846 | ceph_decode_32_safe(&p, end, len, bad); |
847 | val = p; |
848 | p += len; |
849 | |
850 | err = __set_xattr(ci, name, name_len: namelen, val, val_len: len, |
851 | flags: 0, update_xattr: 0, newxattr: &xattrs[numattr]); |
852 | |
853 | if (err < 0) |
854 | goto bad; |
855 | } |
856 | kfree(objp: xattrs); |
857 | } |
858 | ci->i_xattrs.index_version = ci->i_xattrs.version; |
859 | ci->i_xattrs.dirty = false; |
860 | |
861 | return err; |
862 | bad_lock: |
863 | spin_lock(lock: &ci->i_ceph_lock); |
864 | bad: |
865 | if (xattrs) { |
866 | for (i = 0; i < numattr; i++) |
867 | kfree(objp: xattrs[i]); |
868 | kfree(objp: xattrs); |
869 | } |
870 | ci->i_xattrs.names_size = 0; |
871 | return err; |
872 | } |
873 | |
874 | static int __get_required_blob_size(struct ceph_inode_info *ci, int name_size, |
875 | int val_size) |
876 | { |
877 | /* |
878 | * 4 bytes for the length, and additional 4 bytes per each xattr name, |
879 | * 4 bytes per each value |
880 | */ |
881 | int size = 4 + ci->i_xattrs.count*(4 + 4) + |
882 | ci->i_xattrs.names_size + |
883 | ci->i_xattrs.vals_size; |
884 | dout("__get_required_blob_size c=%d names.size=%d vals.size=%d\n" , |
885 | ci->i_xattrs.count, ci->i_xattrs.names_size, |
886 | ci->i_xattrs.vals_size); |
887 | |
888 | if (name_size) |
889 | size += 4 + 4 + name_size + val_size; |
890 | |
891 | return size; |
892 | } |
893 | |
894 | /* |
895 | * If there are dirty xattrs, reencode xattrs into the prealloc_blob |
896 | * and swap into place. It returns the old i_xattrs.blob (or NULL) so |
897 | * that it can be freed by the caller as the i_ceph_lock is likely to be |
898 | * held. |
899 | */ |
900 | struct ceph_buffer *__ceph_build_xattrs_blob(struct ceph_inode_info *ci) |
901 | { |
902 | struct rb_node *p; |
903 | struct ceph_inode_xattr *xattr = NULL; |
904 | struct ceph_buffer *old_blob = NULL; |
905 | void *dest; |
906 | |
907 | dout("__build_xattrs_blob %p\n" , &ci->netfs.inode); |
908 | if (ci->i_xattrs.dirty) { |
909 | int need = __get_required_blob_size(ci, name_size: 0, val_size: 0); |
910 | |
911 | BUG_ON(need > ci->i_xattrs.prealloc_blob->alloc_len); |
912 | |
913 | p = rb_first(&ci->i_xattrs.index); |
914 | dest = ci->i_xattrs.prealloc_blob->vec.iov_base; |
915 | |
916 | ceph_encode_32(p: &dest, v: ci->i_xattrs.count); |
917 | while (p) { |
918 | xattr = rb_entry(p, struct ceph_inode_xattr, node); |
919 | |
920 | ceph_encode_32(p: &dest, v: xattr->name_len); |
921 | memcpy(dest, xattr->name, xattr->name_len); |
922 | dest += xattr->name_len; |
923 | ceph_encode_32(p: &dest, v: xattr->val_len); |
924 | memcpy(dest, xattr->val, xattr->val_len); |
925 | dest += xattr->val_len; |
926 | |
927 | p = rb_next(p); |
928 | } |
929 | |
930 | /* adjust buffer len; it may be larger than we need */ |
931 | ci->i_xattrs.prealloc_blob->vec.iov_len = |
932 | dest - ci->i_xattrs.prealloc_blob->vec.iov_base; |
933 | |
934 | if (ci->i_xattrs.blob) |
935 | old_blob = ci->i_xattrs.blob; |
936 | ci->i_xattrs.blob = ci->i_xattrs.prealloc_blob; |
937 | ci->i_xattrs.prealloc_blob = NULL; |
938 | ci->i_xattrs.dirty = false; |
939 | ci->i_xattrs.version++; |
940 | } |
941 | |
942 | return old_blob; |
943 | } |
944 | |
945 | static inline int __get_request_mask(struct inode *in) { |
946 | struct ceph_mds_request *req = current->journal_info; |
947 | int mask = 0; |
948 | if (req && req->r_target_inode == in) { |
949 | if (req->r_op == CEPH_MDS_OP_LOOKUP || |
950 | req->r_op == CEPH_MDS_OP_LOOKUPINO || |
951 | req->r_op == CEPH_MDS_OP_LOOKUPPARENT || |
952 | req->r_op == CEPH_MDS_OP_GETATTR) { |
953 | mask = le32_to_cpu(req->r_args.getattr.mask); |
954 | } else if (req->r_op == CEPH_MDS_OP_OPEN || |
955 | req->r_op == CEPH_MDS_OP_CREATE) { |
956 | mask = le32_to_cpu(req->r_args.open.mask); |
957 | } |
958 | } |
959 | return mask; |
960 | } |
961 | |
962 | ssize_t __ceph_getxattr(struct inode *inode, const char *name, void *value, |
963 | size_t size) |
964 | { |
965 | struct ceph_inode_info *ci = ceph_inode(inode); |
966 | struct ceph_inode_xattr *xattr; |
967 | struct ceph_vxattr *vxattr; |
968 | int req_mask; |
969 | ssize_t err; |
970 | |
971 | if (strncmp(name, XATTR_CEPH_PREFIX, XATTR_CEPH_PREFIX_LEN)) |
972 | goto handle_non_vxattrs; |
973 | |
974 | /* let's see if a virtual xattr was requested */ |
975 | vxattr = ceph_match_vxattr(inode, name); |
976 | if (vxattr) { |
977 | int mask = 0; |
978 | if (vxattr->flags & VXATTR_FLAG_RSTAT) |
979 | mask |= CEPH_STAT_RSTAT; |
980 | if (vxattr->flags & VXATTR_FLAG_DIRSTAT) |
981 | mask |= CEPH_CAP_FILE_SHARED; |
982 | err = ceph_do_getattr(inode, mask, force: true); |
983 | if (err) |
984 | return err; |
985 | err = -ENODATA; |
986 | if (!(vxattr->exists_cb && !vxattr->exists_cb(ci))) { |
987 | err = vxattr->getxattr_cb(ci, value, size); |
988 | if (size && size < err) |
989 | err = -ERANGE; |
990 | } |
991 | return err; |
992 | } else { |
993 | err = ceph_do_getvxattr(inode, name, value, size); |
994 | /* this would happen with a new client and old server combo */ |
995 | if (err == -EOPNOTSUPP) |
996 | err = -ENODATA; |
997 | return err; |
998 | } |
999 | handle_non_vxattrs: |
1000 | req_mask = __get_request_mask(in: inode); |
1001 | |
1002 | spin_lock(lock: &ci->i_ceph_lock); |
1003 | dout("getxattr %p name '%s' ver=%lld index_ver=%lld\n" , inode, name, |
1004 | ci->i_xattrs.version, ci->i_xattrs.index_version); |
1005 | |
1006 | if (ci->i_xattrs.version == 0 || |
1007 | !((req_mask & CEPH_CAP_XATTR_SHARED) || |
1008 | __ceph_caps_issued_mask_metric(ci, CEPH_CAP_XATTR_SHARED, t: 1))) { |
1009 | spin_unlock(lock: &ci->i_ceph_lock); |
1010 | |
1011 | /* security module gets xattr while filling trace */ |
1012 | if (current->journal_info) { |
1013 | pr_warn_ratelimited("sync getxattr %p " |
1014 | "during filling trace\n" , inode); |
1015 | return -EBUSY; |
1016 | } |
1017 | |
1018 | /* get xattrs from mds (if we don't already have them) */ |
1019 | err = ceph_do_getattr(inode, CEPH_STAT_CAP_XATTR, force: true); |
1020 | if (err) |
1021 | return err; |
1022 | spin_lock(lock: &ci->i_ceph_lock); |
1023 | } |
1024 | |
1025 | err = __build_xattrs(inode); |
1026 | if (err < 0) |
1027 | goto out; |
1028 | |
1029 | err = -ENODATA; /* == ENOATTR */ |
1030 | xattr = __get_xattr(ci, name); |
1031 | if (!xattr) |
1032 | goto out; |
1033 | |
1034 | err = -ERANGE; |
1035 | if (size && size < xattr->val_len) |
1036 | goto out; |
1037 | |
1038 | err = xattr->val_len; |
1039 | if (size == 0) |
1040 | goto out; |
1041 | |
1042 | memcpy(value, xattr->val, xattr->val_len); |
1043 | |
1044 | if (current->journal_info && |
1045 | !strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) && |
1046 | security_ismaclabel(name: name + XATTR_SECURITY_PREFIX_LEN)) |
1047 | ci->i_ceph_flags |= CEPH_I_SEC_INITED; |
1048 | out: |
1049 | spin_unlock(lock: &ci->i_ceph_lock); |
1050 | return err; |
1051 | } |
1052 | |
1053 | ssize_t ceph_listxattr(struct dentry *dentry, char *names, size_t size) |
1054 | { |
1055 | struct inode *inode = d_inode(dentry); |
1056 | struct ceph_inode_info *ci = ceph_inode(inode); |
1057 | bool len_only = (size == 0); |
1058 | u32 namelen; |
1059 | int err; |
1060 | |
1061 | spin_lock(lock: &ci->i_ceph_lock); |
1062 | dout("listxattr %p ver=%lld index_ver=%lld\n" , inode, |
1063 | ci->i_xattrs.version, ci->i_xattrs.index_version); |
1064 | |
1065 | if (ci->i_xattrs.version == 0 || |
1066 | !__ceph_caps_issued_mask_metric(ci, CEPH_CAP_XATTR_SHARED, t: 1)) { |
1067 | spin_unlock(lock: &ci->i_ceph_lock); |
1068 | err = ceph_do_getattr(inode, CEPH_STAT_CAP_XATTR, force: true); |
1069 | if (err) |
1070 | return err; |
1071 | spin_lock(lock: &ci->i_ceph_lock); |
1072 | } |
1073 | |
1074 | err = __build_xattrs(inode); |
1075 | if (err < 0) |
1076 | goto out; |
1077 | |
1078 | /* add 1 byte for each xattr due to the null termination */ |
1079 | namelen = ci->i_xattrs.names_size + ci->i_xattrs.count; |
1080 | if (!len_only) { |
1081 | if (namelen > size) { |
1082 | err = -ERANGE; |
1083 | goto out; |
1084 | } |
1085 | names = __copy_xattr_names(ci, dest: names); |
1086 | size -= namelen; |
1087 | } |
1088 | err = namelen; |
1089 | out: |
1090 | spin_unlock(lock: &ci->i_ceph_lock); |
1091 | return err; |
1092 | } |
1093 | |
1094 | static int ceph_sync_setxattr(struct inode *inode, const char *name, |
1095 | const char *value, size_t size, int flags) |
1096 | { |
1097 | struct ceph_fs_client *fsc = ceph_sb_to_client(sb: inode->i_sb); |
1098 | struct ceph_inode_info *ci = ceph_inode(inode); |
1099 | struct ceph_mds_request *req; |
1100 | struct ceph_mds_client *mdsc = fsc->mdsc; |
1101 | struct ceph_osd_client *osdc = &fsc->client->osdc; |
1102 | struct ceph_pagelist *pagelist = NULL; |
1103 | int op = CEPH_MDS_OP_SETXATTR; |
1104 | int err; |
1105 | |
1106 | if (size > 0) { |
1107 | /* copy value into pagelist */ |
1108 | pagelist = ceph_pagelist_alloc(GFP_NOFS); |
1109 | if (!pagelist) |
1110 | return -ENOMEM; |
1111 | |
1112 | err = ceph_pagelist_append(pl: pagelist, d: value, l: size); |
1113 | if (err) |
1114 | goto out; |
1115 | } else if (!value) { |
1116 | if (flags & CEPH_XATTR_REPLACE) |
1117 | op = CEPH_MDS_OP_RMXATTR; |
1118 | else |
1119 | flags |= CEPH_XATTR_REMOVE; |
1120 | } |
1121 | |
1122 | dout("setxattr value size: %zu\n" , size); |
1123 | |
1124 | /* do request */ |
1125 | req = ceph_mdsc_create_request(mdsc, op, mode: USE_AUTH_MDS); |
1126 | if (IS_ERR(ptr: req)) { |
1127 | err = PTR_ERR(ptr: req); |
1128 | goto out; |
1129 | } |
1130 | |
1131 | req->r_path2 = kstrdup(s: name, GFP_NOFS); |
1132 | if (!req->r_path2) { |
1133 | ceph_mdsc_put_request(req); |
1134 | err = -ENOMEM; |
1135 | goto out; |
1136 | } |
1137 | |
1138 | if (op == CEPH_MDS_OP_SETXATTR) { |
1139 | req->r_args.setxattr.flags = cpu_to_le32(flags); |
1140 | req->r_args.setxattr.osdmap_epoch = |
1141 | cpu_to_le32(osdc->osdmap->epoch); |
1142 | req->r_pagelist = pagelist; |
1143 | pagelist = NULL; |
1144 | } |
1145 | |
1146 | req->r_inode = inode; |
1147 | ihold(inode); |
1148 | req->r_num_caps = 1; |
1149 | req->r_inode_drop = CEPH_CAP_XATTR_SHARED; |
1150 | |
1151 | dout("xattr.ver (before): %lld\n" , ci->i_xattrs.version); |
1152 | err = ceph_mdsc_do_request(mdsc, NULL, req); |
1153 | ceph_mdsc_put_request(req); |
1154 | dout("xattr.ver (after): %lld\n" , ci->i_xattrs.version); |
1155 | |
1156 | out: |
1157 | if (pagelist) |
1158 | ceph_pagelist_release(pl: pagelist); |
1159 | return err; |
1160 | } |
1161 | |
1162 | int __ceph_setxattr(struct inode *inode, const char *name, |
1163 | const void *value, size_t size, int flags) |
1164 | { |
1165 | struct ceph_vxattr *vxattr; |
1166 | struct ceph_inode_info *ci = ceph_inode(inode); |
1167 | struct ceph_mds_client *mdsc = ceph_sb_to_client(sb: inode->i_sb)->mdsc; |
1168 | struct ceph_cap_flush *prealloc_cf = NULL; |
1169 | struct ceph_buffer *old_blob = NULL; |
1170 | int issued; |
1171 | int err; |
1172 | int dirty = 0; |
1173 | int name_len = strlen(name); |
1174 | int val_len = size; |
1175 | char *newname = NULL; |
1176 | char *newval = NULL; |
1177 | struct ceph_inode_xattr *xattr = NULL; |
1178 | int required_blob_size; |
1179 | bool check_realm = false; |
1180 | bool lock_snap_rwsem = false; |
1181 | |
1182 | if (ceph_snap(inode) != CEPH_NOSNAP) |
1183 | return -EROFS; |
1184 | |
1185 | vxattr = ceph_match_vxattr(inode, name); |
1186 | if (vxattr) { |
1187 | if (vxattr->flags & VXATTR_FLAG_READONLY) |
1188 | return -EOPNOTSUPP; |
1189 | if (value && !strncmp(vxattr->name, "ceph.quota" , 10)) |
1190 | check_realm = true; |
1191 | } |
1192 | |
1193 | /* pass any unhandled ceph.* xattrs through to the MDS */ |
1194 | if (!strncmp(name, XATTR_CEPH_PREFIX, XATTR_CEPH_PREFIX_LEN)) |
1195 | goto do_sync_unlocked; |
1196 | |
1197 | /* preallocate memory for xattr name, value, index node */ |
1198 | err = -ENOMEM; |
1199 | newname = kmemdup(p: name, size: name_len + 1, GFP_NOFS); |
1200 | if (!newname) |
1201 | goto out; |
1202 | |
1203 | if (val_len) { |
1204 | newval = kmemdup(p: value, size: val_len, GFP_NOFS); |
1205 | if (!newval) |
1206 | goto out; |
1207 | } |
1208 | |
1209 | xattr = kmalloc(size: sizeof(struct ceph_inode_xattr), GFP_NOFS); |
1210 | if (!xattr) |
1211 | goto out; |
1212 | |
1213 | prealloc_cf = ceph_alloc_cap_flush(); |
1214 | if (!prealloc_cf) |
1215 | goto out; |
1216 | |
1217 | spin_lock(lock: &ci->i_ceph_lock); |
1218 | retry: |
1219 | issued = __ceph_caps_issued(ci, NULL); |
1220 | required_blob_size = __get_required_blob_size(ci, name_size: name_len, val_size: val_len); |
1221 | if ((ci->i_xattrs.version == 0) || !(issued & CEPH_CAP_XATTR_EXCL) || |
1222 | (required_blob_size > mdsc->mdsmap->m_max_xattr_size)) { |
1223 | dout("%s do sync setxattr: version: %llu size: %d max: %llu\n" , |
1224 | __func__, ci->i_xattrs.version, required_blob_size, |
1225 | mdsc->mdsmap->m_max_xattr_size); |
1226 | goto do_sync; |
1227 | } |
1228 | |
1229 | if (!lock_snap_rwsem && !ci->i_head_snapc) { |
1230 | lock_snap_rwsem = true; |
1231 | if (!down_read_trylock(sem: &mdsc->snap_rwsem)) { |
1232 | spin_unlock(lock: &ci->i_ceph_lock); |
1233 | down_read(sem: &mdsc->snap_rwsem); |
1234 | spin_lock(lock: &ci->i_ceph_lock); |
1235 | goto retry; |
1236 | } |
1237 | } |
1238 | |
1239 | dout("setxattr %p name '%s' issued %s\n" , inode, name, |
1240 | ceph_cap_string(issued)); |
1241 | __build_xattrs(inode); |
1242 | |
1243 | if (!ci->i_xattrs.prealloc_blob || |
1244 | required_blob_size > ci->i_xattrs.prealloc_blob->alloc_len) { |
1245 | struct ceph_buffer *blob; |
1246 | |
1247 | spin_unlock(lock: &ci->i_ceph_lock); |
1248 | ceph_buffer_put(b: old_blob); /* Shouldn't be required */ |
1249 | dout(" pre-allocating new blob size=%d\n" , required_blob_size); |
1250 | blob = ceph_buffer_new(len: required_blob_size, GFP_NOFS); |
1251 | if (!blob) |
1252 | goto do_sync_unlocked; |
1253 | spin_lock(lock: &ci->i_ceph_lock); |
1254 | /* prealloc_blob can't be released while holding i_ceph_lock */ |
1255 | if (ci->i_xattrs.prealloc_blob) |
1256 | old_blob = ci->i_xattrs.prealloc_blob; |
1257 | ci->i_xattrs.prealloc_blob = blob; |
1258 | goto retry; |
1259 | } |
1260 | |
1261 | err = __set_xattr(ci, name: newname, name_len, val: newval, val_len, |
1262 | flags, update_xattr: value ? 1 : -1, newxattr: &xattr); |
1263 | |
1264 | if (!err) { |
1265 | dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_XATTR_EXCL, |
1266 | pcf: &prealloc_cf); |
1267 | ci->i_xattrs.dirty = true; |
1268 | inode_set_ctime_current(inode); |
1269 | } |
1270 | |
1271 | spin_unlock(lock: &ci->i_ceph_lock); |
1272 | ceph_buffer_put(b: old_blob); |
1273 | if (lock_snap_rwsem) |
1274 | up_read(sem: &mdsc->snap_rwsem); |
1275 | if (dirty) |
1276 | __mark_inode_dirty(inode, dirty); |
1277 | ceph_free_cap_flush(cf: prealloc_cf); |
1278 | return err; |
1279 | |
1280 | do_sync: |
1281 | spin_unlock(lock: &ci->i_ceph_lock); |
1282 | do_sync_unlocked: |
1283 | if (lock_snap_rwsem) |
1284 | up_read(sem: &mdsc->snap_rwsem); |
1285 | |
1286 | /* security module set xattr while filling trace */ |
1287 | if (current->journal_info) { |
1288 | pr_warn_ratelimited("sync setxattr %p " |
1289 | "during filling trace\n" , inode); |
1290 | err = -EBUSY; |
1291 | } else { |
1292 | err = ceph_sync_setxattr(inode, name, value, size, flags); |
1293 | if (err >= 0 && check_realm) { |
1294 | /* check if snaprealm was created for quota inode */ |
1295 | spin_lock(lock: &ci->i_ceph_lock); |
1296 | if ((ci->i_max_files || ci->i_max_bytes) && |
1297 | !(ci->i_snap_realm && |
1298 | ci->i_snap_realm->ino == ci->i_vino.ino)) |
1299 | err = -EOPNOTSUPP; |
1300 | spin_unlock(lock: &ci->i_ceph_lock); |
1301 | } |
1302 | } |
1303 | out: |
1304 | ceph_free_cap_flush(cf: prealloc_cf); |
1305 | kfree(objp: newname); |
1306 | kfree(objp: newval); |
1307 | kfree(objp: xattr); |
1308 | return err; |
1309 | } |
1310 | |
1311 | static int ceph_get_xattr_handler(const struct xattr_handler *handler, |
1312 | struct dentry *dentry, struct inode *inode, |
1313 | const char *name, void *value, size_t size) |
1314 | { |
1315 | if (!ceph_is_valid_xattr(name)) |
1316 | return -EOPNOTSUPP; |
1317 | return __ceph_getxattr(inode, name, value, size); |
1318 | } |
1319 | |
1320 | static int ceph_set_xattr_handler(const struct xattr_handler *handler, |
1321 | struct mnt_idmap *idmap, |
1322 | struct dentry *unused, struct inode *inode, |
1323 | const char *name, const void *value, |
1324 | size_t size, int flags) |
1325 | { |
1326 | if (!ceph_is_valid_xattr(name)) |
1327 | return -EOPNOTSUPP; |
1328 | return __ceph_setxattr(inode, name, value, size, flags); |
1329 | } |
1330 | |
1331 | static const struct xattr_handler ceph_other_xattr_handler = { |
1332 | .prefix = "" , /* match any name => handlers called with full name */ |
1333 | .get = ceph_get_xattr_handler, |
1334 | .set = ceph_set_xattr_handler, |
1335 | }; |
1336 | |
1337 | #ifdef CONFIG_SECURITY |
1338 | bool ceph_security_xattr_wanted(struct inode *in) |
1339 | { |
1340 | return in->i_security != NULL; |
1341 | } |
1342 | |
1343 | bool ceph_security_xattr_deadlock(struct inode *in) |
1344 | { |
1345 | struct ceph_inode_info *ci; |
1346 | bool ret; |
1347 | if (!in->i_security) |
1348 | return false; |
1349 | ci = ceph_inode(inode: in); |
1350 | spin_lock(lock: &ci->i_ceph_lock); |
1351 | ret = !(ci->i_ceph_flags & CEPH_I_SEC_INITED) && |
1352 | !(ci->i_xattrs.version > 0 && |
1353 | __ceph_caps_issued_mask(ci, CEPH_CAP_XATTR_SHARED, t: 0)); |
1354 | spin_unlock(lock: &ci->i_ceph_lock); |
1355 | return ret; |
1356 | } |
1357 | |
1358 | #ifdef CONFIG_CEPH_FS_SECURITY_LABEL |
1359 | int ceph_security_init_secctx(struct dentry *dentry, umode_t mode, |
1360 | struct ceph_acl_sec_ctx *as_ctx) |
1361 | { |
1362 | struct ceph_pagelist *pagelist = as_ctx->pagelist; |
1363 | const char *name; |
1364 | size_t name_len; |
1365 | int err; |
1366 | |
1367 | err = security_dentry_init_security(dentry, mode, name: &dentry->d_name, |
1368 | xattr_name: &name, ctx: &as_ctx->sec_ctx, |
1369 | ctxlen: &as_ctx->sec_ctxlen); |
1370 | if (err < 0) { |
1371 | WARN_ON_ONCE(err != -EOPNOTSUPP); |
1372 | err = 0; /* do nothing */ |
1373 | goto out; |
1374 | } |
1375 | |
1376 | err = -ENOMEM; |
1377 | if (!pagelist) { |
1378 | pagelist = ceph_pagelist_alloc(GFP_KERNEL); |
1379 | if (!pagelist) |
1380 | goto out; |
1381 | err = ceph_pagelist_reserve(pl: pagelist, PAGE_SIZE); |
1382 | if (err) |
1383 | goto out; |
1384 | ceph_pagelist_encode_32(pl: pagelist, v: 1); |
1385 | } |
1386 | |
1387 | /* |
1388 | * FIXME: Make security_dentry_init_security() generic. Currently |
1389 | * It only supports single security module and only selinux has |
1390 | * dentry_init_security hook. |
1391 | */ |
1392 | name_len = strlen(name); |
1393 | err = ceph_pagelist_reserve(pl: pagelist, |
1394 | space: 4 * 2 + name_len + as_ctx->sec_ctxlen); |
1395 | if (err) |
1396 | goto out; |
1397 | |
1398 | if (as_ctx->pagelist) { |
1399 | /* update count of KV pairs */ |
1400 | BUG_ON(pagelist->length <= sizeof(__le32)); |
1401 | if (list_is_singular(head: &pagelist->head)) { |
1402 | le32_add_cpu(var: (__le32*)pagelist->mapped_tail, val: 1); |
1403 | } else { |
1404 | struct page *page = list_first_entry(&pagelist->head, |
1405 | struct page, lru); |
1406 | void *addr = kmap_atomic(page); |
1407 | le32_add_cpu(var: (__le32*)addr, val: 1); |
1408 | kunmap_atomic(addr); |
1409 | } |
1410 | } else { |
1411 | as_ctx->pagelist = pagelist; |
1412 | } |
1413 | |
1414 | ceph_pagelist_encode_32(pl: pagelist, v: name_len); |
1415 | ceph_pagelist_append(pl: pagelist, d: name, l: name_len); |
1416 | |
1417 | ceph_pagelist_encode_32(pl: pagelist, v: as_ctx->sec_ctxlen); |
1418 | ceph_pagelist_append(pl: pagelist, d: as_ctx->sec_ctx, l: as_ctx->sec_ctxlen); |
1419 | |
1420 | err = 0; |
1421 | out: |
1422 | if (pagelist && !as_ctx->pagelist) |
1423 | ceph_pagelist_release(pl: pagelist); |
1424 | return err; |
1425 | } |
1426 | #endif /* CONFIG_CEPH_FS_SECURITY_LABEL */ |
1427 | #endif /* CONFIG_SECURITY */ |
1428 | |
1429 | void ceph_release_acl_sec_ctx(struct ceph_acl_sec_ctx *as_ctx) |
1430 | { |
1431 | #ifdef CONFIG_CEPH_FS_POSIX_ACL |
1432 | posix_acl_release(acl: as_ctx->acl); |
1433 | posix_acl_release(acl: as_ctx->default_acl); |
1434 | #endif |
1435 | #ifdef CONFIG_CEPH_FS_SECURITY_LABEL |
1436 | security_release_secctx(secdata: as_ctx->sec_ctx, seclen: as_ctx->sec_ctxlen); |
1437 | #endif |
1438 | #ifdef CONFIG_FS_ENCRYPTION |
1439 | kfree(objp: as_ctx->fscrypt_auth); |
1440 | #endif |
1441 | if (as_ctx->pagelist) |
1442 | ceph_pagelist_release(pl: as_ctx->pagelist); |
1443 | } |
1444 | |
1445 | /* |
1446 | * List of handlers for synthetic system.* attributes. Other |
1447 | * attributes are handled directly. |
1448 | */ |
1449 | const struct xattr_handler * const ceph_xattr_handlers[] = { |
1450 | &ceph_other_xattr_handler, |
1451 | NULL, |
1452 | }; |
1453 | |