xref: /freebsd/sys/kern/vfs_extattr.c (revision ba3c1f5972d7b90feb6e6da47905ff2757e0fe57)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
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	user_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	user_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	user_extattr_delete_path(struct thread *td, const char *path,
59 		    int attrnamespace, const char *attrname, int follow);
60 static int	user_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 + 1];
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, sizeof(attrname),
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, UIO_USERSPACE,
106 		    uap->filename);
107 		error = namei(&nd);
108 		if (error)
109 			return (error);
110 		filename_vp = nd.ni_vp;
111 		NDFREE_PNBUF(&nd);
112 	}
113 
114 	/* uap->path is always defined. */
115 	NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNODE1, UIO_USERSPACE,
116 	    uap->path);
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 		vput(nd.ni_vp);
124 		NDFREE_PNBUF(&nd);
125 		mp = NULL;
126 		goto out;
127 	}
128 	VOP_UNLOCK(nd.ni_vp);
129 	error = vn_start_write(nd.ni_vp, &mp_writable, V_WAIT | V_PCATCH);
130 	vrele(nd.ni_vp);
131 	NDFREE_PNBUF(&nd);
132 	if (error)
133 		goto out;
134 	if (filename_vp != NULL) {
135 		/*
136 		 * uap->filename is not always defined.  If it is,
137 		 * grab a vnode lock, which VFS_EXTATTRCTL() will
138 		 * later release.
139 		 */
140 		error = vn_lock(filename_vp, LK_EXCLUSIVE);
141 		if (error) {
142 			vn_finished_write(mp_writable);
143 			goto out;
144 		}
145 	}
146 
147 	error = VFS_EXTATTRCTL(mp, uap->cmd, filename_vp, uap->attrnamespace,
148 	    uap->attrname != NULL ? attrname : NULL);
149 
150 	vn_finished_write(mp_writable);
151 out:
152 	if (mp != NULL)
153 		vfs_unbusy(mp);
154 
155 	/*
156 	 * VFS_EXTATTRCTL will have unlocked, but not de-ref'd, filename_vp,
157 	 * so vrele it if it is defined.
158 	 */
159 	if (filename_vp != NULL)
160 		vrele(filename_vp);
161 	return (error);
162 }
163 
164 /*-
165  * Set a named extended attribute on a file or directory
166  *
167  * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace",
168  *            kernelspace string pointer "attrname", userspace buffer
169  *            pointer "data", buffer length "nbytes", thread "td".
170  * Returns: 0 on success, an error number otherwise
171  * Locks: none
172  * References: vp must be a valid reference for the duration of the call
173  */
174 static int
175 extattr_set_vp(struct vnode *vp, int attrnamespace, const char *attrname,
176     void *data, size_t nbytes, struct thread *td)
177 {
178 	struct mount *mp;
179 	struct uio auio;
180 	struct iovec aiov;
181 	ssize_t cnt;
182 	int error;
183 
184 	if (nbytes > IOSIZE_MAX)
185 		return (EINVAL);
186 
187 	error = vn_start_write(vp, &mp, V_WAIT | V_PCATCH);
188 	if (error)
189 		return (error);
190 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
191 
192 	aiov.iov_base = data;
193 	aiov.iov_len = nbytes;
194 	auio.uio_iov = &aiov;
195 	auio.uio_iovcnt = 1;
196 	auio.uio_offset = 0;
197 	auio.uio_resid = nbytes;
198 	auio.uio_rw = UIO_WRITE;
199 	auio.uio_segflg = UIO_USERSPACE;
200 	auio.uio_td = td;
201 	cnt = nbytes;
202 
203 #ifdef MAC
204 	error = mac_vnode_check_setextattr(td->td_ucred, vp, attrnamespace,
205 	    attrname);
206 	if (error)
207 		goto done;
208 #endif
209 
210 	error = VOP_SETEXTATTR(vp, attrnamespace, attrname, &auio,
211 	    td->td_ucred, td);
212 	cnt -= auio.uio_resid;
213 	td->td_retval[0] = cnt;
214 
215 #ifdef MAC
216 done:
217 #endif
218 	VOP_UNLOCK(vp);
219 	vn_finished_write(mp);
220 	return (error);
221 }
222 
223 #ifndef _SYS_SYSPROTO_H_
224 struct extattr_set_fd_args {
225 	int fd;
226 	int attrnamespace;
227 	const char *attrname;
228 	void *data;
229 	size_t nbytes;
230 };
231 #endif
232 int
233 sys_extattr_set_fd(struct thread *td, struct extattr_set_fd_args *uap)
234 {
235 	char attrname[EXTATTR_MAXNAMELEN + 1];
236 	int error;
237 
238 	error = copyinstr(uap->attrname, attrname, sizeof(attrname), NULL);
239 	if (error)
240 		return (error);
241 	return (kern_extattr_set_fd(td, uap->fd, uap->attrnamespace,
242 	    attrname, uap->data, uap->nbytes));
243 }
244 
245 int
246 kern_extattr_set_fd(struct thread *td, int fd, int attrnamespace,
247     const char *attrname, void *data, size_t nbytes)
248 {
249 	struct file *fp;
250 	cap_rights_t rights;
251 	int error;
252 
253 	AUDIT_ARG_FD(fd);
254 	AUDIT_ARG_VALUE(attrnamespace);
255 	AUDIT_ARG_TEXT(attrname);
256 
257 	error = getvnode_path(td, fd,
258 	    cap_rights_init_one(&rights, CAP_EXTATTR_SET), &fp);
259 	if (error)
260 		return (error);
261 
262 	error = extattr_set_vp(fp->f_vnode, attrnamespace,
263 	    attrname, data, nbytes, td);
264 	fdrop(fp, td);
265 
266 	return (error);
267 }
268 
269 #ifndef _SYS_SYSPROTO_H_
270 struct extattr_set_file_args {
271 	const char *path;
272 	int attrnamespace;
273 	const char *attrname;
274 	void *data;
275 	size_t nbytes;
276 };
277 #endif
278 int
279 sys_extattr_set_file(struct thread *td, struct extattr_set_file_args *uap)
280 {
281 
282 	return (user_extattr_set_path(td, uap->path, uap->attrnamespace,
283 	    uap->attrname, uap->data, uap->nbytes, FOLLOW));
284 }
285 
286 #ifndef _SYS_SYSPROTO_H_
287 struct extattr_set_link_args {
288 	const char *path;
289 	int attrnamespace;
290 	const char *attrname;
291 	void *data;
292 	size_t nbytes;
293 };
294 #endif
295 int
296 sys_extattr_set_link(struct thread *td, struct extattr_set_link_args *uap)
297 {
298 
299 	return (user_extattr_set_path(td, uap->path, uap->attrnamespace,
300 	    uap->attrname, uap->data, uap->nbytes, NOFOLLOW));
301 }
302 
303 static int
304 user_extattr_set_path(struct thread *td, const char *path, int attrnamespace,
305     const char *uattrname, void *data, size_t nbytes, int follow)
306 {
307 	char attrname[EXTATTR_MAXNAMELEN + 1];
308 	int error;
309 
310 	error = copyinstr(uattrname, attrname, sizeof(attrname), NULL);
311 	if (error)
312 		return (error);
313 	return (kern_extattr_set_path(td, path, attrnamespace,
314 	    attrname, data, nbytes, follow, UIO_USERSPACE));
315 }
316 
317 int
318 kern_extattr_set_path(struct thread *td, const char *path, int attrnamespace,
319     const char *attrname, void *data, size_t nbytes, int follow,
320     enum uio_seg pathseg)
321 {
322 	struct nameidata nd;
323 	int error;
324 
325 	AUDIT_ARG_VALUE(attrnamespace);
326 	AUDIT_ARG_TEXT(attrname);
327 
328 	NDINIT(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path);
329 	error = namei(&nd);
330 	if (error)
331 		return (error);
332 	NDFREE_PNBUF(&nd);
333 
334 	error = extattr_set_vp(nd.ni_vp, attrnamespace, attrname, data,
335 	    nbytes, td);
336 
337 	vrele(nd.ni_vp);
338 	return (error);
339 }
340 
341 /*-
342  * Get a named extended attribute on a file or directory
343  *
344  * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace",
345  *            kernelspace string pointer "attrname", userspace buffer
346  *            pointer "data", buffer length "nbytes", thread "td".
347  * Returns: 0 on success, an error number otherwise
348  * Locks: none
349  * References: vp must be a valid reference for the duration of the call
350  */
351 static int
352 extattr_get_vp(struct vnode *vp, int attrnamespace, const char *attrname,
353     void *data, size_t nbytes, struct thread *td)
354 {
355 	struct uio auio, *auiop;
356 	struct iovec aiov;
357 	ssize_t cnt;
358 	size_t size, *sizep;
359 	int error;
360 
361 	if (nbytes > IOSIZE_MAX)
362 		return (EINVAL);
363 
364 	vn_lock(vp, LK_SHARED | LK_RETRY);
365 
366 	/*
367 	 * Slightly unusual semantics: if the user provides a NULL data
368 	 * pointer, they don't want to receive the data, just the maximum
369 	 * read length.
370 	 */
371 	auiop = NULL;
372 	sizep = NULL;
373 	cnt = 0;
374 	if (data != NULL) {
375 		aiov.iov_base = data;
376 		aiov.iov_len = nbytes;
377 		auio.uio_iov = &aiov;
378 		auio.uio_iovcnt = 1;
379 		auio.uio_offset = 0;
380 		auio.uio_resid = nbytes;
381 		auio.uio_rw = UIO_READ;
382 		auio.uio_segflg = UIO_USERSPACE;
383 		auio.uio_td = td;
384 		auiop = &auio;
385 		cnt = nbytes;
386 	} else
387 		sizep = &size;
388 
389 #ifdef MAC
390 	error = mac_vnode_check_getextattr(td->td_ucred, vp, attrnamespace,
391 	    attrname);
392 	if (error)
393 		goto done;
394 #endif
395 
396 	error = VOP_GETEXTATTR(vp, attrnamespace, attrname, auiop, sizep,
397 	    td->td_ucred, td);
398 
399 	if (auiop != NULL) {
400 		cnt -= auio.uio_resid;
401 		td->td_retval[0] = cnt;
402 	} else
403 		td->td_retval[0] = size;
404 #ifdef MAC
405 done:
406 #endif
407 	VOP_UNLOCK(vp);
408 	return (error);
409 }
410 
411 #ifndef _SYS_SYSPROTO_H_
412 struct extattr_get_fd_args {
413 	int fd;
414 	int attrnamespace;
415 	const char *attrname;
416 	void *data;
417 	size_t nbytes;
418 };
419 #endif
420 int
421 sys_extattr_get_fd(struct thread *td, struct extattr_get_fd_args *uap)
422 {
423 	char attrname[EXTATTR_MAXNAMELEN + 1];
424 	int error;
425 
426 	error = copyinstr(uap->attrname, attrname, sizeof(attrname), NULL);
427 	if (error)
428 		return (error);
429 	return (kern_extattr_get_fd(td, uap->fd, uap->attrnamespace,
430 	    attrname, uap->data, uap->nbytes));
431 }
432 
433 int
434 kern_extattr_get_fd(struct thread *td, int fd, int attrnamespace,
435     const char *attrname, void *data, size_t nbytes)
436 {
437 	struct file *fp;
438 	cap_rights_t rights;
439 	int error;
440 
441 	AUDIT_ARG_FD(fd);
442 	AUDIT_ARG_VALUE(attrnamespace);
443 	AUDIT_ARG_TEXT(attrname);
444 
445 	error = getvnode_path(td, fd,
446 	    cap_rights_init_one(&rights, CAP_EXTATTR_GET), &fp);
447 	if (error)
448 		return (error);
449 
450 	error = extattr_get_vp(fp->f_vnode, attrnamespace,
451 	    attrname, data, nbytes, td);
452 
453 	fdrop(fp, td);
454 	return (error);
455 }
456 
457 #ifndef _SYS_SYSPROTO_H_
458 struct extattr_get_file_args {
459 	const char *path;
460 	int attrnamespace;
461 	const char *attrname;
462 	void *data;
463 	size_t nbytes;
464 };
465 #endif
466 int
467 sys_extattr_get_file(struct thread *td, struct extattr_get_file_args *uap)
468 {
469 	return (user_extattr_get_path(td, uap->path, uap->attrnamespace,
470 	    uap->attrname, uap->data, uap->nbytes, FOLLOW));
471 }
472 
473 #ifndef _SYS_SYSPROTO_H_
474 struct extattr_get_link_args {
475 	const char *path;
476 	int attrnamespace;
477 	const char *attrname;
478 	void *data;
479 	size_t nbytes;
480 };
481 #endif
482 int
483 sys_extattr_get_link(struct thread *td, struct extattr_get_link_args *uap)
484 {
485 	return (user_extattr_get_path(td, uap->path, uap->attrnamespace,
486 	    uap->attrname, uap->data, uap->nbytes, NOFOLLOW));
487 }
488 
489 static int
490 user_extattr_get_path(struct thread *td, const char *path, int attrnamespace,
491     const char *uattrname, void *data, size_t nbytes, int follow)
492 {
493 	char attrname[EXTATTR_MAXNAMELEN + 1];
494 	int error;
495 
496 	error = copyinstr(uattrname, attrname, sizeof(attrname), NULL);
497 	if (error)
498 		return (error);
499 	return (kern_extattr_get_path(td, path, attrnamespace,
500 	    attrname, data, nbytes, follow, UIO_USERSPACE));
501 }
502 
503 int
504 kern_extattr_get_path(struct thread *td, const char *path, int attrnamespace,
505     const char *attrname, void *data, size_t nbytes, int follow,
506     enum uio_seg pathseg)
507 {
508 	struct nameidata nd;
509 	int error;
510 
511 	AUDIT_ARG_VALUE(attrnamespace);
512 	AUDIT_ARG_TEXT(attrname);
513 
514 	NDINIT(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path);
515 	error = namei(&nd);
516 	if (error)
517 		return (error);
518 	NDFREE_PNBUF(&nd);
519 
520 	error = extattr_get_vp(nd.ni_vp, attrnamespace, attrname, data,
521 	    nbytes, td);
522 
523 	vrele(nd.ni_vp);
524 	return (error);
525 }
526 
527 /*
528  * extattr_delete_vp(): Delete a named extended attribute on a file or
529  *                      directory
530  *
531  * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace",
532  *            kernelspace string pointer "attrname", proc "p"
533  * Returns: 0 on success, an error number otherwise
534  * Locks: none
535  * References: vp must be a valid reference for the duration of the call
536  */
537 static int
538 extattr_delete_vp(struct vnode *vp, int attrnamespace, const char *attrname,
539     struct thread *td)
540 {
541 	struct mount *mp;
542 	int error;
543 
544 	error = vn_start_write(vp, &mp, V_WAIT | V_PCATCH);
545 	if (error)
546 		return (error);
547 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
548 
549 #ifdef MAC
550 	error = mac_vnode_check_deleteextattr(td->td_ucred, vp, attrnamespace,
551 	    attrname);
552 	if (error)
553 		goto done;
554 #endif
555 
556 	error = VOP_DELETEEXTATTR(vp, attrnamespace, attrname, td->td_ucred,
557 	    td);
558 	if (error == EOPNOTSUPP)
559 		error = VOP_SETEXTATTR(vp, attrnamespace, attrname, NULL,
560 		    td->td_ucred, td);
561 #ifdef MAC
562 done:
563 #endif
564 	VOP_UNLOCK(vp);
565 	vn_finished_write(mp);
566 	return (error);
567 }
568 
569 #ifndef _SYS_SYSPROTO_H_
570 struct extattr_delete_fd_args {
571 	int fd;
572 	int attrnamespace;
573 	const char *attrname;
574 };
575 #endif
576 int
577 sys_extattr_delete_fd(struct thread *td, struct extattr_delete_fd_args *uap)
578 {
579 	char attrname[EXTATTR_MAXNAMELEN + 1];
580 	int error;
581 
582 	error = copyinstr(uap->attrname, attrname, sizeof(attrname), NULL);
583 	if (error)
584 		return (error);
585 	return (kern_extattr_delete_fd(td, uap->fd, uap->attrnamespace,
586 	    attrname));
587 }
588 
589 int
590 kern_extattr_delete_fd(struct thread *td, int fd, int attrnamespace,
591     const char *attrname)
592 {
593 	struct file *fp;
594 	cap_rights_t rights;
595 	int error;
596 
597 	AUDIT_ARG_FD(fd);
598 	AUDIT_ARG_VALUE(attrnamespace);
599 	AUDIT_ARG_TEXT(attrname);
600 
601 	error = getvnode_path(td, fd,
602 	    cap_rights_init_one(&rights, CAP_EXTATTR_DELETE), &fp);
603 	if (error)
604 		return (error);
605 
606 	error = extattr_delete_vp(fp->f_vnode, attrnamespace,
607 	    attrname, td);
608 	fdrop(fp, td);
609 	return (error);
610 }
611 
612 #ifndef _SYS_SYSPROTO_H_
613 struct extattr_delete_file_args {
614 	const char *path;
615 	int attrnamespace;
616 	const char *attrname;
617 };
618 #endif
619 int
620 sys_extattr_delete_file(struct thread *td, struct extattr_delete_file_args *uap)
621 {
622 
623 	return (user_extattr_delete_path(td, uap->path, uap->attrnamespace,
624 	    uap->attrname, FOLLOW));
625 }
626 
627 #ifndef _SYS_SYSPROTO_H_
628 struct extattr_delete_link_args {
629 	const char *path;
630 	int attrnamespace;
631 	const char *attrname;
632 };
633 #endif
634 int
635 sys_extattr_delete_link(struct thread *td, struct extattr_delete_link_args *uap)
636 {
637 
638 	return (user_extattr_delete_path(td, uap->path, uap->attrnamespace,
639 	    uap->attrname, NOFOLLOW));
640 }
641 
642 int
643 user_extattr_delete_path(struct thread *td, const char *path, int attrnamespace,
644     const char *uattrname, int follow)
645 {
646 	char attrname[EXTATTR_MAXNAMELEN + 1];
647 	int error;
648 
649 	error = copyinstr(uattrname, attrname, sizeof(attrname), NULL);
650 	if (error)
651 		return(error);
652 	return (kern_extattr_delete_path(td, path, attrnamespace,
653 	    attrname, follow, UIO_USERSPACE));
654 }
655 
656 int
657 kern_extattr_delete_path(struct thread *td, const char *path, int attrnamespace,
658     const char *attrname, int follow, enum uio_seg pathseg)
659 {
660 	struct nameidata nd;
661 	int error;
662 
663 	AUDIT_ARG_VALUE(attrnamespace);
664 	AUDIT_ARG_TEXT(attrname);
665 
666 	NDINIT(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path);
667 	error = namei(&nd);
668 	if (error)
669 		return(error);
670 	NDFREE_PNBUF(&nd);
671 
672 	error = extattr_delete_vp(nd.ni_vp, attrnamespace, attrname, td);
673 	vrele(nd.ni_vp);
674 	return(error);
675 }
676 
677 /*-
678  * Retrieve a list of extended attributes on a file or directory.
679  *
680  * Arguments: unlocked vnode "vp", attribute namespace 'attrnamespace",
681  *            userspace buffer pointer "data", buffer length "nbytes",
682  *            thread "td".
683  * Returns: 0 on success, an error number otherwise
684  * Locks: none
685  * References: vp must be a valid reference for the duration of the call
686  */
687 static int
688 extattr_list_vp(struct vnode *vp, int attrnamespace, struct uio *auiop,
689     struct thread *td)
690 {
691 	size_t size, *sizep;
692 	ssize_t cnt;
693 	int error;
694 
695 	sizep = NULL;
696 	cnt = 0;
697 	if (auiop != NULL) {
698 		if (auiop->uio_resid > IOSIZE_MAX)
699 			return (EINVAL);
700 		cnt = auiop->uio_resid;
701 	} else
702 		sizep = &size;
703 
704 	vn_lock(vp, LK_SHARED | LK_RETRY);
705 
706 #ifdef MAC
707 	error = mac_vnode_check_listextattr(td->td_ucred, vp, attrnamespace);
708 	if (error) {
709 		VOP_UNLOCK(vp);
710 		return (error);
711 	}
712 #endif
713 
714 	error = VOP_LISTEXTATTR(vp, attrnamespace, auiop, sizep,
715 	    td->td_ucred, td);
716 	VOP_UNLOCK(vp);
717 
718 	if (auiop != NULL) {
719 		cnt -= auiop->uio_resid;
720 		td->td_retval[0] = cnt;
721 	} else
722 		td->td_retval[0] = size;
723 	return (error);
724 }
725 
726 #ifndef _SYS_SYSPROTO_H_
727 struct extattr_list_fd_args {
728 	int fd;
729 	int attrnamespace;
730 	void *data;
731 	size_t nbytes;
732 };
733 #endif
734 int
735 sys_extattr_list_fd(struct thread *td, struct extattr_list_fd_args *uap)
736 {
737 	struct uio auio, *auiop;
738 	struct iovec aiov;
739 
740 	if (uap->data != NULL) {
741 		aiov.iov_base = uap->data;
742 		aiov.iov_len = uap->nbytes;
743 		auio.uio_iov = &aiov;
744 		auio.uio_iovcnt = 1;
745 		auio.uio_offset = 0;
746 		auio.uio_resid = uap->nbytes;
747 		auio.uio_rw = UIO_READ;
748 		auio.uio_segflg = UIO_USERSPACE;
749 		auio.uio_td = td;
750 		auiop = &auio;
751 	} else
752 		auiop = NULL;
753 
754 	return (kern_extattr_list_fd(td, uap->fd, uap->attrnamespace,
755 	    auiop));
756 }
757 
758 int
759 kern_extattr_list_fd(struct thread *td, int fd, int attrnamespace,
760     struct uio *auiop)
761 {
762 	struct file *fp;
763 	cap_rights_t rights;
764 	int error;
765 
766 	AUDIT_ARG_FD(fd);
767 	AUDIT_ARG_VALUE(attrnamespace);
768 	error = getvnode_path(td, fd,
769 	    cap_rights_init_one(&rights, CAP_EXTATTR_LIST), &fp);
770 	if (error)
771 		return (error);
772 
773 	error = extattr_list_vp(fp->f_vnode, attrnamespace, auiop, td);
774 
775 	fdrop(fp, td);
776 	return (error);
777 }
778 
779 #ifndef _SYS_SYSPROTO_H_
780 struct extattr_list_file_args {
781 	const char *path;
782 	int attrnamespace;
783 	void *data;
784 	size_t nbytes;
785 }
786 #endif
787 int
788 sys_extattr_list_file(struct thread *td, struct extattr_list_file_args *uap)
789 {
790 
791 	return (user_extattr_list_path(td, uap->path, uap->attrnamespace,
792 	    uap->data, uap->nbytes, FOLLOW));
793 }
794 
795 #ifndef _SYS_SYSPROTO_H_
796 struct extattr_list_link_args {
797 	const char *path;
798 	int attrnamespace;
799 	void *data;
800 	size_t nbytes;
801 };
802 #endif
803 int
804 sys_extattr_list_link(struct thread *td, struct extattr_list_link_args *uap)
805 {
806 
807 	return (user_extattr_list_path(td, uap->path, uap->attrnamespace,
808 	    uap->data, uap->nbytes, NOFOLLOW));
809 }
810 
811 static int
812 user_extattr_list_path(struct thread *td, const char *path, int attrnamespace,
813     void *data, size_t nbytes, int follow)
814 {
815 	struct uio auio, *auiop;
816 	struct iovec aiov;
817 
818 	if (data != NULL) {
819 		aiov.iov_base = data;
820 		aiov.iov_len = nbytes;
821 		auio.uio_iov = &aiov;
822 		auio.uio_iovcnt = 1;
823 		auio.uio_offset = 0;
824 		auio.uio_resid = nbytes;
825 		auio.uio_rw = UIO_READ;
826 		auio.uio_segflg = UIO_USERSPACE;
827 		auio.uio_td = td;
828 		auiop = &auio;
829 	} else
830 		auiop = NULL;
831 
832 	return (kern_extattr_list_path(td, path, attrnamespace,
833 	    auiop, follow, UIO_USERSPACE));
834 }
835 
836 int
837 kern_extattr_list_path(struct thread *td, const char *path, int attrnamespace,
838     struct uio *auiop, int follow, enum uio_seg pathseg)
839 {
840 	struct nameidata nd;
841 	int error;
842 
843 	AUDIT_ARG_VALUE(attrnamespace);
844 	NDINIT(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path);
845 	error = namei(&nd);
846 	if (error)
847 		return (error);
848 	NDFREE_PNBUF(&nd);
849 
850 	error = extattr_list_vp(nd.ni_vp, attrnamespace, auiop, td);
851 
852 	vrele(nd.ni_vp);
853 	return (error);
854 }
855