diff options
author | 2002-02-22 20:37:45 +0000 | |
---|---|---|
committer | 2002-02-22 20:37:45 +0000 | |
commit | cfa7519356b6013a38a223053872a2f5f28b8181 (patch) | |
tree | 2ad7643831d2b488c88c147354b66fcc564c5c65 /sys | |
parent | Call ether_ioctl() the way [insert favorite diety] intended. (diff) | |
download | wireguard-openbsd-cfa7519356b6013a38a223053872a2f5f28b8181.tar.xz wireguard-openbsd-cfa7519356b6013a38a223053872a2f5f28b8181.zip |
Extended Attribute support from FreeBSD/TrustedBSD ok art@, deraadt@
Diffstat (limited to 'sys')
29 files changed, 2107 insertions, 33 deletions
diff --git a/sys/compat/osf1/osf1_mount.c b/sys/compat/osf1/osf1_mount.c index 506d18bca6b..ec17ba7b2ef 100644 --- a/sys/compat/osf1/osf1_mount.c +++ b/sys/compat/osf1/osf1_mount.c @@ -1,4 +1,4 @@ -/* $OpenBSD: osf1_mount.c,v 1.6 2002/02/12 18:41:20 art Exp $ */ +/* $OpenBSD: osf1_mount.c,v 1.7 2002/02/22 20:37:45 drahn Exp $ */ /* $NetBSD: osf1_mount.c,v 1.14 1999/05/05 01:51:34 cgd Exp $ */ /* @@ -83,6 +83,7 @@ #include <nfs/nfsmount.h> #include <ufs/ufs/quota.h> +#include <ufs/ufs/extattr.h> #include <ufs/ufs/ufsmount.h> #include <machine/vmparam.h> diff --git a/sys/conf/files b/sys/conf/files index 5fccc90a7a4..7fcc9c1dbcc 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,4 +1,4 @@ -# $OpenBSD: files,v 1.239 2002/02/18 01:55:30 krw Exp $ +# $OpenBSD: files,v 1.240 2002/02/22 20:37:45 drahn Exp $ # $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 @@ -771,6 +771,7 @@ file ufs/ffs/ffs_softdep.c ffs_softupdates file ufs/mfs/mfs_vfsops.c mfs file ufs/mfs/mfs_vnops.c mfs file ufs/ufs/ufs_bmap.c ffs | mfs | ext2fs +file ufs/ufs/ufs_extattr.c ffs | mfs file ufs/ufs/ufs_ihash.c ffs | mfs | ext2fs file ufs/ufs/ufs_inode.c ffs | mfs | ext2fs file ufs/ufs/ufs_lookup.c ffs | mfs | ext2fs diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master index 1a6ead6a9ec..9a9a5e90afa 100644 --- a/sys/kern/syscalls.master +++ b/sys/kern/syscalls.master @@ -1,4 +1,4 @@ -; $OpenBSD: syscalls.master,v 1.47 2001/06/26 19:56:52 dugsong Exp $ +; $OpenBSD: syscalls.master,v 1.48 2002/02/22 20:37:45 drahn Exp $ ; $NetBSD: syscalls.master,v 1.32 1996/04/23 10:24:21 mycroft Exp $ ; @(#)syscalls.master 8.2 (Berkeley) 1/13/94 @@ -534,4 +534,32 @@ 271 STD { int sys_mlockall(int flags); } 272 STD { int sys_munlockall(void); } 273 STD { int sys_getpeereid(int fdes, uid_t *euid, gid_t *egid); } - +#ifdef UFS_EXTATTR +274 STD { int sys_extattrctl(const char *path, int cmd, \ + const char *filename, int attrnamespace, \ + const char *attrname); } +275 STD { int sys_extattr_set_file(const char *path, \ + int attrnamespace, const char *attrname, \ + void *data, size_t nbytes); } +276 STD { ssize_t sys_extattr_get_file(const char *path, \ + int attrnamespace, const char *attrname, \ + void *data, size_t nbytes); } +277 STD { int sys_extattr_delete_file(const char *path, \ + int attrnamespace, const char *attrname); } +278 STD { int sys_extattr_set_fd(int fd, int attrnamespace, \ + const char *attrname, void *data, \ + size_t nbytes); } +279 STD { ssize_t sys_extattr_get_fd(int fd, \ + int attrnamespace, const char *attrname, \ + void *data, size_t nbytes); } +280 STD { int sys_extattr_delete_fd(int fd, int attrnamespace, \ + const char *attrname); } +#else +274 UNIMPL sys_extattrctl +275 UNIMPL sys_extattr_set_file +276 UNIMPL sys_extattr_get_file +277 UNIMPL sys_extattr_delete_file +278 UNIMPL sys_extattr_set_fd +279 UNIMPL sys_extattr_get_fd +280 UNIMPL sys_extattr_delete_fd +#endif diff --git a/sys/kern/vfs_default.c b/sys/kern/vfs_default.c index d987b1a78ec..fc15c1619e7 100644 --- a/sys/kern/vfs_default.c +++ b/sys/kern/vfs_default.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vfs_default.c,v 1.16 2001/12/19 08:58:06 art Exp $ */ +/* $OpenBSD: vfs_default.c,v 1.17 2002/02/22 20:37:45 drahn Exp $ */ /* @@ -310,3 +310,19 @@ lease_check(void *v) { return (0); } +/* + * vfs default ops + * used to fill the vfs fucntion table to get reasonable default return values. + */ + +int +vfs_stdextattrctl(mp, cmd, filename_vp, attrnamespace, attrname, td) + struct mount *mp; + int cmd; + struct vnode *filename_vp; + int attrnamespace; + const char *attrname; + struct proc *td; +{ + return(EOPNOTSUPP); +} diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index b4aeb2e150c..7b92e02b0e6 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vfs_syscalls.c,v 1.93 2002/02/12 18:41:21 art Exp $ */ +/* $OpenBSD: vfs_syscalls.c,v 1.94 2002/02/22 20:37:45 drahn Exp $ */ /* $NetBSD: vfs_syscalls.c,v 1.71 1996/04/23 10:29:02 mycroft Exp $ */ /* @@ -54,6 +54,7 @@ #include <sys/uio.h> #include <sys/malloc.h> #include <sys/dirent.h> +#include <sys/extattr.h> #include <sys/syscallargs.h> @@ -2804,3 +2805,408 @@ sys_pwritev(p, v, retval) return (dofilewritev(p, fd, fp, SCARG(uap, iovp), SCARG(uap, iovcnt), &offset, retval)); } + +#ifdef UFS_EXTATTR +/* + * Syscall to push extended attribute configuration information into the + * VFS. Accepts a path, which it converts to a mountpoint, as well as + * a command (int cmd), and attribute name and misc data. For now, the + * attribute name is left in userspace for consumption by the VFS_op. + * It will probably be changed to be copied into sysspace by the + * syscall in the future, once issues with various consumers of the + * attribute code have raised their hands. + * + * Currently this is used only by UFS Extended Attributes. + */ +int +sys_extattrctl(struct proc *p, void *v, register_t *reval) +{ + struct sys_extattrctl_args /* { + syscallarg(const char *) path; + syscallarg(int) cmd; + syscallarg(const char *) filename; + syscallarg(int) attrnamespace; + syscallarg(const char *) attrname; + } */ *uap = v; + struct vnode *filename_vp; + struct nameidata nd; + struct mount *mp; + char attrname[EXTATTR_MAXNAMELEN]; + int error; + + /* + * SCARG(uap, attrname) not always defined. We check again later + * when we invoke the VFS call so as to pass in NULL there if needed. + */ + if (SCARG(uap, attrname) != NULL) { + error = copyinstr(SCARG(uap, attrname), attrname, + EXTATTR_MAXNAMELEN, NULL); + if (error) + return (error); + } + + /* + * SCARG(uap, filename) not always defined. If it is, grab + * a vnode lock, which VFS_EXTATTRCTL() will later release. + */ + filename_vp = NULL; + if (SCARG(uap, filename) != NULL) { + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, filename), p); + if ((error = namei(&nd)) != 0) + return (error); + filename_vp = nd.ni_vp; + } + + /* SCARG(uap, path) always defined. */ + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) { + if (filename_vp != NULL) + vput(filename_vp); + return (error); + } + + mp = nd.ni_vp->v_mount; + if (error) { + if (filename_vp != NULL) + vput(filename_vp); + return (error); + } + + if (SCARG(uap, attrname) != NULL) { + error = VFS_EXTATTRCTL(mp, SCARG(uap, cmd), filename_vp, + SCARG(uap, attrnamespace), attrname, p); + } else { + error = VFS_EXTATTRCTL(mp, SCARG(uap, cmd), filename_vp, + SCARG(uap, attrnamespace), NULL, p); + } + + /* + * VFS_EXTATTRCTL will have unlocked, but not de-ref'd, + * filename_vp, so vrele it if it is defined. + */ + if (filename_vp != NULL) + vrele(filename_vp); + + return (error); +} + +/*- + * Set a named extended attribute on a file or directory + * + * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace", + * kernelspace string pointer "attrname", userspace buffer + * pointer "data", buffer length "nbytes", thread "td". + * Returns: 0 on success, an error number otherwise + * Locks: none + * References: vp must be a valid reference for the duration of the call + */ +static int +extattr_set_vp(struct vnode *vp, int attrnamespace, const char *attrname, + void *data, size_t nbytes, struct proc *p, register_t *retval) +{ + struct uio auio; + struct iovec aiov; + ssize_t cnt; + int error; + + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + + aiov.iov_base = data; + aiov.iov_len = nbytes; + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + auio.uio_offset = 0; + if (nbytes > INT_MAX) { + error = EINVAL; + goto done; + } + auio.uio_resid = nbytes; + auio.uio_rw = UIO_WRITE; + auio.uio_segflg = UIO_USERSPACE; + auio.uio_procp = p; + cnt = nbytes; + + error = VOP_SETEXTATTR(vp, attrnamespace, attrname, &auio, + p->p_ucred, p); + cnt -= auio.uio_resid; + retval[0] = cnt; + +done: + VOP_UNLOCK(vp, 0, p); + return (error); +} + +int +sys_extattr_set_file(struct proc *p, void *v, register_t *retval) +{ + struct sys_extattr_set_file_args /* { + syscallarg(const char *) path; + syscallarg(int) attrnamespace; + syscallarg(const char *) attrname; + syscallarg(void *) data; + syscallarg(size_t) nbytes; + } */ *uap = v; + struct nameidata nd; + char attrname[EXTATTR_MAXNAMELEN]; + int error; + + error = copyinstr(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN, + NULL); + if (error) + return (error); + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return (error); + + error = extattr_set_vp(nd.ni_vp, SCARG(uap, attrnamespace), attrname, + SCARG(uap, data), SCARG(uap, nbytes), p, retval); + + vrele(nd.ni_vp); + return (error); +} + +int +sys_extattr_set_fd(struct proc *p, void *v, register_t *retval) +{ + struct sys_extattr_set_fd_args /* { + syscallarg(int) fd; + syscallarg(int) attrnamespace; + syscallarg(const char *) attrname; + syscallarg(struct iovec *) iovp; + syscallarg(int) iovcnt; + } */ *uap = v; + struct file *fp; + char attrname[EXTATTR_MAXNAMELEN]; + int error; + + error = copyinstr(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN, + NULL); + if (error) + return (error); + + if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) + return (error); + + FREF(fp); + error = extattr_set_vp((struct vnode *)fp->f_data, + SCARG(uap, attrnamespace), attrname, SCARG(uap, data), + SCARG(uap, nbytes), p, retval); + FRELE(fp); + + return (error); +} + +/*- + * Get a named extended attribute on a file or directory + * + * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace", + * kernelspace string pointer "attrname", userspace buffer + * pointer "data", buffer length "nbytes", thread "td". + * Returns: 0 on success, an error number otherwise + * Locks: none + * References: vp must be a valid reference for the duration of the call + */ +static int +extattr_get_vp(struct vnode *vp, int attrnamespace, const char *attrname, + void *data, size_t nbytes, struct proc *p, register_t *retval) +{ + struct uio auio; + struct iovec aiov; + ssize_t cnt; + size_t size; + int error; + + VOP_LEASE(vp, p, p->p_ucred, LEASE_READ); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + + /* + * Slightly unusual semantics: if the user provides a NULL data + * pointer, they don't want to receive the data, just the + * maximum read length. + */ + if (data != NULL) { + aiov.iov_base = data; + aiov.iov_len = nbytes; + auio.uio_iov = &aiov; + auio.uio_offset = 0; + if (nbytes > INT_MAX) { + error = EINVAL; + goto done; + } + auio.uio_resid = nbytes; + auio.uio_rw = UIO_READ; + auio.uio_segflg = UIO_USERSPACE; + auio.uio_procp = p; + cnt = nbytes; + error = VOP_GETEXTATTR(vp, attrnamespace, attrname, &auio, + NULL, p->p_ucred, p); + cnt -= auio.uio_resid; + retval[0] = cnt; + } else { + error = VOP_GETEXTATTR(vp, attrnamespace, attrname, NULL, + &size, p->p_ucred, p); + retval[0] = size; + } +done: + VOP_UNLOCK(vp, 0, p); + return (error); +} + +int +sys_extattr_get_file(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_extattr_get_file_args /* { + syscallarg(const char *) path; + syscallarg(int) attrnamespace; + syscallarg(const char *) attrname; + syscallarg(void *) data; + syscallarg(size_t) nbytes; + } */ *uap = v; + struct nameidata nd; + char attrname[EXTATTR_MAXNAMELEN]; + int error; + + error = copyinstr(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN, + NULL); + if (error) + return (error); + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return (error); + + error = extattr_get_vp(nd.ni_vp, SCARG(uap, attrnamespace), attrname, + SCARG(uap, data), SCARG(uap, nbytes), p, retval); + + vrele(nd.ni_vp); + return (error); +} + +int +sys_extattr_get_fd(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_extattr_get_fd_args /* { + syscallarg(int) fd; + syscallarg(int) attrnamespace; + syscallarg(const char *) attrname; + syscallarg(void *) data; + syscallarg(size_t) nbytes; + } */ *uap = v; + struct file *fp; + char attrname[EXTATTR_MAXNAMELEN]; + int error; + + error = copyinstr(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN, + NULL); + if (error) + return (error); + + if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) + return (error); + + FREF(fp); + error = extattr_get_vp((struct vnode *)fp->f_data, + SCARG(uap, attrnamespace), attrname, SCARG(uap, data), + SCARG(uap, nbytes), p, retval); + FRELE(fp); + + return (error); +} + +/* + * extattr_delete_vp(): Delete a named extended attribute on a file or + * directory + * + * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace", + * kernelspace string pointer "attrname", proc "p" + * Returns: 0 on success, an error number otherwise + * Locks: none + * References: vp must be a valid reference for the duration of the call + */ +static int +extattr_delete_vp(struct vnode *vp, int attrnamespace, const char *attrname, + struct proc *p) +{ + int error; + + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + + error = VOP_SETEXTATTR(vp, attrnamespace, attrname, NULL, + p->p_ucred, p); + + VOP_UNLOCK(vp, 0, p); + return (error); +} + +int +sys_extattr_delete_file(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_extattr_delete_file_args /* { + syscallarg(int) fd; + syscallarg(int) attrnamespace; + syscallarg(const char *) attrname; + } */ *uap = v; + struct nameidata nd; + char attrname[EXTATTR_MAXNAMELEN]; + int error; + + error = copyinstr(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN, + NULL); + if (error) + return(error); + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return(error); + + error = extattr_delete_vp(nd.ni_vp, SCARG(uap, attrnamespace), + attrname, p); + + vrele(nd.ni_vp); + return(error); +} + +int +sys_extattr_delete_fd(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_extattr_delete_fd_args /* { + syscallarg(int) fd; + syscallarg(int) attrnamespace; + syscallarg(const char *) attrname; + } */ *uap = v; + struct file *fp; + char attrname[EXTATTR_MAXNAMELEN]; + int error; + + error = copyinstr(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN, + NULL); + if (error) + return (error); + + if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) + return (error); + + FREF(fp); + error = extattr_delete_vp((struct vnode *)fp->f_data, + SCARG(uap, attrnamespace), attrname, p); + FRELE(fp); + + return (error); +} +#endif diff --git a/sys/kern/vnode_if.src b/sys/kern/vnode_if.src index a1cd5c5b9c5..4eb88e93191 100644 --- a/sys/kern/vnode_if.src +++ b/sys/kern/vnode_if.src @@ -1,4 +1,4 @@ -# $OpenBSD: vnode_if.src,v 1.18 2001/12/19 08:58:06 art Exp $ +# $OpenBSD: vnode_if.src,v 1.19 2002/02/22 20:37:45 drahn Exp $ # $NetBSD: vnode_if.src,v 1.10 1996/05/11 18:26:27 mycroft Exp $ # # Copyright (c) 1992, 1993 @@ -467,3 +467,27 @@ vop_whiteout { #vop_bwrite { # IN struct buf *bp; #}; +# +#% getextattr vp L L L +# +vop_getextattr { + IN struct vnode *vp; + IN int attrnamespace; + IN const char *name; + INOUT struct uio *uio; + OUT size_t *size; + IN struct ucred *cred; + IN struct proc *p; +}; + +# +#% setextattr vp L L L +# +vop_setextattr { + IN struct vnode *vp; + IN int attrnamespace; + IN const char *name; + INOUT struct uio *uio; + IN struct ucred *cred; + IN struct proc *p; +}; diff --git a/sys/sys/extattr.h b/sys/sys/extattr.h new file mode 100644 index 00000000000..90e9cfaac3a --- /dev/null +++ b/sys/sys/extattr.h @@ -0,0 +1,69 @@ +/* $OpenBSD: extattr.h,v 1.1 2002/02/22 20:37:45 drahn Exp $ */ + +/*- + * Copyright (c) 1999, 2000, 2001 Robert N. M. Watson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: extattr.h,v 1.6 2001/03/31 16:20:05 rwatson Exp $ + */ +/* + * Userland/kernel interface for Extended File System Attributes + * + * The POSIX.1e implementation page may be reached at: + * http://www.watson.org/fbsd-hardening/posix1e/ + */ + +#ifndef _SYS_EXTATTR_H_ +#define _SYS_EXTATTR_H_ + +#define EXTATTR_NAMESPACE_USER 0x00000001 +#define EXTATTR_NAMESPACE_USER_STRING "user" +#define EXTATTR_NAMESPACE_SYSTEM 0x00000002 +#define EXTATTR_NAMESPACE_SYSTEM_STRING "system" + +#ifdef _KERNEL + +#define EXTATTR_MAXNAMELEN NAME_MAX + +#else +#include <sys/cdefs.h> + +__BEGIN_DECLS +int extattrctl(const char *_path, int _cmd, const char *_filename, + int _attrnamespace, const char *_attrname); +int extattr_delete_fd(int _fd, int _attrnamespace, const char *_attrname); +int extattr_delete_file(const char *_path, int _attrnamespace, + const char *_attrname); +ssize_t extattr_get_fd(int _fd, int _attrnamespace, const char *_attrname, + void *_data, size_t _nbytes); +ssize_t extattr_get_file(const char *_path, int _attrnamespace, + const char *_attrname, void *_data, size_t _nbytes); +int extattr_set_fd(int _fd, int _attrnamespace, const char *_attrname, + const void *_data, size_t _nbytes); +int extattr_set_file(const char *_path, int _attrnamespace, + const char *_attrname, const void *_data, size_t _nbytes); +__END_DECLS + +#endif /* !_KERNEL */ +#endif /* !_SYS_EXTATTR_H_ */ diff --git a/sys/sys/malloc.h b/sys/sys/malloc.h index 05e70a81a44..cb49740f3f6 100644 --- a/sys/sys/malloc.h +++ b/sys/sys/malloc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: malloc.h,v 1.54 2002/02/12 17:19:41 provos Exp $ */ +/* $OpenBSD: malloc.h,v 1.55 2002/02/22 20:37:45 drahn Exp $ */ /* $NetBSD: malloc.h,v 1.39 1998/07/12 19:52:01 augustss Exp $ */ /* @@ -149,7 +149,8 @@ #define M_USBHC 103 /* USB host controller */ /* 104 - free */ #define M_MEMDESC 105 /* Memory range */ -/* 106-107 - free */ +#define M_UFS_EXTATTR 106 /* Extended Attributes */ +/* 107 - free */ #define M_CRYPTO_DATA 108 /* Crypto framework data buffers (keys etc.) */ /* 109 - free */ #define M_CREDENTIALS 110 /* IPsec-related credentials and ID info */ diff --git a/sys/sys/mount.h b/sys/sys/mount.h index 551f22b76d7..76b52596ac5 100644 --- a/sys/sys/mount.h +++ b/sys/sys/mount.h @@ -1,4 +1,4 @@ -/* $OpenBSD: mount.h,v 1.43 2002/01/18 01:36:29 mickey Exp $ */ +/* $OpenBSD: mount.h,v 1.44 2002/02/22 20:37:45 drahn Exp $ */ /* $NetBSD: mount.h,v 1.48 1996/02/18 11:55:47 fvdl Exp $ */ /* @@ -473,6 +473,10 @@ struct vfsops { size_t, struct proc *)); int (*vfs_checkexp) __P((struct mount *mp, struct mbuf *nam, int *extflagsp, struct ucred **credanonp)); + int (*vfs_extattrctl) __P((struct mount *mp, int cmd, + struct vnode *filename_vp, + int attrnamespace, const char *attrname, + struct proc *p)); }; #define VFS_MOUNT(MP, PATH, DATA, NDP, P) \ @@ -489,6 +493,18 @@ struct vfsops { #define VFS_VPTOFH(VP, FIDP) (*(VP)->v_mount->mnt_op->vfs_vptofh)(VP, FIDP) #define VFS_CHECKEXP(MP, NAM, EXFLG, CRED) \ (*(MP)->mnt_op->vfs_checkexp)(MP, NAM, EXFLG, CRED) +#define VFS_EXTATTRCTL(MP, C, FN, NS, N, P) \ + (*(MP)->mnt_op->vfs_extattrctl)(MP, C, FN, NS, N, P) + +/* + * Declarations for these vfs default operations are located in + * kern/vfs_default.c, they should be used instead of making "dummy" + * functions or casting entries in the VFS op table to "enopnotsupp()". + */ +int vfs_stdextattrctl __P((struct mount *mp, int cmd, + struct vnode *filename_vp, int attrnamespace, const char *attrname, + struct proc *p)); + #endif /* _KERNEL */ /* diff --git a/sys/ufs/ext2fs/ext2fs_bmap.c b/sys/ufs/ext2fs/ext2fs_bmap.c index 84a05ace5aa..8f247adf707 100644 --- a/sys/ufs/ext2fs/ext2fs_bmap.c +++ b/sys/ufs/ext2fs/ext2fs_bmap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ext2fs_bmap.c,v 1.6 2001/09/18 00:22:31 art Exp $ */ +/* $OpenBSD: ext2fs_bmap.c,v 1.7 2002/02/22 20:37:45 drahn Exp $ */ /* $NetBSD: ext2fs_bmap.c,v 1.5 2000/03/30 12:41:11 augustss Exp $ */ /* @@ -53,6 +53,7 @@ #include <miscfs/specfs/specdev.h> +#include <ufs/ufs/extattr.h> #include <ufs/ufs/quota.h> #include <ufs/ufs/inode.h> #include <ufs/ufs/ufsmount.h> diff --git a/sys/ufs/ext2fs/ext2fs_inode.c b/sys/ufs/ext2fs/ext2fs_inode.c index 0e2a975e333..f7eac005b0d 100644 --- a/sys/ufs/ext2fs/ext2fs_inode.c +++ b/sys/ufs/ext2fs/ext2fs_inode.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ext2fs_inode.c,v 1.19 2001/12/19 08:58:07 art Exp $ */ +/* $OpenBSD: ext2fs_inode.c,v 1.20 2002/02/22 20:37:45 drahn Exp $ */ /* $NetBSD: ext2fs_inode.c,v 1.24 2001/06/19 12:59:18 wiz Exp $ */ /* @@ -51,6 +51,7 @@ #include <uvm/uvm_extern.h> +#include <ufs/ufs/extattr.h> #include <ufs/ufs/quota.h> #include <ufs/ufs/inode.h> #include <ufs/ufs/ufsmount.h> diff --git a/sys/ufs/ext2fs/ext2fs_lookup.c b/sys/ufs/ext2fs/ext2fs_lookup.c index 82bd81c7dbd..663d5ef8a83 100644 --- a/sys/ufs/ext2fs/ext2fs_lookup.c +++ b/sys/ufs/ext2fs/ext2fs_lookup.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ext2fs_lookup.c,v 1.11 2001/09/18 01:21:55 art Exp $ */ +/* $OpenBSD: ext2fs_lookup.c,v 1.12 2002/02/22 20:37:45 drahn Exp $ */ /* $NetBSD: ext2fs_lookup.c,v 1.16 2000/08/03 20:29:26 thorpej Exp $ */ /* @@ -62,6 +62,7 @@ #include <sys/malloc.h> #include <sys/dirent.h> +#include <ufs/ufs/extattr.h> #include <ufs/ufs/quota.h> #include <ufs/ufs/inode.h> #include <ufs/ufs/ufsmount.h> diff --git a/sys/ufs/ext2fs/ext2fs_vfsops.c b/sys/ufs/ext2fs/ext2fs_vfsops.c index 6f404d98157..09df6e06183 100644 --- a/sys/ufs/ext2fs/ext2fs_vfsops.c +++ b/sys/ufs/ext2fs/ext2fs_vfsops.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ext2fs_vfsops.c,v 1.20 2001/12/19 08:58:07 art Exp $ */ +/* $OpenBSD: ext2fs_vfsops.c,v 1.21 2002/02/22 20:37:45 drahn Exp $ */ /* $NetBSD: ext2fs_vfsops.c,v 1.1 1997/06/11 09:34:07 bouyer Exp $ */ /* @@ -59,6 +59,7 @@ #include <miscfs/specfs/specdev.h> +#include <ufs/ufs/extattr.h> #include <ufs/ufs/quota.h> #include <ufs/ufs/ufsmount.h> #include <ufs/ufs/inode.h> @@ -97,7 +98,8 @@ struct vfsops ext2fs_vfsops = { ext2fs_vptofh, ext2fs_init, ext2fs_sysctl, - ufs_check_export + ufs_check_export, + vfs_stdextattrctl }; struct pool ext2fs_inode_pool; diff --git a/sys/ufs/ext2fs/ext2fs_vnops.c b/sys/ufs/ext2fs/ext2fs_vnops.c index d85d4eba5d9..43ef065c46f 100644 --- a/sys/ufs/ext2fs/ext2fs_vnops.c +++ b/sys/ufs/ext2fs/ext2fs_vnops.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ext2fs_vnops.c,v 1.21 2001/12/19 08:58:07 art Exp $ */ +/* $OpenBSD: ext2fs_vnops.c,v 1.22 2002/02/22 20:37:45 drahn Exp $ */ /* $NetBSD: ext2fs_vnops.c,v 1.1 1997/06/11 09:34:09 bouyer Exp $ */ /* @@ -65,6 +65,7 @@ #include <miscfs/fifofs/fifo.h> #include <miscfs/specfs/specdev.h> +#include <ufs/ufs/extattr.h> #include <ufs/ufs/quota.h> #include <ufs/ufs/inode.h> #include <ufs/ufs/ufs_extern.h> diff --git a/sys/ufs/ffs/ffs_alloc.c b/sys/ufs/ffs/ffs_alloc.c index c42897ac4d4..a8233c33036 100644 --- a/sys/ufs/ffs/ffs_alloc.c +++ b/sys/ufs/ffs/ffs_alloc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ffs_alloc.c,v 1.38 2001/12/19 08:58:07 art Exp $ */ +/* $OpenBSD: ffs_alloc.c,v 1.39 2002/02/22 20:37:46 drahn Exp $ */ /* $NetBSD: ffs_alloc.c,v 1.11 1996/05/11 18:27:09 mycroft Exp $ */ /* @@ -49,6 +49,7 @@ #include <dev/rndvar.h> +#include <ufs/ufs/extattr.h> #include <ufs/ufs/quota.h> #include <ufs/ufs/inode.h> #include <ufs/ufs/ufs_extern.h> diff --git a/sys/ufs/ffs/ffs_inode.c b/sys/ufs/ffs/ffs_inode.c index cb6e60a956a..9ee61587054 100644 --- a/sys/ufs/ffs/ffs_inode.c +++ b/sys/ufs/ffs/ffs_inode.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ffs_inode.c,v 1.30 2002/01/04 03:53:23 nordin Exp $ */ +/* $OpenBSD: ffs_inode.c,v 1.31 2002/02/22 20:37:46 drahn Exp $ */ /* $NetBSD: ffs_inode.c,v 1.10 1996/05/11 18:27:19 mycroft Exp $ */ /* @@ -49,6 +49,7 @@ #include <uvm/uvm_extern.h> +#include <ufs/ufs/extattr.h> #include <ufs/ufs/quota.h> #include <ufs/ufs/inode.h> #include <ufs/ufs/ufsmount.h> diff --git a/sys/ufs/ffs/ffs_softdep.c b/sys/ufs/ffs/ffs_softdep.c index 027a3f42693..2589c800a3c 100644 --- a/sys/ufs/ffs/ffs_softdep.c +++ b/sys/ufs/ffs/ffs_softdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ffs_softdep.c,v 1.35 2002/01/29 14:31:59 millert Exp $ */ +/* $OpenBSD: ffs_softdep.c,v 1.36 2002/02/22 20:37:46 drahn Exp $ */ /* * Copyright 1998, 2000 Marshall Kirk McKusick. All Rights Reserved. * @@ -58,6 +58,7 @@ #include <sys/systm.h> #include <sys/vnode.h> #include <miscfs/specfs/specdev.h> +#include <ufs/ufs/extattr.h> #include <ufs/ufs/dir.h> #include <ufs/ufs/quota.h> #include <ufs/ufs/inode.h> diff --git a/sys/ufs/ffs/ffs_softdep_stub.c b/sys/ufs/ffs/ffs_softdep_stub.c index 2eabe90e9b3..fb29626b96e 100644 --- a/sys/ufs/ffs/ffs_softdep_stub.c +++ b/sys/ufs/ffs/ffs_softdep_stub.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ffs_softdep_stub.c,v 1.3 2001/02/21 23:24:31 csapuntz Exp $ */ +/* $OpenBSD: ffs_softdep_stub.c,v 1.4 2002/02/22 20:37:46 drahn Exp $ */ /* * Copyright 1998 Marshall Kirk McKusick. All Rights Reserved. @@ -41,6 +41,7 @@ #include <sys/param.h> #include <sys/vnode.h> #include <sys/systm.h> +#include <ufs/ufs/extattr.h> #include <ufs/ufs/quota.h> #include <ufs/ufs/inode.h> #include <ufs/ffs/ffs_extern.h> diff --git a/sys/ufs/ffs/ffs_subr.c b/sys/ufs/ffs/ffs_subr.c index 50916fca74a..b70795e06d3 100644 --- a/sys/ufs/ffs/ffs_subr.c +++ b/sys/ufs/ffs/ffs_subr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ffs_subr.c,v 1.10 2001/12/01 19:12:25 deraadt Exp $ */ +/* $OpenBSD: ffs_subr.c,v 1.11 2002/02/22 20:37:46 drahn Exp $ */ /* $NetBSD: ffs_subr.c,v 1.6 1996/03/17 02:16:23 christos Exp $ */ /* @@ -43,6 +43,7 @@ #include <sys/systm.h> #include <sys/vnode.h> #include <sys/buf.h> +#include <ufs/ufs/extattr.h> #include <ufs/ufs/quota.h> #include <ufs/ufs/inode.h> #include <ufs/ffs/ffs_extern.h> diff --git a/sys/ufs/ffs/ffs_vfsops.c b/sys/ufs/ffs/ffs_vfsops.c index f4374c79bec..03293113e61 100644 --- a/sys/ufs/ffs/ffs_vfsops.c +++ b/sys/ufs/ffs/ffs_vfsops.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ffs_vfsops.c,v 1.50 2002/01/25 02:30:27 millert Exp $ */ +/* $OpenBSD: ffs_vfsops.c,v 1.51 2002/02/22 20:37:46 drahn Exp $ */ /* $NetBSD: ffs_vfsops.c,v 1.19 1996/02/09 22:22:26 christos Exp $ */ /* @@ -58,6 +58,7 @@ #include <miscfs/specfs/specdev.h> +#include <ufs/ufs/extattr.h> #include <ufs/ufs/quota.h> #include <ufs/ufs/ufsmount.h> #include <ufs/ufs/inode.h> @@ -84,7 +85,12 @@ struct vfsops ffs_vfsops = { ffs_vptofh, ffs_init, ffs_sysctl, - ufs_check_export + ufs_check_export, +#ifdef UFS_EXTATTR + ufs_extattrctl, +#else + vfs_stdextattrctl, +#endif }; struct inode_vtbl ffs_vtbl = { @@ -746,6 +752,10 @@ ffs_mountfs(devvp, mp, p) ump->um_seqinc = fs->fs_frag; for (i = 0; i < MAXQUOTAS; i++) ump->um_quotas[i] = NULLVP; +#ifdef UFS_EXTATTR + ufs_extattr_uepm_init(&ump->um_extattr); +#endif + devvp->v_specmountpoint = mp; ffs_oldfscompat(fs); @@ -796,6 +806,21 @@ ffs_mountfs(devvp, mp, p) fs->fs_flags &= ~FS_DOSOFTDEP; (void) ffs_sbupdate(ump, MNT_WAIT); } +#ifdef UFS_EXTATTR +#ifdef UFS_EXTATTR_AUTOSTART + /* + * + * Auto-starting does the following: + * - check for /.attribute in the fs, and extattr_start if so + * - for each file in .attribute, enable that file with + * an attribute of the same name. + * Not clear how to report errors -- probably eat them. + * This would all happen while the file system was busy/not + * available, so would effectively be "atomic". + */ + (void) ufs_extattr_autostart(mp, p); +#endif /* !UFS_EXTATTR_AUTOSTART */ +#endif /* !UFS_EXTATTR */ return (0); out: devvp->v_specmountpoint = NULL; @@ -862,6 +887,15 @@ ffs_unmount(mp, mntflags, p) ump = VFSTOUFS(mp); fs = ump->um_fs; +#ifdef UFS_EXTATTR + if ((error = ufs_extattr_stop(mp, p))) { + if (error != EOPNOTSUPP) + printf("ffs_unmount: ufs_extattr_stop returned %d\n", + error); + } else { + ufs_extattr_uepm_destroy(&ump->um_extattr); + } +#endif if (mp->mnt_flag & MNT_SOFTDEP) error = softdep_flushfiles(mp, flags, p); else diff --git a/sys/ufs/ffs/ffs_vnops.c b/sys/ufs/ffs/ffs_vnops.c index 1020b14a2bb..8e9ce74b9ba 100644 --- a/sys/ufs/ffs/ffs_vnops.c +++ b/sys/ufs/ffs/ffs_vnops.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ffs_vnops.c,v 1.25 2001/12/19 08:58:07 art Exp $ */ +/* $OpenBSD: ffs_vnops.c,v 1.26 2002/02/22 20:37:46 drahn Exp $ */ /* $NetBSD: ffs_vnops.c,v 1.7 1996/05/11 18:27:24 mycroft Exp $ */ /* @@ -56,6 +56,7 @@ #include <miscfs/specfs/specdev.h> #include <miscfs/fifofs/fifo.h> +#include <ufs/ufs/extattr.h> #include <ufs/ufs/quota.h> #include <ufs/ufs/inode.h> #include <ufs/ufs/dir.h> @@ -107,7 +108,11 @@ struct vnodeopv_entry_desc ffs_vnodeop_entries[] = { { &vop_advlock_desc, ufs_advlock }, /* advlock */ { &vop_reallocblks_desc, ffs_reallocblks }, /* reallocblks */ { &vop_bwrite_desc, vop_generic_bwrite }, - { (struct vnodeop_desc*)NULL, (int(*) __P((void*)))NULL } +#ifdef UFS_EXTATTR + { &vop_getextattr_desc, ufs_vop_getextattr }, + { &vop_setextattr_desc, ufs_vop_setextattr }, +#endif + { NULL, NULL } }; struct vnodeopv_desc ffs_vnodeop_opv_desc = { &ffs_vnodeop_p, ffs_vnodeop_entries }; @@ -152,6 +157,10 @@ struct vnodeopv_entry_desc ffs_specop_entries[] = { { &vop_advlock_desc, spec_advlock }, /* advlock */ { &vop_reallocblks_desc, spec_reallocblks }, /* reallocblks */ { &vop_bwrite_desc, vop_generic_bwrite }, +#ifdef UFS_EXTATTR + { &vop_getextattr_desc, ufs_vop_getextattr }, + { &vop_setextattr_desc, ufs_vop_setextattr }, +#endif { NULL, NULL } }; struct vnodeopv_desc ffs_specop_opv_desc = @@ -198,6 +207,10 @@ struct vnodeopv_entry_desc ffs_fifoop_entries[] = { { &vop_advlock_desc, fifo_advlock }, /* advlock */ { &vop_reallocblks_desc, fifo_reallocblks }, /* reallocblks */ { &vop_bwrite_desc, vop_generic_bwrite }, +#ifdef UFS_EXTATTR + { &vop_getextattr_desc, ufs_vop_getextattr }, + { &vop_setextattr_desc, ufs_vop_setextattr }, +#endif { NULL, NULL } }; struct vnodeopv_desc ffs_fifoop_opv_desc = diff --git a/sys/ufs/lfs/lfs_vfsops.c b/sys/ufs/lfs/lfs_vfsops.c index 825129518c6..940918be139 100644 --- a/sys/ufs/lfs/lfs_vfsops.c +++ b/sys/ufs/lfs/lfs_vfsops.c @@ -1,4 +1,4 @@ -/* $OpenBSD: lfs_vfsops.c,v 1.13 2001/02/20 01:50:12 assar Exp $ */ +/* $OpenBSD: lfs_vfsops.c,v 1.14 2002/02/22 20:37:46 drahn Exp $ */ /* $NetBSD: lfs_vfsops.c,v 1.11 1996/03/25 12:53:35 pk Exp $ */ /* @@ -77,7 +77,8 @@ struct vfsops lfs_vfsops = { lfs_vptofh, lfs_init, lfs_sysctl, - ufs_check_export + ufs_check_export, + vfs_stdextattrctl }; int diff --git a/sys/ufs/mfs/mfs_vfsops.c b/sys/ufs/mfs/mfs_vfsops.c index 63259f5408e..134f3c6c2fa 100644 --- a/sys/ufs/mfs/mfs_vfsops.c +++ b/sys/ufs/mfs/mfs_vfsops.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mfs_vfsops.c,v 1.17 2002/02/18 09:23:26 ericj Exp $ */ +/* $OpenBSD: mfs_vfsops.c,v 1.18 2002/02/22 20:37:46 drahn Exp $ */ /* $NetBSD: mfs_vfsops.c,v 1.10 1996/02/09 22:31:28 christos Exp $ */ /* @@ -48,6 +48,7 @@ #include <sys/malloc.h> #include <sys/kthread.h> +#include <ufs/ufs/extattr.h> #include <ufs/ufs/quota.h> #include <ufs/ufs/inode.h> #include <ufs/ufs/ufsmount.h> @@ -82,7 +83,8 @@ struct vfsops mfs_vfsops = { ffs_vptofh, mfs_init, ffs_sysctl, - mfs_checkexp + mfs_checkexp, + vfs_stdextattrctl }; /* diff --git a/sys/ufs/ufs/extattr.h b/sys/ufs/ufs/extattr.h new file mode 100644 index 00000000000..da32ed3f57c --- /dev/null +++ b/sys/ufs/ufs/extattr.h @@ -0,0 +1,109 @@ +/* $OpenBSD: extattr.h,v 1.1 2002/02/22 20:37:46 drahn Exp $ */ +/*- + * Copyright (c) 1999, 2000, 2001 Robert N. M. Watson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: extattr.h,v 1.14 2001/09/12 08:38:10 julian Exp $ + */ +/* + * TrustedBSD Project - extended attribute support for UFS-like file systems + */ + +#ifndef _UFS_UFS_EXTATTR_H_ +#define _UFS_UFS_EXTATTR_H_ + +#define UFS_EXTATTR_MAGIC 0x00b5d5ec +#define UFS_EXTATTR_VERSION 0x00000003 +#define UFS_EXTATTR_FSROOTSUBDIR ".attribute" +#define UFS_EXTATTR_SUBDIR_SYSTEM "system" +#define UFS_EXTATTR_SUBDIR_USER "user" +#define UFS_EXTATTR_MAXEXTATTRNAME 65 /* including null */ + +#define UFS_EXTATTR_ATTR_FLAG_INUSE 0x00000001 /* attr has been set */ +#define UFS_EXTATTR_PERM_KERNEL 0x00000000 +#define UFS_EXTATTR_PERM_ROOT 0x00000001 +#define UFS_EXTATTR_PERM_OWNER 0x00000002 +#define UFS_EXTATTR_PERM_ANYONE 0x00000003 + +#define UFS_EXTATTR_UEPM_INITIALIZED 0x00000001 +#define UFS_EXTATTR_UEPM_STARTED 0x00000002 + +#define UFS_EXTATTR_CMD_START 0x00000001 +#define UFS_EXTATTR_CMD_STOP 0x00000002 +#define UFS_EXTATTR_CMD_ENABLE 0x00000003 +#define UFS_EXTATTR_CMD_DISABLE 0x00000004 + +struct ufs_extattr_fileheader { + u_int uef_magic; /* magic number for sanity checking */ + u_int uef_version; /* version of attribute file */ + u_int uef_size; /* size of attributes, w/o header */ +}; + +struct ufs_extattr_header { + u_int ueh_flags; /* flags for attribute */ + u_int ueh_len; /* local defined length; <= uef_size */ + u_int32_t ueh_i_gen; /* generation number for sanity */ + /* data follows the header */ +}; + +#ifdef _KERNEL + +#ifdef MALLOC_DECLARE +MALLOC_DECLARE(M_EXTATTR); +#endif + +struct vnode; +LIST_HEAD(ufs_extattr_list_head, ufs_extattr_list_entry); +struct ufs_extattr_list_entry { + LIST_ENTRY(ufs_extattr_list_entry) uele_entries; + struct ufs_extattr_fileheader uele_fileheader; + int uele_attrnamespace; + char uele_attrname[UFS_EXTATTR_MAXEXTATTRNAME]; + struct vnode *uele_backing_vnode; +}; + +struct lock; +struct ucred; +struct ufs_extattr_per_mount { + struct lock uepm_lock; + struct ufs_extattr_list_head uepm_list; + struct ucred *uepm_ucred; + int uepm_flags; +}; + +void ufs_extattr_uepm_init(struct ufs_extattr_per_mount *uepm); +void ufs_extattr_uepm_destroy(struct ufs_extattr_per_mount *uepm); +int ufs_extattr_start(struct mount *mp, struct proc *p); +int ufs_extattr_autostart(struct mount *mp, struct proc *p); +int ufs_extattr_stop(struct mount *mp, struct proc *p); +int ufs_extattrctl(struct mount *mp, int cmd, struct vnode *filename, + int attrnamespace, const char *attrname, struct proc *p); +void ufs_extattr_vnode_inactive(struct vnode *vp, struct proc *p); + +int ufs_vop_getextattr(void *); +int ufs_vop_setextattr(void *); + +#endif /* !_KERNEL */ + +#endif /* !_UFS_UFS_EXTATTR_H_ */ diff --git a/sys/ufs/ufs/ufs_bmap.c b/sys/ufs/ufs/ufs_bmap.c index fa060e3c6b8..fd8b1c397b3 100644 --- a/sys/ufs/ufs/ufs_bmap.c +++ b/sys/ufs/ufs/ufs_bmap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ufs_bmap.c,v 1.12 2001/12/19 08:58:07 art Exp $ */ +/* $OpenBSD: ufs_bmap.c,v 1.13 2002/02/22 20:37:46 drahn Exp $ */ /* $NetBSD: ufs_bmap.c,v 1.3 1996/02/09 22:36:00 christos Exp $ */ /* @@ -51,6 +51,7 @@ #include <miscfs/specfs/specdev.h> +#include <ufs/ufs/extattr.h> #include <ufs/ufs/quota.h> #include <ufs/ufs/inode.h> #include <ufs/ufs/ufsmount.h> diff --git a/sys/ufs/ufs/ufs_extattr.c b/sys/ufs/ufs/ufs_extattr.c new file mode 100644 index 00000000000..25f39aae254 --- /dev/null +++ b/sys/ufs/ufs/ufs_extattr.c @@ -0,0 +1,1338 @@ +/* $OpenBSD: ufs_extattr.c,v 1.1 2002/02/22 20:37:46 drahn Exp $ */ +/*- + * Copyright (c) 1999, 2000, 2001, 2002 Robert N. M. Watson + * Copyright (c) 2002 Networks Associates Technologies, Inc. + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * + * This software was developed for the FreeBSD Project in part by NAI Labs, + * the Security Research Division of Network Associates, Inc. under + * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA + * CHATS research program. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the authors may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: ufs_extattr.c,v 1.44 2002/02/10 04:57:08 rwatson Exp $ + */ +/* + * Developed by the TrustedBSD Project. + * Support for file system extended attribute: UFS-specific support functions. + */ + + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/namei.h> +#include <sys/malloc.h> +#include <sys/fcntl.h> +#include <sys/proc.h> +#include <sys/vnode.h> +#include <sys/mount.h> +#include <sys/lock.h> +#include <sys/dirent.h> +#include <sys/extattr.h> +#include <sys/sysctl.h> + +#include <ufs/ufs/dir.h> +#include <ufs/ufs/extattr.h> +#include <ufs/ufs/quota.h> +#include <ufs/ufs/ufsmount.h> +#include <ufs/ufs/inode.h> +#include <ufs/ufs/ufs_extern.h> + +#ifdef UFS_EXTATTR +#ifdef __OpenBSD__ +#define static +#endif +#ifndef LK_NOPAUSE +#define LK_NOPAUSE 0 +#endif /* LK_NOPAUSE */ + +#define MIN(a,b) (((a)<(b))?(a):(b)) + +/* -XXX +static MALLOC_DEFINE(M_UFS_EXTATTR, "ufs_extattr", "ufs extended attribute"); +*/ + +static int ufs_extattr_sync = 0; +/* -XXX +SYSCTL_INT(_debug, OID_AUTO, ufs_extattr_sync, CTLFLAG_RW, &ufs_extattr_sync, + 0, ""); +*/ + +static int ufs_extattr_valid_attrname(int attrnamespace, + const char *attrname); +static int ufs_extattr_credcheck(struct vnode *vp, + struct ufs_extattr_list_entry *uele, struct ucred *cred, struct proc *p, + int access); +static int ufs_extattr_enable_with_open(struct ufsmount *ump, + struct vnode *vp, int attrnamespace, const char *attrname, struct proc *p); +static int ufs_extattr_enable(struct ufsmount *ump, int attrnamespace, + const char *attrname, struct vnode *backing_vnode, struct proc *p); +static int ufs_extattr_disable(struct ufsmount *ump, int attrnamespace, + const char *attrname, struct proc *p); +static int ufs_extattr_get(struct vnode *vp, int attrnamespace, + const char *name, struct uio *uio, size_t *size, struct ucred *cred, + struct proc *p); +static int ufs_extattr_set(struct vnode *vp, int attrnamespace, + const char *name, struct uio *uio, struct ucred *cred, struct proc *p); +static int ufs_extattr_rm(struct vnode *vp, int attrnamespace, + const char *name, struct ucred *cred, struct proc *p); + +static void ufs_extattr_uepm_lock(struct ufsmount *ump, struct proc *p); +static void ufs_extattr_uepm_unlock(struct ufsmount *ump, struct proc *p); +static struct ufs_extattr_list_entry * +ufs_extattr_find_attr(struct ufsmount *ump, int attrnamespace, + const char *attrname); +/* + * Per-FS attribute lock protecting attribute operations. + * XXX Right now there is a lot of lock contention due to having a single + * lock per-FS; really, this should be far more fine-grained. + */ +static void +ufs_extattr_uepm_lock(struct ufsmount *ump, struct proc *p) +{ + + /* Ideally, LK_CANRECURSE would not be used, here. */ + lockmgr(&ump->um_extattr.uepm_lock, LK_EXCLUSIVE | LK_RETRY | + LK_CANRECURSE, 0, p); +} + +static void +ufs_extattr_uepm_unlock(struct ufsmount *ump, struct proc *p) +{ + + lockmgr(&ump->um_extattr.uepm_lock, LK_RELEASE, 0, p); +} + +/* + * Determine whether the name passed is a valid name for an actual + * attribute. + * + * Invalid currently consists of: + * NULL pointer for attrname + * zero-length attrname (used to retrieve application attribute list) + */ +static int +ufs_extattr_valid_attrname(int attrnamespace, const char *attrname) +{ + + if (attrname == NULL) + return (0); + if (strlen(attrname) == 0) + return (0); + return (1); +} + +/* + * Locate an attribute given a name and mountpoint. + * Must be holding uepm lock for the mount point. + */ +static struct ufs_extattr_list_entry * +ufs_extattr_find_attr(struct ufsmount *ump, int attrnamespace, + const char *attrname) +{ + struct ufs_extattr_list_entry *search_attribute; + + for (search_attribute = LIST_FIRST(&ump->um_extattr.uepm_list); + search_attribute; + search_attribute = LIST_NEXT(search_attribute, uele_entries)) { + if (!(strncmp(attrname, search_attribute->uele_attrname, + UFS_EXTATTR_MAXEXTATTRNAME)) && + (attrnamespace == search_attribute->uele_attrnamespace)) { + return (search_attribute); + } + } + + return (0); +} + +/* + * Initialize per-FS structures supporting extended attributes. Do not + * start extended attributes yet. + */ +void +ufs_extattr_uepm_init(struct ufs_extattr_per_mount *uepm) +{ + + uepm->uepm_flags = 0; + + LIST_INIT(&uepm->uepm_list); + /* XXX is PVFS right, here? */ + lockinit(&uepm->uepm_lock, PVFS, "extattr", 0, 0); + uepm->uepm_flags |= UFS_EXTATTR_UEPM_INITIALIZED; +} + +/* + * Destroy per-FS structures supporting extended attributes. Assumes + * that EAs have already been stopped, and will panic if not. + */ +void +ufs_extattr_uepm_destroy(struct ufs_extattr_per_mount *uepm) +{ + + if (!(uepm->uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) + panic("ufs_extattr_uepm_destroy: not initialized"); + + if ((uepm->uepm_flags & UFS_EXTATTR_UEPM_STARTED)) + panic("ufs_extattr_uepm_destroy: called while still started"); + + /* + * It's not clear that either order for the next two lines is + * ideal, and it should never be a problem if this is only called + * during unmount, and with vfs_busy(). + */ + uepm->uepm_flags &= ~UFS_EXTATTR_UEPM_INITIALIZED; + /* - XXX + lockdestroy(&uepm->uepm_lock); + */ +} + +/* + * Start extended attribute support on an FS. + */ +int +ufs_extattr_start(struct mount *mp, struct proc *p) +{ + struct ufsmount *ump; + int error = 0; + + ump = VFSTOUFS(mp); + + ufs_extattr_uepm_lock(ump, p); + + if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) { + error = EOPNOTSUPP; + goto unlock; + } + if (ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED) { + error = EBUSY; + goto unlock; + } + + ump->um_extattr.uepm_flags |= UFS_EXTATTR_UEPM_STARTED; + + crhold(p->p_ucred); + ump->um_extattr.uepm_ucred = p->p_ucred; + +unlock: + ufs_extattr_uepm_unlock(ump, p); + + return (error); +} + +#ifdef UFS_EXTATTR_AUTOSTART +static int +ufs_extattr_lookup(struct vnode *start_dvp, int lockparent, char *dirname, + struct vnode **vp, struct proc *p); +/* + * Helper routine: given a locked parent directory and filename, return + * the locked vnode of the inode associated with the name. Will not + * follow symlinks, may return any type of vnode. Lock on parent will + * be released even in the event of a failure. In the event that the + * target is the parent (i.e., "."), there will be two references and + * one lock, requiring the caller to possibly special-case. + */ +#define UE_GETDIR_LOCKPARENT 1 +#define UE_GETDIR_LOCKPARENT_DONT 2 +static int +ufs_extattr_lookup(struct vnode *start_dvp, int lockparent, char *dirname, + struct vnode **vp, struct proc *p) +{ + struct vop_lookup_args vargs; + struct componentname cnp; + struct vnode *target_vp; + int error; + + bzero(&cnp, sizeof(cnp)); + cnp.cn_nameiop = LOOKUP; + cnp.cn_flags = ISLASTCN; + if (lockparent == UE_GETDIR_LOCKPARENT) + cnp.cn_flags |= LOCKPARENT; + cnp.cn_proc = p; + cnp.cn_cred = p->p_ucred; + MALLOC(cnp.cn_pnbuf, char *, MAXPATHLEN, M_NAMEI, M_WAITOK); + + cnp.cn_nameptr = cnp.cn_pnbuf; + error = copystr(dirname, cnp.cn_pnbuf, MAXPATHLEN, + (size_t *) &cnp.cn_namelen); + if (error) { + if (lockparent == UE_GETDIR_LOCKPARENT_DONT) { + VOP_UNLOCK(start_dvp, 0, p); + } + FREE(cnp.cn_pnbuf, M_NAMEI); + printf("ufs_extattr_lookup: copystr failed\n"); + return (error); + } + cnp.cn_namelen--; /* trim nul termination */ + vargs.a_desc = NULL; + vargs.a_dvp = start_dvp; + vargs.a_vpp = &target_vp; + vargs.a_cnp = &cnp; + error = ufs_lookup(&vargs); + FREE(cnp.cn_pnbuf, M_NAMEI); + + if (error) { +#if 0 + /* -XXX does OpenBSD ufs_lookup always unlock on error? */ + /* + * Error condition, may have to release the lock on the parent + * if ufs_lookup() didn't. + */ + if (!(cnp.cn_flags & PDIRUNLOCK) && + (lockparent == UE_GETDIR_LOCKPARENT_DONT)) + VOP_UNLOCK(start_dvp, 0, p); + + /* + * Check that ufs_lookup() didn't release the lock when we + * didn't want it to. + */ + if ((cnp.cn_flags & PDIRUNLOCK) && + (lockparent == UE_GETDIR_LOCKPARENT)) + panic("ufs_extattr_lookup: lockparent but PDIRUNLOCK"); +#endif + + return (error); + } +/* + if (target_vp == start_dvp) + panic("ufs_extattr_lookup: target_vp == start_dvp"); +*/ + +#if 0 + /* PDIRUNLOCK does not exist on OpenBSD */ + if (target_vp != start_dvp && + !(cnp.cn_flags & PDIRUNLOCK) && + (lockparent == UE_GETDIR_LOCKPARENT_DONT)) + panic("ufs_extattr_lookup: !lockparent but !PDIRUNLOCK"); + + if ((cnp.cn_flags & PDIRUNLOCK) && + (lockparent == UE_GETDIR_LOCKPARENT)) + panic("ufs_extattr_lookup: lockparent but PDIRUNLOCK"); +#endif + + /* printf("ufs_extattr_lookup: success\n"); */ + *vp = target_vp; + return (0); +} +#endif /* !UFS_EXTATTR_AUTOSTART */ + +/* + * Enable an EA using the passed file system, backing vnode, attribute name, + * namespace, and proc. Will perform a VOP_OPEN() on the vp, so expects vp + * to be locked when passed in. The vnode will be returned unlocked, + * regardless of success/failure of the function. As a result, the caller + * will always need to vrele(), but not vput(). + */ +static int +ufs_extattr_enable_with_open(struct ufsmount *ump, struct vnode *vp, + int attrnamespace, const char *attrname, struct proc *p) +{ + int error; + + error = VOP_OPEN(vp, FREAD|FWRITE, p->p_ucred, p); + if (error) { + printf("ufs_extattr_enable_with_open.VOP_OPEN(): failed " + "with %d\n", error); + VOP_UNLOCK(vp, 0, p); + return (error); + } + +#if 0 + /* - XXX */ + /* + * XXX: Note, should VOP_CLOSE() if vfs_object_create() fails, but due + * to a similar piece of code in vn_open(), we don't. + */ + if (vn_canvmio(vp) == TRUE) + if ((error = vfs_object_create(vp, p, p->p_ucred)) != 0) { + /* + * XXX: bug replicated from vn_open(): should + * VOP_CLOSE() here. + */ + VOP_UNLOCK(vp, 0, p); + return (error); + } +#endif + + vp->v_writecount++; + + vref(vp); + + VOP_UNLOCK(vp, 0, p); + + error = ufs_extattr_enable(ump, attrnamespace, attrname, vp, p); + if (error != 0) + vn_close(vp, FREAD|FWRITE, p->p_ucred, p); + return (error); +} + +#ifdef UFS_EXTATTR_AUTOSTART +static int +ufs_extattr_iterate_directory(struct ufsmount *ump, struct vnode *dvp, + int attrnamespace, struct proc *p); +/* + * Given a locked directory vnode, iterate over the names in the directory + * and use ufs_extattr_lookup() to retrieve locked vnodes of potential + * attribute files. Then invoke ufs_extattr_enable_with_open() on each + * to attempt to start the attribute. Leaves the directory locked on + * exit. + */ +static int +ufs_extattr_iterate_directory(struct ufsmount *ump, struct vnode *dvp, + int attrnamespace, struct proc *p) +{ + struct vop_readdir_args vargs; + struct dirent *dp, *edp; + struct vnode *attr_vp; + struct uio auio; + struct iovec aiov; + char *dirbuf; + int error, eofflag = 0; + + if (dvp->v_type != VDIR) + return (ENOTDIR); + + MALLOC(dirbuf, char *, DIRBLKSIZ, M_TEMP, M_WAITOK); + + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + auio.uio_rw = UIO_READ; + auio.uio_segflg = UIO_SYSSPACE; + auio.uio_procp = p; + auio.uio_offset = 0; + + vargs.a_desc = NULL; + vargs.a_vp = dvp; + vargs.a_uio = &auio; + vargs.a_cred = p->p_ucred; + vargs.a_eofflag = &eofflag; + vargs.a_ncookies = NULL; + vargs.a_cookies = NULL; + + while (!eofflag) { + auio.uio_resid = DIRBLKSIZ; + aiov.iov_base = dirbuf; + aiov.iov_len = DIRBLKSIZ; + error = ufs_readdir(&vargs); + if (error) { + printf("ufs_extattr_iterate_directory: ufs_readdir " + "%d\n", error); + return (error); + } + + edp = (struct dirent *)&dirbuf[DIRBLKSIZ]; + for (dp = (struct dirent *)dirbuf; dp < edp; ) { +#if (BYTE_ORDER == LITTLE_ENDIAN) + dp->d_type = dp->d_namlen; + dp->d_namlen = 0; +#else + dp->d_type = 0; +#endif + if (dp->d_reclen == 0) + break; + error = ufs_extattr_lookup(dvp, UE_GETDIR_LOCKPARENT, + dp->d_name, &attr_vp, p); + if (error) { + printf("ufs_extattr_iterate_directory: lookup " + "%s %d\n", dp->d_name, error); + } else if (attr_vp == dvp) { + vrele(attr_vp); + } else if (attr_vp->v_type != VREG) { +/* + * Eventually, this will be uncommented, but in the mean time, the ".." + * entry causes unnecessary console warnings. + printf("ufs_extattr_iterate_directory: " + "%s not VREG\n", dp->d_name); +*/ + vput(attr_vp); + } else { + error = ufs_extattr_enable_with_open(ump, + attr_vp, attrnamespace, dp->d_name, p); + vrele(attr_vp); + if (error) { + printf("ufs_extattr_iterate_directory: " + "enable %s %d\n", dp->d_name, + error); + } else { +/* + * While it's nice to have some visual output here, skip for the time-being. + * Probably should be enabled by -v at boot. + printf("Autostarted %s\n", dp->d_name); + */ +printf("Autostarted %s\n", dp->d_name); /* XXX - debug*/ + } + } + dp = (struct dirent *) ((char *)dp + dp->d_reclen); + if (dp >= edp) + break; + } + } + FREE(dirbuf, M_TEMP); + + return (0); +} + +/* + * Auto-start of extended attributes, to be executed (optionally) at + * mount-time. + */ +int +ufs_extattr_autostart(struct mount *mp, struct proc *p) +{ + struct vnode *rvp, *attr_dvp, *attr_system_dvp, *attr_user_dvp; + int error; + + /* + * Does UFS_EXTATTR_FSROOTSUBDIR exist off the file system root? + * If so, automatically start EA's. + */ + error = VFS_ROOT(mp, &rvp); + if (error) { + printf("ufs_extattr_autostart.VFS_ROOT() returned %d\n", error); + return (error); + } + + error = ufs_extattr_lookup(rvp, UE_GETDIR_LOCKPARENT_DONT, + UFS_EXTATTR_FSROOTSUBDIR, &attr_dvp, p); + if (error) { + /* rvp ref'd but now unlocked */ + vrele(rvp); + return (error); + } + if (rvp == attr_dvp) { + /* Should never happen. */ + vrele(attr_dvp); + vput(rvp); + return (EINVAL); + } + vrele(rvp); + + if (attr_dvp->v_type != VDIR) { + printf("ufs_extattr_autostart: %s != VDIR\n", + UFS_EXTATTR_FSROOTSUBDIR); + goto return_vput_attr_dvp; + } + + error = ufs_extattr_start(mp, p); + if (error) { + printf("ufs_extattr_autostart: ufs_extattr_start failed (%d)\n", + error); + goto return_vput_attr_dvp; + } + + /* + * Look for two subdirectories: UFS_EXTATTR_SUBDIR_SYSTEM, + * UFS_EXTATTR_SUBDIR_USER. For each, iterate over the sub-directory, + * and start with appropriate type. Failures in either don't + * result in an over-all failure. attr_dvp is left locked to + * be cleaned up on exit. + */ + error = ufs_extattr_lookup(attr_dvp, UE_GETDIR_LOCKPARENT, + UFS_EXTATTR_SUBDIR_SYSTEM, &attr_system_dvp, p); + if (!error) { + error = ufs_extattr_iterate_directory(VFSTOUFS(mp), + attr_system_dvp, EXTATTR_NAMESPACE_SYSTEM, p); + if (error) + printf("ufs_extattr_iterate_directory returned %d\n", + error); + vput(attr_system_dvp); + } + + error = ufs_extattr_lookup(attr_dvp, UE_GETDIR_LOCKPARENT, + UFS_EXTATTR_SUBDIR_USER, &attr_user_dvp, p); + if (!error) { + error = ufs_extattr_iterate_directory(VFSTOUFS(mp), + attr_user_dvp, EXTATTR_NAMESPACE_USER, p); + if (error) + printf("ufs_extattr_iterate_directory returned %d\n", + error); + vput(attr_user_dvp); + } + + /* Mask startup failures in sub-directories. */ + error = 0; + +return_vput_attr_dvp: + vput(attr_dvp); + + return (error); +} +#endif /* !UFS_EXTATTR_AUTOSTART */ + +/* + * Stop extended attribute support on an FS. + */ +int +ufs_extattr_stop(struct mount *mp, struct proc *p) +{ + struct ufs_extattr_list_entry *uele; + struct ufsmount *ump = VFSTOUFS(mp); + int error = 0; + + ufs_extattr_uepm_lock(ump, p); + + if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { + error = EOPNOTSUPP; + goto unlock; + } + + while (LIST_FIRST(&ump->um_extattr.uepm_list) != NULL) { + uele = LIST_FIRST(&ump->um_extattr.uepm_list); + ufs_extattr_disable(ump, uele->uele_attrnamespace, + uele->uele_attrname, p); + } + + ump->um_extattr.uepm_flags &= ~UFS_EXTATTR_UEPM_STARTED; + + crfree(ump->um_extattr.uepm_ucred); + ump->um_extattr.uepm_ucred = NULL; + +unlock: + ufs_extattr_uepm_unlock(ump, p); + + return (error); +} + +/* + * Enable a named attribute on the specified file system; provide an + * unlocked backing vnode to hold the attribute data. + */ +static int +ufs_extattr_enable(struct ufsmount *ump, int attrnamespace, + const char *attrname, struct vnode *backing_vnode, struct proc *p) +{ + struct ufs_extattr_list_entry *attribute; + struct iovec aiov; + struct uio auio; + int error = 0; + + if (!ufs_extattr_valid_attrname(attrnamespace, attrname)) + return (EINVAL); + if (backing_vnode->v_type != VREG) + return (EINVAL); + + MALLOC(attribute, struct ufs_extattr_list_entry *, + sizeof(struct ufs_extattr_list_entry), M_UFS_EXTATTR, M_WAITOK); + if (attribute == NULL) + return (ENOMEM); + + if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { + error = EOPNOTSUPP; + goto free_exit; + } + + if (ufs_extattr_find_attr(ump, attrnamespace, attrname)) { + error = EEXIST; + goto free_exit; + } + + strncpy(attribute->uele_attrname, attrname, UFS_EXTATTR_MAXEXTATTRNAME); + attribute->uele_attrnamespace = attrnamespace; + bzero(&attribute->uele_fileheader, + sizeof(struct ufs_extattr_fileheader)); + + attribute->uele_backing_vnode = backing_vnode; + + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + aiov.iov_base = (caddr_t) &attribute->uele_fileheader; + aiov.iov_len = sizeof(struct ufs_extattr_fileheader); + auio.uio_resid = sizeof(struct ufs_extattr_fileheader); + auio.uio_offset = (off_t) 0; + auio.uio_segflg = UIO_SYSSPACE; + auio.uio_rw = UIO_READ; + auio.uio_procp = p; + + VOP_LEASE(backing_vnode, p, p->p_ucred, LEASE_WRITE); + vn_lock(backing_vnode, LK_SHARED | LK_NOPAUSE | LK_RETRY, p); + error = VOP_READ(backing_vnode, &auio, IO_NODELOCKED, + ump->um_extattr.uepm_ucred); + VOP_UNLOCK(backing_vnode, 0, p); + + if (error) + goto free_exit; + + if (auio.uio_resid != 0) { + printf("ufs_extattr_enable: malformed attribute header\n"); + error = EINVAL; + goto free_exit; + } + + if (attribute->uele_fileheader.uef_magic != UFS_EXTATTR_MAGIC) { + printf("ufs_extattr_enable: invalid attribute header magic\n"); + error = EINVAL; + goto free_exit; + } + + if (attribute->uele_fileheader.uef_version != UFS_EXTATTR_VERSION) { + printf("ufs_extattr_enable: incorrect attribute header " + "version\n"); + error = EINVAL; + goto free_exit; + } + + backing_vnode->v_flag |= VSYSTEM; + LIST_INSERT_HEAD(&ump->um_extattr.uepm_list, attribute, uele_entries); + + return (0); + +free_exit: + FREE(attribute, M_UFS_EXTATTR); + return (error); +} + +/* + * Disable extended attribute support on an FS. + */ +static int +ufs_extattr_disable(struct ufsmount *ump, int attrnamespace, + const char *attrname, struct proc *p) +{ + struct ufs_extattr_list_entry *uele; + int error = 0; + + if (!ufs_extattr_valid_attrname(attrnamespace, attrname)) + return (EINVAL); + + uele = ufs_extattr_find_attr(ump, attrnamespace, attrname); + if (!uele) + return (ENOENT); + + LIST_REMOVE(uele, uele_entries); + + uele->uele_backing_vnode->v_flag &= ~VSYSTEM; + error = vn_close(uele->uele_backing_vnode, FREAD|FWRITE, p->p_ucred, p); + + FREE(uele, M_UFS_EXTATTR); + + return (error); +} + +/* + * VFS call to manage extended attributes in UFS. If filename_vp is + * non-NULL, it must be passed in locked, and regardless of errors in + * processing, will be unlocked. + */ +int +ufs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp, + int attrnamespace, const char *attrname, struct proc *p) +{ + struct ufsmount *ump = VFSTOUFS(mp); + int error; + +#if 0 + /* jail? -XXX */ + /* + * Processes with privilege, but in jail, are not allowed to + * configure extended attributes. + */ + if ((error = suser_xxx(p->p_ucred, p, 0))) { + if (filename_vp != NULL) + VOP_UNLOCK(filename_vp, 0, p); + return (error); + } +#endif + + switch(cmd) { + case UFS_EXTATTR_CMD_START: + if (filename_vp != NULL) { + VOP_UNLOCK(filename_vp, 0, p); + return (EINVAL); + } + if (attrname != NULL) + return (EINVAL); + + error = ufs_extattr_start(mp, p); + + return (error); + + case UFS_EXTATTR_CMD_STOP: + if (filename_vp != NULL) { + VOP_UNLOCK(filename_vp, 0, p); + return (EINVAL); + } + if (attrname != NULL) + return (EINVAL); + + error = ufs_extattr_stop(mp, p); + + return (error); + + case UFS_EXTATTR_CMD_ENABLE: + + if (filename_vp == NULL) + return (EINVAL); + if (attrname == NULL) { + VOP_UNLOCK(filename_vp, 0, p); + return (EINVAL); + } + + /* + * ufs_extattr_enable_with_open() will always unlock the + * vnode, regardless of failure. + */ + ufs_extattr_uepm_lock(ump, p); + error = ufs_extattr_enable_with_open(ump, filename_vp, + attrnamespace, attrname, p); + ufs_extattr_uepm_unlock(ump, p); + + return (error); + + case UFS_EXTATTR_CMD_DISABLE: + + if (filename_vp != NULL) { + VOP_UNLOCK(filename_vp, 0, p); + return (EINVAL); + } + if (attrname == NULL) + return (EINVAL); + + ufs_extattr_uepm_lock(ump, p); + error = ufs_extattr_disable(ump, attrnamespace, attrname, p); + ufs_extattr_uepm_unlock(ump, p); + + return (error); + + default: + return (EINVAL); + } +} + +/* + * Credential check based on process requesting service, and per-attribute + * permissions. + */ +static int +ufs_extattr_credcheck(struct vnode *vp, struct ufs_extattr_list_entry *uele, + struct ucred *cred, struct proc *p, int access) +{ + + /* + * Kernel-invoked always succeeds. + */ + if (cred == NULL) + return (0); + + /* + * Do not allow privileged processes in jail to directly + * manipulate system attributes. + * + * XXX What capability should apply here? + * Probably CAP_SYS_SETFFLAG. + */ + switch (uele->uele_attrnamespace) { + case EXTATTR_NAMESPACE_SYSTEM: + return (suser(cred, &p->p_acflag)); + case EXTATTR_NAMESPACE_USER: + return (VOP_ACCESS(vp, access, cred, p)); + default: + return (EPERM); + } +} + +/* + * Vnode operating to retrieve a named extended attribute. + */ +int +ufs_vop_getextattr(void *v) +{ + struct vop_getextattr_args /* { + IN struct vnode *a_vp; + IN int a_attrnamespace; + IN const char *a_name; + INOUT struct uio *a_uio; + OUT struct size_t *a_size; + IN struct ucred *a_cred; + IN struct proc *a_p; + } */ *ap = v; + struct mount *mp = ap->a_vp->v_mount; + struct ufsmount *ump = VFSTOUFS(mp); + int error; + + ufs_extattr_uepm_lock(ump, ap->a_p); + + error = ufs_extattr_get(ap->a_vp, ap->a_attrnamespace, ap->a_name, + ap->a_uio, ap->a_size, ap->a_cred, ap->a_p); + + ufs_extattr_uepm_unlock(ump, ap->a_p); + + return (error); +} + +/* + * Real work associated with retrieving a named attribute--assumes that + * the attribute lock has already been grabbed. + */ +static int +ufs_extattr_get(struct vnode *vp, int attrnamespace, const char *name, + struct uio *uio, size_t *size, struct ucred *cred, struct proc *p) +{ + struct ufs_extattr_list_entry *attribute; + struct ufs_extattr_header ueh; + struct iovec local_aiov; + struct uio local_aio; + struct mount *mp = vp->v_mount; + struct ufsmount *ump = VFSTOUFS(mp); + struct inode *ip = VTOI(vp); + off_t base_offset; + size_t len, old_len; + int error = 0; + + if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) + return (EOPNOTSUPP); + + if (strlen(name) == 0) { + /* XXX retrieve attribute lists. */ + /* XXX should probably be checking for name == NULL? */ + return (EINVAL); + } + + attribute = ufs_extattr_find_attr(ump, attrnamespace, name); + if (!attribute) + /* XXX: ENOENT here will eventually be ENOATTR. */ + return (ENOENT); + + if ((error = ufs_extattr_credcheck(vp, attribute, cred, p, IREAD))) + return (error); + + /* + * Allow only offsets of zero to encourage the read/replace + * extended attribute semantic. Otherwise we can't guarantee + * atomicity, as we don't provide locks for extended attributes. + */ + if (uio != NULL && uio->uio_offset != 0) + return (ENXIO); + + /* + * Find base offset of header in file based on file header size, and + * data header size + maximum data size, indexed by inode number. + */ + base_offset = sizeof(struct ufs_extattr_fileheader) + + ip->i_number * (sizeof(struct ufs_extattr_header) + + attribute->uele_fileheader.uef_size); + + /* + * Read in the data header to see if the data is defined, and if so + * how much. + */ + bzero(&ueh, sizeof(struct ufs_extattr_header)); + local_aiov.iov_base = (caddr_t) &ueh; + local_aiov.iov_len = sizeof(struct ufs_extattr_header); + local_aio.uio_iov = &local_aiov; + local_aio.uio_iovcnt = 1; + local_aio.uio_rw = UIO_READ; + local_aio.uio_segflg = UIO_SYSSPACE; + local_aio.uio_procp = p; + local_aio.uio_offset = base_offset; + local_aio.uio_resid = sizeof(struct ufs_extattr_header); + + /* + * Acquire locks. + */ + VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_READ); + /* + * Don't need to get a lock on the backing file if the getattr is + * being applied to the backing file, as the lock is already held. + */ + if (attribute->uele_backing_vnode != vp) + vn_lock(attribute->uele_backing_vnode, LK_SHARED | + LK_NOPAUSE | LK_RETRY, p); + + error = VOP_READ(attribute->uele_backing_vnode, &local_aio, + IO_NODELOCKED, ump->um_extattr.uepm_ucred); + if (error) + goto vopunlock_exit; + + /* Defined? */ + if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) { + /* XXX: ENOENT here will eventually be ENOATTR. */ + error = ENOENT; + goto vopunlock_exit; + } + + /* Valid for the current inode generation? */ + if (ueh.ueh_i_gen != ip->i_ffs_gen) { + /* + * The inode itself has a different generation number + * than the attribute data. For now, the best solution + * is to coerce this to undefined, and let it get cleaned + * up by the next write or extattrctl clean. + */ + printf("ufs_extattr_get: inode number inconsistency (%d, %d)\n", + ueh.ueh_i_gen, ip->i_ffs_gen); + /* XXX: ENOENT here will eventually be ENOATTR. */ + error = ENOENT; + goto vopunlock_exit; + } + + /* Local size consistency check. */ + if (ueh.ueh_len > attribute->uele_fileheader.uef_size) { + error = ENXIO; + goto vopunlock_exit; + } + + /* Return full data size if caller requested it. */ + if (size != NULL) + *size = ueh.ueh_len; + + /* Return data if the caller requested it. */ + if (uio != NULL) { + /* Allow for offset into the attribute data. */ + uio->uio_offset = base_offset + sizeof(struct + ufs_extattr_header); + + /* + * Figure out maximum to transfer -- use buffer size and + * local data limit. + */ + len = MIN(uio->uio_resid, ueh.ueh_len); + old_len = uio->uio_resid; + uio->uio_resid = len; + + error = VOP_READ(attribute->uele_backing_vnode, uio, + IO_NODELOCKED, ump->um_extattr.uepm_ucred); + if (error) + goto vopunlock_exit; + + uio->uio_resid = old_len - (len - uio->uio_resid); + } + +vopunlock_exit: + + if (uio != NULL) + uio->uio_offset = 0; + + if (attribute->uele_backing_vnode != vp) + VOP_UNLOCK(attribute->uele_backing_vnode, 0, p); + + return (error); +} + +/* + * Vnode operation to set a named attribute. + */ +int +ufs_vop_setextattr(void *v) +{ + struct vop_setextattr_args /* { + IN struct vnode *a_vp; + IN int a_attrnamespace; + IN const char *a_name; + INOUT struct uio *a_uio; + IN struct ucred *a_cred; + IN struct proc *a_p; + } */ *ap = v; + struct mount *mp = ap->a_vp->v_mount; + struct ufsmount *ump = VFSTOUFS(mp); + + int error; + + ufs_extattr_uepm_lock(ump, ap->a_p); + + if (ap->a_uio != NULL) + error = ufs_extattr_set(ap->a_vp, ap->a_attrnamespace, + ap->a_name, ap->a_uio, ap->a_cred, ap->a_p); + else + error = ufs_extattr_rm(ap->a_vp, ap->a_attrnamespace, + ap->a_name, ap->a_cred, ap->a_p); + + ufs_extattr_uepm_unlock(ump, ap->a_p); + + return (error); +} + +/* + * Real work associated with setting a vnode's extended attributes; + * assumes that the attribute lock has already been grabbed. + */ +static int +ufs_extattr_set(struct vnode *vp, int attrnamespace, const char *name, + struct uio *uio, struct ucred *cred, struct proc *p) +{ + struct ufs_extattr_list_entry *attribute; + struct ufs_extattr_header ueh; + struct iovec local_aiov; + struct uio local_aio; + struct mount *mp = vp->v_mount; + struct ufsmount *ump = VFSTOUFS(mp); + struct inode *ip = VTOI(vp); + off_t base_offset; + int error = 0, ioflag; + + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return (EROFS); + if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) + return (EOPNOTSUPP); + if (!ufs_extattr_valid_attrname(attrnamespace, name)) + return (EINVAL); + + attribute = ufs_extattr_find_attr(ump, attrnamespace, name); + if (!attribute) + /* XXX: ENOENT here will eventually be ENOATTR. */ + return (ENOENT); + + if ((error = ufs_extattr_credcheck(vp, attribute, cred, p, IWRITE))) + return (error); + + /* + * Early rejection of invalid offsets/length. + * Reject: any offset but 0 (replace) + * Any size greater than attribute size limit + */ + if (uio->uio_offset != 0 || + uio->uio_resid > attribute->uele_fileheader.uef_size) + return (ENXIO); + + /* + * Find base offset of header in file based on file header size, and + * data header size + maximum data size, indexed by inode number. + */ + base_offset = sizeof(struct ufs_extattr_fileheader) + + ip->i_number * (sizeof(struct ufs_extattr_header) + + attribute->uele_fileheader.uef_size); + + /* + * Write out a data header for the data. + */ + ueh.ueh_len = uio->uio_resid; + ueh.ueh_flags = UFS_EXTATTR_ATTR_FLAG_INUSE; + ueh.ueh_i_gen = ip->i_ffs_gen; + local_aiov.iov_base = (caddr_t) &ueh; + local_aiov.iov_len = sizeof(struct ufs_extattr_header); + local_aio.uio_iov = &local_aiov; + local_aio.uio_iovcnt = 1; + local_aio.uio_rw = UIO_WRITE; + local_aio.uio_segflg = UIO_SYSSPACE; + local_aio.uio_procp = p; + local_aio.uio_offset = base_offset; + local_aio.uio_resid = sizeof(struct ufs_extattr_header); + + /* + * Acquire locks. + */ + VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_WRITE); + + /* + * Don't need to get a lock on the backing file if the setattr is + * being applied to the backing file, as the lock is already held. + */ + if (attribute->uele_backing_vnode != vp) + vn_lock(attribute->uele_backing_vnode, + LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, p); + + ioflag = IO_NODELOCKED; + if (ufs_extattr_sync) + ioflag |= IO_SYNC; + error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag, + ump->um_extattr.uepm_ucred); + if (error) + goto vopunlock_exit; + + if (local_aio.uio_resid != 0) { + error = ENXIO; + goto vopunlock_exit; + } + + /* + * Write out user data. + */ + uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header); + + ioflag = IO_NODELOCKED; + if (ufs_extattr_sync) + ioflag |= IO_SYNC; + error = VOP_WRITE(attribute->uele_backing_vnode, uio, ioflag, + ump->um_extattr.uepm_ucred); + +vopunlock_exit: + uio->uio_offset = 0; + + if (attribute->uele_backing_vnode != vp) + VOP_UNLOCK(attribute->uele_backing_vnode, 0, p); + + return (error); +} + +/* + * Real work associated with removing an extended attribute from a vnode. + * Assumes the attribute lock has already been grabbed. + */ +static int +ufs_extattr_rm(struct vnode *vp, int attrnamespace, const char *name, + struct ucred *cred, struct proc *p) +{ + struct ufs_extattr_list_entry *attribute; + struct ufs_extattr_header ueh; + struct iovec local_aiov; + struct uio local_aio; + struct mount *mp = vp->v_mount; + struct ufsmount *ump = VFSTOUFS(mp); + struct inode *ip = VTOI(vp); + off_t base_offset; + int error = 0, ioflag; + + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return (EROFS); + if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) + return (EOPNOTSUPP); + if (!ufs_extattr_valid_attrname(attrnamespace, name)) + return (EINVAL); + + attribute = ufs_extattr_find_attr(ump, attrnamespace, name); + if (!attribute) + /* XXX: ENOENT here will eventually be ENOATTR. */ + return (ENOENT); + + if ((error = ufs_extattr_credcheck(vp, attribute, cred, p, IWRITE))) + return (error); + + /* + * Find base offset of header in file based on file header size, and + * data header size + maximum data size, indexed by inode number. + */ + base_offset = sizeof(struct ufs_extattr_fileheader) + + ip->i_number * (sizeof(struct ufs_extattr_header) + + attribute->uele_fileheader.uef_size); + + /* + * Check to see if currently defined. + */ + bzero(&ueh, sizeof(struct ufs_extattr_header)); + + local_aiov.iov_base = (caddr_t) &ueh; + local_aiov.iov_len = sizeof(struct ufs_extattr_header); + local_aio.uio_iov = &local_aiov; + local_aio.uio_iovcnt = 1; + local_aio.uio_rw = UIO_READ; + local_aio.uio_segflg = UIO_SYSSPACE; + local_aio.uio_procp = p; + local_aio.uio_offset = base_offset; + local_aio.uio_resid = sizeof(struct ufs_extattr_header); + + VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_WRITE); + + /* + * Don't need to get the lock on the backing vnode if the vnode we're + * modifying is it, as we already hold the lock. + */ + if (attribute->uele_backing_vnode != vp) + vn_lock(attribute->uele_backing_vnode, + LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, p); + + error = VOP_READ(attribute->uele_backing_vnode, &local_aio, + IO_NODELOCKED, ump->um_extattr.uepm_ucred); + if (error) + goto vopunlock_exit; + + /* Defined? */ + if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) { + /* XXX: ENOENT here will eventually be ENOATTR. */ + error = ENOENT; + goto vopunlock_exit; + } + + /* Valid for the current inode generation? */ + if (ueh.ueh_i_gen != ip->i_ffs_gen) { + /* + * The inode itself has a different generation number than + * the attribute data. For now, the best solution is to + * coerce this to undefined, and let it get cleaned up by + * the next write or extattrctl clean. + */ + printf("ufs_extattr_rm: inode number inconsistency (%d, %d)\n", + ueh.ueh_i_gen, ip->i_ffs_gen); + /* XXX: ENOENT here will eventually be ENOATTR. */ + error = ENOENT; + goto vopunlock_exit; + } + + /* Flag it as not in use. */ + ueh.ueh_flags = 0; + ueh.ueh_len = 0; + + local_aiov.iov_base = (caddr_t) &ueh; + local_aiov.iov_len = sizeof(struct ufs_extattr_header); + local_aio.uio_iov = &local_aiov; + local_aio.uio_iovcnt = 1; + local_aio.uio_rw = UIO_WRITE; + local_aio.uio_segflg = UIO_SYSSPACE; + local_aio.uio_procp = p; + local_aio.uio_offset = base_offset; + local_aio.uio_resid = sizeof(struct ufs_extattr_header); + + ioflag = IO_NODELOCKED; + if (ufs_extattr_sync) + ioflag |= IO_SYNC; + error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag, + ump->um_extattr.uepm_ucred); + if (error) + goto vopunlock_exit; + + if (local_aio.uio_resid != 0) + error = ENXIO; + +vopunlock_exit: + VOP_UNLOCK(attribute->uele_backing_vnode, 0, p); + + return (error); +} + +/* + * Called by UFS when an inode is no longer active and should have its + * attributes stripped. + */ +void +ufs_extattr_vnode_inactive(struct vnode *vp, struct proc *p) +{ + struct ufs_extattr_list_entry *uele; + struct mount *mp = vp->v_mount; + struct ufsmount *ump = VFSTOUFS(mp); + + /* + * In that case, we cannot lock. We should not have any active vnodes + * on the fs if this is not yet initialized but is going to be, so + * this can go unlocked. + */ + if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) + return; + + ufs_extattr_uepm_lock(ump, p); + + if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { + ufs_extattr_uepm_unlock(ump, p); + return; + } + + LIST_FOREACH(uele, &ump->um_extattr.uepm_list, uele_entries) + ufs_extattr_rm(vp, uele->uele_attrnamespace, + uele->uele_attrname, NULL, p); + + ufs_extattr_uepm_unlock(ump, p); +} + +#endif /* !UFS_EXTATTR */ diff --git a/sys/ufs/ufs/ufs_inode.c b/sys/ufs/ufs/ufs_inode.c index dd2c6574d30..cdcbd6ecb6f 100644 --- a/sys/ufs/ufs/ufs_inode.c +++ b/sys/ufs/ufs/ufs_inode.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ufs_inode.c,v 1.16 2001/12/19 08:58:07 art Exp $ */ +/* $OpenBSD: ufs_inode.c,v 1.17 2002/02/22 20:37:46 drahn Exp $ */ /* $NetBSD: ufs_inode.c,v 1.7 1996/05/11 18:27:52 mycroft Exp $ */ /* @@ -50,6 +50,7 @@ #include <sys/malloc.h> #include <sys/namei.h> +#include <ufs/ufs/extattr.h> #include <ufs/ufs/quota.h> #include <ufs/ufs/inode.h> #include <ufs/ufs/ufsmount.h> diff --git a/sys/ufs/ufs/ufs_lookup.c b/sys/ufs/ufs/ufs_lookup.c index b7d5802ddc8..1d3f46edf38 100644 --- a/sys/ufs/ufs/ufs_lookup.c +++ b/sys/ufs/ufs/ufs_lookup.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ufs_lookup.c,v 1.18 2001/11/06 19:53:21 miod Exp $ */ +/* $OpenBSD: ufs_lookup.c,v 1.19 2002/02/22 20:37:46 drahn Exp $ */ /* $NetBSD: ufs_lookup.c,v 1.7 1996/02/09 22:36:06 christos Exp $ */ /* @@ -53,6 +53,7 @@ #include <uvm/uvm_extern.h> +#include <ufs/ufs/extattr.h> #include <ufs/ufs/quota.h> #include <ufs/ufs/inode.h> #include <ufs/ufs/dir.h> diff --git a/sys/ufs/ufs/ufs_quota.c b/sys/ufs/ufs/ufs_quota.c index a2fe18d2753..ab7ff29b562 100644 --- a/sys/ufs/ufs/ufs_quota.c +++ b/sys/ufs/ufs/ufs_quota.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ufs_quota.c,v 1.10 2002/02/20 18:40:49 csapuntz Exp $ */ +/* $OpenBSD: ufs_quota.c,v 1.11 2002/02/22 20:37:46 drahn Exp $ */ /* $NetBSD: ufs_quota.c,v 1.8 1996/02/09 22:36:09 christos Exp $ */ /* @@ -48,6 +48,7 @@ #include <sys/vnode.h> #include <sys/mount.h> +#include <ufs/ufs/extattr.h> #include <ufs/ufs/quota.h> #include <ufs/ufs/inode.h> #include <ufs/ufs/ufsmount.h> |