162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* Extended attribute handling for AFS. We use xattrs to get and set metadata 362306a36Sopenharmony_ci * instead of providing pioctl(). 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved. 662306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci#include <linux/fs.h> 1162306a36Sopenharmony_ci#include <linux/xattr.h> 1262306a36Sopenharmony_ci#include "internal.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* 1562306a36Sopenharmony_ci * Deal with the result of a successful fetch ACL operation. 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_cistatic void afs_acl_success(struct afs_operation *op) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci afs_vnode_commit_status(op, &op->file[0]); 2062306a36Sopenharmony_ci} 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic void afs_acl_put(struct afs_operation *op) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci kfree(op->acl); 2562306a36Sopenharmony_ci} 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic const struct afs_operation_ops afs_fetch_acl_operation = { 2862306a36Sopenharmony_ci .issue_afs_rpc = afs_fs_fetch_acl, 2962306a36Sopenharmony_ci .success = afs_acl_success, 3062306a36Sopenharmony_ci .put = afs_acl_put, 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* 3462306a36Sopenharmony_ci * Get a file's ACL. 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_cistatic int afs_xattr_get_acl(const struct xattr_handler *handler, 3762306a36Sopenharmony_ci struct dentry *dentry, 3862306a36Sopenharmony_ci struct inode *inode, const char *name, 3962306a36Sopenharmony_ci void *buffer, size_t size) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci struct afs_operation *op; 4262306a36Sopenharmony_ci struct afs_vnode *vnode = AFS_FS_I(inode); 4362306a36Sopenharmony_ci struct afs_acl *acl = NULL; 4462306a36Sopenharmony_ci int ret; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci op = afs_alloc_operation(NULL, vnode->volume); 4762306a36Sopenharmony_ci if (IS_ERR(op)) 4862306a36Sopenharmony_ci return -ENOMEM; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci afs_op_set_vnode(op, 0, vnode); 5162306a36Sopenharmony_ci op->ops = &afs_fetch_acl_operation; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci afs_begin_vnode_operation(op); 5462306a36Sopenharmony_ci afs_wait_for_operation(op); 5562306a36Sopenharmony_ci acl = op->acl; 5662306a36Sopenharmony_ci op->acl = NULL; 5762306a36Sopenharmony_ci ret = afs_put_operation(op); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if (ret == 0) { 6062306a36Sopenharmony_ci ret = acl->size; 6162306a36Sopenharmony_ci if (size > 0) { 6262306a36Sopenharmony_ci if (acl->size <= size) 6362306a36Sopenharmony_ci memcpy(buffer, acl->data, acl->size); 6462306a36Sopenharmony_ci else 6562306a36Sopenharmony_ci ret = -ERANGE; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci kfree(acl); 7062306a36Sopenharmony_ci return ret; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic bool afs_make_acl(struct afs_operation *op, 7462306a36Sopenharmony_ci const void *buffer, size_t size) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci struct afs_acl *acl; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci acl = kmalloc(sizeof(*acl) + size, GFP_KERNEL); 7962306a36Sopenharmony_ci if (!acl) { 8062306a36Sopenharmony_ci afs_op_nomem(op); 8162306a36Sopenharmony_ci return false; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci acl->size = size; 8562306a36Sopenharmony_ci memcpy(acl->data, buffer, size); 8662306a36Sopenharmony_ci op->acl = acl; 8762306a36Sopenharmony_ci return true; 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic const struct afs_operation_ops afs_store_acl_operation = { 9162306a36Sopenharmony_ci .issue_afs_rpc = afs_fs_store_acl, 9262306a36Sopenharmony_ci .success = afs_acl_success, 9362306a36Sopenharmony_ci .put = afs_acl_put, 9462306a36Sopenharmony_ci}; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/* 9762306a36Sopenharmony_ci * Set a file's AFS3 ACL. 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_cistatic int afs_xattr_set_acl(const struct xattr_handler *handler, 10062306a36Sopenharmony_ci struct mnt_idmap *idmap, 10162306a36Sopenharmony_ci struct dentry *dentry, 10262306a36Sopenharmony_ci struct inode *inode, const char *name, 10362306a36Sopenharmony_ci const void *buffer, size_t size, int flags) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct afs_operation *op; 10662306a36Sopenharmony_ci struct afs_vnode *vnode = AFS_FS_I(inode); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (flags == XATTR_CREATE) 10962306a36Sopenharmony_ci return -EINVAL; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci op = afs_alloc_operation(NULL, vnode->volume); 11262306a36Sopenharmony_ci if (IS_ERR(op)) 11362306a36Sopenharmony_ci return -ENOMEM; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci afs_op_set_vnode(op, 0, vnode); 11662306a36Sopenharmony_ci if (!afs_make_acl(op, buffer, size)) 11762306a36Sopenharmony_ci return afs_put_operation(op); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci op->ops = &afs_store_acl_operation; 12062306a36Sopenharmony_ci return afs_do_sync_operation(op); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic const struct xattr_handler afs_xattr_afs_acl_handler = { 12462306a36Sopenharmony_ci .name = "afs.acl", 12562306a36Sopenharmony_ci .get = afs_xattr_get_acl, 12662306a36Sopenharmony_ci .set = afs_xattr_set_acl, 12762306a36Sopenharmony_ci}; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic const struct afs_operation_ops yfs_fetch_opaque_acl_operation = { 13062306a36Sopenharmony_ci .issue_yfs_rpc = yfs_fs_fetch_opaque_acl, 13162306a36Sopenharmony_ci .success = afs_acl_success, 13262306a36Sopenharmony_ci /* Don't free op->yacl in .put here */ 13362306a36Sopenharmony_ci}; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* 13662306a36Sopenharmony_ci * Get a file's YFS ACL. 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_cistatic int afs_xattr_get_yfs(const struct xattr_handler *handler, 13962306a36Sopenharmony_ci struct dentry *dentry, 14062306a36Sopenharmony_ci struct inode *inode, const char *name, 14162306a36Sopenharmony_ci void *buffer, size_t size) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct afs_operation *op; 14462306a36Sopenharmony_ci struct afs_vnode *vnode = AFS_FS_I(inode); 14562306a36Sopenharmony_ci struct yfs_acl *yacl = NULL; 14662306a36Sopenharmony_ci char buf[16], *data; 14762306a36Sopenharmony_ci int which = 0, dsize, ret = -ENOMEM; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (strcmp(name, "acl") == 0) 15062306a36Sopenharmony_ci which = 0; 15162306a36Sopenharmony_ci else if (strcmp(name, "acl_inherited") == 0) 15262306a36Sopenharmony_ci which = 1; 15362306a36Sopenharmony_ci else if (strcmp(name, "acl_num_cleaned") == 0) 15462306a36Sopenharmony_ci which = 2; 15562306a36Sopenharmony_ci else if (strcmp(name, "vol_acl") == 0) 15662306a36Sopenharmony_ci which = 3; 15762306a36Sopenharmony_ci else 15862306a36Sopenharmony_ci return -EOPNOTSUPP; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci yacl = kzalloc(sizeof(struct yfs_acl), GFP_KERNEL); 16162306a36Sopenharmony_ci if (!yacl) 16262306a36Sopenharmony_ci goto error; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (which == 0) 16562306a36Sopenharmony_ci yacl->flags |= YFS_ACL_WANT_ACL; 16662306a36Sopenharmony_ci else if (which == 3) 16762306a36Sopenharmony_ci yacl->flags |= YFS_ACL_WANT_VOL_ACL; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci op = afs_alloc_operation(NULL, vnode->volume); 17062306a36Sopenharmony_ci if (IS_ERR(op)) 17162306a36Sopenharmony_ci goto error_yacl; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci afs_op_set_vnode(op, 0, vnode); 17462306a36Sopenharmony_ci op->yacl = yacl; 17562306a36Sopenharmony_ci op->ops = &yfs_fetch_opaque_acl_operation; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci afs_begin_vnode_operation(op); 17862306a36Sopenharmony_ci afs_wait_for_operation(op); 17962306a36Sopenharmony_ci ret = afs_put_operation(op); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (ret == 0) { 18262306a36Sopenharmony_ci switch (which) { 18362306a36Sopenharmony_ci case 0: 18462306a36Sopenharmony_ci data = yacl->acl->data; 18562306a36Sopenharmony_ci dsize = yacl->acl->size; 18662306a36Sopenharmony_ci break; 18762306a36Sopenharmony_ci case 1: 18862306a36Sopenharmony_ci data = buf; 18962306a36Sopenharmony_ci dsize = scnprintf(buf, sizeof(buf), "%u", yacl->inherit_flag); 19062306a36Sopenharmony_ci break; 19162306a36Sopenharmony_ci case 2: 19262306a36Sopenharmony_ci data = buf; 19362306a36Sopenharmony_ci dsize = scnprintf(buf, sizeof(buf), "%u", yacl->num_cleaned); 19462306a36Sopenharmony_ci break; 19562306a36Sopenharmony_ci case 3: 19662306a36Sopenharmony_ci data = yacl->vol_acl->data; 19762306a36Sopenharmony_ci dsize = yacl->vol_acl->size; 19862306a36Sopenharmony_ci break; 19962306a36Sopenharmony_ci default: 20062306a36Sopenharmony_ci ret = -EOPNOTSUPP; 20162306a36Sopenharmony_ci goto error_yacl; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci ret = dsize; 20562306a36Sopenharmony_ci if (size > 0) { 20662306a36Sopenharmony_ci if (dsize <= size) 20762306a36Sopenharmony_ci memcpy(buffer, data, dsize); 20862306a36Sopenharmony_ci else 20962306a36Sopenharmony_ci ret = -ERANGE; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci } else if (ret == -ENOTSUPP) { 21262306a36Sopenharmony_ci ret = -ENODATA; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cierror_yacl: 21662306a36Sopenharmony_ci yfs_free_opaque_acl(yacl); 21762306a36Sopenharmony_cierror: 21862306a36Sopenharmony_ci return ret; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic const struct afs_operation_ops yfs_store_opaque_acl2_operation = { 22262306a36Sopenharmony_ci .issue_yfs_rpc = yfs_fs_store_opaque_acl2, 22362306a36Sopenharmony_ci .success = afs_acl_success, 22462306a36Sopenharmony_ci .put = afs_acl_put, 22562306a36Sopenharmony_ci}; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci/* 22862306a36Sopenharmony_ci * Set a file's YFS ACL. 22962306a36Sopenharmony_ci */ 23062306a36Sopenharmony_cistatic int afs_xattr_set_yfs(const struct xattr_handler *handler, 23162306a36Sopenharmony_ci struct mnt_idmap *idmap, 23262306a36Sopenharmony_ci struct dentry *dentry, 23362306a36Sopenharmony_ci struct inode *inode, const char *name, 23462306a36Sopenharmony_ci const void *buffer, size_t size, int flags) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci struct afs_operation *op; 23762306a36Sopenharmony_ci struct afs_vnode *vnode = AFS_FS_I(inode); 23862306a36Sopenharmony_ci int ret; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci if (flags == XATTR_CREATE || 24162306a36Sopenharmony_ci strcmp(name, "acl") != 0) 24262306a36Sopenharmony_ci return -EINVAL; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci op = afs_alloc_operation(NULL, vnode->volume); 24562306a36Sopenharmony_ci if (IS_ERR(op)) 24662306a36Sopenharmony_ci return -ENOMEM; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci afs_op_set_vnode(op, 0, vnode); 24962306a36Sopenharmony_ci if (!afs_make_acl(op, buffer, size)) 25062306a36Sopenharmony_ci return afs_put_operation(op); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci op->ops = &yfs_store_opaque_acl2_operation; 25362306a36Sopenharmony_ci ret = afs_do_sync_operation(op); 25462306a36Sopenharmony_ci if (ret == -ENOTSUPP) 25562306a36Sopenharmony_ci ret = -ENODATA; 25662306a36Sopenharmony_ci return ret; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic const struct xattr_handler afs_xattr_yfs_handler = { 26062306a36Sopenharmony_ci .prefix = "afs.yfs.", 26162306a36Sopenharmony_ci .get = afs_xattr_get_yfs, 26262306a36Sopenharmony_ci .set = afs_xattr_set_yfs, 26362306a36Sopenharmony_ci}; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci/* 26662306a36Sopenharmony_ci * Get the name of the cell on which a file resides. 26762306a36Sopenharmony_ci */ 26862306a36Sopenharmony_cistatic int afs_xattr_get_cell(const struct xattr_handler *handler, 26962306a36Sopenharmony_ci struct dentry *dentry, 27062306a36Sopenharmony_ci struct inode *inode, const char *name, 27162306a36Sopenharmony_ci void *buffer, size_t size) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci struct afs_vnode *vnode = AFS_FS_I(inode); 27462306a36Sopenharmony_ci struct afs_cell *cell = vnode->volume->cell; 27562306a36Sopenharmony_ci size_t namelen; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci namelen = cell->name_len; 27862306a36Sopenharmony_ci if (size == 0) 27962306a36Sopenharmony_ci return namelen; 28062306a36Sopenharmony_ci if (namelen > size) 28162306a36Sopenharmony_ci return -ERANGE; 28262306a36Sopenharmony_ci memcpy(buffer, cell->name, namelen); 28362306a36Sopenharmony_ci return namelen; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic const struct xattr_handler afs_xattr_afs_cell_handler = { 28762306a36Sopenharmony_ci .name = "afs.cell", 28862306a36Sopenharmony_ci .get = afs_xattr_get_cell, 28962306a36Sopenharmony_ci}; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci/* 29262306a36Sopenharmony_ci * Get the volume ID, vnode ID and vnode uniquifier of a file as a sequence of 29362306a36Sopenharmony_ci * hex numbers separated by colons. 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_cistatic int afs_xattr_get_fid(const struct xattr_handler *handler, 29662306a36Sopenharmony_ci struct dentry *dentry, 29762306a36Sopenharmony_ci struct inode *inode, const char *name, 29862306a36Sopenharmony_ci void *buffer, size_t size) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci struct afs_vnode *vnode = AFS_FS_I(inode); 30162306a36Sopenharmony_ci char text[16 + 1 + 24 + 1 + 8 + 1]; 30262306a36Sopenharmony_ci size_t len; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci /* The volume ID is 64-bit, the vnode ID is 96-bit and the 30562306a36Sopenharmony_ci * uniquifier is 32-bit. 30662306a36Sopenharmony_ci */ 30762306a36Sopenharmony_ci len = scnprintf(text, sizeof(text), "%llx:", vnode->fid.vid); 30862306a36Sopenharmony_ci if (vnode->fid.vnode_hi) 30962306a36Sopenharmony_ci len += scnprintf(text + len, sizeof(text) - len, "%x%016llx", 31062306a36Sopenharmony_ci vnode->fid.vnode_hi, vnode->fid.vnode); 31162306a36Sopenharmony_ci else 31262306a36Sopenharmony_ci len += scnprintf(text + len, sizeof(text) - len, "%llx", 31362306a36Sopenharmony_ci vnode->fid.vnode); 31462306a36Sopenharmony_ci len += scnprintf(text + len, sizeof(text) - len, ":%x", 31562306a36Sopenharmony_ci vnode->fid.unique); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (size == 0) 31862306a36Sopenharmony_ci return len; 31962306a36Sopenharmony_ci if (len > size) 32062306a36Sopenharmony_ci return -ERANGE; 32162306a36Sopenharmony_ci memcpy(buffer, text, len); 32262306a36Sopenharmony_ci return len; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic const struct xattr_handler afs_xattr_afs_fid_handler = { 32662306a36Sopenharmony_ci .name = "afs.fid", 32762306a36Sopenharmony_ci .get = afs_xattr_get_fid, 32862306a36Sopenharmony_ci}; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci/* 33162306a36Sopenharmony_ci * Get the name of the volume on which a file resides. 33262306a36Sopenharmony_ci */ 33362306a36Sopenharmony_cistatic int afs_xattr_get_volume(const struct xattr_handler *handler, 33462306a36Sopenharmony_ci struct dentry *dentry, 33562306a36Sopenharmony_ci struct inode *inode, const char *name, 33662306a36Sopenharmony_ci void *buffer, size_t size) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci struct afs_vnode *vnode = AFS_FS_I(inode); 33962306a36Sopenharmony_ci const char *volname = vnode->volume->name; 34062306a36Sopenharmony_ci size_t namelen; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci namelen = strlen(volname); 34362306a36Sopenharmony_ci if (size == 0) 34462306a36Sopenharmony_ci return namelen; 34562306a36Sopenharmony_ci if (namelen > size) 34662306a36Sopenharmony_ci return -ERANGE; 34762306a36Sopenharmony_ci memcpy(buffer, volname, namelen); 34862306a36Sopenharmony_ci return namelen; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic const struct xattr_handler afs_xattr_afs_volume_handler = { 35262306a36Sopenharmony_ci .name = "afs.volume", 35362306a36Sopenharmony_ci .get = afs_xattr_get_volume, 35462306a36Sopenharmony_ci}; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ciconst struct xattr_handler *afs_xattr_handlers[] = { 35762306a36Sopenharmony_ci &afs_xattr_afs_acl_handler, 35862306a36Sopenharmony_ci &afs_xattr_afs_cell_handler, 35962306a36Sopenharmony_ci &afs_xattr_afs_fid_handler, 36062306a36Sopenharmony_ci &afs_xattr_afs_volume_handler, 36162306a36Sopenharmony_ci &afs_xattr_yfs_handler, /* afs.yfs. prefix */ 36262306a36Sopenharmony_ci NULL 36362306a36Sopenharmony_ci}; 364