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