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