1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 1999-2001 Robert N. M. Watson
5 * All rights reserved.
6 *
7 * This software was developed by Robert Watson for the TrustedBSD Project.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#include <sys/param.h>
32#include <sys/systm.h>
33#include <sys/capsicum.h>
34#include <sys/lock.h>
35#include <sys/mount.h>
36#include <sys/mutex.h>
37#include <sys/sysproto.h>
38#include <sys/fcntl.h>
39#include <sys/namei.h>
40#include <sys/filedesc.h>
41#include <sys/limits.h>
42#include <sys/vnode.h>
43#include <sys/proc.h>
44#include <sys/extattr.h>
45#include <sys/syscallsubr.h>
46
47#include <security/audit/audit.h>
48#include <security/mac/mac_framework.h>
49
50static int	user_extattr_set_path(struct thread *td, const char *path,
51		    int attrnamespace, const char *attrname, void *data,
52		    size_t nbytes, int follow);
53static int	user_extattr_get_path(struct thread *td, const char *path,
54		    int attrnamespace, const char *attrname, void *data,
55		    size_t nbytes, int follow);
56static int	user_extattr_delete_path(struct thread *td, const char *path,
57		    int attrnamespace, const char *attrname, int follow);
58static int	user_extattr_list_path(struct thread *td, const char *path,
59		    int attrnamespace, void *data, size_t nbytes, int follow);
60
61/*
62 * Syscall to push extended attribute configuration information into the VFS.
63 * Accepts a path, which it converts to a mountpoint, as well as a command
64 * (int cmd), and attribute name and misc data.
65 *
66 * Currently this is used only by UFS1 extended attributes.
67 */
68#ifndef _SYS_SYSPROTO_H_
69struct extattrctl_args {
70	const char *path;
71	int cmd;
72	const char *filename;
73	int attrnamespace;
74	const char *attrname;
75};
76#endif
77int
78sys_extattrctl(struct thread *td, struct extattrctl_args *uap)
79{
80	struct vnode *filename_vp;
81	struct nameidata nd;
82	struct mount *mp, *mp_writable;
83	char attrname[EXTATTR_MAXNAMELEN + 1];
84	int error;
85
86	AUDIT_ARG_CMD(uap->cmd);
87	AUDIT_ARG_VALUE(uap->attrnamespace);
88	/*
89	 * uap->attrname is not always defined.  We check again later when we
90	 * invoke the VFS call so as to pass in NULL there if needed.
91	 */
92	if (uap->attrname != NULL) {
93		error = copyinstr(uap->attrname, attrname, sizeof(attrname),
94		    NULL);
95		if (error)
96			return (error);
97	}
98	AUDIT_ARG_TEXT(attrname);
99
100	mp = NULL;
101	filename_vp = NULL;
102	if (uap->filename != NULL) {
103		NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE2, UIO_USERSPACE,
104		    uap->filename);
105		error = namei(&nd);
106		if (error)
107			return (error);
108		filename_vp = nd.ni_vp;
109		NDFREE_PNBUF(&nd);
110	}
111
112	/* uap->path is always defined. */
113	NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNODE1, UIO_USERSPACE,
114	    uap->path);
115	error = namei(&nd);
116	if (error)
117		goto out;
118	mp = nd.ni_vp->v_mount;
119	error = vfs_busy(mp, 0);
120	if (error) {
121		vput(nd.ni_vp);
122		NDFREE_PNBUF(&nd);
123		mp = NULL;
124		goto out;
125	}
126	VOP_UNLOCK(nd.ni_vp);
127	error = vn_start_write(nd.ni_vp, &mp_writable, V_WAIT | V_PCATCH);
128	vrele(nd.ni_vp);
129	NDFREE_PNBUF(&nd);
130	if (error)
131		goto out;
132	if (filename_vp != NULL) {
133		/*
134		 * uap->filename is not always defined.  If it is,
135		 * grab a vnode lock, which VFS_EXTATTRCTL() will
136		 * later release.
137		 */
138		error = vn_lock(filename_vp, LK_EXCLUSIVE);
139		if (error) {
140			vn_finished_write(mp_writable);
141			goto out;
142		}
143	}
144
145	error = VFS_EXTATTRCTL(mp, uap->cmd, filename_vp, uap->attrnamespace,
146	    uap->attrname != NULL ? attrname : NULL);
147
148	vn_finished_write(mp_writable);
149out:
150	if (mp != NULL)
151		vfs_unbusy(mp);
152
153	/*
154	 * VFS_EXTATTRCTL will have unlocked, but not de-ref'd, filename_vp,
155	 * so vrele it if it is defined.
156	 */
157	if (filename_vp != NULL)
158		vrele(filename_vp);
159	return (error);
160}
161
162/*-
163 * Set a named extended attribute on a file or directory
164 *
165 * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace",
166 *            kernelspace string pointer "attrname", userspace buffer
167 *            pointer "data", buffer length "nbytes", thread "td".
168 * Returns: 0 on success, an error number otherwise
169 * Locks: none
170 * References: vp must be a valid reference for the duration of the call
171 */
172static int
173extattr_set_vp(struct vnode *vp, int attrnamespace, const char *attrname,
174    void *data, size_t nbytes, struct thread *td)
175{
176	struct mount *mp;
177	struct uio auio;
178	struct iovec aiov;
179	ssize_t cnt;
180	int error;
181
182	if (nbytes > IOSIZE_MAX)
183		return (EINVAL);
184
185	error = vn_start_write(vp, &mp, V_WAIT | V_PCATCH);
186	if (error)
187		return (error);
188	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
189
190	aiov.iov_base = data;
191	aiov.iov_len = nbytes;
192	auio.uio_iov = &aiov;
193	auio.uio_iovcnt = 1;
194	auio.uio_offset = 0;
195	auio.uio_resid = nbytes;
196	auio.uio_rw = UIO_WRITE;
197	auio.uio_segflg = UIO_USERSPACE;
198	auio.uio_td = td;
199	cnt = nbytes;
200
201#ifdef MAC
202	error = mac_vnode_check_setextattr(td->td_ucred, vp, attrnamespace,
203	    attrname);
204	if (error)
205		goto done;
206#endif
207
208	error = VOP_SETEXTATTR(vp, attrnamespace, attrname, &auio,
209	    td->td_ucred, td);
210	cnt -= auio.uio_resid;
211	td->td_retval[0] = cnt;
212
213#ifdef MAC
214done:
215#endif
216	VOP_UNLOCK(vp);
217	vn_finished_write(mp);
218	return (error);
219}
220
221#ifndef _SYS_SYSPROTO_H_
222struct extattr_set_fd_args {
223	int fd;
224	int attrnamespace;
225	const char *attrname;
226	void *data;
227	size_t nbytes;
228};
229#endif
230int
231sys_extattr_set_fd(struct thread *td, struct extattr_set_fd_args *uap)
232{
233	char attrname[EXTATTR_MAXNAMELEN + 1];
234	int error;
235
236	error = copyinstr(uap->attrname, attrname, sizeof(attrname), NULL);
237	if (error)
238		return (error);
239	return (kern_extattr_set_fd(td, uap->fd, uap->attrnamespace,
240	    attrname, uap->data, uap->nbytes));
241}
242
243int
244kern_extattr_set_fd(struct thread *td, int fd, int attrnamespace,
245    const char *attrname, void *data, size_t nbytes)
246{
247	struct file *fp;
248	cap_rights_t rights;
249	int error;
250
251	AUDIT_ARG_FD(fd);
252	AUDIT_ARG_VALUE(attrnamespace);
253	AUDIT_ARG_TEXT(attrname);
254
255	error = getvnode_path(td, fd,
256	    cap_rights_init_one(&rights, CAP_EXTATTR_SET), &fp);
257	if (error)
258		return (error);
259
260	error = extattr_set_vp(fp->f_vnode, attrnamespace,
261	    attrname, data, nbytes, td);
262	fdrop(fp, td);
263
264	return (error);
265}
266
267#ifndef _SYS_SYSPROTO_H_
268struct extattr_set_file_args {
269	const char *path;
270	int attrnamespace;
271	const char *attrname;
272	void *data;
273	size_t nbytes;
274};
275#endif
276int
277sys_extattr_set_file(struct thread *td, struct extattr_set_file_args *uap)
278{
279
280	return (user_extattr_set_path(td, uap->path, uap->attrnamespace,
281	    uap->attrname, uap->data, uap->nbytes, FOLLOW));
282}
283
284#ifndef _SYS_SYSPROTO_H_
285struct extattr_set_link_args {
286	const char *path;
287	int attrnamespace;
288	const char *attrname;
289	void *data;
290	size_t nbytes;
291};
292#endif
293int
294sys_extattr_set_link(struct thread *td, struct extattr_set_link_args *uap)
295{
296
297	return (user_extattr_set_path(td, uap->path, uap->attrnamespace,
298	    uap->attrname, uap->data, uap->nbytes, NOFOLLOW));
299}
300
301static int
302user_extattr_set_path(struct thread *td, const char *path, int attrnamespace,
303    const char *uattrname, void *data, size_t nbytes, int follow)
304{
305	char attrname[EXTATTR_MAXNAMELEN + 1];
306	int error;
307
308	error = copyinstr(uattrname, attrname, sizeof(attrname), NULL);
309	if (error)
310		return (error);
311	return (kern_extattr_set_path(td, path, attrnamespace,
312	    attrname, data, nbytes, follow, UIO_USERSPACE));
313}
314
315int
316kern_extattr_set_path(struct thread *td, const char *path, int attrnamespace,
317    const char *attrname, void *data, size_t nbytes, int follow,
318    enum uio_seg pathseg)
319{
320	struct nameidata nd;
321	int error;
322
323	AUDIT_ARG_VALUE(attrnamespace);
324	AUDIT_ARG_TEXT(attrname);
325
326	NDINIT(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path);
327	error = namei(&nd);
328	if (error)
329		return (error);
330	NDFREE_PNBUF(&nd);
331
332	error = extattr_set_vp(nd.ni_vp, attrnamespace, attrname, data,
333	    nbytes, td);
334
335	vrele(nd.ni_vp);
336	return (error);
337}
338
339/*-
340 * Get a named extended attribute on a file or directory
341 *
342 * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace",
343 *            kernelspace string pointer "attrname", userspace buffer
344 *            pointer "data", buffer length "nbytes", thread "td".
345 * Returns: 0 on success, an error number otherwise
346 * Locks: none
347 * References: vp must be a valid reference for the duration of the call
348 */
349static int
350extattr_get_vp(struct vnode *vp, int attrnamespace, const char *attrname,
351    void *data, size_t nbytes, struct thread *td)
352{
353	struct uio auio, *auiop;
354	struct iovec aiov;
355	ssize_t cnt;
356	size_t size, *sizep;
357	int error;
358
359	if (nbytes > IOSIZE_MAX)
360		return (EINVAL);
361
362	vn_lock(vp, LK_SHARED | LK_RETRY);
363
364	/*
365	 * Slightly unusual semantics: if the user provides a NULL data
366	 * pointer, they don't want to receive the data, just the maximum
367	 * read length.
368	 */
369	auiop = NULL;
370	sizep = NULL;
371	cnt = 0;
372	if (data != NULL) {
373		aiov.iov_base = data;
374		aiov.iov_len = nbytes;
375		auio.uio_iov = &aiov;
376		auio.uio_iovcnt = 1;
377		auio.uio_offset = 0;
378		auio.uio_resid = nbytes;
379		auio.uio_rw = UIO_READ;
380		auio.uio_segflg = UIO_USERSPACE;
381		auio.uio_td = td;
382		auiop = &auio;
383		cnt = nbytes;
384	} else
385		sizep = &size;
386
387#ifdef MAC
388	error = mac_vnode_check_getextattr(td->td_ucred, vp, attrnamespace,
389	    attrname);
390	if (error)
391		goto done;
392#endif
393
394	error = VOP_GETEXTATTR(vp, attrnamespace, attrname, auiop, sizep,
395	    td->td_ucred, td);
396
397	if (auiop != NULL) {
398		cnt -= auio.uio_resid;
399		td->td_retval[0] = cnt;
400	} else
401		td->td_retval[0] = size;
402#ifdef MAC
403done:
404#endif
405	VOP_UNLOCK(vp);
406	return (error);
407}
408
409#ifndef _SYS_SYSPROTO_H_
410struct extattr_get_fd_args {
411	int fd;
412	int attrnamespace;
413	const char *attrname;
414	void *data;
415	size_t nbytes;
416};
417#endif
418int
419sys_extattr_get_fd(struct thread *td, struct extattr_get_fd_args *uap)
420{
421	char attrname[EXTATTR_MAXNAMELEN + 1];
422	int error;
423
424	error = copyinstr(uap->attrname, attrname, sizeof(attrname), NULL);
425	if (error)
426		return (error);
427	return (kern_extattr_get_fd(td, uap->fd, uap->attrnamespace,
428	    attrname, uap->data, uap->nbytes));
429}
430
431int
432kern_extattr_get_fd(struct thread *td, int fd, int attrnamespace,
433    const char *attrname, void *data, size_t nbytes)
434{
435	struct file *fp;
436	cap_rights_t rights;
437	int error;
438
439	AUDIT_ARG_FD(fd);
440	AUDIT_ARG_VALUE(attrnamespace);
441	AUDIT_ARG_TEXT(attrname);
442
443	error = getvnode_path(td, fd,
444	    cap_rights_init_one(&rights, CAP_EXTATTR_GET), &fp);
445	if (error)
446		return (error);
447
448	error = extattr_get_vp(fp->f_vnode, attrnamespace,
449	    attrname, data, nbytes, td);
450
451	fdrop(fp, td);
452	return (error);
453}
454
455#ifndef _SYS_SYSPROTO_H_
456struct extattr_get_file_args {
457	const char *path;
458	int attrnamespace;
459	const char *attrname;
460	void *data;
461	size_t nbytes;
462};
463#endif
464int
465sys_extattr_get_file(struct thread *td, struct extattr_get_file_args *uap)
466{
467	return (user_extattr_get_path(td, uap->path, uap->attrnamespace,
468	    uap->attrname, uap->data, uap->nbytes, FOLLOW));
469}
470
471#ifndef _SYS_SYSPROTO_H_
472struct extattr_get_link_args {
473	const char *path;
474	int attrnamespace;
475	const char *attrname;
476	void *data;
477	size_t nbytes;
478};
479#endif
480int
481sys_extattr_get_link(struct thread *td, struct extattr_get_link_args *uap)
482{
483	return (user_extattr_get_path(td, uap->path, uap->attrnamespace,
484	    uap->attrname, uap->data, uap->nbytes, NOFOLLOW));
485}
486
487static int
488user_extattr_get_path(struct thread *td, const char *path, int attrnamespace,
489    const char *uattrname, void *data, size_t nbytes, int follow)
490{
491	char attrname[EXTATTR_MAXNAMELEN + 1];
492	int error;
493
494	error = copyinstr(uattrname, attrname, sizeof(attrname), NULL);
495	if (error)
496		return (error);
497	return (kern_extattr_get_path(td, path, attrnamespace,
498	    attrname, data, nbytes, follow, UIO_USERSPACE));
499}
500
501int
502kern_extattr_get_path(struct thread *td, const char *path, int attrnamespace,
503    const char *attrname, void *data, size_t nbytes, int follow,
504    enum uio_seg pathseg)
505{
506	struct nameidata nd;
507	int error;
508
509	AUDIT_ARG_VALUE(attrnamespace);
510	AUDIT_ARG_TEXT(attrname);
511
512	NDINIT(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path);
513	error = namei(&nd);
514	if (error)
515		return (error);
516	NDFREE_PNBUF(&nd);
517
518	error = extattr_get_vp(nd.ni_vp, attrnamespace, attrname, data,
519	    nbytes, td);
520
521	vrele(nd.ni_vp);
522	return (error);
523}
524
525/*
526 * extattr_delete_vp(): Delete a named extended attribute on a file or
527 *                      directory
528 *
529 * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace",
530 *            kernelspace string pointer "attrname", proc "p"
531 * Returns: 0 on success, an error number otherwise
532 * Locks: none
533 * References: vp must be a valid reference for the duration of the call
534 */
535static int
536extattr_delete_vp(struct vnode *vp, int attrnamespace, const char *attrname,
537    struct thread *td)
538{
539	struct mount *mp;
540	int error;
541
542	error = vn_start_write(vp, &mp, V_WAIT | V_PCATCH);
543	if (error)
544		return (error);
545	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
546
547#ifdef MAC
548	error = mac_vnode_check_deleteextattr(td->td_ucred, vp, attrnamespace,
549	    attrname);
550	if (error)
551		goto done;
552#endif
553
554	error = VOP_DELETEEXTATTR(vp, attrnamespace, attrname, td->td_ucred,
555	    td);
556	if (error == EOPNOTSUPP)
557		error = VOP_SETEXTATTR(vp, attrnamespace, attrname, NULL,
558		    td->td_ucred, td);
559#ifdef MAC
560done:
561#endif
562	VOP_UNLOCK(vp);
563	vn_finished_write(mp);
564	return (error);
565}
566
567#ifndef _SYS_SYSPROTO_H_
568struct extattr_delete_fd_args {
569	int fd;
570	int attrnamespace;
571	const char *attrname;
572};
573#endif
574int
575sys_extattr_delete_fd(struct thread *td, struct extattr_delete_fd_args *uap)
576{
577	char attrname[EXTATTR_MAXNAMELEN + 1];
578	int error;
579
580	error = copyinstr(uap->attrname, attrname, sizeof(attrname), NULL);
581	if (error)
582		return (error);
583	return (kern_extattr_delete_fd(td, uap->fd, uap->attrnamespace,
584	    attrname));
585}
586
587int
588kern_extattr_delete_fd(struct thread *td, int fd, int attrnamespace,
589    const char *attrname)
590{
591	struct file *fp;
592	cap_rights_t rights;
593	int error;
594
595	AUDIT_ARG_FD(fd);
596	AUDIT_ARG_VALUE(attrnamespace);
597	AUDIT_ARG_TEXT(attrname);
598
599	error = getvnode_path(td, fd,
600	    cap_rights_init_one(&rights, CAP_EXTATTR_DELETE), &fp);
601	if (error)
602		return (error);
603
604	error = extattr_delete_vp(fp->f_vnode, attrnamespace,
605	    attrname, td);
606	fdrop(fp, td);
607	return (error);
608}
609
610#ifndef _SYS_SYSPROTO_H_
611struct extattr_delete_file_args {
612	const char *path;
613	int attrnamespace;
614	const char *attrname;
615};
616#endif
617int
618sys_extattr_delete_file(struct thread *td, struct extattr_delete_file_args *uap)
619{
620
621	return (user_extattr_delete_path(td, uap->path, uap->attrnamespace,
622	    uap->attrname, FOLLOW));
623}
624
625#ifndef _SYS_SYSPROTO_H_
626struct extattr_delete_link_args {
627	const char *path;
628	int attrnamespace;
629	const char *attrname;
630};
631#endif
632int
633sys_extattr_delete_link(struct thread *td, struct extattr_delete_link_args *uap)
634{
635
636	return (user_extattr_delete_path(td, uap->path, uap->attrnamespace,
637	    uap->attrname, NOFOLLOW));
638}
639
640int
641user_extattr_delete_path(struct thread *td, const char *path, int attrnamespace,
642    const char *uattrname, int follow)
643{
644	char attrname[EXTATTR_MAXNAMELEN + 1];
645	int error;
646
647	error = copyinstr(uattrname, attrname, sizeof(attrname), NULL);
648	if (error)
649		return(error);
650	return (kern_extattr_delete_path(td, path, attrnamespace,
651	    attrname, follow, UIO_USERSPACE));
652}
653
654int
655kern_extattr_delete_path(struct thread *td, const char *path, int attrnamespace,
656    const char *attrname, int follow, enum uio_seg pathseg)
657{
658	struct nameidata nd;
659	int error;
660
661	AUDIT_ARG_VALUE(attrnamespace);
662	AUDIT_ARG_TEXT(attrname);
663
664	NDINIT(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path);
665	error = namei(&nd);
666	if (error)
667		return(error);
668	NDFREE_PNBUF(&nd);
669
670	error = extattr_delete_vp(nd.ni_vp, attrnamespace, attrname, td);
671	vrele(nd.ni_vp);
672	return(error);
673}
674
675/*-
676 * Retrieve a list of extended attributes on a file or directory.
677 *
678 * Arguments: unlocked vnode "vp", attribute namespace 'attrnamespace",
679 *            userspace buffer pointer "data", buffer length "nbytes",
680 *            thread "td".
681 * Returns: 0 on success, an error number otherwise
682 * Locks: none
683 * References: vp must be a valid reference for the duration of the call
684 */
685static int
686extattr_list_vp(struct vnode *vp, int attrnamespace, struct uio *auiop,
687    struct thread *td)
688{
689	size_t size, *sizep;
690	ssize_t cnt;
691	int error;
692
693	sizep = NULL;
694	cnt = 0;
695	if (auiop != NULL) {
696		if (auiop->uio_resid > IOSIZE_MAX)
697			return (EINVAL);
698		cnt = auiop->uio_resid;
699	} else
700		sizep = &size;
701
702	vn_lock(vp, LK_SHARED | LK_RETRY);
703
704#ifdef MAC
705	error = mac_vnode_check_listextattr(td->td_ucred, vp, attrnamespace);
706	if (error) {
707		VOP_UNLOCK(vp);
708		return (error);
709	}
710#endif
711
712	error = VOP_LISTEXTATTR(vp, attrnamespace, auiop, sizep,
713	    td->td_ucred, td);
714	VOP_UNLOCK(vp);
715
716	if (auiop != NULL) {
717		cnt -= auiop->uio_resid;
718		td->td_retval[0] = cnt;
719	} else
720		td->td_retval[0] = size;
721	return (error);
722}
723
724#ifndef _SYS_SYSPROTO_H_
725struct extattr_list_fd_args {
726	int fd;
727	int attrnamespace;
728	void *data;
729	size_t nbytes;
730};
731#endif
732int
733sys_extattr_list_fd(struct thread *td, struct extattr_list_fd_args *uap)
734{
735	struct uio auio, *auiop;
736	struct iovec aiov;
737
738	if (uap->data != NULL) {
739		aiov.iov_base = uap->data;
740		aiov.iov_len = uap->nbytes;
741		auio.uio_iov = &aiov;
742		auio.uio_iovcnt = 1;
743		auio.uio_offset = 0;
744		auio.uio_resid = uap->nbytes;
745		auio.uio_rw = UIO_READ;
746		auio.uio_segflg = UIO_USERSPACE;
747		auio.uio_td = td;
748		auiop = &auio;
749	} else
750		auiop = NULL;
751
752	return (kern_extattr_list_fd(td, uap->fd, uap->attrnamespace,
753	    auiop));
754}
755
756int
757kern_extattr_list_fd(struct thread *td, int fd, int attrnamespace,
758    struct uio *auiop)
759{
760	struct file *fp;
761	cap_rights_t rights;
762	int error;
763
764	AUDIT_ARG_FD(fd);
765	AUDIT_ARG_VALUE(attrnamespace);
766	error = getvnode_path(td, fd,
767	    cap_rights_init_one(&rights, CAP_EXTATTR_LIST), &fp);
768	if (error)
769		return (error);
770
771	error = extattr_list_vp(fp->f_vnode, attrnamespace, auiop, td);
772
773	fdrop(fp, td);
774	return (error);
775}
776
777#ifndef _SYS_SYSPROTO_H_
778struct extattr_list_file_args {
779	const char *path;
780	int attrnamespace;
781	void *data;
782	size_t nbytes;
783}
784#endif
785int
786sys_extattr_list_file(struct thread *td, struct extattr_list_file_args *uap)
787{
788
789	return (user_extattr_list_path(td, uap->path, uap->attrnamespace,
790	    uap->data, uap->nbytes, FOLLOW));
791}
792
793#ifndef _SYS_SYSPROTO_H_
794struct extattr_list_link_args {
795	const char *path;
796	int attrnamespace;
797	void *data;
798	size_t nbytes;
799};
800#endif
801int
802sys_extattr_list_link(struct thread *td, struct extattr_list_link_args *uap)
803{
804
805	return (user_extattr_list_path(td, uap->path, uap->attrnamespace,
806	    uap->data, uap->nbytes, NOFOLLOW));
807}
808
809static int
810user_extattr_list_path(struct thread *td, const char *path, int attrnamespace,
811    void *data, size_t nbytes, int follow)
812{
813	struct uio auio, *auiop;
814	struct iovec aiov;
815
816	if (data != NULL) {
817		aiov.iov_base = data;
818		aiov.iov_len = nbytes;
819		auio.uio_iov = &aiov;
820		auio.uio_iovcnt = 1;
821		auio.uio_offset = 0;
822		auio.uio_resid = nbytes;
823		auio.uio_rw = UIO_READ;
824		auio.uio_segflg = UIO_USERSPACE;
825		auio.uio_td = td;
826		auiop = &auio;
827	} else
828		auiop = NULL;
829
830	return (kern_extattr_list_path(td, path, attrnamespace,
831	    auiop, follow, UIO_USERSPACE));
832}
833
834int
835kern_extattr_list_path(struct thread *td, const char *path, int attrnamespace,
836    struct uio *auiop, int follow, enum uio_seg pathseg)
837{
838	struct nameidata nd;
839	int error;
840
841	AUDIT_ARG_VALUE(attrnamespace);
842	NDINIT(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path);
843	error = namei(&nd);
844	if (error)
845		return (error);
846	NDFREE_PNBUF(&nd);
847
848	error = extattr_list_vp(nd.ni_vp, attrnamespace, auiop, td);
849
850	vrele(nd.ni_vp);
851	return (error);
852}
853