1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2023 Dmitry Chagin <dchagin@FreeBSD.org>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/param.h>
29#include <sys/extattr.h>
30#include <sys/fcntl.h>
31#include <sys/namei.h>
32#include <sys/proc.h>
33#include <sys/syscallsubr.h>
34
35#ifdef COMPAT_LINUX32
36#include <machine/../linux32/linux.h>
37#include <machine/../linux32/linux32_proto.h>
38#else
39#include <machine/../linux/linux.h>
40#include <machine/../linux/linux_proto.h>
41#endif
42
43#include <compat/linux/linux_util.h>
44
45#define	LINUX_XATTR_SIZE_MAX	65536
46#define	LINUX_XATTR_LIST_MAX	65536
47#define	LINUX_XATTR_NAME_MAX	255
48
49#define	LINUX_XATTR_CREATE	0x1
50#define	LINUX_XATTR_REPLACE	0x2
51#define	LINUX_XATTR_FLAGS	LINUX_XATTR_CREATE|LINUX_XATTR_REPLACE
52
53struct listxattr_args {
54	int		fd;
55	const char	*path;
56	char		*list;
57	l_size_t	size;
58	int		follow;
59};
60
61struct setxattr_args {
62	int		fd;
63	const char	*path;
64	const char	*name;
65	void 		*value;
66	l_size_t	size;
67	l_int		flags;
68	int		follow;
69};
70
71struct getxattr_args {
72	int		fd;
73	const char	*path;
74	const char	*name;
75	void 		*value;
76	l_size_t	size;
77	int		follow;
78};
79
80struct removexattr_args {
81	int		fd;
82	const char	*path;
83	const char	*name;
84	int		follow;
85};
86
87static char *extattr_namespace_names[] = EXTATTR_NAMESPACE_NAMES;
88
89
90static int
91error_to_xattrerror(int attrnamespace, int error)
92{
93
94	if (attrnamespace == EXTATTR_NAMESPACE_SYSTEM && error == EPERM)
95		return (ENOTSUP);
96	else
97		return (error);
98}
99
100static int
101xatrr_to_extattr(const char *uattrname, int *attrnamespace, char *attrname)
102{
103	char uname[LINUX_XATTR_NAME_MAX + 1], *dot;
104	size_t len, cplen;
105	int error;
106
107	error = copyinstr(uattrname, uname, sizeof(uname), &cplen);
108	if (error != 0)
109		return (error);
110	if (cplen == sizeof(uname))
111		return (ERANGE);
112	dot = strchr(uname, '.');
113	if (dot == NULL)
114		return (ENOTSUP);
115	*dot = '\0';
116	for (*attrnamespace = EXTATTR_NAMESPACE_USER;
117	    *attrnamespace < nitems(extattr_namespace_names);
118	    (*attrnamespace)++) {
119		if (bcmp(uname, extattr_namespace_names[*attrnamespace],
120		    dot - uname + 1) == 0) {
121			dot++;
122			len = strlen(dot) + 1;
123			bcopy(dot, attrname, len);
124			return (0);
125		}
126	}
127	return (ENOTSUP);
128}
129
130static int
131listxattr(struct thread *td, struct listxattr_args *args)
132{
133	char attrname[LINUX_XATTR_NAME_MAX + 1];
134	char *data, *prefix, *key;
135	struct uio auio;
136	struct iovec aiov;
137	unsigned char keylen;
138	size_t sz, cnt, rs, prefixlen, pairlen;
139	int attrnamespace, error;
140
141	if (args->size != 0)
142		sz = min(LINUX_XATTR_LIST_MAX, args->size);
143	else
144		sz = LINUX_XATTR_LIST_MAX;
145
146	data = malloc(sz, M_LINUX, M_WAITOK);
147	auio.uio_iov = &aiov;
148	auio.uio_iovcnt = 1;
149	auio.uio_rw = UIO_READ;
150	auio.uio_segflg = UIO_SYSSPACE;
151	auio.uio_td = td;
152	cnt = 0;
153	for (attrnamespace = EXTATTR_NAMESPACE_USER;
154	    attrnamespace < nitems(extattr_namespace_names);
155	    attrnamespace++) {
156		aiov.iov_base = data;
157		aiov.iov_len = sz;
158		auio.uio_resid = sz;
159		auio.uio_offset = 0;
160
161		if (args->path != NULL)
162			error = kern_extattr_list_path(td, args->path,
163			    attrnamespace, &auio, args->follow, UIO_USERSPACE);
164		else
165			error = kern_extattr_list_fd(td, args->fd,
166			    attrnamespace, &auio);
167		rs = sz - auio.uio_resid;
168		if (error == EPERM)
169			break;
170		if (error != 0 || rs == 0)
171			continue;
172		prefix = extattr_namespace_names[attrnamespace];
173		prefixlen = strlen(prefix);
174		key = data;
175		while (rs > 0) {
176			keylen = (unsigned char)key[0];
177			pairlen = prefixlen + 1 + keylen + 1;
178			cnt += pairlen;
179			if (cnt > LINUX_XATTR_LIST_MAX) {
180				error = E2BIG;
181				break;
182			}
183			/*
184			 * If size is specified as zero, return the current size
185			 * of the list of extended attribute names.
186			 */
187			if ((args->size > 0 && cnt > args->size) ||
188			    pairlen >= sizeof(attrname)) {
189				error = ERANGE;
190				break;
191			}
192			++key;
193			if (args->list != NULL && args->size > 0) {
194				sprintf(attrname, "%s.%.*s", prefix, keylen, key);
195				error = copyout(attrname, args->list, pairlen);
196				if (error != 0)
197					break;
198				args->list += pairlen;
199			}
200			key += keylen;
201			rs -= (keylen + 1);
202		}
203	}
204	if (error == 0)
205		td->td_retval[0] = cnt;
206	free(data, M_LINUX);
207	return (error_to_xattrerror(attrnamespace, error));
208}
209
210int
211linux_listxattr(struct thread *td, struct linux_listxattr_args *args)
212{
213	struct listxattr_args eargs = {
214		.fd = -1,
215		.path = args->path,
216		.list = args->list,
217		.size = args->size,
218		.follow = FOLLOW,
219	};
220
221	return (listxattr(td, &eargs));
222}
223
224int
225linux_llistxattr(struct thread *td, struct linux_llistxattr_args *args)
226{
227	struct listxattr_args eargs = {
228		.fd = -1,
229		.path = args->path,
230		.list = args->list,
231		.size = args->size,
232		.follow = NOFOLLOW,
233	};
234
235	return (listxattr(td, &eargs));
236}
237
238int
239linux_flistxattr(struct thread *td, struct linux_flistxattr_args *args)
240{
241	struct listxattr_args eargs = {
242		.fd = args->fd,
243		.path = NULL,
244		.list = args->list,
245		.size = args->size,
246		.follow = 0,
247	};
248
249	return (listxattr(td, &eargs));
250}
251
252static int
253removexattr(struct thread *td, struct removexattr_args *args)
254{
255	char attrname[LINUX_XATTR_NAME_MAX + 1];
256	int attrnamespace, error;
257
258	error = xatrr_to_extattr(args->name, &attrnamespace, attrname);
259	if (error != 0)
260		return (error);
261	if (args->path != NULL)
262		error = kern_extattr_delete_path(td, args->path, attrnamespace,
263		    attrname, args->follow, UIO_USERSPACE);
264	else
265		error = kern_extattr_delete_fd(td, args->fd, attrnamespace,
266		    attrname);
267	return (error_to_xattrerror(attrnamespace, error));
268}
269
270int
271linux_removexattr(struct thread *td, struct linux_removexattr_args *args)
272{
273	struct removexattr_args eargs = {
274		.fd = -1,
275		.path = args->path,
276		.name = args->name,
277		.follow = FOLLOW,
278	};
279
280	return (removexattr(td, &eargs));
281}
282
283int
284linux_lremovexattr(struct thread *td, struct linux_lremovexattr_args *args)
285{
286	struct removexattr_args eargs = {
287		.fd = -1,
288		.path = args->path,
289		.name = args->name,
290		.follow = NOFOLLOW,
291	};
292
293	return (removexattr(td, &eargs));
294}
295
296int
297linux_fremovexattr(struct thread *td, struct linux_fremovexattr_args *args)
298{
299	struct removexattr_args eargs = {
300		.fd = args->fd,
301		.path = NULL,
302		.name = args->name,
303		.follow = 0,
304	};
305
306	return (removexattr(td, &eargs));
307}
308
309static int
310getxattr(struct thread *td, struct getxattr_args *args)
311{
312	char attrname[LINUX_XATTR_NAME_MAX + 1];
313	int attrnamespace, error;
314
315	error = xatrr_to_extattr(args->name, &attrnamespace, attrname);
316	if (error != 0)
317		return (error);
318	if (args->path != NULL)
319		error = kern_extattr_get_path(td, args->path, attrnamespace,
320		    attrname, args->value, args->size, args->follow, UIO_USERSPACE);
321	else
322		error = kern_extattr_get_fd(td, args->fd, attrnamespace,
323		    attrname, args->value, args->size);
324	return (error == EPERM ? ENOATTR : error);
325}
326
327int
328linux_getxattr(struct thread *td, struct linux_getxattr_args *args)
329{
330	struct getxattr_args eargs = {
331		.fd = -1,
332		.path = args->path,
333		.name = args->name,
334		.value = args->value,
335		.size = args->size,
336		.follow = FOLLOW,
337	};
338
339	return (getxattr(td, &eargs));
340}
341
342int
343linux_lgetxattr(struct thread *td, struct linux_lgetxattr_args *args)
344{
345	struct getxattr_args eargs = {
346		.fd = -1,
347		.path = args->path,
348		.name = args->name,
349		.value = args->value,
350		.size = args->size,
351		.follow = NOFOLLOW,
352	};
353
354	return (getxattr(td, &eargs));
355}
356
357int
358linux_fgetxattr(struct thread *td, struct linux_fgetxattr_args *args)
359{
360	struct getxattr_args eargs = {
361		.fd = args->fd,
362		.path = NULL,
363		.name = args->name,
364		.value = args->value,
365		.size = args->size,
366		.follow = 0,
367	};
368
369	return (getxattr(td, &eargs));
370}
371
372static int
373setxattr(struct thread *td, struct setxattr_args *args)
374{
375	char attrname[LINUX_XATTR_NAME_MAX + 1];
376	int attrnamespace, error;
377
378	if ((args->flags & ~(LINUX_XATTR_FLAGS)) != 0 ||
379	    args->flags == (LINUX_XATTR_FLAGS))
380		return (EINVAL);
381	error = xatrr_to_extattr(args->name, &attrnamespace, attrname);
382	if (error != 0)
383		return (error);
384
385	if ((args->flags & (LINUX_XATTR_FLAGS)) != 0 ) {
386		if (args->path != NULL)
387			error = kern_extattr_get_path(td, args->path,
388			    attrnamespace, attrname, NULL, args->size,
389			    args->follow, UIO_USERSPACE);
390		else
391			error = kern_extattr_get_fd(td, args->fd,
392			    attrnamespace, attrname, NULL, args->size);
393		if ((args->flags & LINUX_XATTR_CREATE) != 0) {
394			if (error == 0)
395				error = EEXIST;
396			else if (error == ENOATTR)
397				error = 0;
398		}
399		if (error != 0)
400			goto out;
401	}
402	if (args->path != NULL)
403		error = kern_extattr_set_path(td, args->path, attrnamespace,
404		    attrname, args->value, args->size, args->follow,
405		    UIO_USERSPACE);
406	else
407		error = kern_extattr_set_fd(td, args->fd, attrnamespace,
408		    attrname, args->value, args->size);
409out:
410	td->td_retval[0] = 0;
411	return (error_to_xattrerror(attrnamespace, error));
412}
413
414int
415linux_setxattr(struct thread *td, struct linux_setxattr_args *args)
416{
417	struct setxattr_args eargs = {
418		.fd = -1,
419		.path = args->path,
420		.name = args->name,
421		.value = args->value,
422		.size = args->size,
423		.flags = args->flags,
424		.follow = FOLLOW,
425	};
426
427	return (setxattr(td, &eargs));
428}
429
430int
431linux_lsetxattr(struct thread *td, struct linux_lsetxattr_args *args)
432{
433	struct setxattr_args eargs = {
434		.fd = -1,
435		.path = args->path,
436		.name = args->name,
437		.value = args->value,
438		.size = args->size,
439		.flags = args->flags,
440		.follow = NOFOLLOW,
441	};
442
443	return (setxattr(td, &eargs));
444}
445
446int
447linux_fsetxattr(struct thread *td, struct linux_fsetxattr_args *args)
448{
449	struct setxattr_args eargs = {
450		.fd = args->fd,
451		.path = NULL,
452		.name = args->name,
453		.value = args->value,
454		.size = args->size,
455		.flags = args->flags,
456		.follow = 0,
457	};
458
459	return (setxattr(td, &eargs));
460}
461