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