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