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