treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 36
[linux-block.git] / fs / afs / xattr.c
CommitLineData
b4d0d230 1// SPDX-License-Identifier: GPL-2.0-or-later
d3e3b7ea
DH
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)
d3e3b7ea
DH
7 */
8
9#include <linux/slab.h>
10#include <linux/fs.h>
11#include <linux/xattr.h>
12#include "internal.h"
13
14static const char afs_xattr_list[] =
260f082b 15 "afs.acl\0"
d3e3b7ea
DH
16 "afs.cell\0"
17 "afs.fid\0"
ae46578b
DH
18 "afs.volume\0"
19 "afs.yfs.acl\0"
20 "afs.yfs.acl_inherited\0"
21 "afs.yfs.acl_num_cleaned\0"
22 "afs.yfs.vol_acl";
d3e3b7ea
DH
23
24/*
25 * Retrieve a list of the supported xattrs.
26 */
27ssize_t afs_listxattr(struct dentry *dentry, char *buffer, size_t size)
28{
29 if (size == 0)
30 return sizeof(afs_xattr_list);
31 if (size < sizeof(afs_xattr_list))
32 return -ERANGE;
33 memcpy(buffer, afs_xattr_list, sizeof(afs_xattr_list));
34 return sizeof(afs_xattr_list);
35}
36
260f082b
DH
37/*
38 * Get a file's ACL.
39 */
40static int afs_xattr_get_acl(const struct xattr_handler *handler,
41 struct dentry *dentry,
42 struct inode *inode, const char *name,
43 void *buffer, size_t size)
44{
45 struct afs_fs_cursor fc;
a58823ac 46 struct afs_status_cb *scb;
260f082b
DH
47 struct afs_vnode *vnode = AFS_FS_I(inode);
48 struct afs_acl *acl = NULL;
49 struct key *key;
a58823ac
DH
50 int ret = -ENOMEM;
51
52 scb = kzalloc(sizeof(struct afs_status_cb), GFP_NOFS);
53 if (!scb)
54 goto error;
260f082b
DH
55
56 key = afs_request_key(vnode->volume->cell);
a58823ac
DH
57 if (IS_ERR(key)) {
58 ret = PTR_ERR(key);
59 goto error_scb;
60 }
260f082b
DH
61
62 ret = -ERESTARTSYS;
20b8391f 63 if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
a58823ac
DH
64 afs_dataversion_t data_version = vnode->status.data_version;
65
260f082b
DH
66 while (afs_select_fileserver(&fc)) {
67 fc.cb_break = afs_calc_vnode_cb_break(vnode);
a58823ac 68 acl = afs_fs_fetch_acl(&fc, scb);
260f082b
DH
69 }
70
71 afs_check_for_remote_deletion(&fc, fc.vnode);
a58823ac
DH
72 afs_vnode_commit_status(&fc, vnode, fc.cb_break,
73 &data_version, scb);
260f082b
DH
74 ret = afs_end_vnode_operation(&fc);
75 }
76
77 if (ret == 0) {
78 ret = acl->size;
79 if (size > 0) {
cc1dd5c8
DH
80 if (acl->size <= size)
81 memcpy(buffer, acl->data, acl->size);
82 else
83 ret = -ERANGE;
260f082b
DH
84 }
85 kfree(acl);
86 }
87
88 key_put(key);
a58823ac
DH
89error_scb:
90 kfree(scb);
91error:
260f082b
DH
92 return ret;
93}
94
b10494af
JG
95/*
96 * Set a file's AFS3 ACL.
97 */
98static int afs_xattr_set_acl(const struct xattr_handler *handler,
99 struct dentry *dentry,
100 struct inode *inode, const char *name,
101 const void *buffer, size_t size, int flags)
102{
103 struct afs_fs_cursor fc;
a58823ac 104 struct afs_status_cb *scb;
b10494af
JG
105 struct afs_vnode *vnode = AFS_FS_I(inode);
106 struct afs_acl *acl = NULL;
107 struct key *key;
a58823ac 108 int ret = -ENOMEM;
b10494af
JG
109
110 if (flags == XATTR_CREATE)
111 return -EINVAL;
112
a58823ac
DH
113 scb = kzalloc(sizeof(struct afs_status_cb), GFP_NOFS);
114 if (!scb)
115 goto error;
b10494af
JG
116
117 acl = kmalloc(sizeof(*acl) + size, GFP_KERNEL);
a58823ac
DH
118 if (!acl)
119 goto error_scb;
120
121 key = afs_request_key(vnode->volume->cell);
122 if (IS_ERR(key)) {
123 ret = PTR_ERR(key);
124 goto error_acl;
b10494af
JG
125 }
126
127 acl->size = size;
128 memcpy(acl->data, buffer, size);
129
130 ret = -ERESTARTSYS;
20b8391f 131 if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
a58823ac
DH
132 afs_dataversion_t data_version = vnode->status.data_version;
133
b10494af
JG
134 while (afs_select_fileserver(&fc)) {
135 fc.cb_break = afs_calc_vnode_cb_break(vnode);
a58823ac 136 afs_fs_store_acl(&fc, acl, scb);
b10494af
JG
137 }
138
139 afs_check_for_remote_deletion(&fc, fc.vnode);
a58823ac
DH
140 afs_vnode_commit_status(&fc, vnode, fc.cb_break,
141 &data_version, scb);
b10494af
JG
142 ret = afs_end_vnode_operation(&fc);
143 }
144
b10494af 145 key_put(key);
a58823ac
DH
146error_acl:
147 kfree(acl);
148error_scb:
149 kfree(scb);
150error:
b10494af
JG
151 return ret;
152}
153
260f082b 154static const struct xattr_handler afs_xattr_afs_acl_handler = {
b10494af
JG
155 .name = "afs.acl",
156 .get = afs_xattr_get_acl,
157 .set = afs_xattr_set_acl,
260f082b
DH
158};
159
ae46578b
DH
160/*
161 * Get a file's YFS ACL.
162 */
163static int afs_xattr_get_yfs(const struct xattr_handler *handler,
164 struct dentry *dentry,
165 struct inode *inode, const char *name,
166 void *buffer, size_t size)
167{
168 struct afs_fs_cursor fc;
a58823ac 169 struct afs_status_cb *scb;
ae46578b
DH
170 struct afs_vnode *vnode = AFS_FS_I(inode);
171 struct yfs_acl *yacl = NULL;
172 struct key *key;
ae46578b 173 char buf[16], *data;
773e0c40 174 int which = 0, dsize, ret = -ENOMEM;
ae46578b
DH
175
176 if (strcmp(name, "acl") == 0)
177 which = 0;
178 else if (strcmp(name, "acl_inherited") == 0)
179 which = 1;
180 else if (strcmp(name, "acl_num_cleaned") == 0)
181 which = 2;
182 else if (strcmp(name, "vol_acl") == 0)
183 which = 3;
184 else
185 return -EOPNOTSUPP;
186
773e0c40
DH
187 yacl = kzalloc(sizeof(struct yfs_acl), GFP_KERNEL);
188 if (!yacl)
189 goto error;
190
ae46578b 191 if (which == 0)
773e0c40 192 yacl->flags |= YFS_ACL_WANT_ACL;
ae46578b 193 else if (which == 3)
773e0c40 194 yacl->flags |= YFS_ACL_WANT_VOL_ACL;
ae46578b 195
a58823ac
DH
196 scb = kzalloc(sizeof(struct afs_status_cb), GFP_NOFS);
197 if (!scb)
198 goto error_yacl;
199
ae46578b 200 key = afs_request_key(vnode->volume->cell);
773e0c40
DH
201 if (IS_ERR(key)) {
202 ret = PTR_ERR(key);
a58823ac 203 goto error_scb;
773e0c40 204 }
ae46578b
DH
205
206 ret = -ERESTARTSYS;
20b8391f 207 if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
a58823ac
DH
208 afs_dataversion_t data_version = vnode->status.data_version;
209
ae46578b
DH
210 while (afs_select_fileserver(&fc)) {
211 fc.cb_break = afs_calc_vnode_cb_break(vnode);
a58823ac 212 yfs_fs_fetch_opaque_acl(&fc, yacl, scb);
ae46578b
DH
213 }
214
215 afs_check_for_remote_deletion(&fc, fc.vnode);
a58823ac
DH
216 afs_vnode_commit_status(&fc, vnode, fc.cb_break,
217 &data_version, scb);
ae46578b
DH
218 ret = afs_end_vnode_operation(&fc);
219 }
220
773e0c40
DH
221 if (ret < 0)
222 goto error_key;
223
224 switch (which) {
225 case 0:
226 data = yacl->acl->data;
227 dsize = yacl->acl->size;
228 break;
229 case 1:
230 data = buf;
231 dsize = snprintf(buf, sizeof(buf), "%u", yacl->inherit_flag);
232 break;
233 case 2:
234 data = buf;
235 dsize = snprintf(buf, sizeof(buf), "%u", yacl->num_cleaned);
236 break;
237 case 3:
238 data = yacl->vol_acl->data;
239 dsize = yacl->vol_acl->size;
240 break;
241 default:
242 ret = -EOPNOTSUPP;
243 goto error_key;
244 }
ae46578b 245
773e0c40
DH
246 ret = dsize;
247 if (size > 0) {
248 if (dsize > size) {
249 ret = -ERANGE;
250 goto error_key;
ae46578b 251 }
773e0c40 252 memcpy(buffer, data, dsize);
ae46578b
DH
253 }
254
773e0c40 255error_key:
ae46578b 256 key_put(key);
a58823ac
DH
257error_scb:
258 kfree(scb);
773e0c40
DH
259error_yacl:
260 yfs_free_opaque_acl(yacl);
261error:
ae46578b
DH
262 return ret;
263}
264
f5e45463
DH
265/*
266 * Set a file's YFS ACL.
267 */
268static int afs_xattr_set_yfs(const struct xattr_handler *handler,
269 struct dentry *dentry,
270 struct inode *inode, const char *name,
271 const void *buffer, size_t size, int flags)
272{
273 struct afs_fs_cursor fc;
a58823ac 274 struct afs_status_cb *scb;
f5e45463
DH
275 struct afs_vnode *vnode = AFS_FS_I(inode);
276 struct afs_acl *acl = NULL;
277 struct key *key;
a58823ac 278 int ret = -ENOMEM;
f5e45463
DH
279
280 if (flags == XATTR_CREATE ||
281 strcmp(name, "acl") != 0)
282 return -EINVAL;
283
a58823ac
DH
284 scb = kzalloc(sizeof(struct afs_status_cb), GFP_NOFS);
285 if (!scb)
286 goto error;
f5e45463
DH
287
288 acl = kmalloc(sizeof(*acl) + size, GFP_KERNEL);
a58823ac
DH
289 if (!acl)
290 goto error_scb;
f5e45463
DH
291
292 acl->size = size;
293 memcpy(acl->data, buffer, size);
294
a58823ac
DH
295 key = afs_request_key(vnode->volume->cell);
296 if (IS_ERR(key)) {
297 ret = PTR_ERR(key);
298 goto error_acl;
299 }
300
f5e45463 301 ret = -ERESTARTSYS;
20b8391f 302 if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
a58823ac
DH
303 afs_dataversion_t data_version = vnode->status.data_version;
304
f5e45463
DH
305 while (afs_select_fileserver(&fc)) {
306 fc.cb_break = afs_calc_vnode_cb_break(vnode);
a58823ac 307 yfs_fs_store_opaque_acl2(&fc, acl, scb);
f5e45463
DH
308 }
309
310 afs_check_for_remote_deletion(&fc, fc.vnode);
a58823ac
DH
311 afs_vnode_commit_status(&fc, vnode, fc.cb_break,
312 &data_version, scb);
f5e45463
DH
313 ret = afs_end_vnode_operation(&fc);
314 }
315
a58823ac 316error_acl:
f5e45463
DH
317 kfree(acl);
318 key_put(key);
a58823ac
DH
319error_scb:
320 kfree(scb);
321error:
f5e45463
DH
322 return ret;
323}
324
ae46578b
DH
325static const struct xattr_handler afs_xattr_yfs_handler = {
326 .prefix = "afs.yfs.",
327 .get = afs_xattr_get_yfs,
f5e45463 328 .set = afs_xattr_set_yfs,
ae46578b
DH
329};
330
d3e3b7ea
DH
331/*
332 * Get the name of the cell on which a file resides.
333 */
334static int afs_xattr_get_cell(const struct xattr_handler *handler,
335 struct dentry *dentry,
336 struct inode *inode, const char *name,
337 void *buffer, size_t size)
338{
339 struct afs_vnode *vnode = AFS_FS_I(inode);
340 struct afs_cell *cell = vnode->volume->cell;
341 size_t namelen;
342
989782dc 343 namelen = cell->name_len;
d3e3b7ea
DH
344 if (size == 0)
345 return namelen;
346 if (namelen > size)
347 return -ERANGE;
c73aa410 348 memcpy(buffer, cell->name, namelen);
d3e3b7ea
DH
349 return namelen;
350}
351
352static const struct xattr_handler afs_xattr_afs_cell_handler = {
353 .name = "afs.cell",
354 .get = afs_xattr_get_cell,
355};
356
357/*
358 * Get the volume ID, vnode ID and vnode uniquifier of a file as a sequence of
359 * hex numbers separated by colons.
360 */
361static int afs_xattr_get_fid(const struct xattr_handler *handler,
362 struct dentry *dentry,
363 struct inode *inode, const char *name,
364 void *buffer, size_t size)
365{
366 struct afs_vnode *vnode = AFS_FS_I(inode);
a2f611a3 367 char text[16 + 1 + 24 + 1 + 8 + 1];
d3e3b7ea
DH
368 size_t len;
369
a2f611a3
DH
370 /* The volume ID is 64-bit, the vnode ID is 96-bit and the
371 * uniquifier is 32-bit.
372 */
373 len = sprintf(text, "%llx:", vnode->fid.vid);
374 if (vnode->fid.vnode_hi)
375 len += sprintf(text + len, "%x%016llx",
376 vnode->fid.vnode_hi, vnode->fid.vnode);
377 else
378 len += sprintf(text + len, "%llx", vnode->fid.vnode);
379 len += sprintf(text + len, ":%x", vnode->fid.unique);
380
d3e3b7ea
DH
381 if (size == 0)
382 return len;
383 if (len > size)
384 return -ERANGE;
385 memcpy(buffer, text, len);
386 return len;
387}
388
389static const struct xattr_handler afs_xattr_afs_fid_handler = {
390 .name = "afs.fid",
391 .get = afs_xattr_get_fid,
392};
393
394/*
395 * Get the name of the volume on which a file resides.
396 */
397static int afs_xattr_get_volume(const struct xattr_handler *handler,
398 struct dentry *dentry,
399 struct inode *inode, const char *name,
400 void *buffer, size_t size)
401{
402 struct afs_vnode *vnode = AFS_FS_I(inode);
d2ddc776 403 const char *volname = vnode->volume->name;
d3e3b7ea
DH
404 size_t namelen;
405
406 namelen = strlen(volname);
407 if (size == 0)
408 return namelen;
409 if (namelen > size)
410 return -ERANGE;
c73aa410 411 memcpy(buffer, volname, namelen);
d3e3b7ea
DH
412 return namelen;
413}
414
415static const struct xattr_handler afs_xattr_afs_volume_handler = {
416 .name = "afs.volume",
417 .get = afs_xattr_get_volume,
418};
419
420const struct xattr_handler *afs_xattr_handlers[] = {
260f082b 421 &afs_xattr_afs_acl_handler,
d3e3b7ea
DH
422 &afs_xattr_afs_cell_handler,
423 &afs_xattr_afs_fid_handler,
424 &afs_xattr_afs_volume_handler,
ae46578b 425 &afs_xattr_yfs_handler, /* afs.yfs. prefix */
d3e3b7ea
DH
426 NULL
427};