]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/kern/vfs_extattr.c
libevent: Import libevent 2.1.12
[FreeBSD/FreeBSD.git] / sys / kern / vfs_extattr.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 1999-2001 Robert N. M. Watson
5  * All rights reserved.
6  *
7  * This software was developed by Robert Watson for the TrustedBSD Project.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30
31 #include <sys/cdefs.h>
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/capsicum.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 #include <sys/syscallsubr.h>
47
48 #include <security/audit/audit.h>
49 #include <security/mac/mac_framework.h>
50
51 static int      user_extattr_set_path(struct thread *td, const char *path,
52                     int attrnamespace, const char *attrname, void *data,
53                     size_t nbytes, int follow);
54 static int      user_extattr_get_path(struct thread *td, const char *path,
55                     int attrnamespace, const char *attrname, void *data,
56                     size_t nbytes, int follow);
57 static int      user_extattr_delete_path(struct thread *td, const char *path,
58                     int attrnamespace, const char *attrname, int follow);
59 static int      user_extattr_list_path(struct thread *td, const char *path,
60                     int attrnamespace, void *data, size_t nbytes, int follow);
61
62 /*
63  * Syscall to push extended attribute configuration information into the VFS.
64  * Accepts a path, which it converts to a mountpoint, as well as a command
65  * (int cmd), and attribute name and misc data.
66  *
67  * Currently this is used only by UFS1 extended attributes.
68  */
69 #ifndef _SYS_SYSPROTO_H_
70 struct extattrctl_args {
71         const char *path;
72         int cmd;
73         const char *filename;
74         int attrnamespace;
75         const char *attrname;
76 };
77 #endif
78 int
79 sys_extattrctl(struct thread *td, struct extattrctl_args *uap)
80 {
81         struct vnode *filename_vp;
82         struct nameidata nd;
83         struct mount *mp, *mp_writable;
84         char attrname[EXTATTR_MAXNAMELEN + 1];
85         int error;
86
87         AUDIT_ARG_CMD(uap->cmd);
88         AUDIT_ARG_VALUE(uap->attrnamespace);
89         /*
90          * uap->attrname is not always defined.  We check again later when we
91          * invoke the VFS call so as to pass in NULL there if needed.
92          */
93         if (uap->attrname != NULL) {
94                 error = copyinstr(uap->attrname, attrname, sizeof(attrname),
95                     NULL);
96                 if (error)
97                         return (error);
98         }
99         AUDIT_ARG_TEXT(attrname);
100
101         mp = NULL;
102         filename_vp = NULL;
103         if (uap->filename != NULL) {
104                 NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE2, UIO_USERSPACE,
105                     uap->filename);
106                 error = namei(&nd);
107                 if (error)
108                         return (error);
109                 filename_vp = nd.ni_vp;
110                 NDFREE_PNBUF(&nd);
111         }
112
113         /* uap->path is always defined. */
114         NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNODE1, UIO_USERSPACE,
115             uap->path);
116         error = namei(&nd);
117         if (error)
118                 goto out;
119         mp = nd.ni_vp->v_mount;
120         error = vfs_busy(mp, 0);
121         if (error) {
122                 vput(nd.ni_vp);
123                 NDFREE_PNBUF(&nd);
124                 mp = NULL;
125                 goto out;
126         }
127         VOP_UNLOCK(nd.ni_vp);
128         error = vn_start_write(nd.ni_vp, &mp_writable, V_WAIT | V_PCATCH);
129         vrele(nd.ni_vp);
130         NDFREE_PNBUF(&nd);
131         if (error)
132                 goto out;
133         if (filename_vp != NULL) {
134                 /*
135                  * uap->filename is not always defined.  If it is,
136                  * grab a vnode lock, which VFS_EXTATTRCTL() will
137                  * later release.
138                  */
139                 error = vn_lock(filename_vp, LK_EXCLUSIVE);
140                 if (error) {
141                         vn_finished_write(mp_writable);
142                         goto out;
143                 }
144         }
145
146         error = VFS_EXTATTRCTL(mp, uap->cmd, filename_vp, uap->attrnamespace,
147             uap->attrname != NULL ? attrname : NULL);
148
149         vn_finished_write(mp_writable);
150 out:
151         if (mp != NULL)
152                 vfs_unbusy(mp);
153
154         /*
155          * VFS_EXTATTRCTL will have unlocked, but not de-ref'd, filename_vp,
156          * so vrele it if it is defined.
157          */
158         if (filename_vp != NULL)
159                 vrele(filename_vp);
160         return (error);
161 }
162
163 /*-
164  * Set a named extended attribute on a file or directory
165  *
166  * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace",
167  *            kernelspace string pointer "attrname", userspace buffer
168  *            pointer "data", buffer length "nbytes", thread "td".
169  * Returns: 0 on success, an error number otherwise
170  * Locks: none
171  * References: vp must be a valid reference for the duration of the call
172  */
173 static int
174 extattr_set_vp(struct vnode *vp, int attrnamespace, const char *attrname,
175     void *data, size_t nbytes, struct thread *td)
176 {
177         struct mount *mp;
178         struct uio auio;
179         struct iovec aiov;
180         ssize_t cnt;
181         int error;
182
183         if (nbytes > IOSIZE_MAX)
184                 return (EINVAL);
185
186         error = vn_start_write(vp, &mp, V_WAIT | V_PCATCH);
187         if (error)
188                 return (error);
189         vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
190
191         aiov.iov_base = data;
192         aiov.iov_len = nbytes;
193         auio.uio_iov = &aiov;
194         auio.uio_iovcnt = 1;
195         auio.uio_offset = 0;
196         auio.uio_resid = nbytes;
197         auio.uio_rw = UIO_WRITE;
198         auio.uio_segflg = UIO_USERSPACE;
199         auio.uio_td = td;
200         cnt = nbytes;
201
202 #ifdef MAC
203         error = mac_vnode_check_setextattr(td->td_ucred, vp, attrnamespace,
204             attrname);
205         if (error)
206                 goto done;
207 #endif
208
209         error = VOP_SETEXTATTR(vp, attrnamespace, attrname, &auio,
210             td->td_ucred, td);
211         cnt -= auio.uio_resid;
212         td->td_retval[0] = cnt;
213
214 #ifdef MAC
215 done:
216 #endif
217         VOP_UNLOCK(vp);
218         vn_finished_write(mp);
219         return (error);
220 }
221
222 #ifndef _SYS_SYSPROTO_H_
223 struct extattr_set_fd_args {
224         int fd;
225         int attrnamespace;
226         const char *attrname;
227         void *data;
228         size_t nbytes;
229 };
230 #endif
231 int
232 sys_extattr_set_fd(struct thread *td, struct extattr_set_fd_args *uap)
233 {
234         char attrname[EXTATTR_MAXNAMELEN + 1];
235         int error;
236
237         error = copyinstr(uap->attrname, attrname, sizeof(attrname), NULL);
238         if (error)
239                 return (error);
240         return (kern_extattr_set_fd(td, uap->fd, uap->attrnamespace,
241             attrname, uap->data, uap->nbytes));
242 }
243
244 int
245 kern_extattr_set_fd(struct thread *td, int fd, int attrnamespace,
246     const char *attrname, void *data, size_t nbytes)
247 {
248         struct file *fp;
249         cap_rights_t rights;
250         int error;
251
252         AUDIT_ARG_FD(fd);
253         AUDIT_ARG_VALUE(attrnamespace);
254         AUDIT_ARG_TEXT(attrname);
255
256         error = getvnode_path(td, fd,
257             cap_rights_init_one(&rights, CAP_EXTATTR_SET), &fp);
258         if (error)
259                 return (error);
260
261         error = extattr_set_vp(fp->f_vnode, attrnamespace,
262             attrname, data, nbytes, td);
263         fdrop(fp, td);
264
265         return (error);
266 }
267
268 #ifndef _SYS_SYSPROTO_H_
269 struct extattr_set_file_args {
270         const char *path;
271         int attrnamespace;
272         const char *attrname;
273         void *data;
274         size_t nbytes;
275 };
276 #endif
277 int
278 sys_extattr_set_file(struct thread *td, struct extattr_set_file_args *uap)
279 {
280
281         return (user_extattr_set_path(td, uap->path, uap->attrnamespace,
282             uap->attrname, uap->data, uap->nbytes, FOLLOW));
283 }
284
285 #ifndef _SYS_SYSPROTO_H_
286 struct extattr_set_link_args {
287         const char *path;
288         int attrnamespace;
289         const char *attrname;
290         void *data;
291         size_t nbytes;
292 };
293 #endif
294 int
295 sys_extattr_set_link(struct thread *td, struct extattr_set_link_args *uap)
296 {
297
298         return (user_extattr_set_path(td, uap->path, uap->attrnamespace,
299             uap->attrname, uap->data, uap->nbytes, NOFOLLOW));
300 }
301
302 static int
303 user_extattr_set_path(struct thread *td, const char *path, int attrnamespace,
304     const char *uattrname, void *data, size_t nbytes, int follow)
305 {
306         char attrname[EXTATTR_MAXNAMELEN + 1];
307         int error;
308
309         error = copyinstr(uattrname, attrname, sizeof(attrname), NULL);
310         if (error)
311                 return (error);
312         return (kern_extattr_set_path(td, path, attrnamespace,
313             attrname, data, nbytes, follow, UIO_USERSPACE));
314 }
315
316 int
317 kern_extattr_set_path(struct thread *td, const char *path, int attrnamespace,
318     const char *attrname, void *data, size_t nbytes, int follow,
319     enum uio_seg pathseg)
320 {
321         struct nameidata nd;
322         int error;
323
324         AUDIT_ARG_VALUE(attrnamespace);
325         AUDIT_ARG_TEXT(attrname);
326
327         NDINIT(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path);
328         error = namei(&nd);
329         if (error)
330                 return (error);
331         NDFREE_PNBUF(&nd);
332
333         error = extattr_set_vp(nd.ni_vp, attrnamespace, attrname, data,
334             nbytes, td);
335
336         vrele(nd.ni_vp);
337         return (error);
338 }
339
340 /*-
341  * Get a named extended attribute on a file or directory
342  *
343  * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace",
344  *            kernelspace string pointer "attrname", userspace buffer
345  *            pointer "data", buffer length "nbytes", thread "td".
346  * Returns: 0 on success, an error number otherwise
347  * Locks: none
348  * References: vp must be a valid reference for the duration of the call
349  */
350 static int
351 extattr_get_vp(struct vnode *vp, int attrnamespace, const char *attrname,
352     void *data, size_t nbytes, struct thread *td)
353 {
354         struct uio auio, *auiop;
355         struct iovec aiov;
356         ssize_t cnt;
357         size_t size, *sizep;
358         int error;
359
360         if (nbytes > IOSIZE_MAX)
361                 return (EINVAL);
362
363         vn_lock(vp, LK_SHARED | LK_RETRY);
364
365         /*
366          * Slightly unusual semantics: if the user provides a NULL data
367          * pointer, they don't want to receive the data, just the maximum
368          * read length.
369          */
370         auiop = NULL;
371         sizep = NULL;
372         cnt = 0;
373         if (data != NULL) {
374                 aiov.iov_base = data;
375                 aiov.iov_len = nbytes;
376                 auio.uio_iov = &aiov;
377                 auio.uio_iovcnt = 1;
378                 auio.uio_offset = 0;
379                 auio.uio_resid = nbytes;
380                 auio.uio_rw = UIO_READ;
381                 auio.uio_segflg = UIO_USERSPACE;
382                 auio.uio_td = td;
383                 auiop = &auio;
384                 cnt = nbytes;
385         } else
386                 sizep = &size;
387
388 #ifdef MAC
389         error = mac_vnode_check_getextattr(td->td_ucred, vp, attrnamespace,
390             attrname);
391         if (error)
392                 goto done;
393 #endif
394
395         error = VOP_GETEXTATTR(vp, attrnamespace, attrname, auiop, sizep,
396             td->td_ucred, td);
397
398         if (auiop != NULL) {
399                 cnt -= auio.uio_resid;
400                 td->td_retval[0] = cnt;
401         } else
402                 td->td_retval[0] = size;
403 #ifdef MAC
404 done:
405 #endif
406         VOP_UNLOCK(vp);
407         return (error);
408 }
409
410 #ifndef _SYS_SYSPROTO_H_
411 struct extattr_get_fd_args {
412         int fd;
413         int attrnamespace;
414         const char *attrname;
415         void *data;
416         size_t nbytes;
417 };
418 #endif
419 int
420 sys_extattr_get_fd(struct thread *td, struct extattr_get_fd_args *uap)
421 {
422         char attrname[EXTATTR_MAXNAMELEN + 1];
423         int error;
424
425         error = copyinstr(uap->attrname, attrname, sizeof(attrname), NULL);
426         if (error)
427                 return (error);
428         return (kern_extattr_get_fd(td, uap->fd, uap->attrnamespace,
429             attrname, uap->data, uap->nbytes));
430 }
431
432 int
433 kern_extattr_get_fd(struct thread *td, int fd, int attrnamespace,
434     const char *attrname, void *data, size_t nbytes)
435 {
436         struct file *fp;
437         cap_rights_t rights;
438         int error;
439
440         AUDIT_ARG_FD(fd);
441         AUDIT_ARG_VALUE(attrnamespace);
442         AUDIT_ARG_TEXT(attrname);
443
444         error = getvnode_path(td, fd,
445             cap_rights_init_one(&rights, CAP_EXTATTR_GET), &fp);
446         if (error)
447                 return (error);
448
449         error = extattr_get_vp(fp->f_vnode, attrnamespace,
450             attrname, data, nbytes, td);
451
452         fdrop(fp, td);
453         return (error);
454 }
455
456 #ifndef _SYS_SYSPROTO_H_
457 struct extattr_get_file_args {
458         const char *path;
459         int attrnamespace;
460         const char *attrname;
461         void *data;
462         size_t nbytes;
463 };
464 #endif
465 int
466 sys_extattr_get_file(struct thread *td, struct extattr_get_file_args *uap)
467 {
468         return (user_extattr_get_path(td, uap->path, uap->attrnamespace,
469             uap->attrname, uap->data, uap->nbytes, FOLLOW));
470 }
471
472 #ifndef _SYS_SYSPROTO_H_
473 struct extattr_get_link_args {
474         const char *path;
475         int attrnamespace;
476         const char *attrname;
477         void *data;
478         size_t nbytes;
479 };
480 #endif
481 int
482 sys_extattr_get_link(struct thread *td, struct extattr_get_link_args *uap)
483 {
484         return (user_extattr_get_path(td, uap->path, uap->attrnamespace,
485             uap->attrname, uap->data, uap->nbytes, NOFOLLOW));
486 }
487
488 static int
489 user_extattr_get_path(struct thread *td, const char *path, int attrnamespace,
490     const char *uattrname, void *data, size_t nbytes, int follow)
491 {
492         char attrname[EXTATTR_MAXNAMELEN + 1];
493         int error;
494
495         error = copyinstr(uattrname, attrname, sizeof(attrname), NULL);
496         if (error)
497                 return (error);
498         return (kern_extattr_get_path(td, path, attrnamespace,
499             attrname, data, nbytes, follow, UIO_USERSPACE));
500 }
501
502 int
503 kern_extattr_get_path(struct thread *td, const char *path, int attrnamespace,
504     const char *attrname, void *data, size_t nbytes, int follow,
505     enum uio_seg pathseg)
506 {
507         struct nameidata nd;
508         int error;
509
510         AUDIT_ARG_VALUE(attrnamespace);
511         AUDIT_ARG_TEXT(attrname);
512
513         NDINIT(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path);
514         error = namei(&nd);
515         if (error)
516                 return (error);
517         NDFREE_PNBUF(&nd);
518
519         error = extattr_get_vp(nd.ni_vp, attrnamespace, attrname, data,
520             nbytes, td);
521
522         vrele(nd.ni_vp);
523         return (error);
524 }
525
526 /*
527  * extattr_delete_vp(): Delete a named extended attribute on a file or
528  *                      directory
529  *
530  * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace",
531  *            kernelspace string pointer "attrname", proc "p"
532  * Returns: 0 on success, an error number otherwise
533  * Locks: none
534  * References: vp must be a valid reference for the duration of the call
535  */
536 static int
537 extattr_delete_vp(struct vnode *vp, int attrnamespace, const char *attrname,
538     struct thread *td)
539 {
540         struct mount *mp;
541         int error;
542
543         error = vn_start_write(vp, &mp, V_WAIT | V_PCATCH);
544         if (error)
545                 return (error);
546         vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
547
548 #ifdef MAC
549         error = mac_vnode_check_deleteextattr(td->td_ucred, vp, attrnamespace,
550             attrname);
551         if (error)
552                 goto done;
553 #endif
554
555         error = VOP_DELETEEXTATTR(vp, attrnamespace, attrname, td->td_ucred,
556             td);
557         if (error == EOPNOTSUPP)
558                 error = VOP_SETEXTATTR(vp, attrnamespace, attrname, NULL,
559                     td->td_ucred, td);
560 #ifdef MAC
561 done:
562 #endif
563         VOP_UNLOCK(vp);
564         vn_finished_write(mp);
565         return (error);
566 }
567
568 #ifndef _SYS_SYSPROTO_H_
569 struct extattr_delete_fd_args {
570         int fd;
571         int attrnamespace;
572         const char *attrname;
573 };
574 #endif
575 int
576 sys_extattr_delete_fd(struct thread *td, struct extattr_delete_fd_args *uap)
577 {
578         char attrname[EXTATTR_MAXNAMELEN + 1];
579         int error;
580
581         error = copyinstr(uap->attrname, attrname, sizeof(attrname), NULL);
582         if (error)
583                 return (error);
584         return (kern_extattr_delete_fd(td, uap->fd, uap->attrnamespace,
585             attrname));
586 }
587
588 int
589 kern_extattr_delete_fd(struct thread *td, int fd, int attrnamespace,
590     const char *attrname)
591 {
592         struct file *fp;
593         cap_rights_t rights;
594         int error;
595
596         AUDIT_ARG_FD(fd);
597         AUDIT_ARG_VALUE(attrnamespace);
598         AUDIT_ARG_TEXT(attrname);
599
600         error = getvnode_path(td, fd,
601             cap_rights_init_one(&rights, CAP_EXTATTR_DELETE), &fp);
602         if (error)
603                 return (error);
604
605         error = extattr_delete_vp(fp->f_vnode, attrnamespace,
606             attrname, td);
607         fdrop(fp, td);
608         return (error);
609 }
610
611 #ifndef _SYS_SYSPROTO_H_
612 struct extattr_delete_file_args {
613         const char *path;
614         int attrnamespace;
615         const char *attrname;
616 };
617 #endif
618 int
619 sys_extattr_delete_file(struct thread *td, struct extattr_delete_file_args *uap)
620 {
621
622         return (user_extattr_delete_path(td, uap->path, uap->attrnamespace,
623             uap->attrname, FOLLOW));
624 }
625
626 #ifndef _SYS_SYSPROTO_H_
627 struct extattr_delete_link_args {
628         const char *path;
629         int attrnamespace;
630         const char *attrname;
631 };
632 #endif
633 int
634 sys_extattr_delete_link(struct thread *td, struct extattr_delete_link_args *uap)
635 {
636
637         return (user_extattr_delete_path(td, uap->path, uap->attrnamespace,
638             uap->attrname, NOFOLLOW));
639 }
640
641 int
642 user_extattr_delete_path(struct thread *td, const char *path, int attrnamespace,
643     const char *uattrname, int follow)
644 {
645         char attrname[EXTATTR_MAXNAMELEN + 1];
646         int error;
647
648         error = copyinstr(uattrname, attrname, sizeof(attrname), NULL);
649         if (error)
650                 return(error);
651         return (kern_extattr_delete_path(td, path, attrnamespace,
652             attrname, follow, UIO_USERSPACE));
653 }
654
655 int
656 kern_extattr_delete_path(struct thread *td, const char *path, int attrnamespace,
657     const char *attrname, int follow, enum uio_seg pathseg)
658 {
659         struct nameidata nd;
660         int error;
661
662         AUDIT_ARG_VALUE(attrnamespace);
663         AUDIT_ARG_TEXT(attrname);
664
665         NDINIT(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path);
666         error = namei(&nd);
667         if (error)
668                 return(error);
669         NDFREE_PNBUF(&nd);
670
671         error = extattr_delete_vp(nd.ni_vp, attrnamespace, attrname, td);
672         vrele(nd.ni_vp);
673         return(error);
674 }
675
676 /*-
677  * Retrieve a list of extended attributes on a file or directory.
678  *
679  * Arguments: unlocked vnode "vp", attribute namespace 'attrnamespace",
680  *            userspace buffer pointer "data", buffer length "nbytes",
681  *            thread "td".
682  * Returns: 0 on success, an error number otherwise
683  * Locks: none
684  * References: vp must be a valid reference for the duration of the call
685  */
686 static int
687 extattr_list_vp(struct vnode *vp, int attrnamespace, struct uio *auiop,
688     struct thread *td)
689 {
690         size_t size, *sizep;
691         ssize_t cnt;
692         int error;
693
694         sizep = NULL;
695         cnt = 0;
696         if (auiop != NULL) {
697                 if (auiop->uio_resid > IOSIZE_MAX)
698                         return (EINVAL);
699                 cnt = auiop->uio_resid;
700         } else
701                 sizep = &size;
702
703         vn_lock(vp, LK_SHARED | LK_RETRY);
704
705 #ifdef MAC
706         error = mac_vnode_check_listextattr(td->td_ucred, vp, attrnamespace);
707         if (error) {
708                 VOP_UNLOCK(vp);
709                 return (error);
710         }
711 #endif
712
713         error = VOP_LISTEXTATTR(vp, attrnamespace, auiop, sizep,
714             td->td_ucred, td);
715         VOP_UNLOCK(vp);
716
717         if (auiop != NULL) {
718                 cnt -= auiop->uio_resid;
719                 td->td_retval[0] = cnt;
720         } else
721                 td->td_retval[0] = size;
722         return (error);
723 }
724
725 #ifndef _SYS_SYSPROTO_H_
726 struct extattr_list_fd_args {
727         int fd;
728         int attrnamespace;
729         void *data;
730         size_t nbytes;
731 };
732 #endif
733 int
734 sys_extattr_list_fd(struct thread *td, struct extattr_list_fd_args *uap)
735 {
736         struct uio auio, *auiop;
737         struct iovec aiov;
738
739         if (uap->data != NULL) {
740                 aiov.iov_base = uap->data;
741                 aiov.iov_len = uap->nbytes;
742                 auio.uio_iov = &aiov;
743                 auio.uio_iovcnt = 1;
744                 auio.uio_offset = 0;
745                 auio.uio_resid = uap->nbytes;
746                 auio.uio_rw = UIO_READ;
747                 auio.uio_segflg = UIO_USERSPACE;
748                 auio.uio_td = td;
749                 auiop = &auio;
750         } else
751                 auiop = NULL;
752
753         return (kern_extattr_list_fd(td, uap->fd, uap->attrnamespace,
754             auiop));
755 }
756
757 int
758 kern_extattr_list_fd(struct thread *td, int fd, int attrnamespace,
759     struct uio *auiop)
760 {
761         struct file *fp;
762         cap_rights_t rights;
763         int error;
764
765         AUDIT_ARG_FD(fd);
766         AUDIT_ARG_VALUE(attrnamespace);
767         error = getvnode_path(td, fd,
768             cap_rights_init_one(&rights, CAP_EXTATTR_LIST), &fp);
769         if (error)
770                 return (error);
771
772         error = extattr_list_vp(fp->f_vnode, attrnamespace, auiop, td);
773
774         fdrop(fp, td);
775         return (error);
776 }
777
778 #ifndef _SYS_SYSPROTO_H_
779 struct extattr_list_file_args {
780         const char *path;
781         int attrnamespace;
782         void *data;
783         size_t nbytes;
784 }
785 #endif
786 int
787 sys_extattr_list_file(struct thread *td, struct extattr_list_file_args *uap)
788 {
789
790         return (user_extattr_list_path(td, uap->path, uap->attrnamespace,
791             uap->data, uap->nbytes, FOLLOW));
792 }
793
794 #ifndef _SYS_SYSPROTO_H_
795 struct extattr_list_link_args {
796         const char *path;
797         int attrnamespace;
798         void *data;
799         size_t nbytes;
800 };
801 #endif
802 int
803 sys_extattr_list_link(struct thread *td, struct extattr_list_link_args *uap)
804 {
805
806         return (user_extattr_list_path(td, uap->path, uap->attrnamespace,
807             uap->data, uap->nbytes, NOFOLLOW));
808 }
809
810 static int
811 user_extattr_list_path(struct thread *td, const char *path, int attrnamespace,
812     void *data, size_t nbytes, int follow)
813 {
814         struct uio auio, *auiop;
815         struct iovec aiov;
816
817         if (data != NULL) {
818                 aiov.iov_base = data;
819                 aiov.iov_len = nbytes;
820                 auio.uio_iov = &aiov;
821                 auio.uio_iovcnt = 1;
822                 auio.uio_offset = 0;
823                 auio.uio_resid = nbytes;
824                 auio.uio_rw = UIO_READ;
825                 auio.uio_segflg = UIO_USERSPACE;
826                 auio.uio_td = td;
827                 auiop = &auio;
828         } else
829                 auiop = NULL;
830
831         return (kern_extattr_list_path(td, path, attrnamespace,
832             auiop, follow, UIO_USERSPACE));
833 }
834
835 int
836 kern_extattr_list_path(struct thread *td, const char *path, int attrnamespace,
837     struct uio *auiop, int follow, enum uio_seg pathseg)
838 {
839         struct nameidata nd;
840         int error;
841
842         AUDIT_ARG_VALUE(attrnamespace);
843         NDINIT(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path);
844         error = namei(&nd);
845         if (error)
846                 return (error);
847         NDFREE_PNBUF(&nd);
848
849         error = extattr_list_vp(nd.ni_vp, attrnamespace, auiop, td);
850
851         vrele(nd.ni_vp);
852         return (error);
853 }