]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/kern/vfs_extattr.c
MFV: xz 5.4.0
[FreeBSD/FreeBSD.git] / sys / kern / vfs_extattr.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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      kern_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      kern_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      kern_extattr_delete_path(struct thread *td, const char *path,
59                     int attrnamespace, const char *attrname, int follow);
60 static int      kern_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         struct file *fp;
236         char attrname[EXTATTR_MAXNAMELEN + 1];
237         cap_rights_t rights;
238         int error;
239
240         AUDIT_ARG_FD(uap->fd);
241         AUDIT_ARG_VALUE(uap->attrnamespace);
242         error = copyinstr(uap->attrname, attrname, sizeof(attrname), NULL);
243         if (error)
244                 return (error);
245         AUDIT_ARG_TEXT(attrname);
246
247         error = getvnode_path(td, uap->fd,
248             cap_rights_init_one(&rights, CAP_EXTATTR_SET), &fp);
249         if (error)
250                 return (error);
251
252         error = extattr_set_vp(fp->f_vnode, uap->attrnamespace,
253             attrname, uap->data, uap->nbytes, td);
254         fdrop(fp, td);
255
256         return (error);
257 }
258
259 #ifndef _SYS_SYSPROTO_H_
260 struct extattr_set_file_args {
261         const char *path;
262         int attrnamespace;
263         const char *attrname;
264         void *data;
265         size_t nbytes;
266 };
267 #endif
268 int
269 sys_extattr_set_file(struct thread *td, struct extattr_set_file_args *uap)
270 {
271
272         return (kern_extattr_set_path(td, uap->path, uap->attrnamespace,
273             uap->attrname, uap->data, uap->nbytes, FOLLOW));
274 }
275
276 #ifndef _SYS_SYSPROTO_H_
277 struct extattr_set_link_args {
278         const char *path;
279         int attrnamespace;
280         const char *attrname;
281         void *data;
282         size_t nbytes;
283 };
284 #endif
285 int
286 sys_extattr_set_link(struct thread *td, struct extattr_set_link_args *uap)
287 {
288
289         return (kern_extattr_set_path(td, uap->path, uap->attrnamespace,
290             uap->attrname, uap->data, uap->nbytes, NOFOLLOW));
291 }
292
293 static int
294 kern_extattr_set_path(struct thread *td, const char *path, int attrnamespace,
295     const char *uattrname, void *data, size_t nbytes, int follow)
296 {
297         struct nameidata nd;
298         char attrname[EXTATTR_MAXNAMELEN + 1];
299         int error;
300
301         AUDIT_ARG_VALUE(attrnamespace);
302         error = copyinstr(uattrname, attrname, sizeof(attrname), NULL);
303         if (error)
304                 return (error);
305         AUDIT_ARG_TEXT(attrname);
306
307         NDINIT(&nd, LOOKUP, follow | AUDITVNODE1, UIO_USERSPACE, path);
308         error = namei(&nd);
309         if (error)
310                 return (error);
311         NDFREE_PNBUF(&nd);
312
313         error = extattr_set_vp(nd.ni_vp, attrnamespace, attrname, data,
314             nbytes, td);
315
316         vrele(nd.ni_vp);
317         return (error);
318 }
319
320 /*-
321  * Get a named extended attribute on a file or directory
322  *
323  * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace",
324  *            kernelspace string pointer "attrname", userspace buffer
325  *            pointer "data", buffer length "nbytes", thread "td".
326  * Returns: 0 on success, an error number otherwise
327  * Locks: none
328  * References: vp must be a valid reference for the duration of the call
329  */
330 static int
331 extattr_get_vp(struct vnode *vp, int attrnamespace, const char *attrname,
332     void *data, size_t nbytes, struct thread *td)
333 {
334         struct uio auio, *auiop;
335         struct iovec aiov;
336         ssize_t cnt;
337         size_t size, *sizep;
338         int error;
339
340         if (nbytes > IOSIZE_MAX)
341                 return (EINVAL);
342
343         vn_lock(vp, LK_SHARED | LK_RETRY);
344
345         /*
346          * Slightly unusual semantics: if the user provides a NULL data
347          * pointer, they don't want to receive the data, just the maximum
348          * read length.
349          */
350         auiop = NULL;
351         sizep = NULL;
352         cnt = 0;
353         if (data != NULL) {
354                 aiov.iov_base = data;
355                 aiov.iov_len = nbytes;
356                 auio.uio_iov = &aiov;
357                 auio.uio_iovcnt = 1;
358                 auio.uio_offset = 0;
359                 auio.uio_resid = nbytes;
360                 auio.uio_rw = UIO_READ;
361                 auio.uio_segflg = UIO_USERSPACE;
362                 auio.uio_td = td;
363                 auiop = &auio;
364                 cnt = nbytes;
365         } else
366                 sizep = &size;
367
368 #ifdef MAC
369         error = mac_vnode_check_getextattr(td->td_ucred, vp, attrnamespace,
370             attrname);
371         if (error)
372                 goto done;
373 #endif
374
375         error = VOP_GETEXTATTR(vp, attrnamespace, attrname, auiop, sizep,
376             td->td_ucred, td);
377
378         if (auiop != NULL) {
379                 cnt -= auio.uio_resid;
380                 td->td_retval[0] = cnt;
381         } else
382                 td->td_retval[0] = size;
383 #ifdef MAC
384 done:
385 #endif
386         VOP_UNLOCK(vp);
387         return (error);
388 }
389
390 #ifndef _SYS_SYSPROTO_H_
391 struct extattr_get_fd_args {
392         int fd;
393         int attrnamespace;
394         const char *attrname;
395         void *data;
396         size_t nbytes;
397 };
398 #endif
399 int
400 sys_extattr_get_fd(struct thread *td, struct extattr_get_fd_args *uap)
401 {
402         struct file *fp;
403         char attrname[EXTATTR_MAXNAMELEN + 1];
404         cap_rights_t rights;
405         int error;
406
407         AUDIT_ARG_FD(uap->fd);
408         AUDIT_ARG_VALUE(uap->attrnamespace);
409         error = copyinstr(uap->attrname, attrname, sizeof(attrname), NULL);
410         if (error)
411                 return (error);
412         AUDIT_ARG_TEXT(attrname);
413
414         error = getvnode_path(td, uap->fd,
415             cap_rights_init_one(&rights, CAP_EXTATTR_GET), &fp);
416         if (error)
417                 return (error);
418
419         error = extattr_get_vp(fp->f_vnode, uap->attrnamespace,
420             attrname, uap->data, uap->nbytes, td);
421
422         fdrop(fp, td);
423         return (error);
424 }
425
426 #ifndef _SYS_SYSPROTO_H_
427 struct extattr_get_file_args {
428         const char *path;
429         int attrnamespace;
430         const char *attrname;
431         void *data;
432         size_t nbytes;
433 };
434 #endif
435 int
436 sys_extattr_get_file(struct thread *td, struct extattr_get_file_args *uap)
437 {
438         return (kern_extattr_get_path(td, uap->path, uap->attrnamespace,
439             uap->attrname, uap->data, uap->nbytes, FOLLOW));
440 }
441
442 #ifndef _SYS_SYSPROTO_H_
443 struct extattr_get_link_args {
444         const char *path;
445         int attrnamespace;
446         const char *attrname;
447         void *data;
448         size_t nbytes;
449 };
450 #endif
451 int
452 sys_extattr_get_link(struct thread *td, struct extattr_get_link_args *uap)
453 {
454         return (kern_extattr_get_path(td, uap->path, uap->attrnamespace,
455             uap->attrname, uap->data, uap->nbytes, NOFOLLOW));
456 }
457
458 static int
459 kern_extattr_get_path(struct thread *td, const char *path, int attrnamespace,
460     const char *uattrname, void *data, size_t nbytes, int follow)
461 {
462         struct nameidata nd;
463         char attrname[EXTATTR_MAXNAMELEN + 1];
464         int error;
465
466         AUDIT_ARG_VALUE(attrnamespace);
467         error = copyinstr(uattrname, attrname, sizeof(attrname), NULL);
468         if (error)
469                 return (error);
470         AUDIT_ARG_TEXT(attrname);
471
472         NDINIT(&nd, LOOKUP, follow | AUDITVNODE1, UIO_USERSPACE, path);
473         error = namei(&nd);
474         if (error)
475                 return (error);
476         NDFREE_PNBUF(&nd);
477
478         error = extattr_get_vp(nd.ni_vp, attrnamespace, attrname, data,
479             nbytes, td);
480
481         vrele(nd.ni_vp);
482         return (error);
483 }
484
485 /*
486  * extattr_delete_vp(): Delete a named extended attribute on a file or
487  *                      directory
488  *
489  * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace",
490  *            kernelspace string pointer "attrname", proc "p"
491  * Returns: 0 on success, an error number otherwise
492  * Locks: none
493  * References: vp must be a valid reference for the duration of the call
494  */
495 static int
496 extattr_delete_vp(struct vnode *vp, int attrnamespace, const char *attrname,
497     struct thread *td)
498 {
499         struct mount *mp;
500         int error;
501
502         error = vn_start_write(vp, &mp, V_WAIT | V_PCATCH);
503         if (error)
504                 return (error);
505         vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
506
507 #ifdef MAC
508         error = mac_vnode_check_deleteextattr(td->td_ucred, vp, attrnamespace,
509             attrname);
510         if (error)
511                 goto done;
512 #endif
513
514         error = VOP_DELETEEXTATTR(vp, attrnamespace, attrname, td->td_ucred,
515             td);
516         if (error == EOPNOTSUPP)
517                 error = VOP_SETEXTATTR(vp, attrnamespace, attrname, NULL,
518                     td->td_ucred, td);
519 #ifdef MAC
520 done:
521 #endif
522         VOP_UNLOCK(vp);
523         vn_finished_write(mp);
524         return (error);
525 }
526
527 #ifndef _SYS_SYSPROTO_H_
528 struct extattr_delete_fd_args {
529         int fd;
530         int attrnamespace;
531         const char *attrname;
532 };
533 #endif
534 int
535 sys_extattr_delete_fd(struct thread *td, struct extattr_delete_fd_args *uap)
536 {
537         struct file *fp;
538         char attrname[EXTATTR_MAXNAMELEN + 1];
539         cap_rights_t rights;
540         int error;
541
542         AUDIT_ARG_FD(uap->fd);
543         AUDIT_ARG_VALUE(uap->attrnamespace);
544         error = copyinstr(uap->attrname, attrname, sizeof(attrname), NULL);
545         if (error)
546                 return (error);
547         AUDIT_ARG_TEXT(attrname);
548
549         error = getvnode_path(td, uap->fd,
550             cap_rights_init_one(&rights, CAP_EXTATTR_DELETE), &fp);
551         if (error)
552                 return (error);
553
554         error = extattr_delete_vp(fp->f_vnode, uap->attrnamespace,
555             attrname, td);
556         fdrop(fp, td);
557         return (error);
558 }
559
560 #ifndef _SYS_SYSPROTO_H_
561 struct extattr_delete_file_args {
562         const char *path;
563         int attrnamespace;
564         const char *attrname;
565 };
566 #endif
567 int
568 sys_extattr_delete_file(struct thread *td, struct extattr_delete_file_args *uap)
569 {
570
571         return (kern_extattr_delete_path(td, uap->path, uap->attrnamespace,
572             uap->attrname, FOLLOW));
573 }
574
575 #ifndef _SYS_SYSPROTO_H_
576 struct extattr_delete_link_args {
577         const char *path;
578         int attrnamespace;
579         const char *attrname;
580 };
581 #endif
582 int
583 sys_extattr_delete_link(struct thread *td, struct extattr_delete_link_args *uap)
584 {
585
586         return (kern_extattr_delete_path(td, uap->path, uap->attrnamespace,
587             uap->attrname, NOFOLLOW));
588 }
589
590 static int
591 kern_extattr_delete_path(struct thread *td, const char *path, int attrnamespace,
592     const char *uattrname, int follow)
593 {
594         struct nameidata nd;
595         char attrname[EXTATTR_MAXNAMELEN + 1];
596         int error;
597
598         AUDIT_ARG_VALUE(attrnamespace);
599         error = copyinstr(uattrname, attrname, sizeof(attrname), NULL);
600         if (error)
601                 return(error);
602         AUDIT_ARG_TEXT(attrname);
603
604         NDINIT(&nd, LOOKUP, follow | AUDITVNODE1, UIO_USERSPACE, path);
605         error = namei(&nd);
606         if (error)
607                 return(error);
608         NDFREE_PNBUF(&nd);
609
610         error = extattr_delete_vp(nd.ni_vp, attrnamespace, attrname, td);
611         vrele(nd.ni_vp);
612         return(error);
613 }
614
615 /*-
616  * Retrieve a list of extended attributes on a file or directory.
617  *
618  * Arguments: unlocked vnode "vp", attribute namespace 'attrnamespace",
619  *            userspace buffer pointer "data", buffer length "nbytes",
620  *            thread "td".
621  * Returns: 0 on success, an error number otherwise
622  * Locks: none
623  * References: vp must be a valid reference for the duration of the call
624  */
625 static int
626 extattr_list_vp(struct vnode *vp, int attrnamespace, void *data,
627     size_t nbytes, struct thread *td)
628 {
629         struct uio auio, *auiop;
630         size_t size, *sizep;
631         struct iovec aiov;
632         ssize_t cnt;
633         int error;
634
635         if (nbytes > IOSIZE_MAX)
636                 return (EINVAL);
637
638         auiop = NULL;
639         sizep = NULL;
640         cnt = 0;
641         if (data != NULL) {
642                 aiov.iov_base = data;
643                 aiov.iov_len = nbytes;
644                 auio.uio_iov = &aiov;
645                 auio.uio_iovcnt = 1;
646                 auio.uio_offset = 0;
647                 auio.uio_resid = nbytes;
648                 auio.uio_rw = UIO_READ;
649                 auio.uio_segflg = UIO_USERSPACE;
650                 auio.uio_td = td;
651                 auiop = &auio;
652                 cnt = nbytes;
653         } else
654                 sizep = &size;
655
656         vn_lock(vp, LK_SHARED | LK_RETRY);
657
658 #ifdef MAC
659         error = mac_vnode_check_listextattr(td->td_ucred, vp, attrnamespace);
660         if (error) {
661                 VOP_UNLOCK(vp);
662                 return (error);
663         }
664 #endif
665
666         error = VOP_LISTEXTATTR(vp, attrnamespace, auiop, sizep,
667             td->td_ucred, td);
668         VOP_UNLOCK(vp);
669
670         if (auiop != NULL) {
671                 cnt -= auio.uio_resid;
672                 td->td_retval[0] = cnt;
673         } else
674                 td->td_retval[0] = size;
675         return (error);
676 }
677
678 #ifndef _SYS_SYSPROTO_H_
679 struct extattr_list_fd_args {
680         int fd;
681         int attrnamespace;
682         void *data;
683         size_t nbytes;
684 };
685 #endif
686 int
687 sys_extattr_list_fd(struct thread *td, struct extattr_list_fd_args *uap)
688 {
689         struct file *fp;
690         cap_rights_t rights;
691         int error;
692
693         AUDIT_ARG_FD(uap->fd);
694         AUDIT_ARG_VALUE(uap->attrnamespace);
695         error = getvnode_path(td, uap->fd,
696             cap_rights_init_one(&rights, CAP_EXTATTR_LIST), &fp);
697         if (error)
698                 return (error);
699
700         error = extattr_list_vp(fp->f_vnode, uap->attrnamespace, uap->data,
701             uap->nbytes, td);
702
703         fdrop(fp, td);
704         return (error);
705 }
706
707 #ifndef _SYS_SYSPROTO_H_
708 struct extattr_list_file_args {
709         const char *path;
710         int attrnamespace;
711         void *data;
712         size_t nbytes;
713 }
714 #endif
715 int
716 sys_extattr_list_file(struct thread *td, struct extattr_list_file_args *uap)
717 {
718
719         return (kern_extattr_list_path(td, uap->path, uap->attrnamespace,
720             uap->data, uap->nbytes, FOLLOW));
721 }
722
723 #ifndef _SYS_SYSPROTO_H_
724 struct extattr_list_link_args {
725         const char *path;
726         int attrnamespace;
727         void *data;
728         size_t nbytes;
729 };
730 #endif
731 int
732 sys_extattr_list_link(struct thread *td, struct extattr_list_link_args *uap)
733 {
734
735         return (kern_extattr_list_path(td, uap->path, uap->attrnamespace,
736             uap->data, uap->nbytes, NOFOLLOW));
737 }
738
739 static int
740 kern_extattr_list_path(struct thread *td, const char *path, int attrnamespace,
741     void *data, size_t nbytes, int follow)
742 {
743         struct nameidata nd;
744         int error;
745
746         AUDIT_ARG_VALUE(attrnamespace);
747         NDINIT(&nd, LOOKUP, follow | AUDITVNODE1, UIO_USERSPACE, path);
748         error = namei(&nd);
749         if (error)
750                 return (error);
751         NDFREE_PNBUF(&nd);
752
753         error = extattr_list_vp(nd.ni_vp, attrnamespace, data, nbytes, td);
754
755         vrele(nd.ni_vp);
756         return (error);
757 }