xref: /netbsd-src/sys/kern/vfs_xattr.c (revision e94a5d02693120d4ad9d909e488894e9fcf0eb76)
1 /*	$NetBSD: vfs_xattr.c,v 1.41 2024/12/07 02:27:38 riastradh Exp $	*/
2 
3 /*-
4  * Copyright (c) 2005, 2008 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * Copyright (c) 1989, 1993
34  *	The Regents of the University of California.  All rights reserved.
35  * (c) UNIX System Laboratories, Inc.
36  * All or some portions of this file are derived from material licensed
37  * to the University of California by American Telephone and Telegraph
38  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
39  * the permission of UNIX System Laboratories, Inc.
40  *
41  * Redistribution and use in source and binary forms, with or without
42  * modification, are permitted provided that the following conditions
43  * are met:
44  * 1. Redistributions of source code must retain the above copyright
45  *    notice, this list of conditions and the following disclaimer.
46  * 2. Redistributions in binary form must reproduce the above copyright
47  *    notice, this list of conditions and the following disclaimer in the
48  *    documentation and/or other materials provided with the distribution.
49  * 3. Neither the name of the University nor the names of its contributors
50  *    may be used to endorse or promote products derived from this software
51  *    without specific prior written permission.
52  *
53  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
54  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
57  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
59  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63  * SUCH DAMAGE.
64  */
65 
66 /*
67  * VFS extended attribute support.
68  */
69 
70 #include <sys/cdefs.h>
71 __KERNEL_RCSID(0, "$NetBSD: vfs_xattr.c,v 1.41 2024/12/07 02:27:38 riastradh Exp $");
72 
73 #include <sys/param.h>
74 #include <sys/types.h>
75 
76 #include <sys/extattr.h>
77 #include <sys/file.h>
78 #include <sys/filedesc.h>
79 #include <sys/kauth.h>
80 #include <sys/kernel.h>
81 #include <sys/ktrace.h>
82 #include <sys/mount.h>
83 #include <sys/namei.h>
84 #include <sys/proc.h>
85 #include <sys/sdt.h>
86 #include <sys/syscallargs.h>
87 #include <sys/sysctl.h>
88 #include <sys/systm.h>
89 #include <sys/uio.h>
90 #include <sys/vnode.h>
91 #include <sys/xattr.h>
92 
93 #include <miscfs/genfs/genfs.h>
94 
95 static void
96 ktr_xattr_name(const char *str)
97 {
98 	ktrkuser("xattr-name", (void *)__UNCONST(str), strlen(str));
99 }
100 
101 static void
102 ktr_xattr_val(const void *data, size_t cnt)
103 {
104 	ktruser("xattr-val", __UNCONST(data), cnt, 0);
105 }
106 
107 /*
108  * Credential check based on process requesting service, and per-attribute
109  * permissions.
110  *
111  * NOTE: Vnode must be locked.
112  */
113 int
114 extattr_check_cred(struct vnode *vp, int attrspace, kauth_cred_t cred,
115     int access)
116 {
117 
118 	if (cred == NOCRED)
119 		return 0;
120 
121 	return kauth_authorize_vnode(cred, kauth_extattr_action(access), vp,
122 	    NULL, genfs_can_extattr(vp, cred, access, attrspace));
123 }
124 
125 /*
126  * Default vfs_extattrctl routine for file systems that do not support
127  * it.
128  */
129 /*ARGSUSED*/
130 int
131 vfs_stdextattrctl(struct mount *mp, int cmt, struct vnode *vp,
132     int attrnamespace, const char *attrname)
133 {
134 
135 	if (vp != NULL)
136 		VOP_UNLOCK(vp);
137 	return SET_ERROR(EOPNOTSUPP);
138 }
139 
140 /*
141  * Push extended attribute configuration information into the file
142  * system.
143  *
144  * NOTE: Not all file systems that support extended attributes will
145  * require the use of this system call.
146  */
147 int
148 sys_extattrctl(struct lwp *l, const struct sys_extattrctl_args *uap,
149     register_t *retval)
150 {
151 	/* {
152 		syscallarg(const char *) path;
153 		syscallarg(int) cmd;
154 		syscallarg(const char *) filename;
155 		syscallarg(int) attrnamespace;
156 		syscallarg(const char *) attrname;
157 	} */
158 	struct vnode *path_vp, *file_vp;
159 	struct pathbuf *file_pb;
160 	struct nameidata file_nd;
161 	char attrname[EXTATTR_MAXNAMELEN];
162 	int error;
163 
164 	if (SCARG(uap, attrname) != NULL) {
165 		error = copyinstr(SCARG(uap, attrname), attrname,
166 		    sizeof(attrname), NULL);
167 		if (error)
168 			return error;
169 	}
170 
171 	error = namei_simple_user(SCARG(uap, path), NSM_FOLLOW_NOEMULROOT,
172 	    &path_vp);
173 	if (error)
174 		return error;
175 
176 	file_vp = NULL;
177 	if (SCARG(uap, filename) != NULL) {
178 		error = pathbuf_copyin(SCARG(uap, filename), &file_pb);
179 		if (error) {
180 			vrele(path_vp);
181 			return error;
182 		}
183 		NDINIT(&file_nd, LOOKUP, FOLLOW | LOCKLEAF, file_pb);
184 		error = namei(&file_nd);
185 		if (error) {
186 			pathbuf_destroy(file_pb);
187 			vrele(path_vp);
188 			return error;
189 		}
190 		file_vp = file_nd.ni_vp;
191 		pathbuf_destroy(file_pb);
192 	}
193 
194 	error = VFS_EXTATTRCTL(path_vp->v_mount, SCARG(uap, cmd), file_vp,
195 	    SCARG(uap, attrnamespace),
196 	    SCARG(uap, attrname) != NULL ? attrname : NULL);
197 
198 	if (file_vp != NULL)
199 		vrele(file_vp);
200 	vrele(path_vp);
201 
202 	return error;
203 }
204 
205 /*****************************************************************************
206  * Internal routines to manipulate file system extended attributes:
207  *	- set
208  *	- get
209  *	- delete
210  *	- list
211  *****************************************************************************/
212 
213 /*
214  * extattr_set_vp:
215  *
216  *	Set a named extended attribute on a file or directory.
217  */
218 static int
219 extattr_set_vp(struct vnode *vp, int attrnamespace, const char *attrname,
220     const void *data, size_t nbytes, struct lwp *l, register_t *retval,
221     int flag)
222 {
223 	struct uio auio;
224 	struct iovec aiov;
225 	ssize_t cnt;
226 	int error;
227 
228 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
229 
230 	if (flag) {
231 		size_t attrlen;
232 
233 		error = VOP_GETEXTATTR(vp, attrnamespace, attrname, NULL,
234 		    &attrlen, l->l_cred);
235 
236 		switch (error) {
237 		case ENODATA:
238 		case ENOATTR:
239 			if (flag & XATTR_REPLACE)
240 				goto done;
241 			break;
242 		case 0:
243 			if (flag & XATTR_CREATE) {
244 				error = SET_ERROR(EEXIST);
245 				goto done;
246 			}
247 			break;
248 		default:
249 			goto done;
250 			break;
251 		}
252 	}
253 
254 	aiov.iov_base = __UNCONST(data);	/* XXXUNCONST kills const */
255 	aiov.iov_len = nbytes;
256 	auio.uio_iov = &aiov;
257 	auio.uio_iovcnt = 1;
258 	auio.uio_offset = 0;
259 	if (nbytes > INT_MAX) {
260 		error = SET_ERROR(EINVAL);
261 		goto done;
262 	}
263 	auio.uio_resid = nbytes;
264 	auio.uio_rw = UIO_WRITE;
265 	KASSERT(l == curlwp);
266 	auio.uio_vmspace = l->l_proc->p_vmspace;
267 	cnt = nbytes;
268 
269 	ktr_xattr_name(attrname);
270 	ktr_xattr_val(data, nbytes);
271 
272 	error = VOP_SETEXTATTR(vp, attrnamespace, attrname, &auio, l->l_cred);
273 	cnt -= auio.uio_resid;
274 	retval[0] = cnt;
275 
276 done:
277 	VOP_UNLOCK(vp);
278 	return error;
279 }
280 
281 /*
282  * extattr_get_vp:
283  *
284  *	Get a named extended attribute on a file or directory.
285  */
286 static int
287 extattr_get_vp(struct vnode *vp, int attrnamespace, const char *attrname,
288     void *data, size_t nbytes, struct lwp *l, register_t *retval)
289 {
290 	struct uio auio, *auiop;
291 	struct iovec aiov;
292 	ssize_t cnt;
293 	size_t size, *sizep;
294 	int error;
295 
296 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
297 
298 	/*
299 	 * Slightly unusual semantics: if the user provides a NULL data
300 	 * pointer, they don't want to receive the data, just the maximum
301 	 * read length.
302 	 */
303 	auiop = NULL;
304 	sizep = NULL;
305 	cnt = 0;
306 	if (data != NULL) {
307 		aiov.iov_base = data;
308 		aiov.iov_len = nbytes;
309 		auio.uio_iov = &aiov;
310 		auio.uio_iovcnt = 1;
311 		auio.uio_offset = 0;
312 		if (nbytes > INT_MAX) {
313 			error = SET_ERROR(EINVAL);
314 			goto done;
315 		}
316 		auio.uio_resid = nbytes;
317 		auio.uio_rw = UIO_READ;
318 		KASSERT(l == curlwp);
319 		auio.uio_vmspace = l->l_proc->p_vmspace;
320 		auiop = &auio;
321 		cnt = nbytes;
322 	} else
323 		sizep = &size;
324 
325 	ktr_xattr_name(attrname);
326 
327 	error = VOP_GETEXTATTR(vp, attrnamespace, attrname, auiop, sizep,
328 	    l->l_cred);
329 
330 	if (auiop != NULL) {
331 		cnt -= auio.uio_resid;
332 		retval[0] = cnt;
333 
334 		ktr_xattr_val(data, cnt);
335 	} else
336 		retval[0] = size;
337 
338 done:
339 	VOP_UNLOCK(vp);
340 	return error;
341 }
342 
343 /*
344  * extattr_delete_vp:
345  *
346  *	Delete a named extended attribute on a file or directory.
347  */
348 static int
349 extattr_delete_vp(struct vnode *vp, int attrnamespace, const char *attrname,
350     struct lwp *l)
351 {
352 	int error;
353 
354 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
355 
356 	ktr_xattr_name(attrname);
357 
358 	error = VOP_DELETEEXTATTR(vp, attrnamespace, attrname, l->l_cred);
359 	if (error == EOPNOTSUPP)
360 		error = VOP_SETEXTATTR(vp, attrnamespace, attrname, NULL,
361 		    l->l_cred);
362 
363 	VOP_UNLOCK(vp);
364 	return error;
365 }
366 
367 /*
368  * extattr_list_vp:
369  *
370  *	Retrieve a list of extended attributes on a file or directory.
371  */
372 static int
373 extattr_list_vp(struct vnode *vp, int attrnamespace, void *data, size_t nbytes,
374     int flag, struct lwp *l, register_t *retval)
375 {
376 	struct uio auio, *auiop;
377 	size_t size, *sizep;
378 	struct iovec aiov;
379 	ssize_t cnt;
380 	int error;
381 
382 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
383 
384 	auiop = NULL;
385 	sizep = NULL;
386 	cnt = 0;
387 	if (data != NULL) {
388 		aiov.iov_base = data;
389 		aiov.iov_len = nbytes;
390 		auio.uio_iov = &aiov;
391 		auio.uio_iovcnt = 1;
392 		auio.uio_offset = 0;
393 		if (nbytes > INT_MAX) {
394 			error = SET_ERROR(EINVAL);
395 			goto done;
396 		}
397 		auio.uio_resid = nbytes;
398 		auio.uio_rw = UIO_READ;
399 		KASSERT(l == curlwp);
400 		auio.uio_vmspace = l->l_proc->p_vmspace;
401 		auiop = &auio;
402 		cnt = nbytes;
403 	} else
404 		sizep = &size;
405 
406 	error = VOP_LISTEXTATTR(vp, attrnamespace, auiop, sizep, flag,
407 	    l->l_cred);
408 
409 	if (auiop != NULL) {
410 		cnt -= auio.uio_resid;
411 		retval[0] = cnt;
412 
413 		ktruser("xattr-list", data, cnt, 0);
414 	} else
415 		retval[0] = size;
416 
417 done:
418 	VOP_UNLOCK(vp);
419 	return error;
420 }
421 
422 /*****************************************************************************
423  * BSD <sys/extattr.h> API for file system extended attributes
424  *****************************************************************************/
425 
426 int
427 sys_extattr_set_fd(struct lwp *l, const struct sys_extattr_set_fd_args *uap,
428     register_t *retval)
429 {
430 	/* {
431 		syscallarg(int) fd;
432 		syscallarg(int) attrnamespace;
433 		syscallarg(const char *) attrname;
434 		syscallarg(const void *) data;
435 		syscallarg(size_t) nbytes;
436 	} */
437 	struct file *fp;
438 	struct vnode *vp;
439 	char attrname[EXTATTR_MAXNAMELEN];
440 	int error;
441 
442 	error = copyinstr(SCARG(uap, attrname), attrname, sizeof(attrname),
443 	    NULL);
444 	if (error)
445 		return error;
446 
447 	error = fd_getvnode(SCARG(uap, fd), &fp);
448 	if (error)
449 		return error;
450 	vp = fp->f_vnode;
451 
452 	error = extattr_set_vp(vp, SCARG(uap, attrnamespace), attrname,
453 	    SCARG(uap, data), SCARG(uap, nbytes), l, retval, 0);
454 
455 	fd_putfile(SCARG(uap, fd));
456 	return error;
457 }
458 
459 int
460 sys_extattr_set_file(struct lwp *l,
461     const struct sys_extattr_set_file_args *uap,
462     register_t *retval)
463 {
464 	/* {
465 		syscallarg(const char *) path;
466 		syscallarg(int) attrnamespace;
467 		syscallarg(const char *) attrname;
468 		syscallarg(const void *) data;
469 		syscallarg(size_t) nbytes;
470 	} */
471 	struct vnode *vp;
472 	char attrname[EXTATTR_MAXNAMELEN];
473 	int error;
474 
475 	error = copyinstr(SCARG(uap, attrname), attrname, sizeof(attrname),
476 	    NULL);
477 	if (error)
478 		return error;
479 
480 	error = namei_simple_user(SCARG(uap, path), NSM_FOLLOW_NOEMULROOT,
481 	    &vp);
482 	if (error)
483 		return error;
484 
485 	error = extattr_set_vp(vp, SCARG(uap, attrnamespace), attrname,
486 	    SCARG(uap, data), SCARG(uap, nbytes), l, retval, 0);
487 
488 	vrele(vp);
489 	return error;
490 }
491 
492 int
493 sys_extattr_set_link(struct lwp *l,
494     const struct sys_extattr_set_link_args *uap,
495     register_t *retval)
496 {
497 	/* {
498 		syscallarg(const char *) path;
499 		syscallarg(int) attrnamespace;
500 		syscallarg(const char *) attrname;
501 		syscallarg(const void *) data;
502 		syscallarg(size_t) nbytes;
503 	} */
504 	struct vnode *vp;
505 	char attrname[EXTATTR_MAXNAMELEN];
506 	int error;
507 
508 	error = copyinstr(SCARG(uap, attrname), attrname, sizeof(attrname),
509 	    NULL);
510 	if (error)
511 		return error;
512 
513 	error = namei_simple_user(SCARG(uap, path), NSM_NOFOLLOW_NOEMULROOT,
514 	    &vp);
515 	if (error)
516 		return error;
517 
518 	error = extattr_set_vp(vp, SCARG(uap, attrnamespace), attrname,
519 	    SCARG(uap, data), SCARG(uap, nbytes), l, retval, 0);
520 
521 	vrele(vp);
522 	return error;
523 }
524 
525 int
526 sys_extattr_get_fd(struct lwp *l,
527     const struct sys_extattr_get_fd_args *uap,
528     register_t *retval)
529 {
530 	/* {
531 		syscallarg(int) fd;
532 		syscallarg(int) attrnamespace;
533 		syscallarg(const char *) attrname;
534 		syscallarg(void *) data;
535 		syscallarg(size_t) nbytes;
536 	} */
537 	struct file *fp;
538 	struct vnode *vp;
539 	char attrname[EXTATTR_MAXNAMELEN];
540 	int error;
541 
542 	error = copyinstr(SCARG(uap, attrname), attrname, sizeof(attrname),
543 	    NULL);
544 	if (error)
545 		return error;
546 
547 	error = fd_getvnode(SCARG(uap, fd), &fp);
548 	if (error)
549 		return error;
550 	vp = fp->f_vnode;
551 
552 	error = extattr_get_vp(vp, SCARG(uap, attrnamespace), attrname,
553 	    SCARG(uap, data), SCARG(uap, nbytes), l, retval);
554 
555 	fd_putfile(SCARG(uap, fd));
556 	return error;
557 }
558 
559 int
560 sys_extattr_get_file(struct lwp *l,
561     const struct sys_extattr_get_file_args *uap,
562     register_t *retval)
563 {
564 	/* {
565 		syscallarg(const char *) path;
566 		syscallarg(int) attrnamespace;
567 		syscallarg(const char *) attrname;
568 		syscallarg(void *) data;
569 		syscallarg(size_t) nbytes;
570 	} */
571 	struct vnode *vp;
572 	char attrname[EXTATTR_MAXNAMELEN];
573 	int error;
574 
575 	error = copyinstr(SCARG(uap, attrname), attrname, sizeof(attrname),
576 	    NULL);
577 	if (error)
578 		return error;
579 
580 	error = namei_simple_user(SCARG(uap, path), NSM_FOLLOW_NOEMULROOT,
581 	    &vp);
582 	if (error)
583 		return error;
584 
585 	error = extattr_get_vp(vp, SCARG(uap, attrnamespace), attrname,
586 	    SCARG(uap, data), SCARG(uap, nbytes), l, retval);
587 
588 	vrele(vp);
589 	return error;
590 }
591 
592 int
593 sys_extattr_get_link(struct lwp *l,
594     const struct sys_extattr_get_link_args *uap,
595     register_t *retval)
596 {
597 	/* {
598 		syscallarg(const char *) path;
599 		syscallarg(int) attrnamespace;
600 		syscallarg(const char *) attrname;
601 		syscallarg(void *) data;
602 		syscallarg(size_t) nbytes;
603 	} */
604 	struct vnode *vp;
605 	char attrname[EXTATTR_MAXNAMELEN];
606 	int error;
607 
608 	error = copyinstr(SCARG(uap, attrname), attrname, sizeof(attrname),
609 	    NULL);
610 	if (error)
611 		return error;
612 
613 	error = namei_simple_user(SCARG(uap, path), NSM_NOFOLLOW_NOEMULROOT,
614 	    &vp);
615 	if (error)
616 		return error;
617 
618 	error = extattr_get_vp(vp, SCARG(uap, attrnamespace), attrname,
619 	    SCARG(uap, data), SCARG(uap, nbytes), l, retval);
620 
621 	vrele(vp);
622 	return error;
623 }
624 
625 int
626 sys_extattr_delete_fd(struct lwp *l,
627     const struct sys_extattr_delete_fd_args *uap,
628     register_t *retval)
629 {
630 	/* {
631 		syscallarg(int) fd;
632 		syscallarg(int) attrnamespace;
633 		syscallarg(const char *) attrname;
634 	} */
635 	struct file *fp;
636 	struct vnode *vp;
637 	char attrname[EXTATTR_MAXNAMELEN];
638 	int error;
639 
640 	error = copyinstr(SCARG(uap, attrname), attrname, sizeof(attrname),
641 	    NULL);
642 	if (error)
643 		return error;
644 
645 	error = fd_getvnode(SCARG(uap, fd), &fp);
646 	if (error)
647 		return error;
648 	vp = fp->f_vnode;
649 
650 	error = extattr_delete_vp(vp, SCARG(uap, attrnamespace), attrname, l);
651 
652 	fd_putfile(SCARG(uap, fd));
653 	return error;
654 }
655 
656 int
657 sys_extattr_delete_file(struct lwp *l,
658     const struct sys_extattr_delete_file_args *uap,
659     register_t *retval)
660 {
661 	/* {
662 		syscallarg(const char *) path;
663 		syscallarg(int) attrnamespace;
664 		syscallarg(const char *) attrname;
665 	} */
666 	struct vnode *vp;
667 	char attrname[EXTATTR_MAXNAMELEN];
668 	int error;
669 
670 	error = copyinstr(SCARG(uap, attrname), attrname, sizeof(attrname),
671 	    NULL);
672 	if (error)
673 		return error;
674 
675 	error = namei_simple_user(SCARG(uap, path), NSM_FOLLOW_NOEMULROOT,
676 	    &vp);
677 	if (error)
678 		return error;
679 
680 	error = extattr_delete_vp(vp, SCARG(uap, attrnamespace), attrname, l);
681 
682 	vrele(vp);
683 	return error;
684 }
685 
686 int
687 sys_extattr_delete_link(struct lwp *l,
688     const struct sys_extattr_delete_link_args *uap,
689     register_t *retval)
690 {
691 	/* {
692 		syscallarg(const char *) path;
693 		syscallarg(int) attrnamespace;
694 		syscallarg(const char *) attrname;
695 	} */
696 	struct vnode *vp;
697 	char attrname[EXTATTR_MAXNAMELEN];
698 	int error;
699 
700 	error = copyinstr(SCARG(uap, attrname), attrname, sizeof(attrname),
701 	    NULL);
702 	if (error)
703 		return error;
704 
705 	error = namei_simple_user(SCARG(uap, path), NSM_NOFOLLOW_NOEMULROOT,
706 	    &vp);
707 	if (error)
708 		return error;
709 
710 	error = extattr_delete_vp(vp, SCARG(uap, attrnamespace), attrname, l);
711 
712 	vrele(vp);
713 	return error;
714 }
715 
716 int
717 sys_extattr_list_fd(struct lwp *l,
718     const struct sys_extattr_list_fd_args *uap,
719     register_t *retval)
720 {
721 	/* {
722 		syscallarg(int) fd;
723 		syscallarg(int) attrnamespace;
724 		syscallarg(void *) data;
725 		syscallarg(size_t) nbytes;
726 	} */
727 	struct file *fp;
728 	struct vnode *vp;
729 	int error;
730 
731 	error = fd_getvnode(SCARG(uap, fd), &fp);
732 	if (error)
733 		return error;
734 	vp = fp->f_vnode;
735 
736 	error = extattr_list_vp(vp, SCARG(uap, attrnamespace),
737 	    SCARG(uap, data), SCARG(uap, nbytes),
738 	    EXTATTR_LIST_LENPREFIX, l, retval);
739 
740 	fd_putfile(SCARG(uap, fd));
741 	return error;
742 }
743 
744 int
745 sys_extattr_list_file(struct lwp *l,
746     const struct sys_extattr_list_file_args *uap,
747     register_t *retval)
748 {
749 	/* {
750 		syscallarg(const char *) path;
751 		syscallarg(int) attrnamespace;
752 		syscallarg(void *) data;
753 		syscallarg(size_t) nbytes;
754 	} */
755 	struct vnode *vp;
756 	int error;
757 
758 	error = namei_simple_user(SCARG(uap, path), NSM_FOLLOW_NOEMULROOT,
759 	    &vp);
760 	if (error)
761 		return error;
762 
763 	error = extattr_list_vp(vp, SCARG(uap, attrnamespace),
764 	    SCARG(uap, data), SCARG(uap, nbytes),
765 	    EXTATTR_LIST_LENPREFIX, l, retval);
766 
767 	vrele(vp);
768 	return error;
769 }
770 
771 int
772 sys_extattr_list_link(struct lwp *l,
773     const struct sys_extattr_list_link_args *uap,
774     register_t *retval)
775 {
776 	/* {
777 		syscallarg(const char *) path;
778 		syscallarg(int) attrnamespace;
779 		syscallarg(void *) data;
780 		syscallarg(size_t) nbytes;
781 	} */
782 	struct vnode *vp;
783 	int error;
784 
785 	error = namei_simple_user(SCARG(uap, path), NSM_NOFOLLOW_NOEMULROOT,
786 	    &vp);
787 	if (error)
788 		return error;
789 
790 	error = extattr_list_vp(vp, SCARG(uap, attrnamespace),
791 	    SCARG(uap, data), SCARG(uap, nbytes),
792 	    EXTATTR_LIST_LENPREFIX, l, retval);
793 
794 	vrele(vp);
795 	return error;
796 }
797 
798 /*****************************************************************************
799  * Linux-compatible <sys/xattr.h> API for file system extended attributes
800  *****************************************************************************/
801 
802 #define MATCH_NS(ns, key) (strncmp(ns, key, sizeof(ns) - 1) == 0)
803 static int
804 xattr_native(const char *key)
805 {
806 
807 	if (MATCH_NS("system.", key))
808 		return EXTATTR_NAMESPACE_SYSTEM;
809 	else if (MATCH_NS("user.", key))
810 		return EXTATTR_NAMESPACE_USER;
811 	else if (MATCH_NS("security.", key))
812 		return EXTATTR_NAMESPACE_SYSTEM;
813 	else if (MATCH_NS("trusted.", key))
814 		return EXTATTR_NAMESPACE_SYSTEM;
815 	else
816 		return EXTATTR_NAMESPACE_USER;
817 }
818 #undef MATCH_NS
819 
820 #define XATTR_ERRNO(e) ((e) == EOPNOTSUPP ? SET_ERROR(ENOTSUP) : (e))
821 
822 int
823 sys_setxattr(struct lwp *l,
824     const struct sys_setxattr_args *uap,
825     register_t *retval)
826 {
827 	/* {
828 		syscallarg(const char *) path;
829 		syscallarg(const char *) name;
830 		syscallarg(void *) value;
831 		syscallarg(size_t) size;
832 		syscallarg(int) flags;
833 	} */
834 	struct vnode *vp;
835 	char attrname[XATTR_NAME_MAX];
836 	int attrnamespace;
837 	register_t attrlen;
838 	int error;
839 
840 	error = copyinstr(SCARG(uap, name), attrname, sizeof(attrname),
841 	    NULL);
842 	if (error)
843 		goto out;
844 
845 	error = namei_simple_user(SCARG(uap, path),
846 	    NSM_FOLLOW_NOEMULROOT, &vp);
847 	if (error)
848 		goto out;
849 
850 	attrnamespace = xattr_native(attrname);
851 
852 	error = extattr_set_vp(vp, attrnamespace,
853 	    attrname, SCARG(uap, value), SCARG(uap, size), l,
854 	    &attrlen, SCARG(uap, flags));
855 
856 	vrele(vp);
857 out:
858 	*retval = (error == 0 ? 0 : -1);
859 	return XATTR_ERRNO(error);
860 }
861 
862 int
863 sys_lsetxattr(struct lwp *l,
864     const struct sys_lsetxattr_args *uap,
865     register_t *retval)
866 {
867 	/* {
868 		syscallarg(const char *) path;
869 		syscallarg(const char *) name;
870 		syscallarg(void *) value;
871 		syscallarg(size_t) size;
872 		syscallarg(int) flags;
873 	} */
874 	struct vnode *vp;
875 	char attrname[XATTR_NAME_MAX];
876 	int attrnamespace;
877 	register_t attrlen;
878 	int error;
879 
880 	error = copyinstr(SCARG(uap, name), attrname, sizeof(attrname),
881 	    NULL);
882 	if (error)
883 		goto out;
884 
885 	error = namei_simple_user(SCARG(uap, path), NSM_NOFOLLOW_NOEMULROOT,
886 	    &vp);
887 	if (error)
888 		goto out;
889 
890 	attrnamespace = xattr_native(attrname);
891 
892 	error = extattr_set_vp(vp, attrnamespace,
893 	    attrname, SCARG(uap, value), SCARG(uap, size), l,
894 	    &attrlen, SCARG(uap, flags));
895 
896 	vrele(vp);
897 out:
898 	*retval = (error == 0 ? 0 : -1);
899 	return XATTR_ERRNO(error);
900 }
901 
902 int
903 sys_fsetxattr(struct lwp *l,
904     const struct sys_fsetxattr_args *uap,
905     register_t *retval)
906 {
907 	/* {
908 		syscallarg(int) fd;
909 		syscallarg(const char *) name;
910 		syscallarg(void *) value;
911 		syscallarg(size_t) size;
912 		syscallarg(int) flags;
913 	} */
914 	struct file *fp;
915 	struct vnode *vp;
916 	char attrname[XATTR_NAME_MAX];
917 	int attrnamespace;
918 	register_t attrlen;
919 	int error;
920 
921 	error = copyinstr(SCARG(uap, name), attrname, sizeof(attrname),
922 	    NULL);
923 	if (error)
924 		goto out;
925 
926 	error = fd_getvnode(SCARG(uap, fd), &fp);
927 	if (error)
928 		goto out;
929 	vp = fp->f_vnode;
930 
931 	attrnamespace = xattr_native(attrname);
932 
933 	error = extattr_set_vp(vp, attrnamespace,
934 	    attrname, SCARG(uap, value), SCARG(uap, size), l,
935 	    &attrlen, SCARG(uap, flags));
936 
937 	fd_putfile(SCARG(uap, fd));
938 out:
939 	*retval = (error == 0 ? 0 : -1);
940 	return XATTR_ERRNO(error);
941 }
942 
943 int
944 sys_getxattr(struct lwp *l,
945     const struct sys_getxattr_args *uap,
946     register_t *retval)
947 {
948 	/* {
949 		syscallarg(const char *) path;
950 		syscallarg(const char *) name;
951 		syscallarg(void *) value;
952 		syscallarg(size_t) size;
953 	} */
954 	struct vnode *vp;
955 	char attrname[XATTR_NAME_MAX];
956 	int attrnamespace;
957 	int error;
958 
959 	error = copyinstr(SCARG(uap, name), attrname, sizeof(attrname),
960 	    NULL);
961 	if (error)
962 		return error;
963 
964 	error = namei_simple_user(SCARG(uap, path), NSM_FOLLOW_NOEMULROOT,
965 	    &vp);
966 	if (error)
967 		return error;
968 
969 	attrnamespace = xattr_native(attrname);
970 
971 	error = extattr_get_vp(vp, attrnamespace,
972 	    attrname, SCARG(uap, value), SCARG(uap, size), l, retval);
973 
974 	vrele(vp);
975 	return XATTR_ERRNO(error);
976 }
977 
978 int
979 sys_lgetxattr(struct lwp *l,
980     const struct sys_lgetxattr_args *uap,
981     register_t *retval)
982 {
983 	/* {
984 		syscallarg(const char *) path;
985 		syscallarg(const char *) name;
986 		syscallarg(void *) value;
987 		syscallarg(size_t) size;
988 	} */
989 	struct vnode *vp;
990 	char attrname[XATTR_NAME_MAX];
991 	int attrnamespace;
992 	int error;
993 
994 	error = copyinstr(SCARG(uap, name), attrname, sizeof(attrname),
995 	    NULL);
996 	if (error)
997 		return error;
998 
999 	error = namei_simple_user(SCARG(uap, path), NSM_NOFOLLOW_NOEMULROOT,
1000 	    &vp);
1001 	if (error)
1002 		return error;
1003 
1004 	attrnamespace = xattr_native(attrname);
1005 
1006 	error = extattr_get_vp(vp, attrnamespace,
1007 	    attrname, SCARG(uap, value), SCARG(uap, size), l, retval);
1008 
1009 	vrele(vp);
1010 	return XATTR_ERRNO(error);
1011 }
1012 
1013 int
1014 sys_fgetxattr(struct lwp *l,
1015     const struct sys_fgetxattr_args *uap,
1016     register_t *retval)
1017 {
1018 	/* {
1019 		syscallarg(int) fd;
1020 		syscallarg(const char *) name;
1021 		syscallarg(void *) value;
1022 		syscallarg(size_t) size;
1023 	} */
1024 	struct file *fp;
1025 	struct vnode *vp;
1026 	char attrname[XATTR_NAME_MAX];
1027 	int attrnamespace;
1028 	int error;
1029 
1030 	error = copyinstr(SCARG(uap, name), attrname, sizeof(attrname),
1031 	    NULL);
1032 	if (error)
1033 		return error;
1034 
1035 	error = fd_getvnode(SCARG(uap, fd), &fp);
1036 	if (error)
1037 		return error;
1038 	vp = fp->f_vnode;
1039 
1040 	attrnamespace = xattr_native(attrname);
1041 
1042 	error = extattr_get_vp(vp, attrnamespace,
1043 	    attrname, SCARG(uap, value), SCARG(uap, size), l, retval);
1044 
1045 	fd_putfile(SCARG(uap, fd));
1046 	return XATTR_ERRNO(error);
1047 }
1048 
1049 int
1050 sys_listxattr(struct lwp *l,
1051     const struct sys_listxattr_args *uap,
1052     register_t *retval)
1053 {
1054 	/* {
1055 		syscallarg(const char *) path;
1056 		syscallarg(char *) list;
1057 		syscallarg(size_t) size;
1058 	} */
1059 	struct vnode *vp;
1060 	char *list;
1061 	size_t size;
1062 	register_t listsize_usr, listsize_sys;
1063 	int error;
1064 
1065 	error = namei_simple_user(SCARG(uap, path), NSM_FOLLOW_NOEMULROOT,
1066 	    &vp);
1067 	if (error)
1068 		return error;
1069 
1070 	list = SCARG(uap, list);
1071 	size = SCARG(uap, size);
1072 
1073 	error = extattr_list_vp(vp, EXTATTR_NAMESPACE_USER,
1074 	    list, size, 0, l, &listsize_usr);
1075 	if (error)
1076 		goto out;
1077 
1078 	if (list)
1079 		list += listsize_usr;
1080 	if (size)
1081 		size -= listsize_usr;
1082 
1083 	error = extattr_list_vp(vp, EXTATTR_NAMESPACE_SYSTEM,
1084 	    list, size, 0, l, &listsize_sys);
1085 	switch (error) {
1086 	case EPERM:
1087 		error = 0; /* Ignore and just skip system EA */
1088 		listsize_sys = 0;
1089 		break;
1090 	case 0:
1091 		break;
1092 	default:
1093 		goto out;
1094 		break;
1095 	}
1096 
1097 	*retval = listsize_usr + listsize_sys;
1098 out:
1099 	vrele(vp);
1100 	return XATTR_ERRNO(error);
1101 }
1102 
1103 int
1104 sys_llistxattr(struct lwp *l,
1105     const struct sys_llistxattr_args *uap,
1106     register_t *retval)
1107 {
1108 	/* {
1109 		syscallarg(const char *) path;
1110 		syscallarg(char *) list;
1111 		syscallarg(size_t) size;
1112 	} */
1113 	struct vnode *vp;
1114 	char *list;
1115 	size_t size;
1116 	register_t listsize_usr, listsize_sys;
1117 	int error;
1118 
1119 	error = namei_simple_user(SCARG(uap, path), NSM_NOFOLLOW_NOEMULROOT,
1120 	    &vp);
1121 	if (error)
1122 		return error;
1123 
1124 	list = SCARG(uap, list);
1125 	size = SCARG(uap, size);
1126 
1127 	error = extattr_list_vp(vp, EXTATTR_NAMESPACE_USER,
1128 	    list, size, 0, l, &listsize_usr);
1129 	if (error)
1130 		goto out;
1131 	if (list)
1132 		list += listsize_usr;
1133 	if (size)
1134 		size -= listsize_usr;
1135 
1136 	error = extattr_list_vp(vp, EXTATTR_NAMESPACE_SYSTEM,
1137 	    list, size, 0, l, &listsize_sys);
1138 	switch (error) {
1139 	case EPERM:
1140 		error = 0; /* Ignore and just skip system EA */
1141 		listsize_sys = 0;
1142 		break;
1143 	case 0:
1144 		break;
1145 	default:
1146 		goto out;
1147 		break;
1148 	}
1149 
1150 	*retval = listsize_usr + listsize_sys;
1151 out:
1152 	vrele(vp);
1153 	return XATTR_ERRNO(error);
1154 }
1155 
1156 int
1157 sys_flistxattr(struct lwp *l,
1158     const struct sys_flistxattr_args *uap,
1159     register_t *retval)
1160 {
1161 	/* {
1162 		syscallarg(int) fd;
1163 		syscallarg(char *) list;
1164 		syscallarg(size_t) size;
1165 	} */
1166 	struct file *fp;
1167 	struct vnode *vp;
1168 	char *list;
1169 	size_t size;
1170 	register_t listsize_usr, listsize_sys;
1171 	int error;
1172 
1173 	error = fd_getvnode(SCARG(uap, fd), &fp);
1174 	if (error)
1175 		return error;
1176 	vp = fp->f_vnode;
1177 
1178 	list = SCARG(uap, list);
1179 	size = SCARG(uap, size);
1180 
1181 	error = extattr_list_vp(vp, EXTATTR_NAMESPACE_USER,
1182 	    list, size, 0, l, &listsize_usr);
1183 	if (error)
1184 		goto out;
1185 
1186 	if (list)
1187 		list += listsize_usr;
1188 	if (size)
1189 		size -= listsize_usr;
1190 
1191 	error = extattr_list_vp(vp, EXTATTR_NAMESPACE_SYSTEM,
1192 	    list, size, 0, l, &listsize_sys);
1193 	switch (error) {
1194 	case EPERM:
1195 		error = 0; /* Ignore and just skip system EA */
1196 		listsize_sys = 0;
1197 		break;
1198 	case 0:
1199 		break;
1200 	default:
1201 		goto out;
1202 		break;
1203 	}
1204 
1205 	*retval = listsize_usr + listsize_sys;
1206 out:
1207 	fd_putfile(SCARG(uap, fd));
1208 	return XATTR_ERRNO(error);
1209 }
1210 
1211 int
1212 sys_removexattr(struct lwp *l,
1213     const struct sys_removexattr_args *uap,
1214     register_t *retval)
1215 {
1216 	/* {
1217 		syscallarg(const char *) path;
1218 		syscallarg(const char *) name;
1219 	} */
1220 	struct vnode *vp;
1221 	char attrname[XATTR_NAME_MAX];
1222 	int attrnamespace;
1223 	int error;
1224 
1225 	error = copyinstr(SCARG(uap, name), attrname, sizeof(attrname),
1226 	    NULL);
1227 	if (error)
1228 		return error;
1229 
1230 	error = namei_simple_user(SCARG(uap, path), NSM_FOLLOW_NOEMULROOT,
1231 	    &vp);
1232 	if (error)
1233 		return error;
1234 
1235 	attrnamespace = xattr_native(attrname);
1236 
1237 	error = extattr_delete_vp(vp, attrnamespace, attrname, l);
1238 
1239 	vrele(vp);
1240 	return XATTR_ERRNO(error);
1241 }
1242 
1243 int
1244 sys_lremovexattr(struct lwp *l,
1245     const struct sys_lremovexattr_args *uap,
1246     register_t *retval)
1247 {
1248 	/* {
1249 		syscallarg(const char *) path;
1250 		syscallarg(const char *) name;
1251 	} */
1252 	struct vnode *vp;
1253 	char attrname[XATTR_NAME_MAX];
1254 	int attrnamespace;
1255 	int error;
1256 
1257 	error = copyinstr(SCARG(uap, name), attrname, sizeof(attrname),
1258 	    NULL);
1259 	if (error)
1260 		return error;
1261 
1262 	error = namei_simple_user(SCARG(uap, path), NSM_NOFOLLOW_NOEMULROOT,
1263 	    &vp);
1264 	if (error)
1265 		return error;
1266 
1267 	attrnamespace = xattr_native(attrname);
1268 
1269 	error = extattr_delete_vp(vp, attrnamespace, attrname, l);
1270 
1271 	vrele(vp);
1272 	return XATTR_ERRNO(error);
1273 }
1274 
1275 int
1276 sys_fremovexattr(struct lwp *l,
1277     const struct sys_fremovexattr_args *uap,
1278     register_t *retval)
1279 {
1280 	/* {
1281 		syscallarg(int) fd;
1282 		syscallarg(const char *) name;
1283 	} */
1284 	struct file *fp;
1285 	struct vnode *vp;
1286 	char attrname[XATTR_NAME_MAX];
1287 	int attrnamespace;
1288 	int error;
1289 
1290 	error = copyinstr(SCARG(uap, name), attrname, sizeof(attrname),
1291 	    NULL);
1292 	if (error)
1293 		return error;
1294 
1295 	error = fd_getvnode(SCARG(uap, fd), &fp);
1296 	if (error)
1297 		return error;
1298 	vp = fp->f_vnode;
1299 
1300 	attrnamespace = xattr_native(attrname);
1301 
1302 	error = extattr_delete_vp(vp, attrnamespace, attrname, l);
1303 
1304 	fd_putfile(SCARG(uap, fd));
1305 	return XATTR_ERRNO(error);
1306 }
1307