xref: /freebsd/sys/kern/vfs_extattr.c (revision e4f6a1bfa31a2299612bbf9dae402bb38d38b1df)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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/cdefs.h>
32 __FBSDID("$FreeBSD$");
33 
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/capsicum.h>
37 #include <sys/lock.h>
38 #include <sys/mount.h>
39 #include <sys/mutex.h>
40 #include <sys/sysproto.h>
41 #include <sys/fcntl.h>
42 #include <sys/namei.h>
43 #include <sys/filedesc.h>
44 #include <sys/limits.h>
45 #include <sys/vnode.h>
46 #include <sys/proc.h>
47 #include <sys/extattr.h>
48 
49 #include <security/audit/audit.h>
50 #include <security/mac/mac_framework.h>
51 
52 /*
53  * Syscall to push extended attribute configuration information into the VFS.
54  * Accepts a path, which it converts to a mountpoint, as well as a command
55  * (int cmd), and attribute name and misc data.
56  *
57  * Currently this is used only by UFS1 extended attributes.
58  */
59 int
60 sys_extattrctl(td, uap)
61 	struct thread *td;
62 	struct extattrctl_args /* {
63 		const char *path;
64 		int cmd;
65 		const char *filename;
66 		int attrnamespace;
67 		const char *attrname;
68 	} */ *uap;
69 {
70 	struct vnode *filename_vp;
71 	struct nameidata nd;
72 	struct mount *mp, *mp_writable;
73 	char attrname[EXTATTR_MAXNAMELEN];
74 	int error;
75 
76 	AUDIT_ARG_CMD(uap->cmd);
77 	AUDIT_ARG_VALUE(uap->attrnamespace);
78 	/*
79 	 * uap->attrname is not always defined.  We check again later when we
80 	 * invoke the VFS call so as to pass in NULL there if needed.
81 	 */
82 	if (uap->attrname != NULL) {
83 		error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN,
84 		    NULL);
85 		if (error)
86 			return (error);
87 	}
88 	AUDIT_ARG_TEXT(attrname);
89 
90 	mp = NULL;
91 	filename_vp = NULL;
92 	if (uap->filename != NULL) {
93 		NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE2,
94 		    UIO_USERSPACE, uap->filename, td);
95 		error = namei(&nd);
96 		if (error)
97 			return (error);
98 		filename_vp = nd.ni_vp;
99 		NDFREE(&nd, NDF_NO_VP_RELE);
100 	}
101 
102 	/* uap->path is always defined. */
103 	NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNODE1,
104 	    UIO_USERSPACE, uap->path, td);
105 	error = namei(&nd);
106 	if (error)
107 		goto out;
108 	mp = nd.ni_vp->v_mount;
109 	error = vfs_busy(mp, 0);
110 	if (error) {
111 		NDFREE(&nd, 0);
112 		mp = NULL;
113 		goto out;
114 	}
115 	VOP_UNLOCK(nd.ni_vp, 0);
116 	error = vn_start_write(nd.ni_vp, &mp_writable, V_WAIT | PCATCH);
117 	NDFREE(&nd, NDF_NO_VP_UNLOCK);
118 	if (error)
119 		goto out;
120 	if (filename_vp != NULL) {
121 		/*
122 		 * uap->filename is not always defined.  If it is,
123 		 * grab a vnode lock, which VFS_EXTATTRCTL() will
124 		 * later release.
125 		 */
126 		error = vn_lock(filename_vp, LK_EXCLUSIVE);
127 		if (error) {
128 			vn_finished_write(mp_writable);
129 			goto out;
130 		}
131 	}
132 
133 	error = VFS_EXTATTRCTL(mp, uap->cmd, filename_vp, uap->attrnamespace,
134 	    uap->attrname != NULL ? attrname : NULL);
135 
136 	vn_finished_write(mp_writable);
137 out:
138 	if (mp != NULL)
139 		vfs_unbusy(mp);
140 
141 	/*
142 	 * VFS_EXTATTRCTL will have unlocked, but not de-ref'd, filename_vp,
143 	 * so vrele it if it is defined.
144 	 */
145 	if (filename_vp != NULL)
146 		vrele(filename_vp);
147 	return (error);
148 }
149 
150 /*-
151  * Set a named extended attribute on a file or directory
152  *
153  * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace",
154  *            kernelspace string pointer "attrname", userspace buffer
155  *            pointer "data", buffer length "nbytes", thread "td".
156  * Returns: 0 on success, an error number otherwise
157  * Locks: none
158  * References: vp must be a valid reference for the duration of the call
159  */
160 static int
161 extattr_set_vp(struct vnode *vp, int attrnamespace, const char *attrname,
162     void *data, size_t nbytes, struct thread *td)
163 {
164 	struct mount *mp;
165 	struct uio auio;
166 	struct iovec aiov;
167 	ssize_t cnt;
168 	int error;
169 
170 	error = vn_start_write(vp, &mp, V_WAIT | PCATCH);
171 	if (error)
172 		return (error);
173 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
174 
175 	aiov.iov_base = data;
176 	aiov.iov_len = nbytes;
177 	auio.uio_iov = &aiov;
178 	auio.uio_iovcnt = 1;
179 	auio.uio_offset = 0;
180 	if (nbytes > IOSIZE_MAX) {
181 		error = EINVAL;
182 		goto done;
183 	}
184 	auio.uio_resid = nbytes;
185 	auio.uio_rw = UIO_WRITE;
186 	auio.uio_segflg = UIO_USERSPACE;
187 	auio.uio_td = td;
188 	cnt = nbytes;
189 
190 #ifdef MAC
191 	error = mac_vnode_check_setextattr(td->td_ucred, vp, attrnamespace,
192 	    attrname);
193 	if (error)
194 		goto done;
195 #endif
196 
197 	error = VOP_SETEXTATTR(vp, attrnamespace, attrname, &auio,
198 	    td->td_ucred, td);
199 	cnt -= auio.uio_resid;
200 	td->td_retval[0] = cnt;
201 
202 done:
203 	VOP_UNLOCK(vp, 0);
204 	vn_finished_write(mp);
205 	return (error);
206 }
207 
208 int
209 sys_extattr_set_fd(td, uap)
210 	struct thread *td;
211 	struct extattr_set_fd_args /* {
212 		int fd;
213 		int attrnamespace;
214 		const char *attrname;
215 		void *data;
216 		size_t nbytes;
217 	} */ *uap;
218 {
219 	struct file *fp;
220 	char attrname[EXTATTR_MAXNAMELEN];
221 	cap_rights_t rights;
222 	int error;
223 
224 	AUDIT_ARG_FD(uap->fd);
225 	AUDIT_ARG_VALUE(uap->attrnamespace);
226 	error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
227 	if (error)
228 		return (error);
229 	AUDIT_ARG_TEXT(attrname);
230 
231 	error = getvnode(td, uap->fd,
232 	    cap_rights_init(&rights, CAP_EXTATTR_SET), &fp);
233 	if (error)
234 		return (error);
235 
236 	error = extattr_set_vp(fp->f_vnode, uap->attrnamespace,
237 	    attrname, uap->data, uap->nbytes, td);
238 	fdrop(fp, td);
239 
240 	return (error);
241 }
242 
243 int
244 sys_extattr_set_file(td, uap)
245 	struct thread *td;
246 	struct extattr_set_file_args /* {
247 		const char *path;
248 		int attrnamespace;
249 		const char *attrname;
250 		void *data;
251 		size_t nbytes;
252 	} */ *uap;
253 {
254 	struct nameidata nd;
255 	char attrname[EXTATTR_MAXNAMELEN];
256 	int error;
257 
258 	AUDIT_ARG_VALUE(uap->attrnamespace);
259 	error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
260 	if (error)
261 		return (error);
262 	AUDIT_ARG_TEXT(attrname);
263 
264 	NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE1, UIO_USERSPACE,
265 	    uap->path, td);
266 	error = namei(&nd);
267 	if (error)
268 		return (error);
269 	NDFREE(&nd, NDF_ONLY_PNBUF);
270 
271 	error = extattr_set_vp(nd.ni_vp, uap->attrnamespace, attrname,
272 	    uap->data, uap->nbytes, td);
273 
274 	vrele(nd.ni_vp);
275 	return (error);
276 }
277 
278 int
279 sys_extattr_set_link(td, uap)
280 	struct thread *td;
281 	struct extattr_set_link_args /* {
282 		const char *path;
283 		int attrnamespace;
284 		const char *attrname;
285 		void *data;
286 		size_t nbytes;
287 	} */ *uap;
288 {
289 	struct nameidata nd;
290 	char attrname[EXTATTR_MAXNAMELEN];
291 	int error;
292 
293 	AUDIT_ARG_VALUE(uap->attrnamespace);
294 	error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
295 	if (error)
296 		return (error);
297 	AUDIT_ARG_TEXT(attrname);
298 
299 	NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNODE1, UIO_USERSPACE,
300 	    uap->path, td);
301 	error = namei(&nd);
302 	if (error)
303 		return (error);
304 	NDFREE(&nd, NDF_ONLY_PNBUF);
305 
306 	error = extattr_set_vp(nd.ni_vp, uap->attrnamespace, attrname,
307 	    uap->data, uap->nbytes, td);
308 
309 	vrele(nd.ni_vp);
310 	return (error);
311 }
312 
313 /*-
314  * Get a named extended attribute on a file or directory
315  *
316  * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace",
317  *            kernelspace string pointer "attrname", userspace buffer
318  *            pointer "data", buffer length "nbytes", thread "td".
319  * Returns: 0 on success, an error number otherwise
320  * Locks: none
321  * References: vp must be a valid reference for the duration of the call
322  */
323 static int
324 extattr_get_vp(struct vnode *vp, int attrnamespace, const char *attrname,
325     void *data, size_t nbytes, struct thread *td)
326 {
327 	struct uio auio, *auiop;
328 	struct iovec aiov;
329 	ssize_t cnt;
330 	size_t size, *sizep;
331 	int error;
332 
333 	vn_lock(vp, LK_SHARED | LK_RETRY);
334 
335 	/*
336 	 * Slightly unusual semantics: if the user provides a NULL data
337 	 * pointer, they don't want to receive the data, just the maximum
338 	 * read length.
339 	 */
340 	auiop = NULL;
341 	sizep = NULL;
342 	cnt = 0;
343 	if (data != NULL) {
344 		aiov.iov_base = data;
345 		aiov.iov_len = nbytes;
346 		auio.uio_iov = &aiov;
347 		auio.uio_iovcnt = 1;
348 		auio.uio_offset = 0;
349 		if (nbytes > IOSIZE_MAX) {
350 			error = EINVAL;
351 			goto done;
352 		}
353 		auio.uio_resid = nbytes;
354 		auio.uio_rw = UIO_READ;
355 		auio.uio_segflg = UIO_USERSPACE;
356 		auio.uio_td = td;
357 		auiop = &auio;
358 		cnt = nbytes;
359 	} else
360 		sizep = &size;
361 
362 #ifdef MAC
363 	error = mac_vnode_check_getextattr(td->td_ucred, vp, attrnamespace,
364 	    attrname);
365 	if (error)
366 		goto done;
367 #endif
368 
369 	error = VOP_GETEXTATTR(vp, attrnamespace, attrname, auiop, sizep,
370 	    td->td_ucred, td);
371 
372 	if (auiop != NULL) {
373 		cnt -= auio.uio_resid;
374 		td->td_retval[0] = cnt;
375 	} else
376 		td->td_retval[0] = size;
377 
378 done:
379 	VOP_UNLOCK(vp, 0);
380 	return (error);
381 }
382 
383 int
384 sys_extattr_get_fd(td, uap)
385 	struct thread *td;
386 	struct extattr_get_fd_args /* {
387 		int fd;
388 		int attrnamespace;
389 		const char *attrname;
390 		void *data;
391 		size_t nbytes;
392 	} */ *uap;
393 {
394 	struct file *fp;
395 	char attrname[EXTATTR_MAXNAMELEN];
396 	cap_rights_t rights;
397 	int error;
398 
399 	AUDIT_ARG_FD(uap->fd);
400 	AUDIT_ARG_VALUE(uap->attrnamespace);
401 	error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
402 	if (error)
403 		return (error);
404 	AUDIT_ARG_TEXT(attrname);
405 
406 	error = getvnode(td, uap->fd,
407 	    cap_rights_init(&rights, CAP_EXTATTR_GET), &fp);
408 	if (error)
409 		return (error);
410 
411 	error = extattr_get_vp(fp->f_vnode, uap->attrnamespace,
412 	    attrname, uap->data, uap->nbytes, td);
413 
414 	fdrop(fp, td);
415 	return (error);
416 }
417 
418 int
419 sys_extattr_get_file(td, uap)
420 	struct thread *td;
421 	struct extattr_get_file_args /* {
422 		const char *path;
423 		int attrnamespace;
424 		const char *attrname;
425 		void *data;
426 		size_t nbytes;
427 	} */ *uap;
428 {
429 	struct nameidata nd;
430 	char attrname[EXTATTR_MAXNAMELEN];
431 	int error;
432 
433 	AUDIT_ARG_VALUE(uap->attrnamespace);
434 	error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
435 	if (error)
436 		return (error);
437 	AUDIT_ARG_TEXT(attrname);
438 
439 	NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path, td);
440 	error = namei(&nd);
441 	if (error)
442 		return (error);
443 	NDFREE(&nd, NDF_ONLY_PNBUF);
444 
445 	error = extattr_get_vp(nd.ni_vp, uap->attrnamespace, attrname,
446 	    uap->data, uap->nbytes, td);
447 
448 	vrele(nd.ni_vp);
449 	return (error);
450 }
451 
452 int
453 sys_extattr_get_link(td, uap)
454 	struct thread *td;
455 	struct extattr_get_link_args /* {
456 		const char *path;
457 		int attrnamespace;
458 		const char *attrname;
459 		void *data;
460 		size_t nbytes;
461 	} */ *uap;
462 {
463 	struct nameidata nd;
464 	char attrname[EXTATTR_MAXNAMELEN];
465 	int error;
466 
467 	AUDIT_ARG_VALUE(uap->attrnamespace);
468 	error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
469 	if (error)
470 		return (error);
471 	AUDIT_ARG_TEXT(attrname);
472 
473 	NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path,
474 	    td);
475 	error = namei(&nd);
476 	if (error)
477 		return (error);
478 	NDFREE(&nd, NDF_ONLY_PNBUF);
479 
480 	error = extattr_get_vp(nd.ni_vp, uap->attrnamespace, attrname,
481 	    uap->data, uap->nbytes, td);
482 
483 	vrele(nd.ni_vp);
484 	return (error);
485 }
486 
487 /*
488  * extattr_delete_vp(): Delete a named extended attribute on a file or
489  *                      directory
490  *
491  * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace",
492  *            kernelspace string pointer "attrname", proc "p"
493  * Returns: 0 on success, an error number otherwise
494  * Locks: none
495  * References: vp must be a valid reference for the duration of the call
496  */
497 static int
498 extattr_delete_vp(struct vnode *vp, int attrnamespace, const char *attrname,
499     struct thread *td)
500 {
501 	struct mount *mp;
502 	int error;
503 
504 	error = vn_start_write(vp, &mp, V_WAIT | PCATCH);
505 	if (error)
506 		return (error);
507 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
508 
509 #ifdef MAC
510 	error = mac_vnode_check_deleteextattr(td->td_ucred, vp, attrnamespace,
511 	    attrname);
512 	if (error)
513 		goto done;
514 #endif
515 
516 	error = VOP_DELETEEXTATTR(vp, attrnamespace, attrname, td->td_ucred,
517 	    td);
518 	if (error == EOPNOTSUPP)
519 		error = VOP_SETEXTATTR(vp, attrnamespace, attrname, NULL,
520 		    td->td_ucred, td);
521 #ifdef MAC
522 done:
523 #endif
524 	VOP_UNLOCK(vp, 0);
525 	vn_finished_write(mp);
526 	return (error);
527 }
528 
529 int
530 sys_extattr_delete_fd(td, uap)
531 	struct thread *td;
532 	struct extattr_delete_fd_args /* {
533 		int fd;
534 		int attrnamespace;
535 		const char *attrname;
536 	} */ *uap;
537 {
538 	struct file *fp;
539 	char attrname[EXTATTR_MAXNAMELEN];
540 	cap_rights_t rights;
541 	int error;
542 
543 	AUDIT_ARG_FD(uap->fd);
544 	AUDIT_ARG_VALUE(uap->attrnamespace);
545 	error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
546 	if (error)
547 		return (error);
548 	AUDIT_ARG_TEXT(attrname);
549 
550 	error = getvnode(td, uap->fd,
551 	    cap_rights_init(&rights, CAP_EXTATTR_DELETE), &fp);
552 	if (error)
553 		return (error);
554 
555 	error = extattr_delete_vp(fp->f_vnode, uap->attrnamespace,
556 	    attrname, td);
557 	fdrop(fp, td);
558 	return (error);
559 }
560 
561 int
562 sys_extattr_delete_file(td, uap)
563 	struct thread *td;
564 	struct extattr_delete_file_args /* {
565 		const char *path;
566 		int attrnamespace;
567 		const char *attrname;
568 	} */ *uap;
569 {
570 	struct nameidata nd;
571 	char attrname[EXTATTR_MAXNAMELEN];
572 	int error;
573 
574 	AUDIT_ARG_VALUE(uap->attrnamespace);
575 	error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
576 	if (error)
577 		return(error);
578 	AUDIT_ARG_TEXT(attrname);
579 
580 	NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path, td);
581 	error = namei(&nd);
582 	if (error)
583 		return(error);
584 	NDFREE(&nd, NDF_ONLY_PNBUF);
585 
586 	error = extattr_delete_vp(nd.ni_vp, uap->attrnamespace, attrname, td);
587 	vrele(nd.ni_vp);
588 	return(error);
589 }
590 
591 int
592 sys_extattr_delete_link(td, uap)
593 	struct thread *td;
594 	struct extattr_delete_link_args /* {
595 		const char *path;
596 		int attrnamespace;
597 		const char *attrname;
598 	} */ *uap;
599 {
600 	struct nameidata nd;
601 	char attrname[EXTATTR_MAXNAMELEN];
602 	int error;
603 
604 	AUDIT_ARG_VALUE(uap->attrnamespace);
605 	error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
606 	if (error)
607 		return(error);
608 	AUDIT_ARG_TEXT(attrname);
609 
610 	NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path, td);
611 	error = namei(&nd);
612 	if (error)
613 		return(error);
614 	NDFREE(&nd, NDF_ONLY_PNBUF);
615 
616 	error = extattr_delete_vp(nd.ni_vp, uap->attrnamespace, attrname, td);
617 	vrele(nd.ni_vp);
618 	return(error);
619 }
620 
621 /*-
622  * Retrieve a list of extended attributes on a file or directory.
623  *
624  * Arguments: unlocked vnode "vp", attribute namespace 'attrnamespace",
625  *            userspace buffer pointer "data", buffer length "nbytes",
626  *            thread "td".
627  * Returns: 0 on success, an error number otherwise
628  * Locks: none
629  * References: vp must be a valid reference for the duration of the call
630  */
631 static int
632 extattr_list_vp(struct vnode *vp, int attrnamespace, void *data,
633     size_t nbytes, struct thread *td)
634 {
635 	struct uio auio, *auiop;
636 	size_t size, *sizep;
637 	struct iovec aiov;
638 	ssize_t cnt;
639 	int error;
640 
641 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
642 
643 	auiop = NULL;
644 	sizep = NULL;
645 	cnt = 0;
646 	if (data != NULL) {
647 		aiov.iov_base = data;
648 		aiov.iov_len = nbytes;
649 		auio.uio_iov = &aiov;
650 		auio.uio_iovcnt = 1;
651 		auio.uio_offset = 0;
652 		if (nbytes > IOSIZE_MAX) {
653 			error = EINVAL;
654 			goto done;
655 		}
656 		auio.uio_resid = nbytes;
657 		auio.uio_rw = UIO_READ;
658 		auio.uio_segflg = UIO_USERSPACE;
659 		auio.uio_td = td;
660 		auiop = &auio;
661 		cnt = nbytes;
662 	} else
663 		sizep = &size;
664 
665 #ifdef MAC
666 	error = mac_vnode_check_listextattr(td->td_ucred, vp, attrnamespace);
667 	if (error)
668 		goto done;
669 #endif
670 
671 	error = VOP_LISTEXTATTR(vp, attrnamespace, auiop, sizep,
672 	    td->td_ucred, td);
673 
674 	if (auiop != NULL) {
675 		cnt -= auio.uio_resid;
676 		td->td_retval[0] = cnt;
677 	} else
678 		td->td_retval[0] = size;
679 
680 done:
681 	VOP_UNLOCK(vp, 0);
682 	return (error);
683 }
684 
685 
686 int
687 sys_extattr_list_fd(td, uap)
688 	struct thread *td;
689 	struct extattr_list_fd_args /* {
690 		int fd;
691 		int attrnamespace;
692 		void *data;
693 		size_t nbytes;
694 	} */ *uap;
695 {
696 	struct file *fp;
697 	cap_rights_t rights;
698 	int error;
699 
700 	AUDIT_ARG_FD(uap->fd);
701 	AUDIT_ARG_VALUE(uap->attrnamespace);
702 	error = getvnode(td, uap->fd,
703 	    cap_rights_init(&rights, CAP_EXTATTR_LIST), &fp);
704 	if (error)
705 		return (error);
706 
707 	error = extattr_list_vp(fp->f_vnode, uap->attrnamespace, uap->data,
708 	    uap->nbytes, td);
709 
710 	fdrop(fp, td);
711 	return (error);
712 }
713 
714 int
715 sys_extattr_list_file(td, uap)
716 	struct thread*td;
717 	struct extattr_list_file_args /* {
718 		const char *path;
719 		int attrnamespace;
720 		void *data;
721 		size_t nbytes;
722 	} */ *uap;
723 {
724 	struct nameidata nd;
725 	int error;
726 
727 	AUDIT_ARG_VALUE(uap->attrnamespace);
728 	NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path, td);
729 	error = namei(&nd);
730 	if (error)
731 		return (error);
732 	NDFREE(&nd, NDF_ONLY_PNBUF);
733 
734 	error = extattr_list_vp(nd.ni_vp, uap->attrnamespace, uap->data,
735 	    uap->nbytes, td);
736 
737 	vrele(nd.ni_vp);
738 	return (error);
739 }
740 
741 int
742 sys_extattr_list_link(td, uap)
743 	struct thread*td;
744 	struct extattr_list_link_args /* {
745 		const char *path;
746 		int attrnamespace;
747 		void *data;
748 		size_t nbytes;
749 	} */ *uap;
750 {
751 	struct nameidata nd;
752 	int error;
753 
754 	AUDIT_ARG_VALUE(uap->attrnamespace);
755 	NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path,
756 	    td);
757 	error = namei(&nd);
758 	if (error)
759 		return (error);
760 	NDFREE(&nd, NDF_ONLY_PNBUF);
761 
762 	error = extattr_list_vp(nd.ni_vp, uap->attrnamespace, uap->data,
763 	    uap->nbytes, td);
764 
765 	vrele(nd.ni_vp);
766 	return (error);
767 }
768