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