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