xref: /freebsd/sys/kern/vfs_extattr.c (revision 7778ab7e0cc22f0824eb1d1047a7ef8b4785267a)
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/capability.h>
35 #include <sys/lock.h>
36 #include <sys/mount.h>
37 #include <sys/mutex.h>
38 #include <sys/sysproto.h>
39 #include <sys/fcntl.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 sys_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 	mp = NULL;
90 	filename_vp = NULL;
91 	if (uap->filename != NULL) {
92 		NDINIT(&nd, LOOKUP, MPSAFE | FOLLOW | AUDITVNODE2,
93 		    UIO_USERSPACE, uap->filename, td);
94 		error = namei(&nd);
95 		if (error)
96 			return (error);
97 		fnvfslocked = NDHASGIANT(&nd);
98 		filename_vp = nd.ni_vp;
99 		NDFREE(&nd, NDF_NO_VP_RELE);
100 	}
101 
102 	/* uap->path is always defined. */
103 	NDINIT(&nd, LOOKUP, MPSAFE | FOLLOW | LOCKLEAF | AUDITVNODE1,
104 	    UIO_USERSPACE, uap->path, td);
105 	error = namei(&nd);
106 	if (error)
107 		goto out;
108 	vfslocked = NDHASGIANT(&nd);
109 	mp = nd.ni_vp->v_mount;
110 	error = vfs_busy(mp, 0);
111 	if (error) {
112 		NDFREE(&nd, 0);
113 		mp = NULL;
114 		goto out;
115 	}
116 	VOP_UNLOCK(nd.ni_vp, 0);
117 	error = vn_start_write(nd.ni_vp, &mp_writable, V_WAIT | PCATCH);
118 	NDFREE(&nd, NDF_NO_VP_UNLOCK);
119 	if (error)
120 		goto out;
121 	if (filename_vp != NULL) {
122 		/*
123 		 * uap->filename is not always defined.  If it is,
124 		 * grab a vnode lock, which VFS_EXTATTRCTL() will
125 		 * later release.
126 		 */
127 		error = vn_lock(filename_vp, LK_EXCLUSIVE);
128 		if (error) {
129 			vn_finished_write(mp_writable);
130 			goto out;
131 		}
132 	}
133 
134 	error = VFS_EXTATTRCTL(mp, uap->cmd, filename_vp, uap->attrnamespace,
135 	    uap->attrname != NULL ? attrname : NULL);
136 
137 	vn_finished_write(mp_writable);
138 out:
139 	if (mp != NULL)
140 		vfs_unbusy(mp);
141 
142 	/*
143 	 * VFS_EXTATTRCTL will have unlocked, but not de-ref'd, filename_vp,
144 	 * so vrele it if it is defined.
145 	 */
146 	if (filename_vp != NULL)
147 		vrele(filename_vp);
148 	VFS_UNLOCK_GIANT(fnvfslocked);
149 	VFS_UNLOCK_GIANT(vfslocked);
150 	return (error);
151 }
152 
153 /*-
154  * Set a named extended attribute on a file or directory
155  *
156  * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace",
157  *            kernelspace string pointer "attrname", userspace buffer
158  *            pointer "data", buffer length "nbytes", thread "td".
159  * Returns: 0 on success, an error number otherwise
160  * Locks: none
161  * References: vp must be a valid reference for the duration of the call
162  */
163 static int
164 extattr_set_vp(struct vnode *vp, int attrnamespace, const char *attrname,
165     void *data, size_t nbytes, struct thread *td)
166 {
167 	struct mount *mp;
168 	struct uio auio;
169 	struct iovec aiov;
170 	ssize_t cnt;
171 	int error;
172 
173 	VFS_ASSERT_GIANT(vp->v_mount);
174 	error = vn_start_write(vp, &mp, V_WAIT | PCATCH);
175 	if (error)
176 		return (error);
177 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
178 
179 	aiov.iov_base = data;
180 	aiov.iov_len = nbytes;
181 	auio.uio_iov = &aiov;
182 	auio.uio_iovcnt = 1;
183 	auio.uio_offset = 0;
184 	if (nbytes > INT_MAX) {
185 		error = EINVAL;
186 		goto done;
187 	}
188 	auio.uio_resid = nbytes;
189 	auio.uio_rw = UIO_WRITE;
190 	auio.uio_segflg = UIO_USERSPACE;
191 	auio.uio_td = td;
192 	cnt = nbytes;
193 
194 #ifdef MAC
195 	error = mac_vnode_check_setextattr(td->td_ucred, vp, attrnamespace,
196 	    attrname);
197 	if (error)
198 		goto done;
199 #endif
200 
201 	error = VOP_SETEXTATTR(vp, attrnamespace, attrname, &auio,
202 	    td->td_ucred, td);
203 	cnt -= auio.uio_resid;
204 	td->td_retval[0] = cnt;
205 
206 done:
207 	VOP_UNLOCK(vp, 0);
208 	vn_finished_write(mp);
209 	return (error);
210 }
211 
212 int
213 sys_extattr_set_fd(td, uap)
214 	struct thread *td;
215 	struct extattr_set_fd_args /* {
216 		int fd;
217 		int attrnamespace;
218 		const char *attrname;
219 		void *data;
220 		size_t nbytes;
221 	} */ *uap;
222 {
223 	struct file *fp;
224 	char attrname[EXTATTR_MAXNAMELEN];
225 	int vfslocked, error;
226 
227 	AUDIT_ARG_FD(uap->fd);
228 	AUDIT_ARG_VALUE(uap->attrnamespace);
229 	error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
230 	if (error)
231 		return (error);
232 	AUDIT_ARG_TEXT(attrname);
233 
234 	error = getvnode(td->td_proc->p_fd, uap->fd, CAP_EXTATTR_SET, &fp);
235 	if (error)
236 		return (error);
237 
238 	vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount);
239 	error = extattr_set_vp(fp->f_vnode, uap->attrnamespace,
240 	    attrname, uap->data, uap->nbytes, td);
241 	fdrop(fp, td);
242 	VFS_UNLOCK_GIANT(vfslocked);
243 
244 	return (error);
245 }
246 
247 int
248 sys_extattr_set_file(td, uap)
249 	struct thread *td;
250 	struct extattr_set_file_args /* {
251 		const char *path;
252 		int attrnamespace;
253 		const char *attrname;
254 		void *data;
255 		size_t nbytes;
256 	} */ *uap;
257 {
258 	struct nameidata nd;
259 	char attrname[EXTATTR_MAXNAMELEN];
260 	int vfslocked, error;
261 
262 	AUDIT_ARG_VALUE(uap->attrnamespace);
263 	error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
264 	if (error)
265 		return (error);
266 	AUDIT_ARG_TEXT(attrname);
267 
268 	NDINIT(&nd, LOOKUP, MPSAFE | FOLLOW | AUDITVNODE1, UIO_USERSPACE,
269 	    uap->path, td);
270 	error = namei(&nd);
271 	if (error)
272 		return (error);
273 	NDFREE(&nd, NDF_ONLY_PNBUF);
274 
275 	vfslocked = NDHASGIANT(&nd);
276 	error = extattr_set_vp(nd.ni_vp, uap->attrnamespace, attrname,
277 	    uap->data, uap->nbytes, td);
278 
279 	vrele(nd.ni_vp);
280 	VFS_UNLOCK_GIANT(vfslocked);
281 	return (error);
282 }
283 
284 int
285 sys_extattr_set_link(td, uap)
286 	struct thread *td;
287 	struct extattr_set_link_args /* {
288 		const char *path;
289 		int attrnamespace;
290 		const char *attrname;
291 		void *data;
292 		size_t nbytes;
293 	} */ *uap;
294 {
295 	struct nameidata nd;
296 	char attrname[EXTATTR_MAXNAMELEN];
297 	int vfslocked, error;
298 
299 	AUDIT_ARG_VALUE(uap->attrnamespace);
300 	error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
301 	if (error)
302 		return (error);
303 	AUDIT_ARG_TEXT(attrname);
304 
305 	NDINIT(&nd, LOOKUP, MPSAFE | NOFOLLOW | AUDITVNODE1, UIO_USERSPACE,
306 	    uap->path, td);
307 	error = namei(&nd);
308 	if (error)
309 		return (error);
310 	NDFREE(&nd, NDF_ONLY_PNBUF);
311 
312 	vfslocked = NDHASGIANT(&nd);
313 	error = extattr_set_vp(nd.ni_vp, uap->attrnamespace, attrname,
314 	    uap->data, uap->nbytes, td);
315 
316 	vrele(nd.ni_vp);
317 	VFS_UNLOCK_GIANT(vfslocked);
318 	return (error);
319 }
320 
321 /*-
322  * Get a named extended attribute on a file or directory
323  *
324  * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace",
325  *            kernelspace string pointer "attrname", userspace buffer
326  *            pointer "data", buffer length "nbytes", thread "td".
327  * Returns: 0 on success, an error number otherwise
328  * Locks: none
329  * References: vp must be a valid reference for the duration of the call
330  */
331 static int
332 extattr_get_vp(struct vnode *vp, int attrnamespace, const char *attrname,
333     void *data, size_t nbytes, struct thread *td)
334 {
335 	struct uio auio, *auiop;
336 	struct iovec aiov;
337 	ssize_t cnt;
338 	size_t size, *sizep;
339 	int error;
340 
341 	VFS_ASSERT_GIANT(vp->v_mount);
342 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
343 
344 	/*
345 	 * Slightly unusual semantics: if the user provides a NULL data
346 	 * pointer, they don't want to receive the data, just the maximum
347 	 * read length.
348 	 */
349 	auiop = NULL;
350 	sizep = NULL;
351 	cnt = 0;
352 	if (data != NULL) {
353 		aiov.iov_base = data;
354 		aiov.iov_len = nbytes;
355 		auio.uio_iov = &aiov;
356 		auio.uio_iovcnt = 1;
357 		auio.uio_offset = 0;
358 		if (nbytes > INT_MAX) {
359 			error = EINVAL;
360 			goto done;
361 		}
362 		auio.uio_resid = nbytes;
363 		auio.uio_rw = UIO_READ;
364 		auio.uio_segflg = UIO_USERSPACE;
365 		auio.uio_td = td;
366 		auiop = &auio;
367 		cnt = nbytes;
368 	} else
369 		sizep = &size;
370 
371 #ifdef MAC
372 	error = mac_vnode_check_getextattr(td->td_ucred, vp, attrnamespace,
373 	    attrname);
374 	if (error)
375 		goto done;
376 #endif
377 
378 	error = VOP_GETEXTATTR(vp, attrnamespace, attrname, auiop, sizep,
379 	    td->td_ucred, td);
380 
381 	if (auiop != NULL) {
382 		cnt -= auio.uio_resid;
383 		td->td_retval[0] = cnt;
384 	} else
385 		td->td_retval[0] = size;
386 
387 done:
388 	VOP_UNLOCK(vp, 0);
389 	return (error);
390 }
391 
392 int
393 sys_extattr_get_fd(td, uap)
394 	struct thread *td;
395 	struct extattr_get_fd_args /* {
396 		int fd;
397 		int attrnamespace;
398 		const char *attrname;
399 		void *data;
400 		size_t nbytes;
401 	} */ *uap;
402 {
403 	struct file *fp;
404 	char attrname[EXTATTR_MAXNAMELEN];
405 	int vfslocked, error;
406 
407 	AUDIT_ARG_FD(uap->fd);
408 	AUDIT_ARG_VALUE(uap->attrnamespace);
409 	error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
410 	if (error)
411 		return (error);
412 	AUDIT_ARG_TEXT(attrname);
413 
414 	error = getvnode(td->td_proc->p_fd, uap->fd, CAP_EXTATTR_GET, &fp);
415 	if (error)
416 		return (error);
417 
418 	vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount);
419 	error = extattr_get_vp(fp->f_vnode, uap->attrnamespace,
420 	    attrname, uap->data, uap->nbytes, td);
421 
422 	fdrop(fp, td);
423 	VFS_UNLOCK_GIANT(vfslocked);
424 	return (error);
425 }
426 
427 int
428 sys_extattr_get_file(td, uap)
429 	struct thread *td;
430 	struct extattr_get_file_args /* {
431 		const char *path;
432 		int attrnamespace;
433 		const char *attrname;
434 		void *data;
435 		size_t nbytes;
436 	} */ *uap;
437 {
438 	struct nameidata nd;
439 	char attrname[EXTATTR_MAXNAMELEN];
440 	int vfslocked, error;
441 
442 	AUDIT_ARG_VALUE(uap->attrnamespace);
443 	error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
444 	if (error)
445 		return (error);
446 	AUDIT_ARG_TEXT(attrname);
447 
448 	NDINIT(&nd, LOOKUP, MPSAFE | FOLLOW | AUDITVNODE1, UIO_USERSPACE,
449 	    uap->path, td);
450 	error = namei(&nd);
451 	if (error)
452 		return (error);
453 	NDFREE(&nd, NDF_ONLY_PNBUF);
454 
455 	vfslocked = NDHASGIANT(&nd);
456 	error = extattr_get_vp(nd.ni_vp, uap->attrnamespace, attrname,
457 	    uap->data, uap->nbytes, td);
458 
459 	vrele(nd.ni_vp);
460 	VFS_UNLOCK_GIANT(vfslocked);
461 	return (error);
462 }
463 
464 int
465 sys_extattr_get_link(td, uap)
466 	struct thread *td;
467 	struct extattr_get_link_args /* {
468 		const char *path;
469 		int attrnamespace;
470 		const char *attrname;
471 		void *data;
472 		size_t nbytes;
473 	} */ *uap;
474 {
475 	struct nameidata nd;
476 	char attrname[EXTATTR_MAXNAMELEN];
477 	int vfslocked, error;
478 
479 	AUDIT_ARG_VALUE(uap->attrnamespace);
480 	error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
481 	if (error)
482 		return (error);
483 	AUDIT_ARG_TEXT(attrname);
484 
485 	NDINIT(&nd, LOOKUP, MPSAFE | NOFOLLOW | AUDITVNODE1, UIO_USERSPACE,
486 	    uap->path, td);
487 	error = namei(&nd);
488 	if (error)
489 		return (error);
490 	NDFREE(&nd, NDF_ONLY_PNBUF);
491 
492 	vfslocked = NDHASGIANT(&nd);
493 	error = extattr_get_vp(nd.ni_vp, uap->attrnamespace, attrname,
494 	    uap->data, uap->nbytes, td);
495 
496 	vrele(nd.ni_vp);
497 	VFS_UNLOCK_GIANT(vfslocked);
498 	return (error);
499 }
500 
501 /*
502  * extattr_delete_vp(): Delete a named extended attribute on a file or
503  *                      directory
504  *
505  * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace",
506  *            kernelspace string pointer "attrname", proc "p"
507  * Returns: 0 on success, an error number otherwise
508  * Locks: none
509  * References: vp must be a valid reference for the duration of the call
510  */
511 static int
512 extattr_delete_vp(struct vnode *vp, int attrnamespace, const char *attrname,
513     struct thread *td)
514 {
515 	struct mount *mp;
516 	int error;
517 
518 	VFS_ASSERT_GIANT(vp->v_mount);
519 	error = vn_start_write(vp, &mp, V_WAIT | PCATCH);
520 	if (error)
521 		return (error);
522 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
523 
524 #ifdef MAC
525 	error = mac_vnode_check_deleteextattr(td->td_ucred, vp, attrnamespace,
526 	    attrname);
527 	if (error)
528 		goto done;
529 #endif
530 
531 	error = VOP_DELETEEXTATTR(vp, attrnamespace, attrname, td->td_ucred,
532 	    td);
533 	if (error == EOPNOTSUPP)
534 		error = VOP_SETEXTATTR(vp, attrnamespace, attrname, NULL,
535 		    td->td_ucred, td);
536 #ifdef MAC
537 done:
538 #endif
539 	VOP_UNLOCK(vp, 0);
540 	vn_finished_write(mp);
541 	return (error);
542 }
543 
544 int
545 sys_extattr_delete_fd(td, uap)
546 	struct thread *td;
547 	struct extattr_delete_fd_args /* {
548 		int fd;
549 		int attrnamespace;
550 		const char *attrname;
551 	} */ *uap;
552 {
553 	struct file *fp;
554 	char attrname[EXTATTR_MAXNAMELEN];
555 	int vfslocked, error;
556 
557 	AUDIT_ARG_FD(uap->fd);
558 	AUDIT_ARG_VALUE(uap->attrnamespace);
559 	error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
560 	if (error)
561 		return (error);
562 	AUDIT_ARG_TEXT(attrname);
563 
564 	error = getvnode(td->td_proc->p_fd, uap->fd, CAP_EXTATTR_DELETE,
565 	    &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 sys_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 sys_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 sys_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, CAP_EXTATTR_LIST, &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 sys_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 sys_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