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);
+}