2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 1999-2001 Robert N. M. Watson
7 * This software was developed by Robert Watson for the TrustedBSD Project.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/capsicum.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>
44 #include <sys/extattr.h>
45 #include <sys/syscallsubr.h>
47 #include <security/audit/audit.h>
48 #include <security/mac/mac_framework.h>
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);
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.
66 * Currently this is used only by UFS1 extended attributes.
68 #ifndef _SYS_SYSPROTO_H_
69 struct extattrctl_args {
78 sys_extattrctl(struct thread *td, struct extattrctl_args *uap)
80 struct vnode *filename_vp;
82 struct mount *mp, *mp_writable;
83 char attrname[EXTATTR_MAXNAMELEN + 1];
86 AUDIT_ARG_CMD(uap->cmd);
87 AUDIT_ARG_VALUE(uap->attrnamespace);
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.
92 if (uap->attrname != NULL) {
93 error = copyinstr(uap->attrname, attrname, sizeof(attrname),
98 AUDIT_ARG_TEXT(attrname);
102 if (uap->filename != NULL) {
103 NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE2, UIO_USERSPACE,
108 filename_vp = nd.ni_vp;
112 /* uap->path is always defined. */
113 NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNODE1, UIO_USERSPACE,
118 mp = nd.ni_vp->v_mount;
119 error = vfs_busy(mp, 0);
126 VOP_UNLOCK(nd.ni_vp);
127 error = vn_start_write(nd.ni_vp, &mp_writable, V_WAIT | V_PCATCH);
132 if (filename_vp != NULL) {
134 * uap->filename is not always defined. If it is,
135 * grab a vnode lock, which VFS_EXTATTRCTL() will
138 error = vn_lock(filename_vp, LK_EXCLUSIVE);
140 vn_finished_write(mp_writable);
145 error = VFS_EXTATTRCTL(mp, uap->cmd, filename_vp, uap->attrnamespace,
146 uap->attrname != NULL ? attrname : NULL);
148 vn_finished_write(mp_writable);
154 * VFS_EXTATTRCTL will have unlocked, but not de-ref'd, filename_vp,
155 * so vrele it if it is defined.
157 if (filename_vp != NULL)
163 * Set a named extended attribute on a file or directory
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
170 * References: vp must be a valid reference for the duration of the call
173 extattr_set_vp(struct vnode *vp, int attrnamespace, const char *attrname,
174 void *data, size_t nbytes, struct thread *td)
182 if (nbytes > IOSIZE_MAX)
185 error = vn_start_write(vp, &mp, V_WAIT | V_PCATCH);
188 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
190 aiov.iov_base = data;
191 aiov.iov_len = nbytes;
192 auio.uio_iov = &aiov;
195 auio.uio_resid = nbytes;
196 auio.uio_rw = UIO_WRITE;
197 auio.uio_segflg = UIO_USERSPACE;
202 error = mac_vnode_check_setextattr(td->td_ucred, vp, attrnamespace,
208 error = VOP_SETEXTATTR(vp, attrnamespace, attrname, &auio,
210 cnt -= auio.uio_resid;
211 td->td_retval[0] = cnt;
217 vn_finished_write(mp);
221 #ifndef _SYS_SYSPROTO_H_
222 struct extattr_set_fd_args {
225 const char *attrname;
231 sys_extattr_set_fd(struct thread *td, struct extattr_set_fd_args *uap)
233 char attrname[EXTATTR_MAXNAMELEN + 1];
236 error = copyinstr(uap->attrname, attrname, sizeof(attrname), NULL);
239 return (kern_extattr_set_fd(td, uap->fd, uap->attrnamespace,
240 attrname, uap->data, uap->nbytes));
244 kern_extattr_set_fd(struct thread *td, int fd, int attrnamespace,
245 const char *attrname, void *data, size_t nbytes)
252 AUDIT_ARG_VALUE(attrnamespace);
253 AUDIT_ARG_TEXT(attrname);
255 error = getvnode_path(td, fd,
256 cap_rights_init_one(&rights, CAP_EXTATTR_SET), &fp);
260 error = extattr_set_vp(fp->f_vnode, attrnamespace,
261 attrname, data, nbytes, td);
267 #ifndef _SYS_SYSPROTO_H_
268 struct extattr_set_file_args {
271 const char *attrname;
277 sys_extattr_set_file(struct thread *td, struct extattr_set_file_args *uap)
280 return (user_extattr_set_path(td, uap->path, uap->attrnamespace,
281 uap->attrname, uap->data, uap->nbytes, FOLLOW));
284 #ifndef _SYS_SYSPROTO_H_
285 struct extattr_set_link_args {
288 const char *attrname;
294 sys_extattr_set_link(struct thread *td, struct extattr_set_link_args *uap)
297 return (user_extattr_set_path(td, uap->path, uap->attrnamespace,
298 uap->attrname, uap->data, uap->nbytes, NOFOLLOW));
302 user_extattr_set_path(struct thread *td, const char *path, int attrnamespace,
303 const char *uattrname, void *data, size_t nbytes, int follow)
305 char attrname[EXTATTR_MAXNAMELEN + 1];
308 error = copyinstr(uattrname, attrname, sizeof(attrname), NULL);
311 return (kern_extattr_set_path(td, path, attrnamespace,
312 attrname, data, nbytes, follow, UIO_USERSPACE));
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)
323 AUDIT_ARG_VALUE(attrnamespace);
324 AUDIT_ARG_TEXT(attrname);
326 NDINIT(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path);
332 error = extattr_set_vp(nd.ni_vp, attrnamespace, attrname, data,
340 * Get a named extended attribute on a file or directory
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
347 * References: vp must be a valid reference for the duration of the call
350 extattr_get_vp(struct vnode *vp, int attrnamespace, const char *attrname,
351 void *data, size_t nbytes, struct thread *td)
353 struct uio auio, *auiop;
359 if (nbytes > IOSIZE_MAX)
362 vn_lock(vp, LK_SHARED | LK_RETRY);
365 * Slightly unusual semantics: if the user provides a NULL data
366 * pointer, they don't want to receive the data, just the maximum
373 aiov.iov_base = data;
374 aiov.iov_len = nbytes;
375 auio.uio_iov = &aiov;
378 auio.uio_resid = nbytes;
379 auio.uio_rw = UIO_READ;
380 auio.uio_segflg = UIO_USERSPACE;
388 error = mac_vnode_check_getextattr(td->td_ucred, vp, attrnamespace,
394 error = VOP_GETEXTATTR(vp, attrnamespace, attrname, auiop, sizep,
398 cnt -= auio.uio_resid;
399 td->td_retval[0] = cnt;
401 td->td_retval[0] = size;
409 #ifndef _SYS_SYSPROTO_H_
410 struct extattr_get_fd_args {
413 const char *attrname;
419 sys_extattr_get_fd(struct thread *td, struct extattr_get_fd_args *uap)
421 char attrname[EXTATTR_MAXNAMELEN + 1];
424 error = copyinstr(uap->attrname, attrname, sizeof(attrname), NULL);
427 return (kern_extattr_get_fd(td, uap->fd, uap->attrnamespace,
428 attrname, uap->data, uap->nbytes));
432 kern_extattr_get_fd(struct thread *td, int fd, int attrnamespace,
433 const char *attrname, void *data, size_t nbytes)
440 AUDIT_ARG_VALUE(attrnamespace);
441 AUDIT_ARG_TEXT(attrname);
443 error = getvnode_path(td, fd,
444 cap_rights_init_one(&rights, CAP_EXTATTR_GET), &fp);
448 error = extattr_get_vp(fp->f_vnode, attrnamespace,
449 attrname, data, nbytes, td);
455 #ifndef _SYS_SYSPROTO_H_
456 struct extattr_get_file_args {
459 const char *attrname;
465 sys_extattr_get_file(struct thread *td, struct extattr_get_file_args *uap)
467 return (user_extattr_get_path(td, uap->path, uap->attrnamespace,
468 uap->attrname, uap->data, uap->nbytes, FOLLOW));
471 #ifndef _SYS_SYSPROTO_H_
472 struct extattr_get_link_args {
475 const char *attrname;
481 sys_extattr_get_link(struct thread *td, struct extattr_get_link_args *uap)
483 return (user_extattr_get_path(td, uap->path, uap->attrnamespace,
484 uap->attrname, uap->data, uap->nbytes, NOFOLLOW));
488 user_extattr_get_path(struct thread *td, const char *path, int attrnamespace,
489 const char *uattrname, void *data, size_t nbytes, int follow)
491 char attrname[EXTATTR_MAXNAMELEN + 1];
494 error = copyinstr(uattrname, attrname, sizeof(attrname), NULL);
497 return (kern_extattr_get_path(td, path, attrnamespace,
498 attrname, data, nbytes, follow, UIO_USERSPACE));
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)
509 AUDIT_ARG_VALUE(attrnamespace);
510 AUDIT_ARG_TEXT(attrname);
512 NDINIT(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path);
518 error = extattr_get_vp(nd.ni_vp, attrnamespace, attrname, data,
526 * extattr_delete_vp(): Delete a named extended attribute on a file or
529 * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace",
530 * kernelspace string pointer "attrname", proc "p"
531 * Returns: 0 on success, an error number otherwise
533 * References: vp must be a valid reference for the duration of the call
536 extattr_delete_vp(struct vnode *vp, int attrnamespace, const char *attrname,
542 error = vn_start_write(vp, &mp, V_WAIT | V_PCATCH);
545 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
548 error = mac_vnode_check_deleteextattr(td->td_ucred, vp, attrnamespace,
554 error = VOP_DELETEEXTATTR(vp, attrnamespace, attrname, td->td_ucred,
556 if (error == EOPNOTSUPP)
557 error = VOP_SETEXTATTR(vp, attrnamespace, attrname, NULL,
563 vn_finished_write(mp);
567 #ifndef _SYS_SYSPROTO_H_
568 struct extattr_delete_fd_args {
571 const char *attrname;
575 sys_extattr_delete_fd(struct thread *td, struct extattr_delete_fd_args *uap)
577 char attrname[EXTATTR_MAXNAMELEN + 1];
580 error = copyinstr(uap->attrname, attrname, sizeof(attrname), NULL);
583 return (kern_extattr_delete_fd(td, uap->fd, uap->attrnamespace,
588 kern_extattr_delete_fd(struct thread *td, int fd, int attrnamespace,
589 const char *attrname)
596 AUDIT_ARG_VALUE(attrnamespace);
597 AUDIT_ARG_TEXT(attrname);
599 error = getvnode_path(td, fd,
600 cap_rights_init_one(&rights, CAP_EXTATTR_DELETE), &fp);
604 error = extattr_delete_vp(fp->f_vnode, attrnamespace,
610 #ifndef _SYS_SYSPROTO_H_
611 struct extattr_delete_file_args {
614 const char *attrname;
618 sys_extattr_delete_file(struct thread *td, struct extattr_delete_file_args *uap)
621 return (user_extattr_delete_path(td, uap->path, uap->attrnamespace,
622 uap->attrname, FOLLOW));
625 #ifndef _SYS_SYSPROTO_H_
626 struct extattr_delete_link_args {
629 const char *attrname;
633 sys_extattr_delete_link(struct thread *td, struct extattr_delete_link_args *uap)
636 return (user_extattr_delete_path(td, uap->path, uap->attrnamespace,
637 uap->attrname, NOFOLLOW));
641 user_extattr_delete_path(struct thread *td, const char *path, int attrnamespace,
642 const char *uattrname, int follow)
644 char attrname[EXTATTR_MAXNAMELEN + 1];
647 error = copyinstr(uattrname, attrname, sizeof(attrname), NULL);
650 return (kern_extattr_delete_path(td, path, attrnamespace,
651 attrname, follow, UIO_USERSPACE));
655 kern_extattr_delete_path(struct thread *td, const char *path, int attrnamespace,
656 const char *attrname, int follow, enum uio_seg pathseg)
661 AUDIT_ARG_VALUE(attrnamespace);
662 AUDIT_ARG_TEXT(attrname);
664 NDINIT(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path);
670 error = extattr_delete_vp(nd.ni_vp, attrnamespace, attrname, td);
676 * Retrieve a list of extended attributes on a file or directory.
678 * Arguments: unlocked vnode "vp", attribute namespace 'attrnamespace",
679 * userspace buffer pointer "data", buffer length "nbytes",
681 * Returns: 0 on success, an error number otherwise
683 * References: vp must be a valid reference for the duration of the call
686 extattr_list_vp(struct vnode *vp, int attrnamespace, struct uio *auiop,
696 if (auiop->uio_resid > IOSIZE_MAX)
698 cnt = auiop->uio_resid;
702 vn_lock(vp, LK_SHARED | LK_RETRY);
705 error = mac_vnode_check_listextattr(td->td_ucred, vp, attrnamespace);
712 error = VOP_LISTEXTATTR(vp, attrnamespace, auiop, sizep,
717 cnt -= auiop->uio_resid;
718 td->td_retval[0] = cnt;
720 td->td_retval[0] = size;
724 #ifndef _SYS_SYSPROTO_H_
725 struct extattr_list_fd_args {
733 sys_extattr_list_fd(struct thread *td, struct extattr_list_fd_args *uap)
735 struct uio auio, *auiop;
738 if (uap->data != NULL) {
739 aiov.iov_base = uap->data;
740 aiov.iov_len = uap->nbytes;
741 auio.uio_iov = &aiov;
744 auio.uio_resid = uap->nbytes;
745 auio.uio_rw = UIO_READ;
746 auio.uio_segflg = UIO_USERSPACE;
752 return (kern_extattr_list_fd(td, uap->fd, uap->attrnamespace,
757 kern_extattr_list_fd(struct thread *td, int fd, int attrnamespace,
765 AUDIT_ARG_VALUE(attrnamespace);
766 error = getvnode_path(td, fd,
767 cap_rights_init_one(&rights, CAP_EXTATTR_LIST), &fp);
771 error = extattr_list_vp(fp->f_vnode, attrnamespace, auiop, td);
777 #ifndef _SYS_SYSPROTO_H_
778 struct extattr_list_file_args {
786 sys_extattr_list_file(struct thread *td, struct extattr_list_file_args *uap)
789 return (user_extattr_list_path(td, uap->path, uap->attrnamespace,
790 uap->data, uap->nbytes, FOLLOW));
793 #ifndef _SYS_SYSPROTO_H_
794 struct extattr_list_link_args {
802 sys_extattr_list_link(struct thread *td, struct extattr_list_link_args *uap)
805 return (user_extattr_list_path(td, uap->path, uap->attrnamespace,
806 uap->data, uap->nbytes, NOFOLLOW));
810 user_extattr_list_path(struct thread *td, const char *path, int attrnamespace,
811 void *data, size_t nbytes, int follow)
813 struct uio auio, *auiop;
817 aiov.iov_base = data;
818 aiov.iov_len = nbytes;
819 auio.uio_iov = &aiov;
822 auio.uio_resid = nbytes;
823 auio.uio_rw = UIO_READ;
824 auio.uio_segflg = UIO_USERSPACE;
830 return (kern_extattr_list_path(td, path, attrnamespace,
831 auiop, follow, UIO_USERSPACE));
835 kern_extattr_list_path(struct thread *td, const char *path, int attrnamespace,
836 struct uio *auiop, int follow, enum uio_seg pathseg)
841 AUDIT_ARG_VALUE(attrnamespace);
842 NDINIT(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path);
848 error = extattr_list_vp(nd.ni_vp, attrnamespace, auiop, td);