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