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