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