xref: /freebsd/sys/kern/vfs_extattr.c (revision 137a344c6341d1469432e9deb3a25593f96672ad)
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 	if (nbytes > IOSIZE_MAX)
171 		return (EINVAL);
172 
173 	error = vn_start_write(vp, &mp, V_WAIT | PCATCH);
174 	if (error)
175 		return (error);
176 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
177 
178 	aiov.iov_base = data;
179 	aiov.iov_len = nbytes;
180 	auio.uio_iov = &aiov;
181 	auio.uio_iovcnt = 1;
182 	auio.uio_offset = 0;
183 	auio.uio_resid = nbytes;
184 	auio.uio_rw = UIO_WRITE;
185 	auio.uio_segflg = UIO_USERSPACE;
186 	auio.uio_td = td;
187 	cnt = nbytes;
188 
189 #ifdef MAC
190 	error = mac_vnode_check_setextattr(td->td_ucred, vp, attrnamespace,
191 	    attrname);
192 	if (error)
193 		goto done;
194 #endif
195 
196 	error = VOP_SETEXTATTR(vp, attrnamespace, attrname, &auio,
197 	    td->td_ucred, td);
198 	cnt -= auio.uio_resid;
199 	td->td_retval[0] = cnt;
200 
201 #ifdef MAC
202 done:
203 #endif
204 	VOP_UNLOCK(vp, 0);
205 	vn_finished_write(mp);
206 	return (error);
207 }
208 
209 int
210 sys_extattr_set_fd(td, uap)
211 	struct thread *td;
212 	struct extattr_set_fd_args /* {
213 		int fd;
214 		int attrnamespace;
215 		const char *attrname;
216 		void *data;
217 		size_t nbytes;
218 	} */ *uap;
219 {
220 	struct file *fp;
221 	char attrname[EXTATTR_MAXNAMELEN];
222 	cap_rights_t rights;
223 	int error;
224 
225 	AUDIT_ARG_FD(uap->fd);
226 	AUDIT_ARG_VALUE(uap->attrnamespace);
227 	error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
228 	if (error)
229 		return (error);
230 	AUDIT_ARG_TEXT(attrname);
231 
232 	error = getvnode(td, uap->fd,
233 	    cap_rights_init(&rights, CAP_EXTATTR_SET), &fp);
234 	if (error)
235 		return (error);
236 
237 	error = extattr_set_vp(fp->f_vnode, uap->attrnamespace,
238 	    attrname, uap->data, uap->nbytes, td);
239 	fdrop(fp, td);
240 
241 	return (error);
242 }
243 
244 int
245 sys_extattr_set_file(td, uap)
246 	struct thread *td;
247 	struct extattr_set_file_args /* {
248 		const char *path;
249 		int attrnamespace;
250 		const char *attrname;
251 		void *data;
252 		size_t nbytes;
253 	} */ *uap;
254 {
255 	struct nameidata nd;
256 	char attrname[EXTATTR_MAXNAMELEN];
257 	int error;
258 
259 	AUDIT_ARG_VALUE(uap->attrnamespace);
260 	error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
261 	if (error)
262 		return (error);
263 	AUDIT_ARG_TEXT(attrname);
264 
265 	NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE1, UIO_USERSPACE,
266 	    uap->path, td);
267 	error = namei(&nd);
268 	if (error)
269 		return (error);
270 	NDFREE(&nd, NDF_ONLY_PNBUF);
271 
272 	error = extattr_set_vp(nd.ni_vp, uap->attrnamespace, attrname,
273 	    uap->data, uap->nbytes, td);
274 
275 	vrele(nd.ni_vp);
276 	return (error);
277 }
278 
279 int
280 sys_extattr_set_link(td, uap)
281 	struct thread *td;
282 	struct extattr_set_link_args /* {
283 		const char *path;
284 		int attrnamespace;
285 		const char *attrname;
286 		void *data;
287 		size_t nbytes;
288 	} */ *uap;
289 {
290 	struct nameidata nd;
291 	char attrname[EXTATTR_MAXNAMELEN];
292 	int error;
293 
294 	AUDIT_ARG_VALUE(uap->attrnamespace);
295 	error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
296 	if (error)
297 		return (error);
298 	AUDIT_ARG_TEXT(attrname);
299 
300 	NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNODE1, UIO_USERSPACE,
301 	    uap->path, td);
302 	error = namei(&nd);
303 	if (error)
304 		return (error);
305 	NDFREE(&nd, NDF_ONLY_PNBUF);
306 
307 	error = extattr_set_vp(nd.ni_vp, uap->attrnamespace, attrname,
308 	    uap->data, uap->nbytes, td);
309 
310 	vrele(nd.ni_vp);
311 	return (error);
312 }
313 
314 /*-
315  * Get a named extended attribute on a file or directory
316  *
317  * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace",
318  *            kernelspace string pointer "attrname", userspace buffer
319  *            pointer "data", buffer length "nbytes", thread "td".
320  * Returns: 0 on success, an error number otherwise
321  * Locks: none
322  * References: vp must be a valid reference for the duration of the call
323  */
324 static int
325 extattr_get_vp(struct vnode *vp, int attrnamespace, const char *attrname,
326     void *data, size_t nbytes, struct thread *td)
327 {
328 	struct uio auio, *auiop;
329 	struct iovec aiov;
330 	ssize_t cnt;
331 	size_t size, *sizep;
332 	int error;
333 
334 	if (nbytes > IOSIZE_MAX)
335 		return (EINVAL);
336 
337 	vn_lock(vp, LK_SHARED | LK_RETRY);
338 
339 	/*
340 	 * Slightly unusual semantics: if the user provides a NULL data
341 	 * pointer, they don't want to receive the data, just the maximum
342 	 * read length.
343 	 */
344 	auiop = NULL;
345 	sizep = NULL;
346 	cnt = 0;
347 	if (data != NULL) {
348 		aiov.iov_base = data;
349 		aiov.iov_len = nbytes;
350 		auio.uio_iov = &aiov;
351 		auio.uio_iovcnt = 1;
352 		auio.uio_offset = 0;
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 #ifdef MAC
378 done:
379 #endif
380 	VOP_UNLOCK(vp, 0);
381 	return (error);
382 }
383 
384 int
385 sys_extattr_get_fd(td, uap)
386 	struct thread *td;
387 	struct extattr_get_fd_args /* {
388 		int fd;
389 		int attrnamespace;
390 		const char *attrname;
391 		void *data;
392 		size_t nbytes;
393 	} */ *uap;
394 {
395 	struct file *fp;
396 	char attrname[EXTATTR_MAXNAMELEN];
397 	cap_rights_t rights;
398 	int error;
399 
400 	AUDIT_ARG_FD(uap->fd);
401 	AUDIT_ARG_VALUE(uap->attrnamespace);
402 	error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
403 	if (error)
404 		return (error);
405 	AUDIT_ARG_TEXT(attrname);
406 
407 	error = getvnode(td, uap->fd,
408 	    cap_rights_init(&rights, CAP_EXTATTR_GET), &fp);
409 	if (error)
410 		return (error);
411 
412 	error = extattr_get_vp(fp->f_vnode, uap->attrnamespace,
413 	    attrname, uap->data, uap->nbytes, td);
414 
415 	fdrop(fp, td);
416 	return (error);
417 }
418 
419 int
420 sys_extattr_get_file(td, uap)
421 	struct thread *td;
422 	struct extattr_get_file_args /* {
423 		const char *path;
424 		int attrnamespace;
425 		const char *attrname;
426 		void *data;
427 		size_t nbytes;
428 	} */ *uap;
429 {
430 	struct nameidata nd;
431 	char attrname[EXTATTR_MAXNAMELEN];
432 	int error;
433 
434 	AUDIT_ARG_VALUE(uap->attrnamespace);
435 	error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
436 	if (error)
437 		return (error);
438 	AUDIT_ARG_TEXT(attrname);
439 
440 	NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path, td);
441 	error = namei(&nd);
442 	if (error)
443 		return (error);
444 	NDFREE(&nd, NDF_ONLY_PNBUF);
445 
446 	error = extattr_get_vp(nd.ni_vp, uap->attrnamespace, attrname,
447 	    uap->data, uap->nbytes, td);
448 
449 	vrele(nd.ni_vp);
450 	return (error);
451 }
452 
453 int
454 sys_extattr_get_link(td, uap)
455 	struct thread *td;
456 	struct extattr_get_link_args /* {
457 		const char *path;
458 		int attrnamespace;
459 		const char *attrname;
460 		void *data;
461 		size_t nbytes;
462 	} */ *uap;
463 {
464 	struct nameidata nd;
465 	char attrname[EXTATTR_MAXNAMELEN];
466 	int error;
467 
468 	AUDIT_ARG_VALUE(uap->attrnamespace);
469 	error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
470 	if (error)
471 		return (error);
472 	AUDIT_ARG_TEXT(attrname);
473 
474 	NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path,
475 	    td);
476 	error = namei(&nd);
477 	if (error)
478 		return (error);
479 	NDFREE(&nd, NDF_ONLY_PNBUF);
480 
481 	error = extattr_get_vp(nd.ni_vp, uap->attrnamespace, attrname,
482 	    uap->data, uap->nbytes, td);
483 
484 	vrele(nd.ni_vp);
485 	return (error);
486 }
487 
488 /*
489  * extattr_delete_vp(): Delete a named extended attribute on a file or
490  *                      directory
491  *
492  * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace",
493  *            kernelspace string pointer "attrname", proc "p"
494  * Returns: 0 on success, an error number otherwise
495  * Locks: none
496  * References: vp must be a valid reference for the duration of the call
497  */
498 static int
499 extattr_delete_vp(struct vnode *vp, int attrnamespace, const char *attrname,
500     struct thread *td)
501 {
502 	struct mount *mp;
503 	int error;
504 
505 	error = vn_start_write(vp, &mp, V_WAIT | PCATCH);
506 	if (error)
507 		return (error);
508 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
509 
510 #ifdef MAC
511 	error = mac_vnode_check_deleteextattr(td->td_ucred, vp, attrnamespace,
512 	    attrname);
513 	if (error)
514 		goto done;
515 #endif
516 
517 	error = VOP_DELETEEXTATTR(vp, attrnamespace, attrname, td->td_ucred,
518 	    td);
519 	if (error == EOPNOTSUPP)
520 		error = VOP_SETEXTATTR(vp, attrnamespace, attrname, NULL,
521 		    td->td_ucred, td);
522 #ifdef MAC
523 done:
524 #endif
525 	VOP_UNLOCK(vp, 0);
526 	vn_finished_write(mp);
527 	return (error);
528 }
529 
530 int
531 sys_extattr_delete_fd(td, uap)
532 	struct thread *td;
533 	struct extattr_delete_fd_args /* {
534 		int fd;
535 		int attrnamespace;
536 		const char *attrname;
537 	} */ *uap;
538 {
539 	struct file *fp;
540 	char attrname[EXTATTR_MAXNAMELEN];
541 	cap_rights_t rights;
542 	int error;
543 
544 	AUDIT_ARG_FD(uap->fd);
545 	AUDIT_ARG_VALUE(uap->attrnamespace);
546 	error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
547 	if (error)
548 		return (error);
549 	AUDIT_ARG_TEXT(attrname);
550 
551 	error = getvnode(td, uap->fd,
552 	    cap_rights_init(&rights, CAP_EXTATTR_DELETE), &fp);
553 	if (error)
554 		return (error);
555 
556 	error = extattr_delete_vp(fp->f_vnode, uap->attrnamespace,
557 	    attrname, td);
558 	fdrop(fp, td);
559 	return (error);
560 }
561 
562 int
563 sys_extattr_delete_file(td, uap)
564 	struct thread *td;
565 	struct extattr_delete_file_args /* {
566 		const char *path;
567 		int attrnamespace;
568 		const char *attrname;
569 	} */ *uap;
570 {
571 	struct nameidata nd;
572 	char attrname[EXTATTR_MAXNAMELEN];
573 	int error;
574 
575 	AUDIT_ARG_VALUE(uap->attrnamespace);
576 	error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
577 	if (error)
578 		return(error);
579 	AUDIT_ARG_TEXT(attrname);
580 
581 	NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path, td);
582 	error = namei(&nd);
583 	if (error)
584 		return(error);
585 	NDFREE(&nd, NDF_ONLY_PNBUF);
586 
587 	error = extattr_delete_vp(nd.ni_vp, uap->attrnamespace, attrname, td);
588 	vrele(nd.ni_vp);
589 	return(error);
590 }
591 
592 int
593 sys_extattr_delete_link(td, uap)
594 	struct thread *td;
595 	struct extattr_delete_link_args /* {
596 		const char *path;
597 		int attrnamespace;
598 		const char *attrname;
599 	} */ *uap;
600 {
601 	struct nameidata nd;
602 	char attrname[EXTATTR_MAXNAMELEN];
603 	int error;
604 
605 	AUDIT_ARG_VALUE(uap->attrnamespace);
606 	error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
607 	if (error)
608 		return(error);
609 	AUDIT_ARG_TEXT(attrname);
610 
611 	NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path, td);
612 	error = namei(&nd);
613 	if (error)
614 		return(error);
615 	NDFREE(&nd, NDF_ONLY_PNBUF);
616 
617 	error = extattr_delete_vp(nd.ni_vp, uap->attrnamespace, attrname, td);
618 	vrele(nd.ni_vp);
619 	return(error);
620 }
621 
622 /*-
623  * Retrieve a list of extended attributes on a file or directory.
624  *
625  * Arguments: unlocked vnode "vp", attribute namespace 'attrnamespace",
626  *            userspace buffer pointer "data", buffer length "nbytes",
627  *            thread "td".
628  * Returns: 0 on success, an error number otherwise
629  * Locks: none
630  * References: vp must be a valid reference for the duration of the call
631  */
632 static int
633 extattr_list_vp(struct vnode *vp, int attrnamespace, void *data,
634     size_t nbytes, struct thread *td)
635 {
636 	struct uio auio, *auiop;
637 	size_t size, *sizep;
638 	struct iovec aiov;
639 	ssize_t cnt;
640 	int error;
641 
642 	if (nbytes > IOSIZE_MAX)
643 		return (EINVAL);
644 
645 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
646 
647 	auiop = NULL;
648 	sizep = NULL;
649 	cnt = 0;
650 	if (data != NULL) {
651 		aiov.iov_base = data;
652 		aiov.iov_len = nbytes;
653 		auio.uio_iov = &aiov;
654 		auio.uio_iovcnt = 1;
655 		auio.uio_offset = 0;
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 #ifdef MAC
680 done:
681 #endif
682 	VOP_UNLOCK(vp, 0);
683 	return (error);
684 }
685 
686 
687 int
688 sys_extattr_list_fd(td, uap)
689 	struct thread *td;
690 	struct extattr_list_fd_args /* {
691 		int fd;
692 		int attrnamespace;
693 		void *data;
694 		size_t nbytes;
695 	} */ *uap;
696 {
697 	struct file *fp;
698 	cap_rights_t rights;
699 	int error;
700 
701 	AUDIT_ARG_FD(uap->fd);
702 	AUDIT_ARG_VALUE(uap->attrnamespace);
703 	error = getvnode(td, uap->fd,
704 	    cap_rights_init(&rights, CAP_EXTATTR_LIST), &fp);
705 	if (error)
706 		return (error);
707 
708 	error = extattr_list_vp(fp->f_vnode, uap->attrnamespace, uap->data,
709 	    uap->nbytes, td);
710 
711 	fdrop(fp, td);
712 	return (error);
713 }
714 
715 int
716 sys_extattr_list_file(td, uap)
717 	struct thread*td;
718 	struct extattr_list_file_args /* {
719 		const char *path;
720 		int attrnamespace;
721 		void *data;
722 		size_t nbytes;
723 	} */ *uap;
724 {
725 	struct nameidata nd;
726 	int error;
727 
728 	AUDIT_ARG_VALUE(uap->attrnamespace);
729 	NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path, td);
730 	error = namei(&nd);
731 	if (error)
732 		return (error);
733 	NDFREE(&nd, NDF_ONLY_PNBUF);
734 
735 	error = extattr_list_vp(nd.ni_vp, uap->attrnamespace, uap->data,
736 	    uap->nbytes, td);
737 
738 	vrele(nd.ni_vp);
739 	return (error);
740 }
741 
742 int
743 sys_extattr_list_link(td, uap)
744 	struct thread*td;
745 	struct extattr_list_link_args /* {
746 		const char *path;
747 		int attrnamespace;
748 		void *data;
749 		size_t nbytes;
750 	} */ *uap;
751 {
752 	struct nameidata nd;
753 	int error;
754 
755 	AUDIT_ARG_VALUE(uap->attrnamespace);
756 	NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path,
757 	    td);
758 	error = namei(&nd);
759 	if (error)
760 		return (error);
761 	NDFREE(&nd, NDF_ONLY_PNBUF);
762 
763 	error = extattr_list_vp(nd.ni_vp, uap->attrnamespace, uap->data,
764 	    uap->nbytes, td);
765 
766 	vrele(nd.ni_vp);
767 	return (error);
768 }
769