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