364 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/* Extended attribute handling for AFS. We use xattrs to get and set metadata3 * instead of providing pioctl().4 *5 * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved.6 * Written by David Howells (dhowells@redhat.com)7 */8 9#include <linux/slab.h>10#include <linux/fs.h>11#include <linux/xattr.h>12#include "internal.h"13 14/*15 * Deal with the result of a successful fetch ACL operation.16 */17static void afs_acl_success(struct afs_operation *op)18{19 afs_vnode_commit_status(op, &op->file[0]);20}21 22static void afs_acl_put(struct afs_operation *op)23{24 kfree(op->acl);25}26 27static const struct afs_operation_ops afs_fetch_acl_operation = {28 .issue_afs_rpc = afs_fs_fetch_acl,29 .success = afs_acl_success,30 .put = afs_acl_put,31};32 33/*34 * Get a file's ACL.35 */36static int afs_xattr_get_acl(const struct xattr_handler *handler,37 struct dentry *dentry,38 struct inode *inode, const char *name,39 void *buffer, size_t size)40{41 struct afs_operation *op;42 struct afs_vnode *vnode = AFS_FS_I(inode);43 struct afs_acl *acl = NULL;44 int ret;45 46 op = afs_alloc_operation(NULL, vnode->volume);47 if (IS_ERR(op))48 return -ENOMEM;49 50 afs_op_set_vnode(op, 0, vnode);51 op->ops = &afs_fetch_acl_operation;52 53 afs_begin_vnode_operation(op);54 afs_wait_for_operation(op);55 acl = op->acl;56 op->acl = NULL;57 ret = afs_put_operation(op);58 59 if (ret == 0) {60 ret = acl->size;61 if (size > 0) {62 if (acl->size <= size)63 memcpy(buffer, acl->data, acl->size);64 else65 ret = -ERANGE;66 }67 }68 69 kfree(acl);70 return ret;71}72 73static bool afs_make_acl(struct afs_operation *op,74 const void *buffer, size_t size)75{76 struct afs_acl *acl;77 78 acl = kmalloc(struct_size(acl, data, size), GFP_KERNEL);79 if (!acl) {80 afs_op_nomem(op);81 return false;82 }83 84 acl->size = size;85 memcpy(acl->data, buffer, size);86 op->acl = acl;87 return true;88}89 90static const struct afs_operation_ops afs_store_acl_operation = {91 .issue_afs_rpc = afs_fs_store_acl,92 .success = afs_acl_success,93 .put = afs_acl_put,94};95 96/*97 * Set a file's AFS3 ACL.98 */99static int afs_xattr_set_acl(const struct xattr_handler *handler,100 struct mnt_idmap *idmap,101 struct dentry *dentry,102 struct inode *inode, const char *name,103 const void *buffer, size_t size, int flags)104{105 struct afs_operation *op;106 struct afs_vnode *vnode = AFS_FS_I(inode);107 108 if (flags == XATTR_CREATE)109 return -EINVAL;110 111 op = afs_alloc_operation(NULL, vnode->volume);112 if (IS_ERR(op))113 return -ENOMEM;114 115 afs_op_set_vnode(op, 0, vnode);116 if (!afs_make_acl(op, buffer, size))117 return afs_put_operation(op);118 119 op->ops = &afs_store_acl_operation;120 return afs_do_sync_operation(op);121}122 123static const struct xattr_handler afs_xattr_afs_acl_handler = {124 .name = "afs.acl",125 .get = afs_xattr_get_acl,126 .set = afs_xattr_set_acl,127};128 129static const struct afs_operation_ops yfs_fetch_opaque_acl_operation = {130 .issue_yfs_rpc = yfs_fs_fetch_opaque_acl,131 .success = afs_acl_success,132 /* Don't free op->yacl in .put here */133};134 135/*136 * Get a file's YFS ACL.137 */138static int afs_xattr_get_yfs(const struct xattr_handler *handler,139 struct dentry *dentry,140 struct inode *inode, const char *name,141 void *buffer, size_t size)142{143 struct afs_operation *op;144 struct afs_vnode *vnode = AFS_FS_I(inode);145 struct yfs_acl *yacl = NULL;146 char buf[16], *data;147 int which = 0, dsize, ret = -ENOMEM;148 149 if (strcmp(name, "acl") == 0)150 which = 0;151 else if (strcmp(name, "acl_inherited") == 0)152 which = 1;153 else if (strcmp(name, "acl_num_cleaned") == 0)154 which = 2;155 else if (strcmp(name, "vol_acl") == 0)156 which = 3;157 else158 return -EOPNOTSUPP;159 160 yacl = kzalloc(sizeof(struct yfs_acl), GFP_KERNEL);161 if (!yacl)162 goto error;163 164 if (which == 0)165 yacl->flags |= YFS_ACL_WANT_ACL;166 else if (which == 3)167 yacl->flags |= YFS_ACL_WANT_VOL_ACL;168 169 op = afs_alloc_operation(NULL, vnode->volume);170 if (IS_ERR(op))171 goto error_yacl;172 173 afs_op_set_vnode(op, 0, vnode);174 op->yacl = yacl;175 op->ops = &yfs_fetch_opaque_acl_operation;176 177 afs_begin_vnode_operation(op);178 afs_wait_for_operation(op);179 ret = afs_put_operation(op);180 181 if (ret == 0) {182 switch (which) {183 case 0:184 data = yacl->acl->data;185 dsize = yacl->acl->size;186 break;187 case 1:188 data = buf;189 dsize = scnprintf(buf, sizeof(buf), "%u", yacl->inherit_flag);190 break;191 case 2:192 data = buf;193 dsize = scnprintf(buf, sizeof(buf), "%u", yacl->num_cleaned);194 break;195 case 3:196 data = yacl->vol_acl->data;197 dsize = yacl->vol_acl->size;198 break;199 default:200 ret = -EOPNOTSUPP;201 goto error_yacl;202 }203 204 ret = dsize;205 if (size > 0) {206 if (dsize <= size)207 memcpy(buffer, data, dsize);208 else209 ret = -ERANGE;210 }211 } else if (ret == -ENOTSUPP) {212 ret = -ENODATA;213 }214 215error_yacl:216 yfs_free_opaque_acl(yacl);217error:218 return ret;219}220 221static const struct afs_operation_ops yfs_store_opaque_acl2_operation = {222 .issue_yfs_rpc = yfs_fs_store_opaque_acl2,223 .success = afs_acl_success,224 .put = afs_acl_put,225};226 227/*228 * Set a file's YFS ACL.229 */230static int afs_xattr_set_yfs(const struct xattr_handler *handler,231 struct mnt_idmap *idmap,232 struct dentry *dentry,233 struct inode *inode, const char *name,234 const void *buffer, size_t size, int flags)235{236 struct afs_operation *op;237 struct afs_vnode *vnode = AFS_FS_I(inode);238 int ret;239 240 if (flags == XATTR_CREATE ||241 strcmp(name, "acl") != 0)242 return -EINVAL;243 244 op = afs_alloc_operation(NULL, vnode->volume);245 if (IS_ERR(op))246 return -ENOMEM;247 248 afs_op_set_vnode(op, 0, vnode);249 if (!afs_make_acl(op, buffer, size))250 return afs_put_operation(op);251 252 op->ops = &yfs_store_opaque_acl2_operation;253 ret = afs_do_sync_operation(op);254 if (ret == -ENOTSUPP)255 ret = -ENODATA;256 return ret;257}258 259static const struct xattr_handler afs_xattr_yfs_handler = {260 .prefix = "afs.yfs.",261 .get = afs_xattr_get_yfs,262 .set = afs_xattr_set_yfs,263};264 265/*266 * Get the name of the cell on which a file resides.267 */268static int afs_xattr_get_cell(const struct xattr_handler *handler,269 struct dentry *dentry,270 struct inode *inode, const char *name,271 void *buffer, size_t size)272{273 struct afs_vnode *vnode = AFS_FS_I(inode);274 struct afs_cell *cell = vnode->volume->cell;275 size_t namelen;276 277 namelen = cell->name_len;278 if (size == 0)279 return namelen;280 if (namelen > size)281 return -ERANGE;282 memcpy(buffer, cell->name, namelen);283 return namelen;284}285 286static const struct xattr_handler afs_xattr_afs_cell_handler = {287 .name = "afs.cell",288 .get = afs_xattr_get_cell,289};290 291/*292 * Get the volume ID, vnode ID and vnode uniquifier of a file as a sequence of293 * hex numbers separated by colons.294 */295static int afs_xattr_get_fid(const struct xattr_handler *handler,296 struct dentry *dentry,297 struct inode *inode, const char *name,298 void *buffer, size_t size)299{300 struct afs_vnode *vnode = AFS_FS_I(inode);301 char text[16 + 1 + 24 + 1 + 8 + 1];302 size_t len;303 304 /* The volume ID is 64-bit, the vnode ID is 96-bit and the305 * uniquifier is 32-bit.306 */307 len = scnprintf(text, sizeof(text), "%llx:", vnode->fid.vid);308 if (vnode->fid.vnode_hi)309 len += scnprintf(text + len, sizeof(text) - len, "%x%016llx",310 vnode->fid.vnode_hi, vnode->fid.vnode);311 else312 len += scnprintf(text + len, sizeof(text) - len, "%llx",313 vnode->fid.vnode);314 len += scnprintf(text + len, sizeof(text) - len, ":%x",315 vnode->fid.unique);316 317 if (size == 0)318 return len;319 if (len > size)320 return -ERANGE;321 memcpy(buffer, text, len);322 return len;323}324 325static const struct xattr_handler afs_xattr_afs_fid_handler = {326 .name = "afs.fid",327 .get = afs_xattr_get_fid,328};329 330/*331 * Get the name of the volume on which a file resides.332 */333static int afs_xattr_get_volume(const struct xattr_handler *handler,334 struct dentry *dentry,335 struct inode *inode, const char *name,336 void *buffer, size_t size)337{338 struct afs_vnode *vnode = AFS_FS_I(inode);339 const char *volname = vnode->volume->name;340 size_t namelen;341 342 namelen = strlen(volname);343 if (size == 0)344 return namelen;345 if (namelen > size)346 return -ERANGE;347 memcpy(buffer, volname, namelen);348 return namelen;349}350 351static const struct xattr_handler afs_xattr_afs_volume_handler = {352 .name = "afs.volume",353 .get = afs_xattr_get_volume,354};355 356const struct xattr_handler * const afs_xattr_handlers[] = {357 &afs_xattr_afs_acl_handler,358 &afs_xattr_afs_cell_handler,359 &afs_xattr_afs_fid_handler,360 &afs_xattr_afs_volume_handler,361 &afs_xattr_yfs_handler, /* afs.yfs. prefix */362 NULL363};364