xref: /freebsd-14.2/sys/compat/linux/linux_xattr.c (revision 53b3e15d)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2023 Dmitry Chagin <[email protected]>
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 
53 struct listxattr_args {
54 	int		fd;
55 	const char	*path;
56 	char		*list;
57 	l_size_t	size;
58 	int		follow;
59 };
60 
61 struct 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 
71 struct 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 
80 struct removexattr_args {
81 	int		fd;
82 	const char	*path;
83 	const char	*name;
84 	int		follow;
85 };
86 
87 static char *extattr_namespace_names[] = EXTATTR_NAMESPACE_NAMES;
88 
89 
90 static int
91 error_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 
100 static int
101 xatrr_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 
130 static int
131 listxattr(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 != 0 || rs == 0)
169 			continue;
170 		prefix = extattr_namespace_names[attrnamespace];
171 		prefixlen = strlen(prefix);
172 		key = data;
173 		while (rs > 0) {
174 			keylen = (unsigned char)key[0];
175 			pairlen = prefixlen + 1 + keylen + 1;
176 			cnt += pairlen;
177 			if (cnt > LINUX_XATTR_LIST_MAX) {
178 				error = E2BIG;
179 				break;
180 			}
181 			/*
182 			 * If size is specified as zero, return the current size
183 			 * of the list of extended attribute names.
184 			 */
185 			if ((args->size > 0 && cnt > args->size) ||
186 			    pairlen >= sizeof(attrname)) {
187 				error = ERANGE;
188 				break;
189 			}
190 			++key;
191 			if (args->list != NULL && args->size > 0) {
192 				sprintf(attrname, "%s.%.*s", prefix, keylen, key);
193 				error = copyout(attrname, args->list, pairlen);
194 				if (error != 0)
195 					break;
196 				args->list += pairlen;
197 			}
198 			key += keylen;
199 			rs -= (keylen + 1);
200 		}
201 	}
202 	if (error == 0)
203 		td->td_retval[0] = cnt;
204 	free(data, M_LINUX);
205 	return (error_to_xattrerror(attrnamespace, error));
206 }
207 
208 int
209 linux_listxattr(struct thread *td, struct linux_listxattr_args *args)
210 {
211 	struct listxattr_args eargs = {
212 		.fd = -1,
213 		.path = args->path,
214 		.list = args->list,
215 		.size = args->size,
216 		.follow = FOLLOW,
217 	};
218 
219 	return (listxattr(td, &eargs));
220 }
221 
222 int
223 linux_llistxattr(struct thread *td, struct linux_llistxattr_args *args)
224 {
225 	struct listxattr_args eargs = {
226 		.fd = -1,
227 		.path = args->path,
228 		.list = args->list,
229 		.size = args->size,
230 		.follow = NOFOLLOW,
231 	};
232 
233 	return (listxattr(td, &eargs));
234 }
235 
236 int
237 linux_flistxattr(struct thread *td, struct linux_flistxattr_args *args)
238 {
239 	struct listxattr_args eargs = {
240 		.fd = args->fd,
241 		.path = NULL,
242 		.list = args->list,
243 		.size = args->size,
244 		.follow = 0,
245 	};
246 
247 	return (listxattr(td, &eargs));
248 }
249 
250 static int
251 removexattr(struct thread *td, struct removexattr_args *args)
252 {
253 	char attrname[LINUX_XATTR_NAME_MAX + 1];
254 	int attrnamespace, error;
255 
256 	error = xatrr_to_extattr(args->name, &attrnamespace, attrname);
257 	if (error != 0)
258 		return (error);
259 	if (args->path != NULL)
260 		error = kern_extattr_delete_path(td, args->path, attrnamespace,
261 		    attrname, args->follow, UIO_USERSPACE);
262 	else
263 		error = kern_extattr_delete_fd(td, args->fd, attrnamespace,
264 		    attrname);
265 	return (error_to_xattrerror(attrnamespace, error));
266 }
267 
268 int
269 linux_removexattr(struct thread *td, struct linux_removexattr_args *args)
270 {
271 	struct removexattr_args eargs = {
272 		.fd = -1,
273 		.path = args->path,
274 		.name = args->name,
275 		.follow = FOLLOW,
276 	};
277 
278 	return (removexattr(td, &eargs));
279 }
280 
281 int
282 linux_lremovexattr(struct thread *td, struct linux_lremovexattr_args *args)
283 {
284 	struct removexattr_args eargs = {
285 		.fd = -1,
286 		.path = args->path,
287 		.name = args->name,
288 		.follow = NOFOLLOW,
289 	};
290 
291 	return (removexattr(td, &eargs));
292 }
293 
294 int
295 linux_fremovexattr(struct thread *td, struct linux_fremovexattr_args *args)
296 {
297 	struct removexattr_args eargs = {
298 		.fd = args->fd,
299 		.path = NULL,
300 		.name = args->name,
301 		.follow = 0,
302 	};
303 
304 	return (removexattr(td, &eargs));
305 }
306 
307 static int
308 getxattr(struct thread *td, struct getxattr_args *args)
309 {
310 	char attrname[LINUX_XATTR_NAME_MAX + 1];
311 	int attrnamespace, error;
312 
313 	error = xatrr_to_extattr(args->name, &attrnamespace, attrname);
314 	if (error != 0)
315 		return (error);
316 	if (args->path != NULL)
317 		error = kern_extattr_get_path(td, args->path, attrnamespace,
318 		    attrname, args->value, args->size, args->follow, UIO_USERSPACE);
319 	else
320 		error = kern_extattr_get_fd(td, args->fd, attrnamespace,
321 		    attrname, args->value, args->size);
322 	return (error == EPERM ? ENOATTR : error);
323 }
324 
325 int
326 linux_getxattr(struct thread *td, struct linux_getxattr_args *args)
327 {
328 	struct getxattr_args eargs = {
329 		.fd = -1,
330 		.path = args->path,
331 		.name = args->name,
332 		.value = args->value,
333 		.size = args->size,
334 		.follow = FOLLOW,
335 	};
336 
337 	return (getxattr(td, &eargs));
338 }
339 
340 int
341 linux_lgetxattr(struct thread *td, struct linux_lgetxattr_args *args)
342 {
343 	struct getxattr_args eargs = {
344 		.fd = -1,
345 		.path = args->path,
346 		.name = args->name,
347 		.value = args->value,
348 		.size = args->size,
349 		.follow = NOFOLLOW,
350 	};
351 
352 	return (getxattr(td, &eargs));
353 }
354 
355 int
356 linux_fgetxattr(struct thread *td, struct linux_fgetxattr_args *args)
357 {
358 	struct getxattr_args eargs = {
359 		.fd = args->fd,
360 		.path = NULL,
361 		.name = args->name,
362 		.value = args->value,
363 		.size = args->size,
364 		.follow = 0,
365 	};
366 
367 	return (getxattr(td, &eargs));
368 }
369 
370 static int
371 setxattr(struct thread *td, struct setxattr_args *args)
372 {
373 	char attrname[LINUX_XATTR_NAME_MAX + 1];
374 	int attrnamespace, error;
375 
376 	if ((args->flags & ~(LINUX_XATTR_FLAGS)) != 0 ||
377 	    args->flags == (LINUX_XATTR_FLAGS))
378 		return (EINVAL);
379 	error = xatrr_to_extattr(args->name, &attrnamespace, attrname);
380 	if (error != 0)
381 		return (error);
382 
383 	if ((args->flags & (LINUX_XATTR_FLAGS)) != 0 ) {
384 		if (args->path != NULL)
385 			error = kern_extattr_get_path(td, args->path,
386 			    attrnamespace, attrname, NULL, args->size,
387 			    args->follow, UIO_USERSPACE);
388 		else
389 			error = kern_extattr_get_fd(td, args->fd,
390 			    attrnamespace, attrname, NULL, args->size);
391 		if ((args->flags & LINUX_XATTR_CREATE) != 0) {
392 			if (error == 0)
393 				error = EEXIST;
394 			else if (error == ENOATTR)
395 				error = 0;
396 		}
397 		if (error != 0)
398 			goto out;
399 	}
400 	if (args->path != NULL)
401 		error = kern_extattr_set_path(td, args->path, attrnamespace,
402 		    attrname, args->value, args->size, args->follow,
403 		    UIO_USERSPACE);
404 	else
405 		error = kern_extattr_set_fd(td, args->fd, attrnamespace,
406 		    attrname, args->value, args->size);
407 out:
408 	td->td_retval[0] = 0;
409 	return (error_to_xattrerror(attrnamespace, error));
410 }
411 
412 int
413 linux_setxattr(struct thread *td, struct linux_setxattr_args *args)
414 {
415 	struct setxattr_args eargs = {
416 		.fd = -1,
417 		.path = args->path,
418 		.name = args->name,
419 		.value = args->value,
420 		.size = args->size,
421 		.flags = args->flags,
422 		.follow = FOLLOW,
423 	};
424 
425 	return (setxattr(td, &eargs));
426 }
427 
428 int
429 linux_lsetxattr(struct thread *td, struct linux_lsetxattr_args *args)
430 {
431 	struct setxattr_args eargs = {
432 		.fd = -1,
433 		.path = args->path,
434 		.name = args->name,
435 		.value = args->value,
436 		.size = args->size,
437 		.flags = args->flags,
438 		.follow = NOFOLLOW,
439 	};
440 
441 	return (setxattr(td, &eargs));
442 }
443 
444 int
445 linux_fsetxattr(struct thread *td, struct linux_fsetxattr_args *args)
446 {
447 	struct setxattr_args eargs = {
448 		.fd = args->fd,
449 		.path = NULL,
450 		.name = args->name,
451 		.value = args->value,
452 		.size = args->size,
453 		.flags = args->flags,
454 		.follow = 0,
455 	};
456 
457 	return (setxattr(td, &eargs));
458 }
459