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