1// SPDX-License-Identifier: GPL-2.0-or-later
2/* Extended attribute handling for AFS.  We use xattrs to get and set metadata
3 * 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			else
65				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	else
158		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			else
209				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 of
293 * 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 the
305	 * 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	else
312		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	NULL
363};
364