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 }
Cache object: e82b7e79afbd80020d00fa7dd44f51a9
|