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