Index: lib/libc/Makefile.inc =================================================================== RCS file: /data/cvs/openbsd/src/lib/libc/Makefile.inc,v retrieving revision 1.11 diff -u -p -r1.11 Makefile.inc --- lib/libc/Makefile.inc 17 Jun 2005 20:37:31 -0000 1.11 +++ lib/libc/Makefile.inc 20 Jul 2007 13:55:05 -0000 @@ -51,6 +51,7 @@ AINC+= -nostdinc -idirafter ${DESTDIR}/ .include "${LIBCSRCDIR}/thread/Makefile.inc" .include "${LIBCSRCDIR}/time/Makefile.inc" .include "${LIBCSRCDIR}/sys/Makefile.inc" +.include "${LIBCSRCDIR}/posix1e/Makefile.inc" .if (${YP:L} == "yes") .include "${LIBCSRCDIR}/yp/Makefile.inc" .endif --- /dev/null Fri Jul 20 15:56:45 2007 +++ lib/libc/posix1e/Makefile.inc Thu Jul 19 12:34:22 2007 @@ -0,0 +1,10 @@ +# $FreeBSD: src/lib/libc/posix1e/Makefile.inc,v 1.19 2006/07/07 14:02:17 rwatson Exp $ + +.PATH: ${.CURDIR}/posix1e + +SRCS+= extattr.c + +MAN+= extattr.3 + +MLINKS+=extattr.3 extattr_namespace_to_string.3 \ + extattr.3 extattr_string_to_namespace.3 --- /dev/null Fri Jul 20 15:56:52 2007 +++ lib/libc/posix1e/extattr.3 Sun Jul 8 12:02:20 2007 @@ -0,0 +1,100 @@ +.\" +.\" Copyright (c) 2001 Dima Dorfman +.\" 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.3,v 1.5 2002/12/12 17:25:53 ru Exp $ +.\" +.Dd June 24, 2001 +.Dt EXTATTR 3 +.Os +.Sh NAME +.Nm extattr_namespace_to_string , +.Nm extattr_string_to_namespace +.Nd convert an extended attribute namespace identifier to a string and +vice versa +.Sh LIBRARY +.Lb libutil +.Sh SYNOPSIS +.In sys/extattr.h +.In libutil.h +.Ft int +.Fn extattr_namespace_to_string "int attrnamespace" "char **string" +.Ft int +.Fn extattr_string_to_namespace "const char *string" "int *attrnamespace" +.Sh DESCRIPTION +The +.Fn extattr_namespace_to_string +function converts a VFS extended attribute identifier to a human-readable +string; +the +.Fn extattr_string_to_namespace +function undoes the aforementioned operation, +and converts a human-readable string representing a namespace to a +namespace identifier. +Although a file system may implement arbitrary namespaces, +these functions only support the +.Dv EXTATTR_NAMESPACE_USER +.Pq Dq user +and +.Dv EXTATTR_NAMESPACE_SYSTEM +.Pq Dq system +namespaces, +which are defined in +.Xr extattr 9 . +.Pp +These functions are meant to be used in error reporting and other +interactive tasks. +For example, +instead of printing the integer identifying an extended attribute in +an error message, +a program might use +.Fn extattr_namespace_to_string +to obtain a human-readable representation. +Likewise, +instead of requiring a user to enter the integer representing a namespace, +an interactive program might ask for a name and use +.Fn extattr_string_to_namespace +to get the desired identifier. +.Sh RETURN VALUES +If any of the calls are unsuccessful, the value \-1 is returned +and the global variable +.Va errno +is set to indicate the error. +.Sh ERRORS +.Bl -tag -width Er +.It Bq Er EINVAL +The requested namespace could not be identified. +.El +.Sh SEE ALSO +.Xr extattr 2 , +.Xr getextattr 8 , +.Xr setextattr 8 , +.Xr extattr 9 +.Sh HISTORY +Extended attribute support was developed as part of the +.Tn TrustedBSD +Project, and introduced in +.Fx 5.0 . +It was developed to support security extensions requiring additional labels +to be associated with each file or directory. --- /dev/null Fri Jul 20 15:56:55 2007 +++ lib/libc/posix1e/extattr.c Sun Jul 8 12:02:20 2007 @@ -0,0 +1,76 @@ +/* + * Copyright (c) 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.c,v 1.7 2005/09/12 19:52:41 stefanf Exp $ + */ + +/* + * TrustedBSD: Utility functions for extended attributes. + */ + +#include +#include + +#include +#include + +int +extattr_namespace_to_string(int attrnamespace, char **string) +{ + + switch(attrnamespace) { + case EXTATTR_NAMESPACE_USER: + if (string != NULL) + *string = strdup(EXTATTR_NAMESPACE_USER_STRING); + return (0); + + case EXTATTR_NAMESPACE_SYSTEM: + if (string != NULL) + *string = strdup(EXTATTR_NAMESPACE_SYSTEM_STRING); + return (0); + + default: + errno = EINVAL; + return (-1); + } +} + +int +extattr_string_to_namespace(const char *string, int *attrnamespace) +{ + + if (!strcmp(string, EXTATTR_NAMESPACE_USER_STRING)) { + if (attrnamespace != NULL) + *attrnamespace = EXTATTR_NAMESPACE_USER; + return (0); + } else if (!strcmp(string, EXTATTR_NAMESPACE_SYSTEM_STRING)) { + if (attrnamespace != NULL) + *attrnamespace = EXTATTR_NAMESPACE_SYSTEM; + return (0); + } else { + errno = EINVAL; + return (-1); + } +} Index: lib/libc/sys/Makefile.inc =================================================================== RCS file: /data/cvs/openbsd/src/lib/libc/sys/Makefile.inc,v retrieving revision 1.80 diff -u -p -r1.80 Makefile.inc --- lib/libc/sys/Makefile.inc 24 Oct 2006 04:40:59 -0000 1.80 +++ lib/libc/sys/Makefile.inc 20 Jul 2007 13:55:05 -0000 @@ -61,6 +61,13 @@ ASM= accept.o access.o acct.o adjfreq.o __syscall.o __sysctl.o __getcwd.o sched_yield.o getthrid.o \ thrsleep.o thrwakeup.o threxit.o thrsigdivert.o +# Extended attributes system calls. +ASM+= extattr_set_fd.o extattr_get_fd.o extattr_delete_fd.o \ + extattr_set_link.o extattr_get_link.o extattr_delete_link.o \ + extattr_list_fd.o extattr_list_file.o extattr_list_link.o \ + extattrctl.o extattr_set_file.o extattr_get_file.o \ + extattr_delete_file.o + GASM= ${ASM:.o=.go} PASM= ${ASM:.o=.po} SASM= ${ASM:.o=.so} Index: sbin/fsck_ffs/pass1.c =================================================================== RCS file: /data/cvs/openbsd/src/sbin/fsck_ffs/pass1.c,v retrieving revision 1.26 diff -u -p -r1.26 pass1.c --- sbin/fsck_ffs/pass1.c 25 Jun 2007 19:59:55 -0000 1.26 +++ sbin/fsck_ffs/pass1.c 20 Jul 2007 13:55:10 -0000 @@ -122,7 +122,7 @@ checkinode(ino_t inumber, struct inodesc { union dinode *dp; struct zlncnt *zlnp; - int ndb, j; + int ndb, j, offset, ret; mode_t mode; char *symbuf; u_int64_t lndb; @@ -281,6 +281,23 @@ checkinode(ino_t inumber, struct inodesc badblk = dupblk = 0; idesc->id_number = inumber; (void)ckinode(dp, idesc); + if (sblock.fs_magic == FS_UFS2_MAGIC && dp->dp2.di_extsize > 0) { + ndb = howmany(dp->dp2.di_extsize, sblock.fs_bsize); + for (j = 0; j < NXADDR; j++) { + if (--ndb == 0 && + (offset = blkoff(&sblock, dp->dp2.di_extsize)) != 0) + idesc->id_numfrags = numfrags(&sblock, + fragroundup(&sblock, offset)); + else + idesc->id_numfrags = sblock.fs_frag; + if (dp->dp2.di_extb[j] == 0) + continue; + idesc->id_blkno = dp->dp2.di_extb[j]; + ret = (*idesc->id_func)(idesc); + if (ret & STOP) + break; + } + } idesc->id_entryno *= btodb(sblock.fs_fsize); if (DIP(dp, di_blocks) != idesc->id_entryno) { pwarn("INCORRECT BLOCK COUNT I=%u (%ld should be %d)", Index: sys/conf/files =================================================================== RCS file: /data/cvs/openbsd/src/sys/conf/files,v retrieving revision 1.409 diff -u -p -r1.409 files --- sys/conf/files 18 Jul 2007 18:17:09 -0000 1.409 +++ sys/conf/files 20 Jul 2007 13:55:14 -0000 @@ -691,6 +691,7 @@ file kern/vfs_cache.c file kern/vfs_cluster.c file kern/vfs_conf.c file kern/vfs_default.c +file kern/vfs_extattr.c ffs2_extattr file kern/vfs_init.c file kern/vfs_lockf.c file kern/vfs_lookup.c @@ -857,6 +858,7 @@ file nfs/nfs_vfsops.c nfsclient file nfs/nfs_vnops.c nfsclient file ufs/ffs/ffs_alloc.c ffs | mfs file ufs/ffs/ffs_balloc.c ffs | mfs +file ufs/ffs/ffs_extattr.c ffs2_extattr file ufs/ffs/ffs_inode.c ffs | mfs file ufs/ffs/ffs_subr.c ffs | mfs file ufs/ffs/ffs_softdep_stub.c ffs | mfs Index: sys/kern/syscalls.master =================================================================== RCS file: /data/cvs/openbsd/src/sys/kern/syscalls.master,v retrieving revision 1.86 diff -u -p -r1.86 syscalls.master --- sys/kern/syscalls.master 22 Sep 2006 17:35:41 -0000 1.86 +++ sys/kern/syscalls.master 20 Jul 2007 13:55:15 -0000 @@ -539,13 +539,13 @@ 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); } -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 +274 UNIMPL +275 UNIMPL +276 UNIMPL +277 UNIMPL +278 UNIMPL +279 UNIMPL +280 UNIMPL 281 STD { int sys_getresuid(uid_t *ruid, uid_t *euid, \ uid_t *suid); } 282 STD { int sys_setresuid(uid_t ruid, uid_t euid, \ @@ -611,3 +611,63 @@ 304 STD { int sys___getcwd(char *buf, size_t len); } 305 STD { int sys_adjfreq(const int64_t *freq, \ int64_t *oldfreq); } +#ifdef FFS2_EXTATTR +306 STD { int sys_extattr_set_fd(int fd, \ + int attrnamespace, const char *attrname, \ + void *data, size_t nbytes); } +307 STD { ssize_t sys_extattr_get_fd(int fd, \ + int attrnamespace, const char *attrname, \ + void *data, size_t nbytes); } +308 STD { int sys_extattr_delete_fd(int fd, \ + int attrnamespace, \ + const char *attrname); } +309 STD { int sys_extattr_set_link( \ + const char *path, int attrnamespace, \ + const char *attrname, void *data, \ + size_t nbytes); } +310 STD { ssize_t sys_extattr_get_link( \ + const char *path, int attrnamespace, \ + const char *attrname, void *data, \ + size_t nbytes); } +311 STD { int sys_extattr_delete_link( \ + const char *path, int attrnamespace, \ + const char *attrname); } +312 STD { ssize_t sys_extattr_list_fd(int fd, \ + int attrnamespace, void *data, \ + size_t nbytes); } +313 STD { ssize_t sys_extattr_list_file( \ + const char *path, int attrnamespace, \ + void *data, size_t nbytes); } +314 STD { ssize_t sys_extattr_list_link( \ + const char *path, int attrnamespace, \ + void *data, size_t nbytes); } +315 STD { int sys_extattrctl(const char *path, int cmd, \ + const char *filename, int attrnamespace, \ + const char *attrname); } +316 STD { int sys_extattr_set_file( \ + const char *path, int attrnamespace, \ + const char *attrname, void *data, \ + size_t nbytes); } +317 STD { ssize_t sys_extattr_get_file( \ + const char *path, int attrnamespace, \ + const char *attrname, void *data, \ + size_t nbytes); } +318 STD { int sys_extattr_delete_file( \ + const char *path, int attrnamespace, \ + const char *attrname, void *data, \ + size_t nbytes); } +#else +306 UNIMPL +307 UNIMPL +308 UNIMPL +309 UNIMPL +310 UNIMPL +311 UNIMPL +312 UNIMPL +313 UNIMPL +314 UNIMPL +315 UNIMPL +316 UNIMPL +317 UNIMPL +318 UNIMPL +#endif Index: sys/kern/vfs_extattr.c =================================================================== RCS file: sys/kern/vfs_extattr.c diff -N sys/kern/vfs_extattr.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/kern/vfs_extattr.c 20 Jul 2007 13:55:15 -0000 @@ -0,0 +1,589 @@ +/* + * Copyright (c) 1999-2001 Robert N. M. Watson + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * + * 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: vfs_extattr.c,v 1.431 2006/12/23 00:30:03 rwatson Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int extattr_set_vp(struct vnode *, int, const char *, void *, size_t, + struct proc *, register_t *); +int extattr_get_vp(struct vnode *, int, const char *, void *, size_t, + struct proc *, register_t *); +int extattr_delete_vp(struct vnode *, int, const char *, struct proc *); +int extattr_list_vp(struct vnode *, int, void *, size_t, struct proc *, + register_t *); + +/* + * 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. + * + * Currently this is used only by UFS1 extended attributes. + */ +int +sys_extattrctl(struct proc *p, void *v, register_t *retval) +{ + struct sys_extattrctl_args *uap = v; + struct vnode *filename_vp; + struct nameidata nd; + struct mount *mp; + char attrname[EXTATTR_MAXNAMELEN]; + int error; + + /* + * uap->attrname is 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); + } + + /* + * uap->filename is 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); + error = namei(&nd); + if (error) + return (error); + filename_vp = nd.ni_vp; + } + + /* uap->path is always defined. */ + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + error = namei(&nd); + if (error) { + if (filename_vp != NULL) + vput(filename_vp); + goto out; + } + mp = nd.ni_vp->v_mount; + + error = VFS_EXTATTRCTL(mp, SCARG(uap, cmd), filename_vp, + SCARG(uap, attrnamespace), + SCARG(uap, attrname) != NULL ? attrname : 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); +out: + 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", 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 + */ +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; + + 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 = cnt; + +done: + VOP_UNLOCK(vp, 0, p); + return (error); +} + +int +sys_extattr_set_fd(struct proc *p, void *v, register_t *retval) +{ + struct sys_extattr_set_fd_args *uap = v; + struct file *fp; + char attrname[EXTATTR_MAXNAMELEN]; + int error; + + error = copyinstr(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN, + NULL); + if (error) + return (error); + + error = getvnode(p->p_fd, SCARG(uap, fd), &fp); + if (error) + return (error); + + 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); +} + +int +sys_extattr_set_file(struct proc *p, void *v, register_t *retval) +{ + struct sys_extattr_set_file_args *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); + error = namei(&nd); + if (error) + 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_link(struct proc *p, void *v, register_t *retval) +{ + struct sys_extattr_set_link_args *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, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + error = namei(&nd); + if (error) + 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); +} + +/*- + * 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", 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 + */ +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, *auiop; + struct iovec aiov; + ssize_t cnt; + size_t size, *sizep; + int error; + + 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. + */ + auiop = NULL; + sizep = NULL; + cnt = 0; + if (data != NULL) { + 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_READ; + auio.uio_segflg = UIO_USERSPACE; + auio.uio_procp = p; + auiop = &auio; + cnt = nbytes; + } else + sizep = &size; + + error = VOP_GETEXTATTR(vp, attrnamespace, attrname, auiop, sizep, + p->p_ucred, p); + + if (auiop != NULL) { + cnt -= auio.uio_resid; + *retval = cnt; + } else + *retval = size; + +done: + VOP_UNLOCK(vp, 0, p); + return (error); +} + +int +sys_extattr_get_fd(struct proc *p, void *v, register_t *retval) +{ + struct sys_extattr_get_fd_args *uap = v; + struct file *fp; + char attrname[EXTATTR_MAXNAMELEN]; + int error; + + error = copyinstr(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN, + NULL); + if (error) + return (error); + + error = getvnode(p->p_fd, SCARG(uap, fd), &fp); + if (error) + return (error); + + 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); +} + +int +sys_extattr_get_file(struct proc *p, void *v, register_t *retval) +{ + struct sys_extattr_get_file_args *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); + error = namei(&nd); + if (error) + 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_link(struct proc *p, void *v, register_t *retval) +{ + struct sys_extattr_get_link_args *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, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + error = namei(&nd); + if (error) + 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); +} + +/* + * 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 + */ +int +extattr_delete_vp(struct vnode *vp, int attrnamespace, const char *attrname, + struct proc *p) +{ + int error; + + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + + error = VOP_DELETEEXTATTR(vp, attrnamespace, attrname, p->p_ucred, p); + if (error == EOPNOTSUPP) + error = VOP_SETEXTATTR(vp, attrnamespace, attrname, NULL, + p->p_ucred, p); + VOP_UNLOCK(vp, 0, p); + return (error); +} + +int +sys_extattr_delete_fd(struct proc *p, void *v, register_t *retval) +{ + struct sys_extattr_delete_fd_args *uap = v; + struct file *fp; + char attrname[EXTATTR_MAXNAMELEN]; + int error; + + error = copyinstr(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN, + NULL); + if (error) + return (error); + + error = getvnode(p->p_fd, SCARG(uap, fd), &fp); + if (error) + return (error); + + error = extattr_delete_vp((struct vnode *)fp->f_data, + SCARG(uap, attrnamespace), attrname, p); + FRELE(fp); + return (error); +} + +int +sys_extattr_delete_file(struct proc *p, void *v, register_t *retval) +{ + struct sys_extattr_delete_file_args *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); + error = namei(&nd); + if (error) + return(error); + + error = extattr_delete_vp(nd.ni_vp, SCARG(uap, attrnamespace), + attrname, p); + vrele(nd.ni_vp); + return(error); +} + +int +sys_extattr_delete_link(struct proc *p, void *v, register_t *retval) +{ + struct sys_extattr_delete_link_args *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, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + error = namei(&nd); + if (error) + return(error); + + error = extattr_delete_vp(nd.ni_vp, SCARG(uap, attrnamespace), + attrname, p); + vrele(nd.ni_vp); + return(error); +} + +/*- + * Retrieve a list of extended attributes on a file or directory. + * + * Arguments: unlocked vnode "vp", attribute namespace 'attrnamespace", + * userspace buffer pointer "data", buffer length "nbytes", + * 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 + */ +int +extattr_list_vp(struct vnode *vp, int attrnamespace, void *data, + size_t nbytes, struct proc *p, register_t *retval) +{ + struct uio auio, *auiop; + size_t size, *sizep; + struct iovec aiov; + ssize_t cnt; + int error; + + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + + auiop = NULL; + sizep = NULL; + cnt = 0; + if (data != NULL) { + 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_READ; + auio.uio_segflg = UIO_USERSPACE; + auio.uio_procp = p; + auiop = &auio; + cnt = nbytes; + } else + sizep = &size; + + error = VOP_LISTEXTATTR(vp, attrnamespace, auiop, sizep, + p->p_ucred, p); + + if (auiop != NULL) { + cnt -= auio.uio_resid; + *retval = cnt; + } else + *retval = size; + +done: + VOP_UNLOCK(vp, 0, p); + return (error); +} + + +int +sys_extattr_list_fd(struct proc *p, void *v, register_t *retval) +{ + struct sys_extattr_list_fd_args *uap = v; + struct file *fp; + int error; + + error = getvnode(p->p_fd, SCARG(uap, fd), &fp); + if (error) + return (error); + + error = extattr_list_vp((struct vnode *)fp->f_data, + SCARG(uap, attrnamespace), SCARG(uap, data), SCARG(uap, nbytes), + p, retval); + + FRELE(fp); + return (error); +} + +int +sys_extattr_list_file(struct proc *p, void *v, register_t *retval) +{ + struct sys_extattr_list_file_args *uap = v; + struct nameidata nd; + int error; + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + error = namei(&nd); + if (error) + return (error); + + error = extattr_list_vp(nd.ni_vp, SCARG(uap, attrnamespace), + SCARG(uap, data), SCARG(uap, nbytes), p, retval); + + vrele(nd.ni_vp); + return (error); +} + +int +sys_extattr_list_link(struct proc *p, void *v, register_t *retval) +{ + struct sys_extattr_list_link_args *uap = v; + struct nameidata nd; + int error; + + NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + error = namei(&nd); + if (error) + return (error); + + error = extattr_list_vp(nd.ni_vp, SCARG(uap, attrnamespace), + SCARG(uap, data), SCARG(uap, nbytes), p, retval); + + vrele(nd.ni_vp); + return (error); +} Index: sys/kern/vfs_subr.c =================================================================== RCS file: /data/cvs/openbsd/src/sys/kern/vfs_subr.c,v retrieving revision 1.154 diff -u -p -r1.154 vfs_subr.c --- sys/kern/vfs_subr.c 1 Jun 2007 17:29:10 -0000 1.154 +++ sys/kern/vfs_subr.c 20 Jul 2007 13:55:15 -0000 @@ -59,6 +59,7 @@ #include #include #include +#include #include #include @@ -1612,6 +1613,35 @@ vaccess(mode_t file_mode, uid_t uid, gid } /* + * Credential check based on process requesting service, and per-attribute + * permissions. + */ +int +extattr_check_cred(struct vnode *vp, int attrnamespace, struct ucred *cred, + struct proc *p, int access) +{ + /* + * Kernel-invoked always succeeds. + */ + if (cred == NOCRED) + return (0); + + /* + * Do not allow privileged processes in jail to directly manipulate + * system attributes. + */ + switch (attrnamespace) { + case EXTATTR_NAMESPACE_SYSTEM: + /* Potentially should be: return (EPERM); */ + return (suser_ucred(cred)); + case EXTATTR_NAMESPACE_USER: + return (VOP_ACCESS(vp, access, cred, p)); + default: + return (EPERM); + } +} + +/* * Unmount all file systems. * We traverse the list in reverse order under the assumption that doing so * will avoid needing to worry about dependencies. @@ -1851,22 +1881,27 @@ vinvalbuf(struct vnode *vp, int flags, s loop: s = splbio(); for (;;) { - if ((blist = LIST_FIRST(&vp->v_cleanblkhd)) && - (flags & V_SAVEMETA)) - while (blist && blist->b_lblkno < 0) - blist = LIST_NEXT(blist, b_vnbufs); - if (blist == NULL && - (blist = LIST_FIRST(&vp->v_dirtyblkhd)) && - (flags & V_SAVEMETA)) - while (blist && blist->b_lblkno < 0) - blist = LIST_NEXT(blist, b_vnbufs); - if (!blist) + blist = LIST_FIRST(&vp->v_cleanblkhd); + if (blist == NULL) + blist = LIST_FIRST(&vp->v_dirtyblkhd); + if (blist != NULL) { + if (flags & V_SAVEMETA) + while (blist && blist->b_lblkno < 0) + blist = LIST_NEXT(blist, b_vnbufs); + else if (flags & V_EXT) + while (blist && !(blist->b_flags & B_EXTATTR)) + blist = LIST_NEXT(blist, b_vnbufs); + } + if (blist == NULL) break; - for (bp = blist; bp; bp = nbp) { nbp = LIST_NEXT(bp, b_vnbufs); if (flags & V_SAVEMETA && bp->b_lblkno < 0) continue; + if (flags & V_NORMAL && bp->b_flags & B_EXTATTR) + continue; + if (flags & V_EXT && !(bp->b_flags & B_EXTATTR)) + continue; if (bp->b_flags & B_BUSY) { bp->b_flags |= B_WANTED; error = tsleep(bp, slpflag | (PRIBIO + 1), @@ -1893,7 +1928,7 @@ loop: brelse(bp); } } - if (!(flags & V_SAVEMETA) && + if (!(flags & V_SAVEMETA) && !(flags & V_EXT) && (!LIST_EMPTY(&vp->v_dirtyblkhd) || !LIST_EMPTY(&vp->v_cleanblkhd))) panic("vinvalbuf: flush failed"); splx(s); Index: sys/kern/vnode_if.src =================================================================== RCS file: /data/cvs/openbsd/src/sys/kern/vnode_if.src,v retrieving revision 1.32 diff -u -p -r1.32 vnode_if.src --- sys/kern/vnode_if.src 16 Jan 2007 17:52:18 -0000 1.32 +++ sys/kern/vnode_if.src 20 Jul 2007 13:55:15 -0000 @@ -437,3 +437,47 @@ vop_reallocblks { #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; +}; + +#% listextattr vp L L L + +vop_listextattr { + IN struct vnode *vp; + IN int attrnamespace; + INOUT struct uio *uio; + OUT size_t *size; + IN struct ucred *cred; + IN struct proc *p; +}; + +#% deleteextattr vp L L L + +vop_deleteextattr { + IN struct vnode *vp; + IN int attrnamespace; + IN const char *name; + 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; +}; Index: sys/sys/buf.h =================================================================== RCS file: /data/cvs/openbsd/src/sys/sys/buf.h,v retrieving revision 1.57 diff -u -p -r1.57 buf.h --- sys/sys/buf.h 28 May 2007 18:08:47 -0000 1.57 +++ sys/sys/buf.h 20 Jul 2007 13:55:15 -0000 @@ -161,12 +161,13 @@ struct buf *bufq_default_get(struct bufq #define B_DEFERRED 0x04000000 /* Skipped over for cleaning */ #define B_SCANNED 0x08000000 /* Block already pushed during sync */ #define B_PDAEMON 0x10000000 /* I/O started by pagedaemon */ +#define B_EXTATTR 0x20000000 /* Buffer holds extended attributes */ #define B_BITS "\010\001AGE\002NEEDCOMMIT\003ASYNC\004BAD\005BUSY\006CACHE" \ "\007CALL\010DELWRI\012DONE\013EINTR\014ERROR" \ "\016INVAL\020NOCACHE\023PHYS\024RAW\025READ" \ "\030WANTED\031WRITEINPROG\032XXX\033DEFERRED" \ - "\034SCANNED\035PDAEMON" + "\034SCANNED\035PDAEMON\036EXTATTR" /* * This structure describes a clustered I/O. It is stored in the b_saveaddr Index: sys/sys/extattr.h =================================================================== RCS file: sys/sys/extattr.h diff -N sys/sys/extattr.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/sys/extattr.h 20 Jul 2007 13:55:15 -0000 @@ -0,0 +1,101 @@ +/* + * Copyright (c) 1999-2001 Robert N. M. Watson + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * + * 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.18 2007/03/16 19:18:49 rwatson Exp $ + */ + +/* + * Support for extended filesystem attributes. + */ + +#ifndef _SYS_EXTATTR_H_ +#define _SYS_EXTATTR_H_ + +/* + * Defined name spaces for extended attributes. Numeric constants are passed + * via system calls, but a user-friendly string is also defined. + */ +#define EXTATTR_NAMESPACE_EMPTY 0x00000000 +#define EXTATTR_NAMESPACE_EMPTY_STRING "empty" +#define EXTATTR_NAMESPACE_USER 0x00000001 +#define EXTATTR_NAMESPACE_USER_STRING "user" +#define EXTATTR_NAMESPACE_SYSTEM 0x00000002 +#define EXTATTR_NAMESPACE_SYSTEM_STRING "system" + +/* + * The following macro is designed to initialize an array that maps + * extended-attribute namespace values to their names, e.g.: + * + * char *extattr_namespace_names[] = EXTATTR_NAMESPACE_NAMES; + */ +#define EXTATTR_NAMESPACE_NAMES { \ + EXTATTR_NAMESPACE_EMPTY_STRING, \ + EXTATTR_NAMESPACE_USER_STRING, \ + EXTATTR_NAMESPACE_SYSTEM_STRING } + +#ifdef _KERNEL + +#define EXTATTR_MAXNAMELEN NAME_MAX +struct proc; +struct ucred; +struct vnode; +int extattr_check_cred(struct vnode *vp, int attrnamespace, + struct ucred *cred, struct proc *td, int access); + +#else +#include + +struct iovec; + +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); +int extattr_delete_link(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); +ssize_t extattr_get_link(const char *_path, int _attrnamespace, + const char *_attrname, void *_data, size_t _nbytes); +ssize_t extattr_list_fd(int _fd, int _attrnamespace, void *_data, + size_t _nbytes); +ssize_t extattr_list_file(const char *_path, int _attrnamespace, void *_data, + size_t _nbytes); +ssize_t extattr_list_link(const char *_path, int _attrnamespace, 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); +int extattr_set_link(const char *_path, int _attrnamespace, + const char *_attrname, const void *_data, size_t _nbytes); + +#endif /* !_KERNEL */ +#endif /* !_SYS_EXTATTR_H_ */ Index: sys/sys/mount.h =================================================================== RCS file: /data/cvs/openbsd/src/sys/sys/mount.h,v retrieving revision 1.81 diff -u -p -r1.81 mount.h --- sys/sys/mount.h 1 Jun 2007 05:37:14 -0000 1.81 +++ sys/sys/mount.h 20 Jul 2007 13:55:15 -0000 @@ -491,6 +491,8 @@ struct vfsops { size_t, struct proc *); int (*vfs_checkexp)(struct mount *mp, struct mbuf *nam, int *extflagsp, struct ucred **credanonp); + int (*vfs_extattrctl)(struct mount *, int, struct vnode *, int, + const char *, struct proc *); }; #define VFS_MOUNT(MP, PATH, DATA, NDP, P) \ @@ -507,6 +509,12 @@ 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) + +int vfs_stdextattrctl(struct mount *, int, struct vnode *, int, const char *, + struct proc *); + #endif /* _KERNEL */ /* Index: sys/sys/vnode.h =================================================================== RCS file: /data/cvs/openbsd/src/sys/sys/vnode.h,v retrieving revision 1.88 diff -u -p -r1.88 vnode.h --- sys/sys/vnode.h 14 Jun 2007 20:36:34 -0000 1.88 +++ sys/sys/vnode.h 20 Jul 2007 13:55:15 -0000 @@ -176,16 +176,19 @@ struct vattr { #define IO_NODELOCKED 0x08 /* underlying node already locked */ #define IO_NDELAY 0x10 /* FNDELAY flag set in file table */ #define IO_NOLIMIT 0x20 /* don't enforce limits on i/o */ +#define IO_EXT 0x40 /* operate on extended attributes */ +#define IO_NORMAL 0x80 /* operate on regular data */ /* * Modes. Some values same as Ixxx entries from inode.h for now. */ -#define VSUID 04000 /* set user id on execution */ -#define VSGID 02000 /* set group id on execution */ -#define VSVTX 01000 /* save swapped text even after use */ -#define VREAD 00400 /* read, write, execute permissions */ -#define VWRITE 00200 -#define VEXEC 00100 +#define VEXEC 000100 /* execute/search permission */ +#define VWRITE 000200 /* write permission */ +#define VREAD 000400 /* read permission */ +#define VSVTX 001000 /* save swapped text even after use */ +#define VSGID 002000 /* set group id on execution */ +#define VSUID 004000 /* set user id on execution */ +#define VADMIN 010000 /* permission to administer */ /* * Token indicating no attribute value yet assigned. @@ -214,15 +217,15 @@ extern int vttoif_tab[]; /* * Flags to various vnode functions. */ -#define SKIPSYSTEM 0x0001 /* vflush: skip vnodes marked VSYSTEM */ -#define FORCECLOSE 0x0002 /* vflush: force file closeure */ -#define WRITECLOSE 0x0004 /* vflush: only close writeable files */ -#define DOCLOSE 0x0008 /* vclean: close active files */ -#define V_SAVE 0x0001 /* vinvalbuf: sync file first */ -#define V_SAVEMETA 0x0002 /* vinvalbuf: leave indirect blocks */ - -#define REVOKEALL 0x0001 /* vop_revoke: revoke all aliases */ - +#define SKIPSYSTEM 0x0001 /* vflush: skip vnodes marked VSYSTEM */ +#define FORCECLOSE 0x0002 /* vflush: force file closeure */ +#define WRITECLOSE 0x0004 /* vflush: only close writeable files */ +#define DOCLOSE 0x0008 /* vclean: close active files */ +#define V_SAVE 0x0001 /* vinvalbuf: sync file first */ +#define V_SAVEMETA 0x0002 /* vinvalbuf: leave indirect blocks */ +#define V_EXT 0x0004 /* vinvalbuf: invalidate only alternate bufs */ +#define V_NORMAL 0x0008 /* vinvalbuf: invalidate only regular bufs */ +#define REVOKEALL 0x0001 /* vop_revoke: revoke all aliases */ TAILQ_HEAD(freelst, vnode); extern struct freelst vnode_hold_list; /* free vnodes referencing buffers */ @@ -438,6 +441,11 @@ int vn_statfile(struct file *, struct st int vn_lock(struct vnode *, int, struct proc *); int vn_writechk(struct vnode *); int vn_ioctl(struct file *, u_long, caddr_t, struct proc *); +int vn_extattr_get(struct vnode *, int, int, const char *, int *, char *, + struct proc *); +int vn_extattr_set(struct vnode *, int, int, const char *, int, char *, + struct proc *); +int vn_extattr_rm(struct vnode *, int, int, const char *, struct proc *); void vn_marktext(struct vnode *); /* vfs_sync.c */ Index: sys/ufs/ffs/ffs_alloc.c =================================================================== RCS file: /data/cvs/openbsd/src/sys/ufs/ffs/ffs_alloc.c,v retrieving revision 1.78 diff -u -p -r1.78 ffs_alloc.c --- sys/ufs/ffs/ffs_alloc.c 22 Jun 2007 13:59:12 -0000 1.78 +++ sys/ufs/ffs/ffs_alloc.c 20 Jul 2007 13:55:15 -0000 @@ -174,14 +174,14 @@ nospace: * invoked to get an appropriate block. */ int -ffs_realloccg(struct inode *ip, daddr_t lbprev, daddr_t bpref, int osize, - int nsize, struct ucred *cred, struct buf **bpp, daddr_t *blknop) +ffs_realloccg(struct inode *ip, daddr_t lbprev, daddr_t bprev, daddr_t bpref, + int osize, int nsize, struct ucred *cred, struct buf **bpp, daddr_t *blknop) { struct fs *fs; struct buf *bp = NULL; daddr_t quota_updated = 0; int cg, request, error; - daddr_t bprev, bno; + daddr_t bno; if (bpp != NULL) *bpp = NULL; @@ -199,8 +199,6 @@ ffs_realloccg(struct inode *ip, daddr_t #endif /* DIAGNOSTIC */ if (cred->cr_uid != 0 && freespace(fs, fs->fs_minfree) <= 0) goto nospace; - - bprev = DIP(ip, db[lbprev]); if (bprev == 0) { printf("dev = 0x%x, bsize = %d, bprev = %d, fs = %s\n", Index: sys/ufs/ffs/ffs_balloc.c =================================================================== RCS file: /data/cvs/openbsd/src/sys/ufs/ffs/ffs_balloc.c,v retrieving revision 1.34 diff -u -p -r1.34 ffs_balloc.c --- sys/ufs/ffs/ffs_balloc.c 1 Jun 2007 18:54:27 -0000 1.34 +++ sys/ufs/ffs/ffs_balloc.c 20 Jul 2007 13:55:15 -0000 @@ -59,9 +59,13 @@ #include #include -int ffs1_balloc(struct inode *, off_t, int, struct ucred *, int, struct buf **); +int ffs1_balloc(struct inode *, off_t, int, struct ucred *, int, + struct buf **); #ifdef FFS2 -int ffs2_balloc(struct inode *, off_t, int, struct ucred *, int, struct buf **); +int ffs2_balloc(struct inode *, off_t, int, struct ucred *, int, + struct buf **); +int ffs2_balloc_ea(struct inode *, off_t, int, struct ucred *, int, + struct buf **); #endif /* @@ -106,7 +110,7 @@ ffs1_balloc(struct inode *ip, off_t star if (nb < NDADDR && nb < lbn) { osize = blksize(fs, ip, nb); if (osize < fs->fs_bsize && osize > 0) { - error = ffs_realloccg(ip, nb, + error = ffs_realloccg(ip, nb, DIP(ip, db[nb]), ffs1_blkpref(ip, nb, (int)nb, &ip->i_ffs1_db[0]), osize, (int)fs->fs_bsize, cred, bpp, &newb); if (error) @@ -178,7 +182,7 @@ ffs1_balloc(struct inode *ip, off_t star * The existing block is smaller than we * want, grow it. */ - error = ffs_realloccg(ip, lbn, + error = ffs_realloccg(ip, lbn, DIP(ip, db[lbn]), ffs1_blkpref(ip, lbn, (int)lbn, &ip->i_ffs1_db[0]), osize, nsize, cred, bpp, &newb); @@ -465,6 +469,12 @@ ffs2_balloc(struct inode *ip, off_t off, return (EFBIG); /* + * Check for allocating extended data. + */ + if (flags & IO_EXT) + return (ffs2_balloc_ea(ip, off, size, cred, flags, bpp)); + + /* * If the next write will extend the file into a new block, and the * file is currently composed of a fragment, this fragment has to be * extended to be a full block. @@ -474,9 +484,9 @@ ffs2_balloc(struct inode *ip, off_t off, nb = lastlbn; osize = blksize(fs, ip, nb); if (osize < fs->fs_bsize && osize > 0) { - error = ffs_realloccg(ip, nb, ffs2_blkpref(ip, - lastlbn, nb, &ip->i_ffs2_db[0]), osize, - (int) fs->fs_bsize, cred, bpp, &newb); + error = ffs_realloccg(ip, nb, DIP(ip, db[nb]), + ffs2_blkpref(ip, lastlbn, nb, &ip->i_ffs2_db[0]), + osize, (int) fs->fs_bsize, cred, bpp, &newb); if (error) return (error); @@ -552,7 +562,7 @@ ffs2_balloc(struct inode *ip, off_t off, * The existing block is smaller than we want, * grow it. */ - error = ffs_realloccg(ip, lbn, + error = ffs_realloccg(ip, lbn, DIP(ip, db[lbn]), ffs2_blkpref(ip, lbn, (int) lbn, &ip->i_ffs2_db[0]), osize, nsize, cred, bpp, &newb); @@ -888,6 +898,160 @@ fail: } VOP_FSYNC(vp, p->p_ucred, MNT_WAIT, p); return (error); +} + +int +ffs2_balloc_ea(struct inode *ip, off_t off, int size, struct ucred *cred, + int flags, struct buf **bpp) +{ + daddr64_t lbn, lastlbn; + struct fs *fs; + struct vnode *vp; + struct buf *bp; + daddr_t nb, newb; + int osize, nsize, error; + + vp = ITOV(ip); + fs = ip->i_fs; + lbn = lblkno(fs, off); + size = blkoff(fs, off) + size; + + /* + * All extended blocks are direct blocks. + */ + if (lbn >= NXADDR) + return (EFBIG); + + /* + * If the next write will extend the data into a new block, an the data + * is currently composed of a fragment, this fragment has to be + * extended to be a full block. + */ + lastlbn = lblkno(fs, ip->i_ffs2_extsize); + if (lastlbn < lbn) { + nb = lastlbn; + osize = sblksize(fs, ip->i_ffs2_extsize, nb); + if (osize < fs->fs_bsize && osize > 0) { + error = ffs_realloccg(ip, -1 - nb, ip->i_ffs2_extb[nb], + ffs2_blkpref(ip, lastlbn, nb, &ip->i_ffs2_extb[0]), + osize, fs->fs_bsize, cred, bpp, &newb); + if (error) + return (error); + if (DOINGSOFTDEP(vp)) + softdep_setup_allocext(ip, nb, dbtofsb(fs, + bp->b_blkno), ip->i_ffs2_extb[nb], + fs->fs_bsize, osize, bpp ? *bpp : NULL); + ip->i_ffs2_extsize = lblktosize(fs, nb + 1); + ip->i_ffs2_extb[nb] = newb; + ip->i_flag |= IN_CHANGE | IN_UPDATE; + + if (bpp) { + (*bpp)->b_flags |= B_EXTATTR; + if (flags & B_SYNC) + bwrite(*bpp); + else + bawrite(*bpp); + } + } + } + + nb = ip->i_ffs2_extb[lbn]; + + if (nb != 0 && ip->i_ffs2_extsize >= lblktosize(fs, lbn + 1)) { + /* + * The block is already allocated and the file extends past + * this block, thus this must be a whole block. Just read it, + * if requested. + */ + if (bpp != NULL) { + error = bread(vp, -1 - lbn, fs->fs_bsize, NOCRED, bpp); + if (error) { + brelse(*bpp); + return (error); + } + + (*bpp)->b_blkno = fsbtodb(fs, nb); + (*bpp)->b_flags |= B_EXTATTR; + } + + return (0); + } + + if (nb != 0) { + /* + * Consider the need to allocate a fragment. + */ + osize = fragroundup(fs, blkoff(fs, ip->i_ffs2_extsize)); + nsize = fragroundup(fs, size); + if (nsize <= osize) { + /* + * The existing block is already at least as big as we + * want. Just read it, if requested. + */ + if (bpp != NULL) { + error = bread(vp, -1 - lbn, fs->fs_bsize, + NOCRED, bpp); + if (error) { + brelse(*bpp); + return (error); + } + + (*bpp)->b_bcount = osize; + (*bpp)->b_blkno = fsbtodb(fs, nb); + (*bpp)->b_flags |= B_EXTATTR; + } + + return (0); + } else { + /* + * The existing block is smaller than we want, grow it. + */ + error = ffs_realloccg(ip, -1 - lbn, + ip->i_ffs2_extb[lbn], ffs2_blkpref(ip, lbn, lbn, + &ip->i_ffs2_extb[0]), osize, nsize, cred, bpp, + &newb); + if (error) + return (error); + if (bpp != NULL) + (*bpp)->b_flags |= B_EXTATTR; + if (DOINGSOFTDEP(vp)) + softdep_setup_allocext(ip, lbn, newb, nb, + nsize, osize, bpp ? *bpp : NULL); + } + } else { + /* + * The block was not previously allocated, allocate a new block + * or fragment. + */ + if (ip->i_ffs2_extsize < lblktosize(fs, lbn + 1)) + nsize = fragroundup(fs, size); + else + nsize = fs->fs_bsize; + + error = ffs_alloc(ip, lbn, ffs2_blkpref(ip, lbn, lbn, + &ip->i_ffs2_extb[0]), nsize, cred, &newb); + if (error) + return (error); + if (bpp != NULL) { + bp = getblk(vp, -1 - lbn, fs->fs_bsize, 0, 0); + if (nsize < fs->fs_bsize) + bp->b_bcount = nsize; + bp->b_blkno = fsbtodb(fs, newb); + bp->b_flags |= B_EXTATTR; + if (flags & B_CLRBUF) + clrbuf(bp); + *bpp = bp; + } else + bp = NULL; + + if (DOINGSOFTDEP(vp)) + softdep_setup_allocext(ip, lbn, newb, 0, nsize, 0, bp); + } + + ip->i_ffs2_extb[lbn] = newb; + ip->i_flag |= IN_CHANGE | IN_UPDATE; + + return (0); } #endif /* FFS2 */ Index: sys/ufs/ffs/ffs_extattr.c =================================================================== RCS file: sys/ufs/ffs/ffs_extattr.c diff -N sys/ufs/ffs/ffs_extattr.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/ufs/ffs/ffs_extattr.c 20 Jul 2007 13:55:15 -0000 @@ -0,0 +1,765 @@ +/* + * Copyright (c) 2007 Pedro Martelletto + * Copyright (c) 2002, 2003 Networks Associates Technology, Inc. + * All rights reserved. + * + * This software was developed for the FreeBSD Project by Marshall + * Kirk McKusick and Network Associates Laboratories, 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. + * + * 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. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +int ffs_ea_read(struct vnode *, struct uio *, int); +int ffs_ea_write(struct vnode *, struct uio *, int, struct ucred *); +int ffs_ea_find(struct inode *, int, const char *, unsigned char **, + unsigned char **); +int ffs_ea_iget(struct vnode *, struct ucred *, struct proc *); +int ffs_ea_iput(struct vnode *, int, struct ucred *, struct proc *); + +/* + * Extended attribute area reading. + */ +int +ffs_ea_read(struct vnode *vp, struct uio *uio, int ioflag) +{ + struct inode *ip; + struct ufs2_dinode *dp; + struct fs *fs; + struct buf *bp; + daddr64_t lbn, nextlbn; + off_t ealeft; + int error, size, xfersize; + size_t oresid; + + ip = VTOI(vp); + fs = ip->i_fs; + dp = ip->i_din2; + error = 0; + oresid = uio->uio_resid; + ealeft = dp->di_extsize; + + /* + * Loop over the amount of data requested by the caller, stopping only + * if an error occurs. By default, we always try to copy a file system + * block worth of bytes per iteration ('xfersize'). Check this value + * against what is left to be copied ('uio->uio_resid'), and the amount + * of bytes past our current position in the extended attribute area + * ('ealeft'). + */ + + while (uio->uio_resid > 0) { + + ealeft -= uio->uio_offset; + if (ealeft <= 0) + break; + + xfersize = fs->fs_bsize; + if (uio->uio_resid < xfersize) + xfersize = uio->uio_resid; + if (ealeft < xfersize) + xfersize = ealeft; + + /* + * Get the corresponding logical block number. Read it in, + * doing read-ahead if possible. + */ + + lbn = lblkno(fs, uio->uio_offset); + size = sblksize(fs, dp->di_extsize, lbn); + nextlbn = lbn + 1; + + if (lblktosize(fs, nextlbn) >= dp->di_extsize) + error = bread(vp, -1 - lbn, size, NOCRED, &bp); + else { + int nextsize = sblksize(fs, dp->di_extsize, nextlbn); + nextlbn = -1 - nextlbn; + error = breadn(vp, -1 - lbn, + size, &nextlbn, &nextsize, 1, NOCRED, &bp); + } + + if (error) { + brelse(bp); + break; + } + + /* Check for short-reads. */ + if (bp->b_resid) { + brelse(bp); + error = EIO; + break; + } + + /* Finally, copy out the data, and release the buffer. */ + error = uiomove(bp->b_data, xfersize, uio); + brelse(bp); + if (error) + break; + } + + if ((error == 0 || uio->uio_resid != oresid) && + (vp->v_mount->mnt_flag & MNT_NOATIME) == 0) + ip->i_flag |= IN_ACCESS; + + return (error); +} + +/* + * Extended attribute area writing. + */ +int +ffs_ea_write(struct vnode *vp, struct uio *uio, int ioflag, struct ucred *ucred) +{ + struct inode *ip; + struct ufs2_dinode *dp; + struct fs *fs; + struct buf *bp; + off_t osize; + size_t resid; + int error, flags, xfersize; + + ip = VTOI(vp); + fs = ip->i_fs; + dp = ip->i_din2; + error = 0; + + /* Don't cross the limit for the extended attribute area length. */ + if ((unsigned)uio->uio_offset + uio->uio_resid > NXADDR * fs->fs_bsize) + return (EFBIG); + + /* + * Save the original amount of data to be written ('uio->uio_resid') as + * well as the extended attribute area size ('dp->di_extsize') in case + * we need to truncate in the end due to an unpredicted error. + */ + + resid = uio->uio_resid; + osize = dp->di_extsize; + + /* Prepare flags to be passed to UFS_BUF_ALLOC(). */ + flags = IO_EXT | B_CLRBUF; + if (!DOINGASYNC(vp)) + flags |= B_SYNC; + + /* + * Loop over the amount of data requested by the caller, stopping only + * if an error occurs. By default, we always try to write a file system + * block worth of bytes per iteration ('xfersize'). Check this value + * against what is left to be copied ('uio->uio_resid'). If we are + * writing less than a full block, ask the buffer to be cleaned first. + */ + + while (uio->uio_resid > 0) { + + xfersize = fs->fs_bsize; + if (uio->uio_resid < xfersize) { + xfersize = uio->uio_resid; + flags |= B_CLRBUF; + } + + /* Ask the ffs_balloc.c code for the block. */ + error = UFS_BUF_ALLOC(ip, uio->uio_offset, xfersize, ucred, + flags, &bp); + if (error) + break; + + /* Check if we are growing the extended attributes area. */ + if (uio->uio_offset + xfersize > dp->di_extsize) + dp->di_extsize = uio->uio_offset + xfersize; + + error = uiomove(bp->b_data, xfersize, uio); + if (error) { + clrbuf(bp); /* Get rid of stray contents. */ + error = EIO; + break; + } + + /* + * We use the same criteria for calling bwrite(), bawrite(), or + * bdwrite() as the rest of the FFS code does. + */ + + if (ioflag & IO_SYNC) + bwrite(bp); + else if (xfersize == fs->fs_bsize) + bawrite(bp); + else + bdwrite(bp); + + if (error || xfersize == 0) + break; + + ip->i_flag |= IN_CHANGE | IN_UPDATE; + } + + /* + * If we successfully wrote any data, and we are not the superuser we + * clear the setuid and setgid bits as a precaution against tampering. + */ + + if ((DIP(ip, mode) & (ISUID | ISGID)) && resid > uio->uio_resid) + if (ucred && suser_ucred(ucred)) + DIP(ip, mode) &= ~(ISUID | ISGID); + + if (error) { + /* Release blocks if we failed. */ + if (ioflag & IO_UNIT) { + ffs_truncate(ip, osize, flags, ucred); + uio->uio_offset -= resid - uio->uio_resid; + uio->uio_resid = resid; + } + + return (error); + } + + /* If needed, sync the inode now. */ + if (resid > uio->uio_resid && (ioflag & IO_SYNC)) + return (ffs_update(ip, NULL, NULL, MNT_WAIT)); + + return (0); +} + +/* + * Locate a particular extended attribute in a given area. Return its length, + * and possibly the pointer to the entry and to the data. + */ +int +ffs_ea_find(struct inode *ip, int nspace, const char *name, + unsigned char **eap, unsigned char **eac) +{ + unsigned char *eaptr, *eaend, *enext, *estart; + int eapad1, eapad2, elength, ealen, nlen; + u_int32_t rz; + + eaend = ip->i_ea_area + ip->i_ea_len; + nlen = strlen(name); + + for (eaptr = ip->i_ea_area; eaptr < eaend; eaptr = enext) { + estart = eaptr; + bcopy(eaptr, &rz, sizeof(rz)); + enext = eaptr + rz; + /* make sure this entry is complete */ + if (enext > eaend) + break; + eaptr += sizeof(u_int32_t); + if (*eaptr != nspace) + continue; + eaptr++; + eapad2 = *eaptr++; + if (*eaptr != nlen) + continue; + eaptr++; + if (bcmp(eaptr, name, nlen)) + continue; + elength = sizeof(u_int32_t) + 3 + nlen; + eapad1 = 8 - (elength % 8); + if (eapad1 == 8) + eapad1 = 0; + elength += eapad1; + ealen = rz - elength - eapad2; + eaptr += nlen + eapad1; + if (eap != NULL) + *eap = estart; + if (eac != NULL) + *eac = eaptr; + return (ealen); + } + return(-1); +} + +/* + * Read in and acquire reference to an inode's extended attribute area. + */ +int +ffs_ea_iget(struct vnode *vp, struct ucred *cred, struct proc *p) +{ + struct inode *ip; + struct ufs2_dinode *dp; + struct uio luio; + struct iovec liovec; + int error; + unsigned char *eae; + + ip = VTOI(vp); +#ifdef DIAGNOSTIC + if (ip->i_ea_area != NULL) + panic("ffs_ea_iget: found extended attribute area"); +#endif + + dp = ip->i_din2; + eae = malloc(dp->di_extsize, M_TEMP, M_WAITOK | M_CANFAIL); + if (eae == NULL) + return (ENOMEM); + + liovec.iov_base = eae; + liovec.iov_len = dp->di_extsize; + luio.uio_iov = &liovec; + luio.uio_iovcnt = 1; + luio.uio_offset = 0; + luio.uio_resid = dp->di_extsize; + luio.uio_segflg = UIO_SYSSPACE; + luio.uio_rw = UIO_READ; + + error = ffs_ea_read(vp, &luio, IO_EXT | IO_SYNC); + if (error) { + free(eae, M_TEMP); + return(error); + } + + ip->i_ea_area = eae; + ip->i_ea_len = dp->di_extsize; + + return (0); +} + +/* + * Write out (if requested) and release reference to an inode's extended + * attribute area. + */ +int +ffs_ea_iput(struct vnode *vp, int write, struct ucred *cred, struct proc *p) +{ + struct inode *ip; + struct uio luio; + struct iovec liovec; + int error = 0; + struct ufs2_dinode *dp; + + ip = VTOI(vp); +#ifdef DIAGNOSTIC + if (ip->i_ea_area == NULL) + panic("ffs_ea_iput: missing extended attribute area"); +#endif + + dp = ip->i_din2; + + if (write) { + liovec.iov_base = ip->i_ea_area; + liovec.iov_len = ip->i_ea_len; + luio.uio_iov = &liovec; + luio.uio_iovcnt = 1; + luio.uio_offset = 0; + luio.uio_resid = ip->i_ea_len; + luio.uio_segflg = UIO_SYSSPACE; + luio.uio_rw = UIO_WRITE; + if (ip->i_ea_len < dp->di_extsize) { + error = ffs_truncate(ip, 0, IO_EXT, cred); + if (error) + return (error); + } + error = ffs_ea_write(vp, &luio, IO_EXT | IO_SYNC, cred); + } + + free(ip->i_ea_area, M_TEMP); + + ip->i_ea_area = NULL; + ip->i_ea_len = 0; + + return (error); +} + +/* + * Vnode operation to remove a named attribute. + */ +int +ffs_ea_del(void *v) +{ + struct vop_deleteextattr_args *ap = v; + struct inode *ip; + int error, olen; + u_int32_t esize; + unsigned int eoffset; + unsigned char *eae, *eaddr; + + ip = VTOI(ap->a_vp); + + /* + * Validate the vnode. It has to be a FFS2 file, directory, or symlink, + * and must reside in a writable file system. + */ + + if (ip->i_fs->fs_magic != FS_UFS2_MAGIC) + return (EOPNOTSUPP); + + if (ap->a_vp->v_type != VREG && + ap->a_vp->v_type != VDIR && + ap->a_vp->v_type != VLNK) + return (EOPNOTSUPP); + + if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) + return (EROFS); + + /* The attribute name must be at least one byte long. */ + if (strlen(ap->a_name) == 0) + return (EINVAL); + + /* + * Validate credentials, read in the inode's extended attributes area, + * and make sure there is an attribute with the given name. + */ + + error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, ap->a_cred, + ap->a_p, IWRITE); + if (error) + return (error); + + error = ffs_ea_iget(ap->a_vp, ap->a_cred, ap->a_p); + if (error) + return (error); + + olen = ffs_ea_find(ip, ap->a_attrnamespace, ap->a_name, &eaddr, NULL); + if (olen == -1) { + /* No such attribute. */ + (void) ffs_ea_iput(ap->a_vp, 0, ap->a_cred, ap->a_p); + return (ENOATTR); + } + + /* + * Get absolute offset of entry. Make sure it is within the extended + * attribute area. + */ + + eoffset = eaddr - ip->i_ea_area; + if (eoffset >= ip->i_ea_len) { + (void) ffs_ea_iput(ap->a_vp, 0, ap->a_cred, ap->a_p); + return (EINVAL); + } + + /* + * Get size of entry. Make sure it is a sane value. + */ + + bcopy(eaddr, &esize, sizeof(esize)); + if (eoffset + esize > ip->i_ea_len) { + (void) ffs_ea_iput(ap->a_vp, 0, ap->a_cred, ap->a_p); + return (EINVAL); + } + + /* + * Allocate a new extended attribute area and copy the contents over, + * skipping the entry found. + */ + + eae = malloc(ip->i_ea_len, M_TEMP, M_WAITOK | M_CANFAIL); + if (eae == NULL) { + (void) ffs_ea_iput(ap->a_vp, 0, ap->a_cred, ap->a_p); + return (ENOMEM); + } + + bcopy(ip->i_ea_area, eae, eoffset); + bcopy(eaddr + esize, eae + eoffset, ip->i_ea_len - eoffset - esize); + + free(ip->i_ea_area, M_TEMP); + ip->i_ea_area = eae; + ip->i_ea_len -= esize; + + return (ffs_ea_iput(ap->a_vp, 1, ap->a_cred, ap->a_p)); +} + +/* + * Vnode operation to retrieve a named extended attribute. + */ +int +ffs_ea_get(void *v) +{ + struct vop_getextattr_args *ap = v; + struct inode *ip; + struct fs *fs; + unsigned char *p; + int error, ealen; + + ip = VTOI(ap->a_vp); + fs = ip->i_fs; + + /* + * Validate the vnode. It has to be a FFS2 file, directory, or symlink. + */ + if (ip->i_fs->fs_magic != FS_UFS2_MAGIC) + return (EOPNOTSUPP); + + if (ap->a_vp->v_type != VREG && + ap->a_vp->v_type != VDIR && + ap->a_vp->v_type != VLNK) + return (EOPNOTSUPP); + + /* + * Validate credentials, read in the inode's extended attributes area, + * and make sure there is no attribute with the same name. + */ + + error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, ap->a_cred, + ap->a_p, IREAD); + if (error) + return (error); + + error = ffs_ea_iget(ap->a_vp, ap->a_cred, ap->a_p); + if (error) + return (error); + + /* Call ffs_ea_find() to do the real job. */ + ealen = ffs_ea_find(ip, ap->a_attrnamespace, ap->a_name, NULL, &p); + if (ealen >= 0) { + error = 0; + if (ap->a_size != NULL) + *ap->a_size = ealen; + else if (ap->a_uio != NULL) + error = uiomove(p, ealen, ap->a_uio); + } else + error = ENOATTR; + + + (void) ffs_ea_iput(ap->a_vp, 0, ap->a_cred, ap->a_p); + + return (error); +} + +/* + * Vnode operation to retrieve extended attributes on a vnode. + */ +int +ffs_ea_list(void *v) +{ + struct vop_listextattr_args *ap = v; + struct inode *ip; + struct fs *fs; + unsigned char *p, *pend, *pnext; + u_int32_t rz; + int error, attrnamelen; + + ip = VTOI(ap->a_vp); + fs = ip->i_fs; + + /* + * Validate the vnode. It has to be a FFS2 file, directory, or symlink. + */ + if (ip->i_fs->fs_magic != FS_UFS2_MAGIC) + return (EOPNOTSUPP); + + if (ap->a_vp->v_type != VREG && + ap->a_vp->v_type != VDIR && + ap->a_vp->v_type != VLNK) + return (EOPNOTSUPP); + + /* + * Validate credentials and read in the inode's extended attributes + * area. + */ + + error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, ap->a_cred, + ap->a_p, IREAD); + if (error) + return (error); + + error = ffs_ea_iget(ap->a_vp, ap->a_cred, ap->a_p); + if (error) + return (error); + + error = 0; + if (ap->a_size != NULL) + *ap->a_size = 0; + + pend = ip->i_ea_area + ip->i_ea_len; + + /* + * See comment in ffs_ea_set() for a detailed description of how + * extended attribute entries are laid out. + */ + for(p = (unsigned char *)ip->i_ea_area; p < pend; p = pnext) { + /* + * Read in the record size, set pointer to next entry. Make + * sure this is an entry belonging to the attribute space we + * are interesting in, and copy the attribute name over. + */ + bcopy(p, &rz, sizeof(rz)); + pnext = p + rz; + if (pnext > pend) + break; /* Stepping out of extended attribute area. */ + + p += sizeof(rz); + if (*p++ != ap->a_attrnamespace) + continue; + + p++; /* Skip length of second padding. */ + attrnamelen = (int)*p; + + if (ap->a_size != NULL) + *ap->a_size += attrnamelen + 1; + else if (ap->a_uio != NULL) { + error = uiomove(p, attrnamelen + 1, ap->a_uio); + if (error) { + ffs_ea_iput(ap->a_vp, 0, ap->a_cred, ap->a_p); + return (error); + } + } + } + + return (ffs_ea_iput(ap->a_vp, 0, ap->a_cred, ap->a_p)); +} + +/* + * Vnode operation to set a named attribute. + */ +int +ffs_ea_set(void *v) +{ + struct vop_setextattr_args *ap = v; + struct inode *ip; + struct fs *fs; + u_int32_t prefixlen, bodylen, entrysize, pad1, pad2; + int error; + unsigned char *eae, *p; + + ip = VTOI(ap->a_vp); + fs = ip->i_fs; + + /* + * Validate the vnode. It has to be a FFS2 file, directory, or symlink, + * and must reside in a writable file system. + */ + + if (ip->i_fs->fs_magic != FS_UFS2_MAGIC) + return (EOPNOTSUPP); + + if (ap->a_vp->v_type != VREG && + ap->a_vp->v_type != VDIR && + ap->a_vp->v_type != VLNK) + return (EOPNOTSUPP); + + if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) + return (EROFS); + + /* The attribute name and content must be at least one byte long. */ + if (strlen(ap->a_name) == 0 || ap->a_uio->uio_resid == 0) + return (EINVAL); + + /* + * Validate credentials, read in the inode's extended attributes area, + * and make sure there is no attribute with the same name. + */ + + error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, ap->a_cred, + ap->a_p, IWRITE); + if (error) + return (error); + + error = ffs_ea_iget(ap->a_vp, ap->a_cred, ap->a_p); + if (error) + return (error); + + if (ffs_ea_find(ip, ap->a_attrnamespace, ap->a_name, NULL, NULL) != -1) { + ffs_ea_iput(ap->a_vp, 0, ap->a_cred, ap->a_p); + return (EEXIST); + } + + /* + * Start constructing the extended attribute entry in memory. An entry + * is made up of the following fields, laid out in top/bottom order: + * + * +-----------------------------+---------+ + * | | 1. Record size | 4 bytes | + * | | 2. Attribute name space | 1 byte | + * | | 3. Length of second padding | 1 byte | + * | | 4. Length of attribute name | 1 byte | + * | +-----------------------------+---------+ + * | | 5. Attribute name | + * | | 6. First padding | + * | | 7. Attribute content | + * V | 8. Second padding | + * +-----------------------------+ + * + * The first 5 fields have their length computed in 'prefixlen'. Based + * on this value, the first padding is calculated. Likewise, the 7th + * field (the attribute content) has its length computed in 'bodylen', + * based on which the second padding is calculated. The complete entry + * length is computed in 'entrysize'. + */ + + prefixlen = sizeof(u_int32_t) + 3 + strlen(ap->a_name); + pad1 = 8 - (prefixlen % 8); + if (pad1 == 8) + pad1 = 0; + + bodylen = ap->a_uio->uio_resid; + pad2 = 8 - (bodylen % 8); + if (pad2 == 8) + pad2 = 0; + + entrysize = prefixlen + pad1 + bodylen + pad2; + + /* Make sure we're not crossing the limit for extended attributes. */ + if (ip->i_ea_len + entrysize > NXADDR * fs->fs_bsize) { + ffs_ea_iput(ap->a_vp, 0, ap->a_cred, ap->a_p); + return (ENOSPC); + } + + eae = malloc(ip->i_ea_len + entrysize, M_TEMP, M_WAITOK | M_CANFAIL); + if (eae == NULL) { + ffs_ea_iput(ap->a_vp, 0, ap->a_cred, ap->a_p); + return (ENOMEM); + } + + bcopy(ip->i_ea_area, eae, ip->i_ea_len); + p = eae + ip->i_ea_len; + + /* Prefix (1st-5th fields). */ + bcopy(&entrysize, p, sizeof(entrysize)); + p += sizeof(entrysize); + *p++ = ap->a_attrnamespace; + *p++ = pad2; + *p++ = strlen(ap->a_name); + bcopy(ap->a_name, p, strlen(ap->a_name)); + p += strlen(ap->a_name); + + /* Paddings and content (6th-8th fields). */ + bzero(p, pad1); + p += pad1; + error = uiomove(p, bodylen, ap->a_uio); + if (error) { + free(eae, M_TEMP); + ffs_ea_iput(ap->a_vp, 0, ap->a_cred, ap->a_p); + return (error); + } + p += bodylen; + bzero(p, pad2); + + /* Swap old extended attributes area with new one, and write it out. */ + free(ip->i_ea_area, M_TEMP); + ip->i_ea_area = eae; + ip->i_ea_len += entrysize; + + return (ffs_ea_iput(ap->a_vp, 1, ap->a_cred, ap->a_p)); +} Index: sys/ufs/ffs/ffs_extern.h =================================================================== RCS file: /data/cvs/openbsd/src/sys/ufs/ffs/ffs_extern.h,v retrieving revision 1.32 diff -u -p -r1.32 ffs_extern.h --- sys/ufs/ffs/ffs_extern.h 1 Jun 2007 20:23:25 -0000 1.32 +++ sys/ufs/ffs/ffs_extern.h 20 Jul 2007 13:55:15 -0000 @@ -99,7 +99,7 @@ __BEGIN_DECLS /* ffs_alloc.c */ int ffs_alloc(struct inode *, daddr_t, daddr_t , int, struct ucred *, daddr_t *); -int ffs_realloccg(struct inode *, daddr_t, daddr_t, int, int , +int ffs_realloccg(struct inode *, daddr_t, daddr_t, daddr_t, int, int , struct ucred *, struct buf **, daddr_t *); int ffs_reallocblks(void *); int ffs_inode_alloc(struct inode *, mode_t, struct ucred *, struct vnode **); @@ -158,6 +158,12 @@ int ffs_fsync(void *); int ffs_reclaim(void *); int ffsfifo_reclaim(void *); +/* ffs_extattr.c */ +int ffs_ea_del(void *); +int ffs_ea_get(void *); +int ffs_ea_list(void *); +int ffs_ea_set(void *); + /* * Soft dependency function prototypes. */ @@ -174,11 +180,13 @@ int softdep_flushfiles(struct mount *, void softdep_update_inodeblock(struct inode *, struct buf *, int); void softdep_load_inodeblock(struct inode *); void softdep_freefile(struct vnode *, ino_t, mode_t); -void softdep_setup_freeblocks(struct inode *, off_t); +void softdep_setup_freeblocks(struct inode *, off_t, int); void softdep_setup_inomapdep(struct buf *, struct inode *, ino_t); void softdep_setup_blkmapdep(struct buf *, struct fs *, daddr_t); void softdep_setup_allocdirect(struct inode *, daddr64_t, daddr_t, daddr_t, long, long, struct buf *); +void softdep_setup_allocext(struct inode *, daddr64_t, daddr64_t, daddr64_t, + long, long, struct buf *); void softdep_setup_allocindir_meta(struct buf *, struct inode *, struct buf *, int, daddr_t); void softdep_setup_allocindir_page(struct inode *, daddr64_t, Index: sys/ufs/ffs/ffs_inode.c =================================================================== RCS file: /data/cvs/openbsd/src/sys/ufs/ffs/ffs_inode.c,v retrieving revision 1.49 diff -u -p -r1.49 ffs_inode.c --- sys/ufs/ffs/ffs_inode.c 1 Jun 2007 18:54:27 -0000 1.49 +++ sys/ufs/ffs/ffs_inode.c 20 Jul 2007 13:55:15 -0000 @@ -162,32 +162,104 @@ int ffs_truncate(struct inode *oip, off_t length, int flags, struct ucred *cred) { struct vnode *ovp; - daddr64_t lastblock; + daddr64_t lastblock, datablocks; daddr64_t bn, lbn, lastiblock[NIADDR], indir_lbn[NIADDR]; daddr64_t oldblks[NDADDR + NIADDR], newblks[NDADDR + NIADDR]; struct fs *fs; struct buf *bp; int offset, size, level; long count, nblocks, vflags, blocksreleased = 0; - int i, aflags, error, allerror; + int i, aflags, error, allerror, needextclean = 0; off_t osize; +#ifdef FFS2 + daddr64_t extblocks; + int softdepslowdown; +#endif if (length < 0) return (EINVAL); + ovp = ITOV(oip); + fs = oip->i_fs; if (ovp->v_type != VREG && ovp->v_type != VDIR && ovp->v_type != VLNK) return (0); - if (DIP(oip, size) == length) + /* + * Historically clients did not have to specify which data they were + * truncating. So, if not specified, we assume traditional behavior, + * e.g., just the normal data. + */ + if ((flags & (IO_EXT | IO_NORMAL)) == 0) + flags |= IO_NORMAL; + + if (DIP(oip, size) == length && !(flags & IO_EXT)) return (0); + datablocks = DIP(oip, blocks); + +#ifdef FFS2 + /* + * If we are truncating the extended-attributes, and cannot do it with + * soft updates, then do it slowly here. If we are truncating both the + * extended attributes and the file contents (e.g., the file is being + * unlinked), then pick it off with soft updates below. + */ + needextclean = 0; + softdepslowdown = DOINGSOFTDEP(ovp) && softdep_slowdown(ovp); + extblocks = 0; + if (fs->fs_magic == FS_UFS2_MAGIC && oip->i_ffs2_extsize > 0) { + extblocks = btodb(fragroundup(fs, oip->i_ffs2_extsize)); + datablocks -= extblocks; + } + if ((flags & IO_EXT) && extblocks > 0) { + if (DOINGSOFTDEP(ovp) && softdepslowdown == 0 && length == 0) { + if ((flags & IO_NORMAL) == 0) { + softdep_setup_freeblocks(oip, length, IO_EXT); + return (0); + } + needextclean = 1; + } else { +#ifdef DIAGNOSTIC + if (length != 0) + panic("ffs_truncate: partial truncation of " + "extended attributes"); +#endif + error = VOP_FSYNC(ovp, cred, MNT_WAIT, curproc); + if (error) + return (error); + osize = oip->i_ffs2_extsize; + oip->i_ffs2_blocks -= extblocks; + (void)ufs_quota_free_blocks(oip, extblocks, NOCRED); + (void) vinvalbuf(ovp, V_EXT, cred, curproc, 0, 0); + oip->i_ffs2_extsize = 0; + for (i = 0; i < NXADDR; i++) { + oldblks[i] = oip->i_ffs2_extb[i]; + oip->i_ffs2_extb[i] = 0; + } + oip->i_flag |= IN_CHANGE | IN_UPDATE; + error = UFS_UPDATE(oip, MNT_WAIT); + if (error) + return (error); + for (i = 0; i < NXADDR; i++) { + if (oldblks[i] == 0) + continue; + ffs_blkfree(oip, oldblks[i], + sblksize(fs, osize, i)); + } + } + } + + if (!(flags & IO_NORMAL)) + return (0); /* Nothing else to do. */ +#endif /* FFS2 */ + if (ovp->v_type == VLNK && (DIP(oip, size) < ovp->v_mount->mnt_maxsymlinklen || (ovp->v_mount->mnt_maxsymlinklen == 0 && - oip->i_din1->di_blocks == 0))) { + datablocks == 0))) { #ifdef DIAGNOSTIC if (length != 0) panic("ffs_truncate: partial truncate of symlink"); @@ -195,6 +267,10 @@ ffs_truncate(struct inode *oip, off_t le memset(SHORTLINK(oip), 0, (size_t) DIP(oip, size)); DIP_ASSIGN(oip, size, 0); oip->i_flag |= IN_CHANGE | IN_UPDATE; +#ifdef FFS2 + if (needextclean) + softdep_setup_freeblocks(oip, length, IO_EXT); +#endif return (UFS_UPDATE(oip, MNT_WAIT)); } @@ -220,16 +296,16 @@ ffs_truncate(struct inode *oip, off_t le curproc)) != 0) return (error); } else { - (void)ufs_quota_free_blocks(oip, DIP(oip, blocks), - NOCRED); - softdep_setup_freeblocks(oip, length); - (void) vinvalbuf(ovp, 0, cred, curproc, 0, 0); + (void)ufs_quota_free_blocks(oip, datablocks, NOCRED); + softdep_setup_freeblocks(oip, length, needextclean ? + IO_EXT | IO_NORMAL : IO_NORMAL); + (void) vinvalbuf(ovp, needextclean ? 0 : V_NORMAL, + cred, curproc, 0, 0); oip->i_flag |= IN_CHANGE | IN_UPDATE; return (UFS_UPDATE(oip, 0)); } } - fs = oip->i_fs; osize = DIP(oip, size); /* * Lengthen the size of the file. We must ensure that the Index: sys/ufs/ffs/ffs_softdep.c =================================================================== RCS file: /data/cvs/openbsd/src/sys/ufs/ffs/ffs_softdep.c,v retrieving revision 1.92 diff -u -p -r1.92 ffs_softdep.c --- sys/ufs/ffs/ffs_softdep.c 11 Jul 2007 15:32:22 -0000 1.92 +++ sys/ufs/ffs/ffs_softdep.c 20 Jul 2007 13:55:15 -0000 @@ -1,4 +1,4 @@ -/* $OpenBSD: ffs_softdep.c,v 1.92 2007/07/11 15:32:22 millert Exp $ */ +/* $OpenBSD: ffs_softdep.c,v 1.91 2007/06/01 20:23:26 pedro Exp $ */ /* * Copyright 1998, 2000 Marshall Kirk McKusick. All Rights Reserved. @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -146,7 +147,7 @@ STATIC void free_allocdirect(struct allo STATIC int check_inode_unwritten(struct inodedep *); STATIC int free_inodedep(struct inodedep *); STATIC void handle_workitem_freeblocks(struct freeblks *); -STATIC void merge_inode_lists(struct inodedep *); +STATIC void merge_inode_lists(struct allocdirectlst *,struct allocdirectlst *); STATIC void setup_allocindir_phase2(struct buf *, struct inode *, struct allocindir *); STATIC struct allocindir *newallocindir(struct inode *, int, daddr_t, @@ -1112,12 +1113,15 @@ top: inodedep->id_nlinkdelta = 0; inodedep->id_savedino1 = NULL; inodedep->id_savedsize = -1; + inodedep->id_savedextsize = -1; inodedep->id_buf = NULL; LIST_INIT(&inodedep->id_pendinghd); LIST_INIT(&inodedep->id_inowait); LIST_INIT(&inodedep->id_bufwait); TAILQ_INIT(&inodedep->id_inoupdt); TAILQ_INIT(&inodedep->id_newinoupdt); + TAILQ_INIT(&inodedep->id_extupdt); + TAILQ_INIT(&inodedep->id_newextupdt); ACQUIRE_LOCK(&lk); LIST_INSERT_HEAD(inodedephd, inodedep, id_hash); sema_release(&inodedep_in_progress); @@ -1664,6 +1668,101 @@ handle_workitem_freefrag(freefrag) } /* + * Set up a dependency structure for an external attributes data block. + * This routine follows much of the structure of softdep_setup_allocdirect. + * See the description of softdep_setup_allocdirect for details. + */ +void +softdep_setup_allocext(ip, lbn, newblkno, oldblkno, newsize, oldsize, bp) + struct inode *ip; + daddr64_t lbn; + daddr64_t newblkno; + daddr64_t oldblkno; + long newsize; + long oldsize; + struct buf *bp; +{ + struct allocdirect *adp, *oldadp; + struct allocdirectlst *adphead; + struct bmsafemap *bmsafemap; + struct inodedep *inodedep; + struct newblk *newblk; + + adp = pool_get(&allocdirect_pool, PR_WAITOK); + bzero(adp, sizeof(struct allocdirect)); + adp->ad_list.wk_type = D_ALLOCDIRECT; + adp->ad_lbn = lbn; + adp->ad_newblkno = newblkno; + adp->ad_oldblkno = oldblkno; + adp->ad_newsize = newsize; + adp->ad_oldsize = oldsize; + adp->ad_state = ATTACHED | EXTDATA; + LIST_INIT(&adp->ad_newdirblk); + if (newblkno == oldblkno) + adp->ad_freefrag = NULL; + else + adp->ad_freefrag = newfreefrag(ip, oldblkno, oldsize); + + if (newblk_lookup(ip->i_fs, newblkno, 0, &newblk) == 0) + panic("softdep_setup_allocext: lost block"); + + ACQUIRE_LOCK(&lk); + inodedep_lookup(ip->i_fs, ip->i_number, DEPALLOC | NODELAY, &inodedep); + adp->ad_inodedep = inodedep; + + if (newblk->nb_state == DEPCOMPLETE) { + adp->ad_state |= DEPCOMPLETE; + adp->ad_buf = NULL; + } else { + bmsafemap = newblk->nb_bmsafemap; + adp->ad_buf = bmsafemap->sm_buf; + LIST_REMOVE(newblk, nb_deps); + LIST_INSERT_HEAD(&bmsafemap->sm_allocdirecthd, adp, ad_deps); + } + + LIST_REMOVE(newblk, nb_hash); + pool_put(&newblk_pool, newblk); + + WORKLIST_INSERT(&bp->b_dep, &adp->ad_list); + if (lbn >= NXADDR) + panic("softdep_setup_allocext: lbn %lld > NXADDR", + (long long)lbn); + + /* + * The list of allocdirects must be kept in sorted and ascending order + * so that the rollback routines can quickly determine the first + * uncommitted block (the size of the file stored on disk ends at the + * end of the lowest committed fragment, or if there are no fragments, + * at the end of the highest committed block). Since files generally + * grow, the typical case is that the new block is to be added at the + * end of the list. We speed this special case by checking against the + * last allocdirect in the list before laboriously traversing the list + * looking for the insertion point. + */ + adphead = &inodedep->id_newextupdt; + oldadp = TAILQ_LAST(adphead, allocdirectlst); + if (oldadp == NULL || oldadp->ad_lbn <= lbn) { + /* insert at end of list */ + TAILQ_INSERT_TAIL(adphead, adp, ad_next); + if (oldadp != NULL && oldadp->ad_lbn == lbn) + allocdirect_merge(adphead, adp, oldadp); + FREE_LOCK(&lk); + return; + } + TAILQ_FOREACH(oldadp, adphead, ad_next) { + if (oldadp->ad_lbn >= lbn) + break; + } + if (oldadp == NULL) + panic("softdep_setup_allocext: lost entry"); + /* insert in middle of list */ + TAILQ_INSERT_BEFORE(oldadp, adp, ad_next); + if (oldadp->ad_lbn == lbn) + allocdirect_merge(adphead, adp, oldadp); + FREE_LOCK(&lk); +} + +/* * Indirect block allocation dependencies. * * The same dependencies that exist for a direct block also exist when @@ -1913,9 +2012,10 @@ setup_allocindir_phase2(bp, ip, aip) * can release it. */ void -softdep_setup_freeblocks(ip, length) +softdep_setup_freeblocks(ip, length, flags) struct inode *ip; /* The inode whose length is to be reduced */ off_t length; /* The new length for the file */ + int flags; /* IO_EXT and/or IO_NORMAL */ { struct freeblks *freeblks; struct inodedep *inodedep; @@ -1923,6 +2023,7 @@ softdep_setup_freeblocks(ip, length) struct vnode *vp; struct buf *bp; struct fs *fs; + daddr64_t extblocks, datablocks; int i, delay, error; fs = ip->i_fs; @@ -1936,22 +2037,39 @@ softdep_setup_freeblocks(ip, length) freeblks->fb_previousinum = ip->i_number; freeblks->fb_devvp = ip->i_devvp; freeblks->fb_mnt = ITOV(ip)->v_mount; - freeblks->fb_oldsize = DIP(ip, size); - freeblks->fb_newsize = length; - freeblks->fb_chkcnt = DIP(ip, blocks); - - for (i = 0; i < NDADDR; i++) { - freeblks->fb_dblks[i] = DIP(ip, db[i]); - DIP_ASSIGN(ip, db[i], 0); + extblocks = 0; + if (fs->fs_magic == FS_UFS2_MAGIC) + extblocks = btodb(fragroundup(fs, ip->i_ffs2_extsize)); + datablocks = DIP(ip, blocks) - extblocks; + if ((flags & IO_NORMAL) == 0) { + freeblks->fb_oldsize = 0; + freeblks->fb_chkcnt = 0; + } else { + freeblks->fb_oldsize = DIP(ip, size); + DIP_ASSIGN(ip, size, 0); + freeblks->fb_chkcnt = datablocks; + for (i = 0; i < NDADDR; i++) { + freeblks->fb_dblks[i] = DIP(ip, db[i]); + DIP_ASSIGN(ip, db[i], 0); + } + for (i = 0; i < NIADDR; i++) { + freeblks->fb_iblks[i] = DIP(ip, ib[i]); + DIP_ASSIGN(ip, ib[i], 0); + } } - - for (i = 0; i < NIADDR; i++) { - freeblks->fb_iblks[i] = DIP(ip, ib[i]); - DIP_ASSIGN(ip, ib[i], 0); + if ((flags & IO_EXT) == 0) { + freeblks->fb_oldextsize = 0; + } else { + freeblks->fb_oldextsize = ip->i_ffs2_extsize; + ip->i_ffs2_extsize = 0; + freeblks->fb_chkcnt += extblocks; + for (i = 0; i < NXADDR; i++) { + freeblks->fb_eblks[i] = ip->i_ffs2_extb[i]; + ip->i_ffs2_extb[i] = 0; + } } - DIP_ASSIGN(ip, blocks, 0); - DIP_ASSIGN(ip, size, 0); + DIP_ASSIGN(ip, blocks, DIP(ip, blocks) - freeblks->fb_chkcnt); /* * Push the zero'ed inode to to its disk buffer so that we are free @@ -1998,9 +2116,18 @@ softdep_setup_freeblocks(ip, length) * If we still have a bitmap dependency, then the inode has never * been written to disk, so we can free any fragments without delay. */ - merge_inode_lists(inodedep); - while ((adp = TAILQ_FIRST(&inodedep->id_inoupdt)) != 0) - free_allocdirect(&inodedep->id_inoupdt, adp, delay); + if (flags & IO_NORMAL) { + merge_inode_lists(&inodedep->id_newinoupdt, + &inodedep->id_inoupdt); + while ((adp = TAILQ_FIRST(&inodedep->id_inoupdt)) != 0) + free_allocdirect(&inodedep->id_inoupdt, adp, delay); + } + if (flags & IO_EXT) { + merge_inode_lists(&inodedep->id_newextupdt, + &inodedep->id_extupdt); + while ((adp = TAILQ_FIRST(&inodedep->id_extupdt)) != 0) + free_allocdirect(&inodedep->id_extupdt, adp, delay); + } FREE_LOCK(&lk); bdwrite(bp); /* @@ -2334,6 +2461,8 @@ check_inode_unwritten(inodedep) LIST_FIRST(&inodedep->id_inowait) != NULL || TAILQ_FIRST(&inodedep->id_inoupdt) != NULL || TAILQ_FIRST(&inodedep->id_newinoupdt) != NULL || + TAILQ_FIRST(&inodedep->id_extupdt) != NULL || + TAILQ_FIRST(&inodedep->id_newextupdt) != NULL || inodedep->id_nlinkdelta != 0) return (0); inodedep->id_state |= ALLCOMPLETE; @@ -2367,6 +2496,8 @@ free_inodedep(inodedep) LIST_FIRST(&inodedep->id_inowait) != NULL || TAILQ_FIRST(&inodedep->id_inoupdt) != NULL || TAILQ_FIRST(&inodedep->id_newinoupdt) != NULL || + TAILQ_FIRST(&inodedep->id_extupdt) != NULL || + TAILQ_FIRST(&inodedep->id_newextupdt) != NULL || inodedep->id_nlinkdelta != 0 || inodedep->id_savedino1 != NULL) return (0); LIST_REMOVE(inodedep, id_hash); @@ -2420,6 +2551,18 @@ handle_workitem_freeblocks(freeblks) nblocks = btodb(fs->fs_bsize); blocksreleased = 0; /* + * Release all extended attribute blocks or frags. + */ + if (freeblks->fb_oldextsize > 0) { + for (i = (NXADDR - 1); i >= 0; i--) { + if ((bn = freeblks->fb_eblks[i]) == 0) + continue; + bsize = sblksize(fs, freeblks->fb_oldextsize, i); + ffs_blkfree(&tip, bn, bsize); + blocksreleased += btodb(bsize); + } + } + /* * Indirect blocks first. */ for (level = (NIADDR - 1); level >= 0; level--) { @@ -3475,6 +3618,7 @@ initiate_write_inodeblock_ufs1(inodedep, * If no dependencies, then there is nothing to roll back. */ inodedep->id_savedsize = dp->di_size; + inodedep->id_savedextsize = 0; if (TAILQ_FIRST(&inodedep->id_inoupdt) == NULL) return; /* @@ -3616,14 +3760,11 @@ initiate_write_inodeblock_ufs2(inodedep, * If no dependencies, then there is nothing to roll back. */ inodedep->id_savedsize = dp->di_size; - if (TAILQ_FIRST(&inodedep->id_inoupdt) == NULL) - return; - -#ifdef notyet inodedep->id_savedextsize = dp->di_extsize; if (TAILQ_FIRST(&inodedep->id_inoupdt) == NULL && TAILQ_FIRST(&inodedep->id_extupdt) == NULL) return; + /* * Set the ext data dependencies to busy. */ @@ -3689,7 +3830,6 @@ initiate_write_inodeblock_ufs2(inodedep, break; dp->di_extsize = (i + 1) * fs->fs_bsize; } -#endif /* notyet */ /* * Set the file data dependencies to busy. @@ -3934,6 +4074,7 @@ STATIC void handle_allocdirect_partdone(adp) struct allocdirect *adp; /* the completed allocdirect */ { + struct allocdirectlst *listhead; struct allocdirect *listadp; struct inodedep *inodedep; long bsize, delay; @@ -3952,11 +4093,16 @@ handle_allocdirect_partdone(adp) * which would corrupt the filesystem. Thus, we cannot free any * allocdirects after one whose ad_oldblkno claims a fragment as * these blocks must be rolled back to zero before writing the inode. - * We check the currently active set of allocdirects in id_inoupdt. + * We check the currently active set of allocdirects in id_inoupdt or + * id_extupdt as appropriate. */ inodedep = adp->ad_inodedep; bsize = inodedep->id_fs->fs_bsize; - TAILQ_FOREACH(listadp, &inodedep->id_inoupdt, ad_next) { + if (adp->ad_state & EXTDATA) + listhead = &inodedep->id_extupdt; + else + listhead = &inodedep->id_inoupdt; + TAILQ_FOREACH(listadp, listhead, ad_next) { /* found our block */ if (listadp == adp) break; @@ -3996,7 +4142,7 @@ handle_allocdirect_partdone(adp) listadp = TAILQ_NEXT(adp, ad_next); if ((adp->ad_state & ALLCOMPLETE) != ALLCOMPLETE) return; - free_allocdirect(&inodedep->id_inoupdt, adp, delay); + free_allocdirect(listhead, adp, delay); } } @@ -4140,12 +4286,27 @@ handle_written_inodeblock(inodedep, bp) adp->ad_state |= ATTACHED; hadchanges = 1; } + for (adp = TAILQ_FIRST(&inodedep->id_extupdt); adp; adp = nextadp) { + nextadp = TAILQ_NEXT(adp, ad_next); + if (adp->ad_state & ATTACHED) + panic("handle_written_inodeblock: new entry"); + if (dp2->di_extb[adp->ad_lbn] != adp->ad_oldblkno) + panic("%s: direct pointers #%jd %s %jd != %jd", + "handle_written_inodeblock", + (intmax_t)adp->ad_lbn, "mismatch", + (intmax_t)dp2->di_extb[adp->ad_lbn], + (intmax_t)adp->ad_oldblkno); + dp2->di_extb[adp->ad_lbn] = adp->ad_newblkno; + adp->ad_state &= ~UNDONE; + adp->ad_state |= ATTACHED; + hadchanges = 1; + } if (hadchanges && (bp->b_flags & B_DELWRI) == 0) stat_direct_blk_ptrs++; /* * Reset the file size to its most up-to-date value. */ - if (inodedep->id_savedsize == -1) + if (inodedep->id_savedsize == -1 || inodedep->id_savedextsize == -1) panic("handle_written_inodeblock: bad size"); if (fstype == UM_UFS1) { @@ -4158,8 +4319,13 @@ handle_written_inodeblock(inodedep, bp) dp2->di_size = inodedep->id_savedsize; hadchanges = 1; } + if (dp2->di_extsize != inodedep->id_savedextsize) { + dp2->di_extsize = inodedep->id_savedextsize; + hadchanges = 1; + } } inodedep->id_savedsize = -1; + inodedep->id_savedextsize = -1; /* * If there were any rollbacks in the inode block, then it must be * marked dirty so that its will eventually get written back in @@ -4172,6 +4338,8 @@ handle_written_inodeblock(inodedep, bp) */ if ((adp = TAILQ_FIRST(&inodedep->id_inoupdt)) != NULL) handle_allocdirect_partdone(adp); + if ((adp = TAILQ_FIRST(&inodedep->id_extupdt)) != NULL) + handle_allocdirect_partdone(adp); /* * Process deallocations that were held pending until the * inode had been written to disk. Freeing of the inode @@ -4234,7 +4402,9 @@ handle_written_inodeblock(inodedep, bp) /* * If no outstanding dependencies, free it. */ - if (free_inodedep(inodedep) || TAILQ_FIRST(&inodedep->id_inoupdt) == 0) + if (free_inodedep(inodedep) || + (TAILQ_FIRST(&inodedep->id_inoupdt) == 0 && + TAILQ_FIRST(&inodedep->id_extupdt) == 0)) return (0); return (hadchanges); } @@ -4471,9 +4641,12 @@ softdep_update_inodeblock(ip, bp, waitfo * the in-memory copy of the inode. Once merged process any * allocdirects that are completed by the merger. */ - merge_inode_lists(inodedep); + merge_inode_lists(&inodedep->id_newinoupdt, &inodedep->id_inoupdt); if (TAILQ_FIRST(&inodedep->id_inoupdt) != NULL) handle_allocdirect_partdone(TAILQ_FIRST(&inodedep->id_inoupdt)); + merge_inode_lists(&inodedep->id_newextupdt, &inodedep->id_extupdt); + if (TAILQ_FIRST(&inodedep->id_extupdt) != NULL) + handle_allocdirect_partdone(TAILQ_FIRST(&inodedep->id_extupdt)); /* * Now that the inode has been pushed into the buffer, the * operations dependent on the inode being written to disk @@ -4505,36 +4678,35 @@ softdep_update_inodeblock(ip, bp, waitfo } /* - * Merge the new inode dependency list (id_newinoupdt) into the old - * inode dependency list (id_inoupdt). This routine must be called - * with splbio interrupts blocked. + * Merge the a new inode dependency list (such as id_newinoupdt) into an + * old inode dependency list (such as id_inoupdt). This routine must be + * called with splbio interrupts blocked. */ STATIC void -merge_inode_lists(inodedep) - struct inodedep *inodedep; +merge_inode_lists(newlisthead, oldlisthead) + struct allocdirectlst *newlisthead; + struct allocdirectlst *oldlisthead; { struct allocdirect *listadp, *newadp; - splassert(IPL_BIO); - - newadp = TAILQ_FIRST(&inodedep->id_newinoupdt); - for (listadp = TAILQ_FIRST(&inodedep->id_inoupdt); listadp && newadp;) { + newadp = TAILQ_FIRST(newlisthead); + for (listadp = TAILQ_FIRST(oldlisthead); listadp && newadp;) { if (listadp->ad_lbn < newadp->ad_lbn) { listadp = TAILQ_NEXT(listadp, ad_next); continue; } - TAILQ_REMOVE(&inodedep->id_newinoupdt, newadp, ad_next); + TAILQ_REMOVE(newlisthead, newadp, ad_next); TAILQ_INSERT_BEFORE(listadp, newadp, ad_next); if (listadp->ad_lbn == newadp->ad_lbn) { - allocdirect_merge(&inodedep->id_inoupdt, newadp, + allocdirect_merge(oldlisthead, newadp, listadp); listadp = newadp; } - newadp = TAILQ_FIRST(&inodedep->id_newinoupdt); + newadp = TAILQ_FIRST(newlisthead); } - while ((newadp = TAILQ_FIRST(&inodedep->id_newinoupdt)) != NULL) { - TAILQ_REMOVE(&inodedep->id_newinoupdt, newadp, ad_next); - TAILQ_INSERT_TAIL(&inodedep->id_inoupdt, newadp, ad_next); + while ((newadp = TAILQ_FIRST(newlisthead)) != NULL) { + TAILQ_REMOVE(newlisthead, newadp, ad_next); + TAILQ_INSERT_TAIL(oldlisthead, newadp, ad_next); } } @@ -4571,7 +4743,9 @@ softdep_fsync(vp) if (LIST_FIRST(&inodedep->id_inowait) != NULL || LIST_FIRST(&inodedep->id_bufwait) != NULL || TAILQ_FIRST(&inodedep->id_inoupdt) != NULL || - TAILQ_FIRST(&inodedep->id_newinoupdt) != NULL) { + TAILQ_FIRST(&inodedep->id_newinoupdt) != NULL || + TAILQ_FIRST(&inodedep->id_extupdt) != NULL || + TAILQ_FIRST(&inodedep->id_newextupdt) != NULL) { FREE_LOCK(&lk); panic("softdep_fsync: pending ops"); } @@ -4718,8 +4892,7 @@ softdep_fsync_mountdev(vp, waitfor) */ nbp = LIST_FIRST(&vp->v_dirtyblkhd); } - if (waitfor == MNT_WAIT) - drain_output(vp, 1); + drain_output(vp, 1); FREE_LOCK(&lk); } @@ -5061,6 +5234,48 @@ flush_inodedep_deps(fs, ino) } if (adp != NULL) continue; + TAILQ_FOREACH(adp, &inodedep->id_extupdt, ad_next) { + if (adp->ad_state & DEPCOMPLETE) + continue; + bp = adp->ad_buf; + if (getdirtybuf(bp, waitfor) == 0) { + if (waitfor == MNT_NOWAIT) + continue; + break; + } + FREE_LOCK(&lk); + if (waitfor == MNT_NOWAIT) { + bawrite(bp); + } else if ((error = VOP_BWRITE(bp)) != 0) { + ACQUIRE_LOCK(&lk); + return (error); + } + ACQUIRE_LOCK(&lk); + break; + } + if (adp != NULL) + continue; + TAILQ_FOREACH(adp, &inodedep->id_newextupdt, ad_next) { + if (adp->ad_state & DEPCOMPLETE) + continue; + bp = adp->ad_buf; + if (getdirtybuf(bp, waitfor) == 0) { + if (waitfor == MNT_NOWAIT) + continue; + break; + } + FREE_LOCK(&lk); + if (waitfor == MNT_NOWAIT) { + bawrite(bp); + } else if ((error = VOP_BWRITE(bp)) != 0) { + ACQUIRE_LOCK(&lk); + return (error); + } + ACQUIRE_LOCK(&lk); + break; + } + if (adp != NULL) + continue; /* * If pass2, we are done, otherwise do pass 2. */ @@ -5539,6 +5754,12 @@ softdep_count_dependencies(bp, wantcount if (!wantcount) goto out; } + if (TAILQ_FIRST(&inodedep->id_extupdt)) { + /* direct block pointer dependency */ + retval += 1; + if (!wantcount) + goto out; + } continue; case D_INDIRDEP: @@ -5767,10 +5988,9 @@ worklist_print(struct worklist *wk, int break; case D_FREEBLKS: freeblks = WK_FREEBLKS(wk); - (*pr)("previno %u devvp %p mp %p oldsz %lld newsz %lld\n" + (*pr)("previno %u devvp %p mp %p oldsz %lld\n" "%s chkcnt %d uid %d\n", freeblks->fb_previousinum, freeblks->fb_devvp, freeblks->fb_mnt, freeblks->fb_oldsize, - freeblks->fb_newsize, prefix, freeblks->fb_chkcnt, freeblks->fb_uid); break; case D_FREEFILE: Index: sys/ufs/ffs/ffs_softdep_stub.c =================================================================== RCS file: /data/cvs/openbsd/src/sys/ufs/ffs/ffs_softdep_stub.c,v retrieving revision 1.15 diff -u -p -r1.15 ffs_softdep_stub.c --- sys/ufs/ffs/ffs_softdep_stub.c 1 Jun 2007 20:23:26 -0000 1.15 +++ sys/ufs/ffs/ffs_softdep_stub.c 20 Jul 2007 13:55:15 -0000 @@ -101,7 +101,7 @@ softdep_setup_allocindir_meta(struct buf } void -softdep_setup_freeblocks(struct inode *ip, off_t length) +softdep_setup_freeblocks(struct inode *ip, off_t length, int flags) { panic("softdep_setup_freeblocks called"); } Index: sys/ufs/ffs/ffs_vnops.c =================================================================== RCS file: /data/cvs/openbsd/src/sys/ufs/ffs/ffs_vnops.c,v retrieving revision 1.45 diff -u -p -r1.45 ffs_vnops.c --- sys/ufs/ffs/ffs_vnops.c 1 Jun 2007 23:47:57 -0000 1.45 +++ sys/ufs/ffs/ffs_vnops.c 20 Jul 2007 13:55:15 -0000 @@ -102,6 +102,12 @@ struct vnodeopv_entry_desc ffs_vnodeop_e { &vop_advlock_desc, ufs_advlock }, /* advlock */ { &vop_reallocblks_desc, ffs_reallocblks }, /* reallocblks */ { &vop_bwrite_desc, vop_generic_bwrite }, +#ifdef FFS2_EXTATTR + { &vop_deleteextattr_desc, ffs_ea_del}, /* deleteextattr */ + { &vop_getextattr_desc, ffs_ea_get }, /* getextattr */ + { &vop_listextattr_desc, ffs_ea_list }, /* listextattr */ + { &vop_setextattr_desc, ffs_ea_set }, /* setextattr */ +#endif { NULL, NULL } }; @@ -382,7 +388,7 @@ ffs_write(void *v) if (error) { if (ioflag & IO_UNIT) { (void)UFS_TRUNCATE(ip, osize, - ioflag & IO_SYNC, ap->a_cred); + IO_NORMAL | (ioflag & IO_SYNC), ap->a_cred); uio->uio_offset -= resid - uio->uio_resid; uio->uio_resid = resid; } Index: sys/ufs/ffs/softdep.h =================================================================== RCS file: /data/cvs/openbsd/src/sys/ufs/ffs/softdep.h,v retrieving revision 1.14 diff -u -p -r1.14 softdep.h --- sys/ufs/ffs/softdep.h 1 Jun 2007 20:23:26 -0000 1.14 +++ sys/ufs/ffs/softdep.h 20 Jul 2007 13:55:15 -0000 @@ -89,8 +89,9 @@ * space count. The NEWBLOCK flag marks pagedep structures that have * just been allocated, so must be claimed by the inode before all * dependencies are complete. The ONWORKLIST flag shows whether the - * structure is currently linked onto a worklist. - * + * structure is currently linked onto a worklist. The EXTDATA flag + * indicates that the allocdirect describes an extended-attributes + * dependency. */ #define ATTACHED 0x0001 #define UNDONE 0x0002 @@ -105,6 +106,7 @@ #define SPACECOUNTED 0x0400 /* inodedep only */ #define NEWBLOCK 0x0800 /* pagedep only */ #define UFS1FMT 0x2000 /* indirdep only */ +#define EXTDATA 0x4000 /* allocdirect only */ #define ONWORKLIST 0x8000 #define ALLCOMPLETE (ATTACHED | COMPLETE | DEPCOMPLETE) @@ -258,12 +260,15 @@ struct inodedep { } id_un; LIST_ENTRY(inodedep) id_deps; /* bmsafemap's list of inodedep's */ struct buf *id_buf; /* related bmsafemap (if pending) */ + long id_savedextsize; /* ext size saved during rollback */ off_t id_savedsize; /* file size saved during rollback */ struct workhead id_pendinghd; /* entries awaiting directory write */ struct workhead id_bufwait; /* operations after inode written */ struct workhead id_inowait; /* operations waiting inode update */ struct allocdirectlst id_inoupdt; /* updates before inode written */ struct allocdirectlst id_newinoupdt; /* updates when inode written */ + struct allocdirectlst id_extupdt; /* extdata updates pre-inode write */ + struct allocdirectlst id_newextupdt; /* extdata updates at ino write */ }; #define id_savedino1 id_un.idu_savedino1 @@ -432,12 +437,13 @@ struct freeblks { ino_t fb_previousinum; /* inode of previous owner of blocks */ struct vnode *fb_devvp; /* filesystem device vnode */ struct mount *fb_mnt; /* associated mount point */ + long fb_oldextsize; /* previous ext data size */ off_t fb_oldsize; /* previous file size */ - off_t fb_newsize; /* new file size */ int fb_chkcnt; /* used to check cnt of blks released */ uid_t fb_uid; /* uid of previous owner of blocks */ daddr_t fb_dblks[NDADDR]; /* direct blk ptrs to deallocate */ daddr_t fb_iblks[NIADDR]; /* indirect blk ptrs to deallocate */ + daddr_t fb_eblks[NXADDR]; /* ext data blk ptrs to deallocate */ }; /* Index: sys/ufs/ufs/inode.h =================================================================== RCS file: /data/cvs/openbsd/src/sys/ufs/ufs/inode.h,v retrieving revision 1.35 diff -u -p -r1.35 inode.h --- sys/ufs/ufs/inode.h 2 Jun 2007 00:45:50 -0000 1.35 +++ sys/ufs/ufs/inode.h 20 Jul 2007 13:55:15 -0000 @@ -103,6 +103,12 @@ struct inode { struct dirhash *dirhash; } inode_ext; + /* + * Data for extended attribute modification. + */ + u_char *i_ea_area; /* Pointer to malloced copy of EA area */ + unsigned i_ea_len; /* Length of i_ea_area */ + #define i_e2fs_last_lblk inode_ext.e2fs.ext2fs_last_lblk #define i_e2fs_last_blk inode_ext.e2fs.ext2fs_last_blk #define i_e2fs_uid inode_ext.e2fs.ext2fs_effective_uid @@ -198,6 +204,8 @@ struct inode_vtbl { #define i_ffs2_rdev i_din2->di_rdev #define i_ffs2_size i_din2->di_size #define i_ffs2_uid i_din2->di_uid +#define i_ffs2_extsize i_din2->di_extsize +#define i_ffs2_extb i_din2->di_extb #ifndef _KERNEL /* Index: sys/ufs/ufs/ufs_bmap.c =================================================================== RCS file: /data/cvs/openbsd/src/sys/ufs/ufs/ufs_bmap.c,v retrieving revision 1.25 diff -u -p -r1.25 ufs_bmap.c --- sys/ufs/ufs/ufs_bmap.c 1 Jun 2007 23:47:57 -0000 1.25 +++ sys/ufs/ufs/ufs_bmap.c 20 Jul 2007 13:55:15 -0000 @@ -71,7 +71,7 @@ ufs_bmap(void *v) if (ap->a_bnp == NULL) return (0); - return (ufs_bmaparray(ap->a_vp, ap->a_bn, ap->a_bnp, NULL, NULL, + return (ufs_bmaparray(ap->a_vp, ap->a_bn, ap->a_bnp, NULL, NULL, NULL, ap->a_runp)); } @@ -90,7 +90,7 @@ ufs_bmap(void *v) */ int ufs_bmaparray(struct vnode *vp, daddr_t bn, daddr64_t *bnp, struct indir *ap, - int *nump, int *runp) + struct buf *nbp, int *nump, int *runp) { struct inode *ip; struct buf *bp; @@ -129,6 +129,17 @@ ufs_bmaparray(struct vnode *vp, daddr_t num = *nump; if (num == 0) { +#ifdef FFS2 + if (bn < 0 && bn >= -NXADDR) { + *bnp = blkptrtodb(ump, ip->i_ffs2_extb[-1 - bn]); + if (*bnp == 0) + *bnp = -1; + if (nbp == NULL) + panic("ufs_bmaparray: mapping ext data"); + nbp->b_flags |= B_EXTATTR; + return (0); + } +#endif *bnp = blkptrtodb(ump, DIP(ip, db[bn])); if (*bnp == 0) *bnp = -1; @@ -240,13 +251,6 @@ ufs_getlbns(struct vnode *vp, daddr_t bn if ((long)bn < 0) bn = -(long)bn; -#ifdef DIAGNOSTIC - if (realbn < 0 && realbn > -NDADDR) { - panic ("ufs_getlbns: Invalid indirect block %d specified", - realbn); - } -#endif - /* The first NDADDR blocks are direct blocks. */ if (bn < NDADDR) return (0); @@ -297,11 +301,6 @@ ufs_getlbns(struct vnode *vp, daddr_t bn metalbn -= -1 + off * blockcnt; } -#ifdef DIAGNOSTIC - if (realbn < 0 && metalbn != realbn) { - panic("ufs_getlbns: indirect block %d not found", realbn); - } -#endif if (nump) *nump = numlevels; return (0); Index: sys/ufs/ufs/ufs_extern.h =================================================================== RCS file: /data/cvs/openbsd/src/sys/ufs/ufs/ufs_extern.h,v retrieving revision 1.28 diff -u -p -r1.28 ufs_extern.h --- sys/ufs/ufs/ufs_extern.h 9 May 2007 17:04:22 -0000 1.28 +++ sys/ufs/ufs/ufs_extern.h 20 Jul 2007 13:55:15 -0000 @@ -96,7 +96,7 @@ int ufsfifo_close(void *); /* ufs_bmap.c */ int ufs_bmaparray(struct vnode *, daddr_t, daddr64_t *, struct indir *, - int *, int *); + struct buf *, int *, int *); int ufs_getlbns(struct vnode *, daddr_t, struct indir *, int *); /* ufs_ihash.c */ Index: sys/ufs/ufs/ufs_inode.c =================================================================== RCS file: /data/cvs/openbsd/src/sys/ufs/ufs/ufs_inode.c,v retrieving revision 1.37 diff -u -p -r1.37 ufs_inode.c --- sys/ufs/ufs/ufs_inode.c 1 Jun 2007 23:47:57 -0000 1.37 +++ sys/ufs/ufs/ufs_inode.c 20 Jul 2007 13:55:15 -0000 @@ -84,7 +84,7 @@ ufs_inactive(void *v) if (getinoquota(ip) == 0) (void)ufs_quota_free_inode(ip, NOCRED); - error = UFS_TRUNCATE(ip, (off_t)0, 0, NOCRED); + error = UFS_TRUNCATE(ip, (off_t)0, IO_EXT | IO_NORMAL, NOCRED); DIP_ASSIGN(ip, rdev, 0); mode = DIP(ip, mode); Index: sys/ufs/ufs/ufs_lookup.c =================================================================== RCS file: /data/cvs/openbsd/src/sys/ufs/ufs/ufs_lookup.c,v retrieving revision 1.37 diff -u -p -r1.37 ufs_lookup.c --- sys/ufs/ufs/ufs_lookup.c 1 Jun 2007 23:47:57 -0000 1.37 +++ sys/ufs/ufs/ufs_lookup.c 20 Jul 2007 13:55:15 -0000 @@ -926,7 +926,8 @@ ufs_direnter(struct vnode *dvp, struct v #endif - error = UFS_TRUNCATE(dp, (off_t)dp->i_endoff, IO_SYNC, cr); + error = UFS_TRUNCATE(dp, (off_t)dp->i_endoff, + IO_NORMAL | IO_SYNC, cr); if (tvp != NULL) vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY, p); Index: sys/ufs/ufs/ufs_vnops.c =================================================================== RCS file: /data/cvs/openbsd/src/sys/ufs/ufs/ufs_vnops.c,v retrieving revision 1.82 diff -u -p -r1.82 ufs_vnops.c --- sys/ufs/ufs/ufs_vnops.c 20 Jun 2007 15:03:40 -0000 1.82 +++ sys/ufs/ufs/ufs_vnops.c 20 Jul 2007 13:55:16 -0000 @@ -377,8 +377,9 @@ ufs_setattr(void *v) default: break; } - if ((error = UFS_TRUNCATE(ip, vap->va_size, 0, cred)) != 0) - return (error); + error = UFS_TRUNCATE(ip, vap->va_size, IO_NORMAL, cred); + if (error) + return (error); if (vap->va_size < oldsize) hint |= NOTE_TRUNCATE; } @@ -988,8 +989,8 @@ abortit: DIP_ADD(xp, nlink, -1); xp->i_flag |= IN_CHANGE; - if ((error = UFS_TRUNCATE(VTOI(tvp), (off_t)0, IO_SYNC, - tcnp->cn_cred)) != 0) + if ((error = UFS_TRUNCATE(VTOI(tvp), (off_t)0, + IO_NORMAL | IO_SYNC, tcnp->cn_cred)) != 0) goto bad; } VN_KNOTE(tdvp, NOTE_WRITE); @@ -1316,7 +1317,8 @@ ufs_rmdir(void *v) dp->i_flag |= IN_CHANGE; DIP_ADD(ip, nlink, -1); ip->i_flag |= IN_CHANGE; - ioflag = DOINGASYNC(vp) ? 0 : IO_SYNC; + ioflag = IO_NORMAL; + ioflag |= DOINGASYNC(vp) ? 0 : IO_SYNC; error = UFS_TRUNCATE(ip, (off_t)0, ioflag, cnp->cn_cred); } cache_purge(vp); @@ -1553,8 +1555,8 @@ ufs_strategy(void *v) if (vp->v_type == VBLK || vp->v_type == VCHR) panic("ufs_strategy: spec"); if (bp->b_blkno == bp->b_lblkno) { - error = VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno, - NULL); + error = ufs_bmaparray(vp, bp->b_lblkno, &bp->b_blkno, NULL, bp, + NULL, NULL); if (error) { bp->b_error = error; bp->b_flags |= B_ERROR; Index: usr.sbin/Makefile =================================================================== RCS file: /data/cvs/openbsd/src/usr.sbin/Makefile,v retrieving revision 1.135 diff -u -p -r1.135 Makefile --- usr.sbin/Makefile 26 May 2007 21:39:45 -0000 1.135 +++ usr.sbin/Makefile 20 Jul 2007 13:55:18 -0000 @@ -4,7 +4,7 @@ SUBDIR= ac accton acpidump adduser amd apm apmd arp \ authpf bgpctl bgpd bind chroot config cron dev_mkdb \ - dhcpd dhcrelay dvmrpctl dvmrpd edquota eeprom faithd fdformat \ + dhcpd dhcrelay dvmrpctl dvmrpd edquota eeprom extattr faithd fdformat \ ftp-proxy gpioctl hostapd hoststatectl hoststated hotplugd \ httpd ifstated inetd iostat kgmon kvm_mkdb lpr mailwrapper \ map-mbone memconfig mopd mrinfo mrouted mtrace mtree ndp \ --- /dev/null Fri Jul 20 15:58:30 2007 +++ usr.sbin/extattr/Makefile Mon Jul 9 19:45:33 2007 @@ -0,0 +1,14 @@ +# $FreeBSD: src/usr.sbin/extattr/Makefile,v 1.1 2002/08/30 08:53:03 phk Exp $ + +PROG= rmextattr +MAN= rmextattr.8 + +LINKS+= ${BINDIR}/rmextattr ${BINDIR}/getextattr +LINKS+= ${BINDIR}/rmextattr ${BINDIR}/setextattr +LINKS+= ${BINDIR}/rmextattr ${BINDIR}/lsextattr + +MLINKS+= rmextattr.8 setextattr.8 +MLINKS+= rmextattr.8 getextattr.8 +MLINKS+= rmextattr.8 lsextattr.8 + +.include --- /dev/null Fri Jul 20 15:58:36 2007 +++ usr.sbin/extattr/rmextattr.8 Mon Jul 9 19:45:33 2007 @@ -0,0 +1,135 @@ +.\"- +.\" Copyright (c) 2000, 2001 Robert N. M. Watson +.\" Copyright (c) 2002 Networks Associates Technology, Inc. +.\" All rights reserved. +.\" +.\" This software was developed for the FreeBSD Project by Poul-Henning +.\" Kamp and Network Associates Laboratories, 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. +.\" +.\" 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: src/usr.sbin/extattr/rmextattr.8,v 1.4 2003/02/24 22:53:25 ru Exp $ +.\" +.Dd August 30, 2000 +.Dt RMEXTATTR 8 +.Os +.Sh NAME +.Nm getextattr , +.Nm lsextattr , +.Nm rmextattr , +.Nm setextattr +.Nd manipulate extended attributes +.Sh SYNOPSIS +.Nm getextattr +.Op Fl fhqsx +.Ar attrnamespace +.Ar attrname +.Ar filename ... +.Nm lsextattr +.Op Fl fhq +.Ar attrnamespace +.Ar filename ... +.Nm rmextattr +.Op Fl fhq +.Ar attrnamespace +.Ar attrname +.Ar filename ... +.Nm setextattr +.Op Fl fhnq +.Ar attrnamespace +.Ar attrname +.Ar attrvalue +.Ar filename ... +.Sh DESCRIPTION +These +utilities +are user tools to manipulate the named extended attributes on files and +directories. +The +.Ar attrnamespace +argument should be the namespace of the attribute to retrieve: legal +values are +.Cm user +and +.Cm system . +The +.Ar attrname +argument should be the name of the attribute, +.Ar filename +the name of the target file or directory, +.Ar attrvalue +a string to store in the attribute. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl f +(Force.) +Ignore errors on individual filenames and continue with +the remaining arguments. +.It Fl h +(No follow.) +If the file is a symbolic link, perform the operation on the +link itself rather than the file that the link points to. +.It Fl n +.Dv ( NUL Ns +-terminate.) +.Dv NUL Ns +-terminate the extent content written out. +.It Fl q +(Quiet.) +Do not print out the pathname and suppress error messages. +.It Fl s +(Stringify.) +Escape nonprinting characters and put quotes around the output. +.It Fl x +(Hex.) +Print the output in hexadecimal. +.El +.Sh EXAMPLES +.Bd -literal +setextattr system md5 `md5 -q /boot/kernel/kernel` /boot/kernel/kernel +getextattr system md5 /boot/kernel/kernel +lsextattr system /boot/kernel/kernel +rmextattr system md5 /boot/kernel/kernel +.Ed +.Sh SEE ALSO +.Xr extattr 2 , +.Xr extattr 3 , +.Xr extattrctl 8 , +.Xr extattr 9 +.Sh HISTORY +Extended attribute support was developed as part of the +.Tn TrustedBSD +Project, +and introduced in +.Fx 5.0 . +It was developed to support security extensions requiring additional labels +to be associated with each file or directory. +.Sh AUTHORS +.An Robert N M Watson +.An Poul-Henning Kamp +.Sh BUGS +The +.Nm setextattr +utility can only be used to set attributes to strings. --- /dev/null Fri Jul 20 15:58:40 2007 +++ usr.sbin/extattr/rmextattr.c Mon Jul 9 19:45:33 2007 @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2002, 2003 Networks Associates Technology, Inc. + * Copyright (c) 2002 Poul-Henning Kamp. + * Copyright (c) 1999, 2000, 2001, 2002 Robert N. M. Watson + * All rights reserved. + * + * This software was developed for the FreeBSD Project by Poul-Henning + * Kamp and Network Associates Laboratories, 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: rmextattr.c,v 1.6 2003/06/05 04:30:00 rwatson Exp $ + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static enum { EADUNNO, EAGET, EASET, EARM, EALS } what = EADUNNO; + +static void __dead +usage(void) +{ + + switch (what) { + case EAGET: + fprintf(stderr, "usage: getextattr [-fhqsx] attrnamespace"); + fprintf(stderr, " attrname filename ...\n"); + exit(-1); + case EASET: + fprintf(stderr, "usage: setextattr [-fhnq] attrnamespace"); + fprintf(stderr, " attrname attrvalue filename ...\n"); + exit(-1); + case EARM: + fprintf(stderr, "usage: rmextattr [-fhq] attrnamespace"); + fprintf(stderr, " attrname filename ...\n"); + exit(-1); + case EALS: + fprintf(stderr, "usage: lsextattr [-fhq] attrnamespace"); + fprintf(stderr, " filename ...\n"); + exit(-1); + case EADUNNO: + default: + fprintf(stderr, "usage: (getextattr|lsextattr|rmextattr"); + fprintf(stderr, "|setextattr)\n"); + exit (-1); + } +} + +static void +mkbuf(char **buf, int *oldlen, int newlen) +{ + + if (*oldlen >= newlen) + return; + if (*buf != NULL) + free(*buf); + *buf = malloc(newlen); + if (*buf == NULL) + err(1, "malloc"); + *oldlen = newlen; + return; +} + +int +main(int argc, char *argv[]) +{ + char *buf, *visbuf, *p; + + const char *options, *attrname; + int buflen, visbuflen, ch, error, i, arg_counter, attrnamespace, + minargc; + + int flag_force = 0; + int flag_nofollow = 0; + int flag_null = 0; + int flag_quiet = 0; + int flag_string = 0; + int flag_hex = 0; + + visbuflen = buflen = 0; + visbuf = buf = NULL; + + p = basename(argv[0]); + if (p == NULL) + p = argv[0]; + if (!strcmp(p, "getextattr")) { + what = EAGET; + options = "fhqsx"; + minargc = 3; + } else if (!strcmp(p, "setextattr")) { + what = EASET; + options = "fhnq"; + minargc = 4; + } else if (!strcmp(p, "rmextattr")) { + what = EARM; + options = "fhq"; + minargc = 3; + } else if (!strcmp(p, "lsextattr")) { + what = EALS; + options = "fhq"; + minargc = 2; + } else { + usage(); + } + + while ((ch = getopt(argc, argv, options)) != -1) { + switch (ch) { + case 'f': + flag_force = 1; + break; + case 'h': + flag_nofollow = 1; + break; + case 'n': + flag_null = 1; + break; + case 'q': + flag_quiet = 1; + break; + case 's': + flag_string = 1; + break; + case 'x': + flag_hex = 1; + break; + case '?': + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + if (argc < minargc) + usage(); + + error = extattr_string_to_namespace(argv[0], &attrnamespace); + if (error) + err(-1, argv[0]); + argc--; argv++; + + if (what != EALS) { + attrname = argv[0]; + argc--; argv++; + } else + attrname = NULL; + + if (what == EASET) { + mkbuf(&buf, &buflen, strlen(argv[0]) + 1); + strlcpy(buf, argv[0], strlen(argv[0]) + 1); + argc--; argv++; + } + + for (arg_counter = 0; arg_counter < argc; arg_counter++) { + switch (what) { + case EARM: + if (flag_nofollow) + error = extattr_delete_link(argv[arg_counter], + attrnamespace, attrname); + else + error = extattr_delete_file(argv[arg_counter], + attrnamespace, attrname); + if (error >= 0) + continue; + break; + case EASET: + if (flag_nofollow) + error = extattr_set_link(argv[arg_counter], + attrnamespace, attrname, buf, + strlen(buf) + flag_null); + else + error = extattr_set_file(argv[arg_counter], + attrnamespace, attrname, buf, + strlen(buf) + flag_null); + if (error >= 0) + continue; + break; + case EALS: + if (flag_nofollow) + error = extattr_list_link(argv[arg_counter], + attrnamespace, NULL, 0); + else + error = extattr_list_file(argv[arg_counter], + attrnamespace, NULL, 0); + if (error < 0) + break; + mkbuf(&buf, &buflen, error); + if (flag_nofollow) + error = extattr_list_link(argv[arg_counter], + attrnamespace, buf, buflen); + else + error = extattr_list_file(argv[arg_counter], + attrnamespace, buf, buflen); + if (error < 0) + break; + if (!flag_quiet) + printf("%s\t", argv[arg_counter]); + for (i = 0; i < error; i += buf[i] + 1) + printf("%s%*.*s", i ? "\t" : "", + buf[i], buf[i], buf + i + 1); + printf("\n"); + continue; + case EAGET: + if (flag_nofollow) + error = extattr_get_link(argv[arg_counter], + attrnamespace, attrname, NULL, 0); + else + error = extattr_get_file(argv[arg_counter], + attrnamespace, attrname, NULL, 0); + if (error < 0) + break; + mkbuf(&buf, &buflen, error); + if (flag_nofollow) + error = extattr_get_link(argv[arg_counter], + attrnamespace, attrname, buf, buflen); + else + error = extattr_get_file(argv[arg_counter], + attrnamespace, attrname, buf, buflen); + if (error < 0) + break; + if (!flag_quiet) + printf("%s\t", argv[arg_counter]); + if (flag_string) { + mkbuf(&visbuf, &visbuflen, error * 4 + 1); + strvisx(visbuf, buf, error, + VIS_SAFE | VIS_WHITE); + printf("\"%s\"\n", visbuf); + continue; + } else if (flag_hex) { + for (i = 0; i < error; i++) + printf("%s%02x", i ? " " : "", + buf[i]); + printf("\n"); + continue; + } else { + fwrite(buf, buflen, 1, stdout); + printf("\n"); + continue; + } + default: + break; + } + if (!flag_quiet) + warn("%s: failed", argv[arg_counter]); + if (flag_force) + continue; + return(1); + } + return (0); +}