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