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