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 */
57int
58sys_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);
138out:
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 */
163static int
164extattr_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 > IOSIZE_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
206done:
207	VOP_UNLOCK(vp, 0);
208	vn_finished_write(mp);
209	return (error);
210}
211
212int
213sys_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
247int
248sys_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
284int
285sys_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 */
331static int
332extattr_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_SHARED | 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 > IOSIZE_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
387done:
388	VOP_UNLOCK(vp, 0);
389	return (error);
390}
391
392int
393sys_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
427int
428sys_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
464int
465sys_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 */
511static int
512extattr_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
537done:
538#endif
539	VOP_UNLOCK(vp, 0);
540	vn_finished_write(mp);
541	return (error);
542}
543
544int
545sys_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
577int
578sys_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
610int
611sys_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 */
653static int
654extattr_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 > IOSIZE_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
703done:
704	VOP_UNLOCK(vp, 0);
705	return (error);
706}
707
708
709int
710sys_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
737int
738sys_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
767int
768sys_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