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