summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sbin/mount_unionfs/Makefile2
-rw-r--r--sbin/mount_unionfs/mount_unionfs.8207
-rw-r--r--sbin/mount_unionfs/mount_unionfs.c177
-rw-r--r--sys/fs/unionfs/union.h179
-rw-r--r--sys/fs/unionfs/union_subr.c2006
-rw-r--r--sys/fs/unionfs/union_vfsops.c712
-rw-r--r--sys/fs/unionfs/union_vnops.c3359
7 files changed, 3506 insertions, 3136 deletions
diff --git a/sbin/mount_unionfs/Makefile b/sbin/mount_unionfs/Makefile
index 0296504..4794dea 100644
--- a/sbin/mount_unionfs/Makefile
+++ b/sbin/mount_unionfs/Makefile
@@ -7,7 +7,7 @@ MAN= mount_unionfs.8
MOUNT= ${.CURDIR}/../mount
CFLAGS+=-I${MOUNT}
-WARNS?= 0
+WARNS?= 3
.PATH: ${MOUNT}
diff --git a/sbin/mount_unionfs/mount_unionfs.8 b/sbin/mount_unionfs/mount_unionfs.8
index 20f1451..9da461c 100644
--- a/sbin/mount_unionfs/mount_unionfs.8
+++ b/sbin/mount_unionfs/mount_unionfs.8
@@ -31,7 +31,7 @@
.\" @(#)mount_union.8 8.6 (Berkeley) 3/27/94
.\" $FreeBSD$
.\"
-.Dd March 27, 1994
+.Dd November 30, 2006
.Dt MOUNT_UNIONFS 8
.Os
.Sh NAME
@@ -64,6 +64,17 @@ layer.
The options are as follows:
.Bl -tag -width indent
.It Fl b
+Deprecated. Use
+.Fl o
+.Ar below
+instead.
+.It Fl o
+Options are specified with a
+.Fl o
+flag followed by an option.
+The following options are available:
+.Bl -tag -width indent
+.It Cm below
Invert the default position, so that
.Ar directory
becomes the lower layer and
@@ -72,16 +83,36 @@ becomes the upper layer.
However,
.Ar uniondir
remains the mount point.
-.It Fl o
-Options are specified with a
-.Fl o
-flag followed by a comma separated string of options.
-See the
-.Xr mount 8
-man page for possible options and their meanings.
-.It Fl r
-Hide the lower layer completely in the same way as mounting with
-.Xr mount_nullfs 8 .
+.It Cm copymode=traditional | transparent | masquerade
+Specifies the way to create a file or a directory in the upper layer
+automatically when needed.
+.Ar traditional
+uses the same way as the old unionfs for backward compatibility, and
+.Ar transparent
+duplicates the file and directory mode bits and the ownership in the
+lower layer to the created file in the upper layer.
+For behavior of the
+.Ar masquerade
+mode, see
+.Sx MASQUERADE MODE .
+.It Cm udir=mode
+Specifies directory mode bits in octal for
+.Ar masquerade
+mode.
+.It Cm ufile=mode
+Specifies file mode bits in octal for
+.Ar masquerade
+mode.
+.It Cm gid=gid
+Specifies group for
+.Ar masquerade
+mode.
+.It Cm uid=uid
+.uid
+Specifies user for
+.Ar masquerade
+mode.
+.El
.El
.Pp
To enforce file system security, the user mounting the file system
@@ -91,6 +122,13 @@ In addition, the
.Va vfs.usermount
.Xr sysctl 8
variable must be set to 1 to permit file system mounting by ordinary users.
+However, note that
+.Ar transparent
+and
+.Ar masquerade
+mode require
+.Va vfs.usermount
+be set to 0 because this functionality can only be used by superusers.
.Pp
Filenames are looked up in the upper layer and then in the
lower layer.
@@ -98,10 +136,14 @@ If a directory is found in the lower layer, and there is no entry
in the upper layer, then a
.Em shadow
directory will be created in the upper layer.
-It will be owned by the user who originally did the union mount,
-with mode
-.Dq rwxrwxrwx
-(0777) modified by the umask in effect at that time.
+The ownership and the mode bits are set depending on the
+.Ar copymode
+option. In
+.Ar traditional
+mode, it will be owned by the user who originally did the
+union mount, with mode 0777
+.Dq rwxrwxrwx
+modified by the umask in effect at that time.
.Pp
If a file exists in the upper layer then there is no way to access
a file with the same name in the lower layer.
@@ -142,15 +184,74 @@ option to
.Xr mount 8
which only applies the union operation to the mount point itself,
and then only for lookups.
+.Sh MASQUERADE MODE
+When a file
+.Pq or a directory
+is created in the upper layer, the
+.Ar masquerade
+mode sets it the fixed access mode bits given in
+.Ar ufile Pq for files
+or
+.Ar udir Pq for directories
+option and the owner given in
+.Ar udir
+and
+.Ar gid
+options, instead of ones in the lower layer. Note that in the
+.Ar masquerade
+mode and when owner of the file or directory matches
+one specified in
+.Ar uid
+option, only mode bits for the owner will be modified.
+More specifically, the file mode bits in the upper layer will
+be
+.Pq mode in the lower layer
+OR
+.Pq Po mode given in .Ar ufile
+AND 0700
+.Pc , and the ownership will be the same as one in the lower layer.
+.Pp
+The default values for
+.Ar ufile , udir , uid ,
+and
+.Ar gid
+are as follow:
+.Pp
+.Bl -bullet -compact
+.It
+If both
+.Ar ufile
+and
+.Ar udir
+are not specified, access mode bits in the mount point will be used.
+.It
+If both
+.Ar uid
+and
+.Ar gid
+are not specified, ownership in the mount point will be used.
+.It
+If either
+.Ar udir
+or
+.Ar ufile
+is not specified, the other will be the same as the specified one.
+.It
+If either
+.Ar uid
+or
+.Ar gid
+is not specified, the other will be the same as the specified one.
+.El
.Sh EXAMPLES
The commands
.Bd -literal -offset indent
-mount -t cd9660 -o ro /dev/cd0a /usr/src
-mount -t unionfs /var/obj /usr/src
+mount -t cd9660 -o ro /dev/cd0 /usr/src
+mount -t unionfs -o noatime /var/obj /usr/src
.Ed
.Pp
mount the CD-ROM drive
-.Pa /dev/cd0a
+.Pa /dev/cd0
on
.Pa /usr/src
and then attaches
@@ -158,11 +259,42 @@ and then attaches
on top.
For most purposes the effect of this is to make the
source tree appear writable
-even though it is stored on a CD-ROM.
+even though it is stored on a CD-ROM. The
+.Fl o Ar noatime
+option is useful to avoid unnecessary copying from the lower to the
+upper layer.
+.Pp
+The commands
+.Bd -literal -offset indent
+mount -t cd9660 -o ro /dev/cd0 /usr/src
+chown 2020 /usr/src
+mount -t unionfs -o noatime -o copymode=masquerade -o uid=builder \\
+ -o udir=755 -o ufile=644 /var/obj /usr/src
+.Ed
+.Pp
+also mount the CD-ROM drive
+.Pa /dev/cd0
+on
+.Pa /usr/src
+and then attaches
+.Pa /var/obj
+on top. Furthermore, the owner of all files and directories in /usr/src
+is a regular user with uid
+.Pq 2020
+when seen from the upper layer. Note that for the access mode bits,
+ones in the lower layer
+.Pq on the CD-ROM, in this example
+are still used without change.
+Thus, write privilege to the upper layer can be controlled
+independently from access mode bits and ownership in the lower layer.
+If a user does not have read privilege from the lower layer,
+one cannot still read even when the upper layer is mounted by using
+.Ar masquerade
+mode.
.Pp
The command
.Bd -literal -offset indent
-mount -t unionfs -o -b /sys $HOME/sys
+mount -t unionfs -o noatime -o below /sys $HOME/sys
.Ed
.Pp
attaches the system source tree below the
@@ -186,8 +318,20 @@ The
.Nm
utility first appeared in
.Bx 4.4 .
-It first worked in
-.Fx Ns -(fill this in) .
+.Pp
+The
+.Fl r
+option for hiding the lower layer completely was removed in
+.Fx 7.0
+because this is identical to using
+.Xr mount_nullfs 8 .
+.Sh AUTHORS
+In
+.Fx 7.0 ,
+.An Masanori OZAWA Aq ozawa@ongs.co.jp
+reimplemented handling of locking, whiteout, and file mode bits, and
+.An Hiroki Sato Aq hrs@FreeBSD.org
+wrote about the changes in this manual page.
.Sh BUGS
THIS FILE SYSTEM TYPE IS NOT YET FULLY SUPPORTED (READ: IT DOESN'T WORK)
AND USING IT MAY, IN FACT, DESTROY DATA ON YOUR SYSTEM.
@@ -198,7 +342,7 @@ SLIPPERY WHEN WET.
.Pp
This code also needs an owner in order to be less dangerous - serious
hackers can apply by sending mail to
-.Aq hackers@FreeBSD.org
+.Aq freebsd-fs@FreeBSD.org
and announcing
their intent to take it over.
.Pp
@@ -214,3 +358,20 @@ Running
.Xr find 1
over a union tree has the side-effect of creating
a tree of shadow directories in the upper layer.
+.Pp
+The current implementation does not support copying extended attributes
+for
+.Xr acl 9 ,
+.Xr mac 9 ,
+or so on to the upper layer. Note that this may be a security issue.
+.Pp
+A shadow directory, which is one automatically created in the upper
+layer when it exists in the lower layer and does not exist in the
+upper layer, is always created with the superuser privilege.
+However, a file copied from the lower layer in the same way
+is created by the user who accessed it. Because of this,
+if the user is not the superuser, even in
+.Ar transparent
+mode the access mode bits in the copied file in the upper layer
+will not always be the same as ones in the lower layer.
+This behavior should be fixed.
diff --git a/sbin/mount_unionfs/mount_unionfs.c b/sbin/mount_unionfs/mount_unionfs.c
index 7b619ea..90ebd8a 100644
--- a/sbin/mount_unionfs/mount_unionfs.c
+++ b/sbin/mount_unionfs/mount_unionfs.c
@@ -1,6 +1,9 @@
-/*
+/*-
* Copyright (c) 1992, 1993, 1994
- * The Regents of the University of California. All rights reserved.
+ * The Regents of the University of California.
+ * Copyright (c) 2005, 2006 Masanori Ozawa <ozawa@ongs.co.jp>, ONGS Inc.
+ * Copyright (c) 2006 Daichi Goto <daichi@freebsd.org>
+ * All rights reserved.
*
* This code is derived from software donated to Berkeley by
* Jan-Simon Pendry.
@@ -48,6 +51,7 @@ static const char rcsid[] =
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/uio.h>
+#include <sys/errno.h>
#include <err.h>
#include <stdio.h>
@@ -55,54 +59,115 @@ static const char rcsid[] =
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
+#include <grp.h>
+#include <pwd.h>
#include "mntopts.h"
-static struct mntopt mopts[] = {
- MOPT_STDOPTS,
- MOPT_END
-};
+static int
+subdir(const char *p, const char *dir)
+{
+ int l;
-static int subdir(const char *, const char *);
-static void usage (void) __dead2;
+ l = strlen(dir);
+ if (l <= 1)
+ return (1);
-int
-main(argc, argv)
- int argc;
- char *argv[];
+ if ((strncmp(p, dir, l) == 0) && (p[l] == '/' || p[l] == '\0'))
+ return (1);
+
+ return (0);
+}
+
+static void
+usage(void)
{
- struct iovec iov[8];
- int ch, mntflags;
- char source[MAXPATHLEN];
- char target[MAXPATHLEN];
- int iovcnt;
+ (void)fprintf(stderr,
+ "usage: mount_unionfs [-o options] directory uniondir\n");
+ exit(EX_USAGE);
+}
+
+static void
+parse_gid(const char *s, char *buf, size_t bufsize)
+{
+ struct group *gr;
+ char *inval;
+
+ if ((gr = getgrnam(s)) != NULL)
+ snprintf(buf, bufsize, "%d", gr->gr_gid);
+ else {
+ strtol(s, &inval, 10);
+ if (*inval != 0) {
+ errx(EX_NOUSER, "unknown group id: %s", s);
+ usage();
+ } else {
+ strncpy(buf, s, bufsize);
+ }
+ }
+}
+
+static uid_t
+parse_uid(const char *s, char *buf, size_t bufsize)
+{
+ struct passwd *pw;
+ char *inval;
+
+ if ((pw = getpwnam(s)) != NULL)
+ snprintf(buf, bufsize, "%d", pw->pw_uid);
+ else {
+ strtol(s, &inval, 10);
+ if (*inval != 0) {
+ errx(EX_NOUSER, "unknown user id: %s", s);
+ usage();
+ } else {
+ strncpy(buf, s, bufsize);
+ }
+ }
+}
- iovcnt = 6;
+int
+main(int argc, char *argv[])
+{
+ struct iovec *iov;
+ int ch, mntflags, iovlen;
+ char source [MAXPATHLEN], target[MAXPATHLEN], errmsg[255];
+ char uid_str[20], gid_str[20];
+ char *p, *val;
+
+ iov = NULL;
+ iovlen = 0;
mntflags = 0;
- while ((ch = getopt(argc, argv, "bo:r")) != -1)
+ memset(errmsg, 0, sizeof(errmsg));
+
+ while ((ch = getopt(argc, argv, "bo:")) != -1) {
switch (ch) {
case 'b':
- iov[6].iov_base = "below";
- iov[6].iov_len = strlen(iov[6].iov_base) + 1;
- iov[7].iov_base = NULL;
- iov[7].iov_len = 0;
- iovcnt = 8;
+ printf("\n -b is deprecated. Use \"-o below\" instead\n");
+ build_iovec(&iov, &iovlen, "below", NULL, 0);
break;
case 'o':
- getmntopts(optarg, mopts, &mntflags, 0);
- break;
- case 'r':
- iov[6].iov_base = "replace";
- iov[6].iov_len = strlen(iov[6].iov_base) + 1;
- iov[7].iov_base = NULL;
- iov[7].iov_len = 0;
- iovcnt = 8;
+ p = strchr(optarg, '=');
+ val = NULL;
+ if (p != NULL) {
+ *p = '\0';
+ val = p + 1;
+ if (strncmp(optarg, "gid", 3) == 0) {
+ parse_gid(val, gid_str, sizeof(gid_str));
+ val = gid_str;
+ }
+ else if (strncmp(optarg, "uid", 3) == 0) {
+ parse_uid(val, uid_str, sizeof(uid_str));
+ val = uid_str;
+ }
+ }
+ build_iovec(&iov, &iovlen, optarg, val, (size_t)-1);
break;
case '?':
default:
usage();
/* NOTREACHED */
}
+ }
argc -= optind;
argv += optind;
@@ -115,46 +180,14 @@ main(argc, argv)
if (subdir(target, source) || subdir(source, target))
errx(EX_USAGE, "%s (%s) and %s (%s) are not distinct paths",
- argv[0], target, argv[1], source);
-
- iov[0].iov_base = "fstype";
- iov[0].iov_len = strlen(iov[0].iov_base) + 1;
- iov[1].iov_base = "unionfs";
- iov[1].iov_len = strlen(iov[1].iov_base) + 1;
- iov[2].iov_base = "fspath";
- iov[2].iov_len = strlen(iov[2].iov_base) + 1;
- iov[3].iov_base = source;
- iov[3].iov_len = strlen(source) + 1;
- iov[4].iov_base = "target";
- iov[4].iov_len = strlen(iov[4].iov_base) + 1;
- iov[5].iov_base = target;
- iov[5].iov_len = strlen(target) + 1;
- if (nmount(iov, iovcnt, mntflags))
- err(EX_OSERR, "%s", target);
- exit(0);
-}
-
-int
-subdir(p, dir)
- const char *p;
- const char *dir;
-{
- int l;
-
- l = strlen(dir);
- if (l <= 1)
- return (1);
+ argv[0], target, argv[1], source);
- if ((strncmp(p, dir, l) == 0) && (p[l] == '/' || p[l] == '\0'))
- return (1);
-
- return (0);
-}
+ build_iovec(&iov, &iovlen, "fstype", "unionfs", (size_t)-1);
+ build_iovec(&iov, &iovlen, "fspath", source, (size_t)-1);
+ build_iovec(&iov, &iovlen, "from", target, (size_t)-1);
+ build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
-void
-usage()
-{
- (void)fprintf(stderr,
- "usage: mount_unionfs [-br] [-o options] directory uniondir\n");
- exit(EX_USAGE);
+ if (nmount(iov, iovlen, mntflags))
+ err(EX_OSERR, "%s: %s", source, errmsg);
+ exit(0);
}
diff --git a/sys/fs/unionfs/union.h b/sys/fs/unionfs/union.h
index 757e242..f35e1bb 100644
--- a/sys/fs/unionfs/union.h
+++ b/sys/fs/unionfs/union.h
@@ -1,6 +1,8 @@
/*-
* Copyright (c) 1994 The Regents of the University of California.
* Copyright (c) 1994 Jan-Simon Pendry.
+ * Copyright (c) 2005, 2006 Masanori Ozawa <ozawa@ongs.co.jp>, ONGS Inc.
+ * Copyright (c) 2006 Daichi Goto <daichi@freebsd.org>
* All rights reserved.
*
* This code is derived from software donated to Berkeley by
@@ -34,107 +36,100 @@
* $FreeBSD$
*/
-#define UNMNT_ABOVE 0x0001 /* Target appears above mount point */
-#define UNMNT_BELOW 0x0002 /* Target appears below mount point */
-#define UNMNT_REPLACE 0x0003 /* Target replaces mount point */
+#ifdef _KERNEL
-struct union_mount {
- struct vnode *um_uppervp; /* UN_ULOCK holds locking state */
- struct vnode *um_lowervp; /* Left unlocked */
- struct ucred *um_cred; /* Credentials of user calling mount */
- int um_cmode; /* cmask from mount process */
- int um_op; /* Operation mode */
- dev_t um_upperdev; /* Upper root node fsid[0]*/
+/* copy method of attr from lower to upper */
+typedef enum _unionfs_copymode {
+ UNIONFS_TRADITIONAL = 0,
+ UNIONFS_TRANSPARENT,
+ UNIONFS_MASQUERADE
+} unionfs_copymode;
+
+struct unionfs_mount {
+ struct vnode *um_lowervp; /* VREFed once */
+ struct vnode *um_uppervp; /* VREFed once */
+ struct vnode *um_rootvp; /* ROOT vnode */
+ unionfs_copymode um_copymode;
+ uid_t um_uid;
+ gid_t um_gid;
+ u_short um_udir;
+ u_short um_ufile;
};
-#ifdef _KERNEL
+/* unionfs status list */
+struct unionfs_node_status {
+ LIST_ENTRY(unionfs_node_status) uns_list; /* Status list */
+ lwpid_t uns_tid; /* current thread id */
+ int uns_node_flag; /* uns flag */
+ int uns_lower_opencnt; /* open count of lower */
+ int uns_upper_opencnt; /* open count of upper */
+ int uns_lower_openmode; /* open mode of lower */
+ int uns_lower_fdidx; /* open fdidx of lower */
+ int uns_readdir_status; /* read status of readdir */
+};
-#ifndef DIAGNOSTIC
-#define DIAGNOSTIC
-#endif
+/* union node status flags */
+#define UNS_OPENL_4_READDIR 0x01 /* open lower layer for readdir */
+
+/* A cache of vnode references */
+struct unionfs_node {
+ LIST_ENTRY(unionfs_node) un_hash; /* Hash list */
+ struct vnode *un_lowervp; /* lower side vnode */
+ struct vnode *un_uppervp; /* upper side vnode */
+ struct vnode *un_dvp; /* parent unionfs vnode */
+ struct vnode *un_vnode; /* Back pointer */
+ LIST_HEAD(, unionfs_node_status) un_unshead; /* unionfs status head */
+ char *un_path; /* path */
+ int un_flag; /* unionfs node flag */
+};
-/*
- * DEFDIRMODE is the mode bits used to create a shadow directory.
- */
-#define VRWXMODE (VREAD|VWRITE|VEXEC)
-#define VRWMODE (VREAD|VWRITE)
-#define UN_DIRMODE ((VRWXMODE)|(VRWXMODE>>3)|(VRWXMODE>>6))
-#define UN_FILEMODE ((VRWMODE)|(VRWMODE>>3)|(VRWMODE>>6))
+/* unionfs node flags */
+#define UNIONFS_CACHED 0x01 /* is cached */
+#define UNIONFS_OPENEXTL 0x02 /* openextattr (lower) */
+#define UNIONFS_OPENEXTU 0x04 /* openextattr (upper) */
+
+#define MOUNTTOUNIONFSMOUNT(mp) ((struct unionfs_mount *)((mp)->mnt_data))
+#define VTOUNIONFS(vp) ((struct unionfs_node *)(vp)->v_data)
+#define UNIONFSTOV(xp) ((xp)->un_vnode)
+
+int unionfs_init(struct vfsconf *vfsp);
+int unionfs_uninit(struct vfsconf *vfsp);
+int unionfs_nodeget(struct mount *mp, struct vnode *uppervp, struct vnode *lowervp, struct vnode *dvp, struct vnode **vpp, struct componentname *cnp, struct thread *td);
+void unionfs_hashrem(struct vnode *vp, struct thread *td);
+void unionfs_get_node_status(struct unionfs_node *unp, struct thread *td, struct unionfs_node_status **unspp);
+void unionfs_tryrem_node_status(struct unionfs_node *unp, struct thread *td, struct unionfs_node_status *unsp);
+
+int unionfs_check_rmdir(struct vnode *vp, struct ucred *cred, struct thread *td);
+int unionfs_copyfile(struct unionfs_node *unp, int docopy, struct ucred *cred, struct thread *td);
+void unionfs_create_uppervattr_core(struct unionfs_mount *ump, struct vattr *lva, struct vattr *uva, struct thread *td);
+int unionfs_create_uppervattr(struct unionfs_mount *ump, struct vnode *lvp, struct vattr *uva, struct ucred *cred, struct thread *td);
+int unionfs_mkshadowdir(struct unionfs_mount *ump, struct vnode *duvp, struct unionfs_node *unp, struct componentname *cnp, struct thread *td);
+int unionfs_mkwhiteout(struct vnode *dvp, struct componentname *cnp, struct thread *td, char *path);
+int unionfs_relookup_for_create(struct vnode *dvp, struct componentname *cnp, struct thread *td);
+int unionfs_relookup_for_delete(struct vnode *dvp, struct componentname *cnp, struct thread *td);
+int unionfs_relookup_for_rename(struct vnode *dvp, struct componentname *cnp, struct thread *td);
-/*
- * A cache of vnode references (hangs off v_data)
- */
-struct union_node {
- LIST_ENTRY(union_node) un_cache; /* Hash chain */
- struct vnode *un_vnode; /* Back pointer */
- struct vnode *un_uppervp; /* overlaying object */
- struct vnode *un_lowervp; /* underlying object */
- struct vnode *un_dirvp; /* Parent dir of uppervp */
- struct vnode *un_pvp; /* Parent vnode */
- char *un_path; /* saved component name */
- int un_openl; /* # of opens on lowervp */
- int un_exclcnt; /* exclusive count */
- unsigned int un_flags;
- struct vnode **un_dircache; /* cached union stack */
- off_t un_uppersz; /* size of upper object */
- off_t un_lowersz; /* size of lower object */
#ifdef DIAGNOSTIC
- pid_t un_pid;
+struct vnode *unionfs_checklowervp(struct vnode *vp, char *fil, int lno);
+struct vnode *unionfs_checkuppervp(struct vnode *vp, char *fil, int lno);
+#define UNIONFSVPTOLOWERVP(vp) unionfs_checklowervp((vp), __FILE__, __LINE__)
+#define UNIONFSVPTOUPPERVP(vp) unionfs_checkuppervp((vp), __FILE__, __LINE__)
+#else
+#define UNIONFSVPTOLOWERVP(vp) (VTOUNIONFS(vp)->un_lowervp)
+#define UNIONFSVPTOUPPERVP(vp) (VTOUNIONFS(vp)->un_uppervp)
#endif
-};
-
-/*
- * XXX UN_ULOCK - indicates that the uppervp is locked
- *
- * UN_CACHED - node is in the union cache
- */
-
-/*#define UN_ULOCK 0x04*/ /* Upper node is locked */
-#define UN_CACHED 0x10 /* In union cache */
-
-/*
- * Hash table locking flags
- */
-#define UNVP_WANT 0x01
-#define UNVP_LOCKED 0x02
+extern struct vop_vector unionfs_vnodeops;
-extern int union_allocvp(struct vnode **, struct mount *,
- struct vnode *,
- struct vnode *,
- struct componentname *, struct vnode *,
- struct vnode *, int);
-extern int union_freevp(struct vnode *);
-extern struct vnode *union_dircache_get(struct vnode *, struct thread *);
-extern void union_dircache_free(struct union_node *);
-extern int union_copyup(struct union_node *, int, struct ucred *,
- struct thread *);
-extern int union_dowhiteout(struct union_node *, struct ucred *,
- struct thread *);
-extern int union_mkshadow(struct union_mount *, struct vnode *,
- struct componentname *, struct vnode **);
-extern int union_mkwhiteout(struct union_mount *, struct vnode *,
- struct componentname *, char *);
-extern int union_cn_close(struct vnode *, int, struct ucred *,
- struct thread *);
-extern void union_removed_upper(struct union_node *un);
-extern struct vnode *union_lowervp(struct vnode *);
-extern void union_newsize(struct vnode *, off_t, off_t);
-
-extern int (*union_dircheckp)(struct thread *, struct vnode **,
- struct file *);
-
-#define MOUNTTOUNIONMOUNT(mp) ((struct union_mount *)((mp)->mnt_data))
-#define VTOUNION(vp) ((struct union_node *)(vp)->v_data)
-#define UNIONTOV(un) ((un)->un_vnode)
-#define LOWERVP(vp) (VTOUNION(vp)->un_lowervp)
-#define UPPERVP(vp) (VTOUNION(vp)->un_uppervp)
-#define OTHERVP(vp) (UPPERVP(vp) ? UPPERVP(vp) : LOWERVP(vp))
-
-#define UDEBUG(x) if (uniondebug) printf x
-#define UDEBUG_ENABLED 1
+#ifdef MALLOC_DECLARE
+MALLOC_DECLARE(M_UNIONFSNODE);
+MALLOC_DECLARE(M_UNIONFSPATH);
+#endif
-extern struct vop_vector union_vnodeops;
-extern int uniondebug;
+#ifdef UNIONFS_DEBUG
+#define UNIONFSDEBUG(format, args...) printf(format ,## args)
+#else
+#define UNIONFSDEBUG(format, args...)
+#endif /* UNIONFS_DEBUG */
-#endif /* _KERNEL */
+#endif /* _KERNEL */
diff --git a/sys/fs/unionfs/union_subr.c b/sys/fs/unionfs/union_subr.c
index 6c63519..8d7e0a5 100644
--- a/sys/fs/unionfs/union_subr.c
+++ b/sys/fs/unionfs/union_subr.c
@@ -2,6 +2,8 @@
* Copyright (c) 1994 Jan-Simon Pendry
* Copyright (c) 1994
* The Regents of the University of California. All rights reserved.
+ * Copyright (c) 2005, 2006 Masanori Ozawa <ozawa@ongs.co.jp>, ONGS Inc.
+ * Copyright (c) 2006 Daichi Goto <daichi@freebsd.org>
*
* This code is derived from software contributed to Berkeley by
* Jan-Simon Pendry.
@@ -36,782 +38,631 @@
#include <sys/param.h>
#include <sys/systm.h>
-#include <sys/fcntl.h>
-#include <sys/file.h>
-#include <sys/filedesc.h>
#include <sys/kernel.h>
#include <sys/lock.h>
+#include <sys/mutex.h>
#include <sys/malloc.h>
-#include <sys/module.h>
#include <sys/mount.h>
-#include <sys/mutex.h>
#include <sys/namei.h>
-#include <sys/stat.h>
+#include <sys/proc.h>
#include <sys/vnode.h>
+#include <sys/dirent.h>
+#include <sys/fcntl.h>
+#include <sys/filedesc.h>
+#include <sys/stat.h>
+#include <sys/resourcevar.h>
+
+#ifdef MAC
+#include <sys/mac.h>
+#endif
-#include <vm/vm.h>
-#include <vm/vm_extern.h> /* for vnode_pager_setsize */
-#include <vm/vm_object.h> /* for vm cache coherency */
#include <vm/uma.h>
#include <fs/unionfs/union.h>
-#include <sys/proc.h>
+#define NUNIONFSNODECACHE 32
-extern int union_init(void);
-
-/* must be power of two, otherwise change UNION_HASH() */
-#define NHASH 32
-
-/* unsigned int ... */
-#define UNION_HASH(u, l) \
- (((((uintptr_t) (u)) + ((uintptr_t) l)) >> 8) & (NHASH-1))
-
-static MALLOC_DEFINE(M_UNPATH, "unpath", "UNION path component");
-static MALLOC_DEFINE(M_UNDCACHE, "undcac", "UNION directory cache");
-
-static LIST_HEAD(unhead, union_node) unhead[NHASH];
-static int unvplock[NHASH];
-
-static void union_dircache_r(struct vnode *vp, struct vnode ***vppp,
- int *cntp);
-static int union_list_lock(int ix);
-static void union_list_unlock(int ix);
-static int union_relookup(struct union_mount *um, struct vnode *dvp,
- struct vnode **vpp,
- struct componentname *cnp,
- struct componentname *cn, char *path,
- int pathlen);
-static void union_updatevp(struct union_node *un,
- struct vnode *uppervp,
- struct vnode *lowervp);
-static void union_newlower(struct union_node *, struct vnode *);
-static void union_newupper(struct union_node *, struct vnode *);
-static int union_copyfile(struct vnode *, struct vnode *,
- struct ucred *, struct thread *);
-static int union_vn_create(struct vnode **, struct union_node *,
- struct thread *);
-static int union_vn_close(struct vnode *, int, struct ucred *,
- struct thread *);
+#define UNIONFS_NHASH(upper, lower) \
+ (&unionfs_node_hashtbl[(((uintptr_t)upper + (uintptr_t)lower) >> 8) & unionfs_node_hash])
-int
-union_init()
+static LIST_HEAD(unionfs_node_hashhead, unionfs_node) *unionfs_node_hashtbl;
+static u_long unionfs_node_hash;
+struct mtx unionfs_hashmtx;
+
+static MALLOC_DEFINE(M_UNIONFSHASH, "UNIONFS hash", "UNIONFS hash table");
+MALLOC_DEFINE(M_UNIONFSNODE, "UNIONFS node", "UNIONFS vnode private part");
+MALLOC_DEFINE(M_UNIONFSPATH, "UNIONFS path", "UNIONFS path private part");
+
+/*
+ * Initialize cache headers
+ */
+int
+unionfs_init(struct vfsconf *vfsp)
{
- int i;
+ UNIONFSDEBUG("unionfs_init\n"); /* printed during system boot */
+ unionfs_node_hashtbl = hashinit(NUNIONFSNODECACHE, M_UNIONFSHASH, &unionfs_node_hash);
+ mtx_init(&unionfs_hashmtx, "unionfs", NULL, MTX_DEF);
- for (i = 0; i < NHASH; i++)
- LIST_INIT(&unhead[i]);
- bzero((caddr_t)unvplock, sizeof(unvplock));
return (0);
}
-static int
-union_list_lock(ix)
- int ix;
+/*
+ * Destroy cache headers
+ */
+int
+unionfs_uninit(struct vfsconf *vfsp)
{
- if (unvplock[ix] & UNVP_LOCKED) {
- unvplock[ix] |= UNVP_WANT;
- (void) tsleep( &unvplock[ix], PINOD, "unllck", 0);
- return (1);
- }
- unvplock[ix] |= UNVP_LOCKED;
+ mtx_destroy(&unionfs_hashmtx);
+ free(unionfs_node_hashtbl, M_UNIONFSHASH);
return (0);
}
-static void
-union_list_unlock(ix)
- int ix;
+/*
+ * Return a VREF'ed alias for unionfs vnode if already exists, else 0.
+ */
+static struct vnode *
+unionfs_hashget(struct mount *mp, struct vnode *uppervp,
+ struct vnode *lowervp, struct vnode *dvp, char *path,
+ int lkflags, struct thread *td)
{
- unvplock[ix] &= ~UNVP_LOCKED;
+ struct unionfs_node_hashhead *hd;
+ struct unionfs_node *unp;
+ struct vnode *vp;
- if (unvplock[ix] & UNVP_WANT) {
- unvplock[ix] &= ~UNVP_WANT;
- wakeup( &unvplock[ix]);
+ if (lkflags & LK_TYPE_MASK)
+ lkflags |= LK_RETRY;
+ hd = UNIONFS_NHASH(uppervp, lowervp);
+
+loop:
+ mtx_lock(&unionfs_hashmtx);
+ LIST_FOREACH(unp, hd, un_hash) {
+ if (unp->un_uppervp == uppervp &&
+ unp->un_lowervp == lowervp &&
+ unp->un_dvp == dvp &&
+ UNIONFSTOV(unp)->v_mount == mp &&
+ (!path || !(unp->un_path) || !strcmp(unp->un_path, path))) {
+ vp = UNIONFSTOV(unp);
+ VI_LOCK(vp);
+
+ /*
+ * If the unionfs node is being recycled we have to
+ * wait until it finishes prior to scanning again.
+ */
+ mtx_unlock(&unionfs_hashmtx);
+ if (vp->v_iflag & VI_DOOMED) {
+ /* Wait for recycling to finish. */
+ vn_lock(vp, LK_EXCLUSIVE | LK_INTERLOCK, td);
+ VOP_UNLOCK(vp, 0, td);
+ goto loop;
+ }
+ /*
+ * We need to clear the OWEINACT flag here as this
+ * may lead vget() to try to lock our vnode which is
+ * already locked via vp.
+ */
+ vp->v_iflag &= ~VI_OWEINACT;
+ vget(vp, lkflags | LK_INTERLOCK, td);
+
+ return (vp);
+ }
}
+
+ mtx_unlock(&unionfs_hashmtx);
+
+ return (NULLVP);
}
/*
- * union_updatevp:
- *
- * The uppervp, if not NULL, must be referenced and not locked by us
- * The lowervp, if not NULL, must be referenced.
- *
- * If uppervp and lowervp match pointers already installed, then
- * nothing happens. The passed vp's (when matching) are not adjusted.
- *
- * This routine may only be called by union_newupper() and
- * union_newlower().
+ * Act like unionfs_hashget, but add passed unionfs_node to hash if no existing
+ * node found.
*/
-
-static void
-union_updatevp(un, uppervp, lowervp)
- struct union_node *un;
- struct vnode *uppervp;
- struct vnode *lowervp;
+static struct vnode *
+unionfs_hashins(struct mount *mp, struct unionfs_node *uncp,
+ char *path, int lkflags, struct thread *td)
{
- int ohash = UNION_HASH(un->un_uppervp, un->un_lowervp);
- int nhash = UNION_HASH(uppervp, lowervp);
- int docache = (lowervp != NULLVP || uppervp != NULLVP);
- int lhash, uhash;
+ struct unionfs_node_hashhead *hd;
+ struct unionfs_node *unp;
+ struct vnode *vp;
- /*
- * Ensure locking is ordered from lower to higher
- * to avoid deadlocks.
- */
- if (nhash < ohash) {
- lhash = nhash;
- uhash = ohash;
- } else {
- lhash = ohash;
- uhash = nhash;
- }
+ if (lkflags & LK_TYPE_MASK)
+ lkflags |= LK_RETRY;
+ hd = UNIONFS_NHASH(uncp->un_uppervp, uncp->un_lowervp);
- if (lhash != uhash) {
- while (union_list_lock(lhash))
- continue;
- }
-
- while (union_list_lock(uhash))
- continue;
+loop:
+ mtx_lock(&unionfs_hashmtx);
+ LIST_FOREACH(unp, hd, un_hash) {
+ if (unp->un_uppervp == uncp->un_uppervp &&
+ unp->un_lowervp == uncp->un_lowervp &&
+ unp->un_dvp == uncp->un_dvp &&
+ UNIONFSTOV(unp)->v_mount == mp &&
+ (!path || !(unp->un_path) || !strcmp(unp->un_path, path))) {
+ vp = UNIONFSTOV(unp);
+ VI_LOCK(vp);
+
+ mtx_unlock(&unionfs_hashmtx);
+ if (vp->v_iflag & VI_DOOMED) {
+ /* Wait for recycling to finish. */
+ vn_lock(vp, LK_EXCLUSIVE | LK_INTERLOCK, td);
+ VOP_UNLOCK(vp, 0, td);
+ goto loop;
+ }
+ vp->v_iflag &= ~VI_OWEINACT;
+ vget(vp, lkflags | LK_INTERLOCK, td);
- if (ohash != nhash || !docache) {
- if (un->un_flags & UN_CACHED) {
- un->un_flags &= ~UN_CACHED;
- LIST_REMOVE(un, un_cache);
+ return (vp);
}
}
- if (ohash != nhash)
- union_list_unlock(ohash);
+ LIST_INSERT_HEAD(hd, uncp, un_hash);
+ uncp->un_flag |= UNIONFS_CACHED;
+ mtx_unlock(&unionfs_hashmtx);
- if (un->un_lowervp != lowervp) {
- if (un->un_lowervp) {
- vrele(un->un_lowervp);
- if (un->un_path) {
- free(un->un_path, M_UNPATH);
- un->un_path = 0;
- }
- }
- un->un_lowervp = lowervp;
- un->un_lowersz = VNOVAL;
+ return (NULLVP);
+}
+
+/*
+ * Make a new or get existing unionfs node.
+ *
+ * uppervp and lowervp should be unlocked. Because if new unionfs vnode is
+ * locked, uppervp or lowervp is locked too. In order to prevent dead lock,
+ * you should not lock plurality simultaneously.
+ */
+int
+unionfs_nodeget(struct mount *mp, struct vnode *uppervp,
+ struct vnode *lowervp, struct vnode *dvp,
+ struct vnode **vpp, struct componentname *cnp,
+ struct thread *td)
+{
+ struct unionfs_mount *ump;
+ struct unionfs_node *unp;
+ struct vnode *vp;
+ int error;
+ int lkflags;
+ char *path;
+
+ ump = MOUNTTOUNIONFSMOUNT(mp);
+ lkflags = (cnp ? cnp->cn_lkflags : 0);
+ path = (cnp ? cnp->cn_nameptr : "");
+
+ if (uppervp == NULLVP && lowervp == NULLVP)
+ panic("unionfs_nodeget: upper and lower is null");
+
+ /* If it has no ISLASTCN flag, path check is skipped. */
+ if (!cnp || !(cnp->cn_flags & ISLASTCN))
+ path = NULL;
+
+ /* Lookup the hash first. */
+ *vpp = unionfs_hashget(mp, uppervp, lowervp, dvp, path, lkflags, td);
+ if (*vpp != NULLVP)
+ return (0);
+
+ if ((uppervp == NULLVP || ump->um_uppervp != uppervp) ||
+ (lowervp == NULLVP || ump->um_lowervp != lowervp)) {
+ if (dvp == NULLVP)
+ return (EINVAL);
}
- if (un->un_uppervp != uppervp) {
- if (un->un_uppervp)
- vrele(un->un_uppervp);
- un->un_uppervp = uppervp;
- un->un_uppersz = VNOVAL;
+ /*
+ * Do the MALLOC before the getnewvnode since doing so afterward
+ * might cause a bogus v_data pointer to get dereferenced elsewhere
+ * if MALLOC should block.
+ */
+ MALLOC(unp, struct unionfs_node *, sizeof(struct unionfs_node),
+ M_UNIONFSNODE, M_WAITOK | M_ZERO);
+
+ error = getnewvnode("unionfs", mp, &unionfs_vnodeops, &vp);
+ if (error) {
+ FREE(unp, M_UNIONFSNODE);
+ return (error);
}
+ if (dvp != NULLVP)
+ vref(dvp);
+ if (uppervp != NULLVP)
+ vref(uppervp);
+ if (lowervp != NULLVP)
+ vref(lowervp);
+
+ unp->un_vnode = vp;
+ unp->un_uppervp = uppervp;
+ unp->un_lowervp = lowervp;
+ unp->un_dvp = dvp;
+ if (uppervp != NULLVP)
+ vp->v_vnlock = uppervp->v_vnlock;
+ else
+ vp->v_vnlock = lowervp->v_vnlock;
- if (docache && (ohash != nhash)) {
- LIST_INSERT_HEAD(&unhead[nhash], un, un_cache);
- un->un_flags |= UN_CACHED;
+ if (cnp) {
+ unp->un_path = (char *)
+ malloc(cnp->cn_namelen +1, M_UNIONFSPATH, M_WAITOK | M_ZERO);
+ bcopy(cnp->cn_nameptr, unp->un_path, cnp->cn_namelen);
+ unp->un_path[cnp->cn_namelen] = '\0';
}
+ vp->v_type = (uppervp != NULLVP ? uppervp->v_type : lowervp->v_type);
+ vp->v_data = unp;
+
+ if ((uppervp != NULLVP && ump->um_uppervp == uppervp) &&
+ (lowervp != NULLVP && ump->um_lowervp == lowervp))
+ vp->v_vflag |= VV_ROOT;
+
+ *vpp = unionfs_hashins(mp, unp, path, lkflags, td);
+ if (*vpp != NULLVP) {
+ if (dvp != NULLVP)
+ vrele(dvp);
+ if (uppervp != NULLVP)
+ vrele(uppervp);
+ if (lowervp != NULLVP)
+ vrele(lowervp);
- union_list_unlock(nhash);
-}
+ unp->un_uppervp = NULLVP;
+ unp->un_lowervp = NULLVP;
+ unp->un_dvp = NULLVP;
+ vrele(vp);
-/*
- * Set a new lowervp. The passed lowervp must be referenced and will be
- * stored in the vp in a referenced state.
- */
+ return (0);
+ }
-static void
-union_newlower(un, lowervp)
- struct union_node *un;
- struct vnode *lowervp;
-{
- union_updatevp(un, un->un_uppervp, lowervp);
-}
+ if (lkflags & LK_TYPE_MASK)
+ vn_lock(vp, lkflags | LK_RETRY, td);
-/*
- * Set a new uppervp. The passed uppervp must be locked and will be
- * stored in the vp in a locked state. The caller should not unlock
- * uppervp.
- */
+ *vpp = vp;
-static void
-union_newupper(un, uppervp)
- struct union_node *un;
- struct vnode *uppervp;
-{
- union_updatevp(un, uppervp, un->un_lowervp);
+ return (0);
}
/*
- * Keep track of size changes in the underlying vnodes.
- * If the size changes, then callback to the vm layer
- * giving priority to the upper layer size.
+ * Remove node from hash.
*/
void
-union_newsize(vp, uppersz, lowersz)
- struct vnode *vp;
- off_t uppersz, lowersz;
+unionfs_hashrem(struct vnode *vp, struct thread *td)
{
- struct union_node *un;
- off_t sz;
-
- /* only interested in regular files */
- if (vp->v_type != VREG)
- return;
+ int vfslocked;
+ struct unionfs_node *unp;
+ struct unionfs_node_status *unsp;
+ struct vnode *lvp;
+ struct vnode *uvp;
- un = VTOUNION(vp);
- sz = VNOVAL;
-
- if ((uppersz != VNOVAL) && (un->un_uppersz != uppersz)) {
- un->un_uppersz = uppersz;
- if (sz == VNOVAL)
- sz = un->un_uppersz;
+ /*
+ * Use the interlock to protect the clearing of v_data to
+ * prevent faults in unionfs_lock().
+ */
+ VI_LOCK(vp);
+ unp = VTOUNIONFS(vp);
+ lvp = unp->un_lowervp;
+ uvp = unp->un_uppervp;
+ unp->un_lowervp = unp->un_uppervp = NULLVP;
+
+ vp->v_vnlock = &(vp->v_lock);
+ vp->v_data = NULL;
+ lockmgr(vp->v_vnlock, LK_EXCLUSIVE | LK_INTERLOCK, VI_MTX(vp), td);
+ if (lvp != NULLVP)
+ VOP_UNLOCK(lvp, 0, td);
+ if (uvp != NULLVP)
+ VOP_UNLOCK(uvp, 0, td);
+
+ mtx_lock(&unionfs_hashmtx);
+ if (unp->un_flag & UNIONFS_CACHED) {
+ LIST_REMOVE(unp, un_hash);
+ unp->un_flag &= ~UNIONFS_CACHED;
}
+ mtx_unlock(&unionfs_hashmtx);
+ vp->v_object = NULL;
- if ((lowersz != VNOVAL) && (un->un_lowersz != lowersz)) {
- un->un_lowersz = lowersz;
- if (sz == VNOVAL)
- sz = un->un_lowersz;
+ if (lvp != NULLVP) {
+ vfslocked = VFS_LOCK_GIANT(lvp->v_mount);
+ vrele(lvp);
+ VFS_UNLOCK_GIANT(vfslocked);
}
-
- if (sz != VNOVAL) {
- UDEBUG(("union: %s size now %ld\n",
- (uppersz != VNOVAL ? "upper" : "lower"), (long)sz));
- /*
- * There is no need to change size of non-existent object.
- */
- /* vnode_pager_setsize(vp, sz); */
+ if (uvp != NULLVP) {
+ vfslocked = VFS_LOCK_GIANT(uvp->v_mount);
+ vrele(uvp);
+ VFS_UNLOCK_GIANT(vfslocked);
+ }
+ if (unp->un_dvp != NULLVP) {
+ vfslocked = VFS_LOCK_GIANT(unp->un_dvp->v_mount);
+ vrele(unp->un_dvp);
+ VFS_UNLOCK_GIANT(vfslocked);
+ unp->un_dvp = NULLVP;
+ }
+ if (unp->un_path) {
+ free(unp->un_path, M_UNIONFSPATH);
+ unp->un_path = NULL;
+ }
+ while ((unsp = LIST_FIRST(&(unp->un_unshead))), NULL != unsp) {
+ LIST_REMOVE(unsp, uns_list);
+ free(unsp, M_TEMP);
}
+ FREE(unp, M_UNIONFSNODE);
}
/*
- * union_allocvp: allocate a union_node and associate it with a
- * parent union_node and one or two vnodes.
- *
- * vpp Holds the returned vnode locked and referenced if no
- * error occurs.
- *
- * mp Holds the mount point. mp may or may not be busied.
- * allocvp() makes no changes to mp.
- *
- * dvp Holds the parent union_node to the one we wish to create.
- * XXX may only be used to traverse an uncopied lowervp-based
- * tree? XXX
- *
- * dvp may or may not be locked. allocvp() makes no changes
- * to dvp.
- *
- * upperdvp Holds the parent vnode to uppervp, generally used along
- * with path component information to create a shadow of
- * lowervp when uppervp does not exist.
- *
- * upperdvp is referenced but unlocked on entry, and will be
- * dereferenced on return.
- *
- * uppervp Holds the new uppervp vnode to be stored in the
- * union_node we are allocating. uppervp is referenced but
- * not locked, and will be dereferenced on return.
- *
- * lowervp Holds the new lowervp vnode to be stored in the
- * union_node we are allocating. lowervp is referenced but
- * not locked, and will be dereferenced on return.
- *
- * cnp Holds path component information to be coupled with
- * lowervp and upperdvp to allow unionfs to create an uppervp
- * later on. Only used if lowervp is valid. The contents
- * of cnp is only valid for the duration of the call.
- *
- * docache Determine whether this node should be entered in the
- * cache or whether it should be destroyed as soon as possible.
- *
- * All union_nodes are maintained on a singly-linked
- * list. New nodes are only allocated when they cannot
- * be found on this list. Entries on the list are
- * removed when the vfs reclaim entry is called.
- *
- * A single lock is kept for the entire list. This is
- * needed because the getnewvnode() function can block
- * waiting for a vnode to become free, in which case there
- * may be more than one process trying to get the same
- * vnode. This lock is only taken if we are going to
- * call getnewvnode(), since the kernel itself is single-threaded.
- *
- * If an entry is found on the list, then call vget() to
- * take a reference. This is done because there may be
- * zero references to it and so it needs to removed from
- * the vnode free list.
+ * Get the unionfs node status.
+ * You need exclusive lock this vnode.
*/
-
-int
-union_allocvp(vpp, mp, dvp, upperdvp, cnp, uppervp, lowervp, docache)
- struct vnode **vpp;
- struct mount *mp;
- struct vnode *dvp; /* parent union vnode */
- struct vnode *upperdvp; /* parent vnode of uppervp */
- struct componentname *cnp; /* may be null */
- struct vnode *uppervp; /* may be null */
- struct vnode *lowervp; /* may be null */
- int docache;
+void
+unionfs_get_node_status(struct unionfs_node *unp, struct thread *td,
+ struct unionfs_node_status **unspp)
{
- int error;
- struct union_node *un = 0;
- struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
- struct thread *td = (cnp) ? cnp->cn_thread : curthread;
- int hash = 0;
- int vflag;
- int try;
+ struct unionfs_node_status *unsp;
- if (uppervp == NULLVP && lowervp == NULLVP)
- panic("union: unidentifiable allocation");
-
- if (uppervp && lowervp && (uppervp->v_type != lowervp->v_type)) {
- vrele(lowervp);
- lowervp = NULLVP;
- }
+ KASSERT(NULL != unspp, ("null pointer"));
+ ASSERT_VOP_ELOCKED(UNIONFSTOV(unp), "unionfs_get_node_status");
- /* detect the root vnode (and aliases) */
- vflag = 0;
- if ((uppervp == um->um_uppervp) &&
- ((lowervp == NULLVP) || lowervp == um->um_lowervp)) {
- if (lowervp == NULLVP) {
- lowervp = um->um_lowervp;
- if (lowervp != NULLVP)
- VREF(lowervp);
+ LIST_FOREACH(unsp, &(unp->un_unshead), uns_list) {
+ if (unsp->uns_tid == td->td_tid) {
+ *unspp = unsp;
+ return;
}
- vflag = VV_ROOT;
}
-loop:
- if (!docache) {
- un = 0;
- } else for (try = 0; try < 3; try++) {
- switch (try) {
- case 0:
- if (lowervp == NULLVP)
- continue;
- hash = UNION_HASH(uppervp, lowervp);
- break;
-
- case 1:
- if (uppervp == NULLVP)
- continue;
- hash = UNION_HASH(uppervp, NULLVP);
- break;
+ /* create a new unionfs node status */
+ MALLOC(unsp, struct unionfs_node_status *,
+ sizeof(struct unionfs_node_status), M_TEMP, M_WAITOK | M_ZERO);
- case 2:
- if (lowervp == NULLVP)
- continue;
- hash = UNION_HASH(NULLVP, lowervp);
- break;
- }
+ unsp->uns_tid = td->td_tid;
+ LIST_INSERT_HEAD(&(unp->un_unshead), unsp, uns_list);
- while (union_list_lock(hash))
- continue;
-
- LIST_FOREACH(un, &unhead[hash], un_cache) {
- if ((un->un_lowervp == lowervp ||
- un->un_lowervp == NULLVP) &&
- (un->un_uppervp == uppervp ||
- un->un_uppervp == NULLVP) &&
- (UNIONTOV(un)->v_mount == mp)) {
- if (vget(UNIONTOV(un), 0,
- cnp ? cnp->cn_thread : NULL)) {
- union_list_unlock(hash);
- goto loop;
- }
- break;
- }
- }
+ *unspp = unsp;
+}
- union_list_unlock(hash);
+/*
+ * Remove the unionfs node status, if you can.
+ * You need exclusive lock this vnode.
+ */
+void
+unionfs_tryrem_node_status(struct unionfs_node *unp, struct thread *td,
+ struct unionfs_node_status *unsp)
+{
+ KASSERT(NULL != unsp, ("null pointer"));
+ ASSERT_VOP_ELOCKED(UNIONFSTOV(unp), "unionfs_get_node_status");
- if (un)
- break;
- }
+ if (0 < unsp->uns_lower_opencnt || 0 < unsp->uns_upper_opencnt)
+ return;
- if (un) {
- /*
- * Obtain a lock on the union_node. Everything is unlocked
- * except for dvp, so check that case. If they match, our
- * new un is already locked. Otherwise we have to lock our
- * new un.
- *
- * A potential deadlock situation occurs when we are holding
- * one lock while trying to get another. We must follow
- * strict ordering rules to avoid it. We try to locate dvp
- * by scanning up from un_vnode, since the most likely
- * scenario is un being under dvp.
- */
+ LIST_REMOVE(unsp, uns_list);
+ free(unsp, M_TEMP);
+}
- if (dvp && un->un_vnode != dvp) {
- struct vnode *scan = un->un_vnode;
-
- do {
- scan = VTOUNION(scan)->un_pvp;
- } while (scan && scan->v_op == &union_vnodeops &&
- scan != dvp);
- if (scan != dvp) {
- /*
- * our new un is above dvp (we never saw dvp
- * while moving up the tree).
- */
- VREF(dvp);
- VOP_UNLOCK(dvp, 0, td);
- error = vn_lock(un->un_vnode, LK_EXCLUSIVE, td);
- vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td);
- vrele(dvp);
- } else {
- /*
- * our new un is under dvp
- */
- error = vn_lock(un->un_vnode, LK_EXCLUSIVE, td);
- }
- } else if (dvp == NULLVP) {
- /*
- * dvp is NULL, we need to lock un.
- */
- error = vn_lock(un->un_vnode, LK_EXCLUSIVE, td);
+/*
+ * Create upper node attr.
+ */
+void
+unionfs_create_uppervattr_core(struct unionfs_mount *ump,
+ struct vattr *lva,
+ struct vattr *uva,
+ struct thread *td)
+{
+ VATTR_NULL(uva);
+ uva->va_type = lva->va_type;
+ uva->va_atime = lva->va_atime;
+ uva->va_mtime = lva->va_mtime;
+ uva->va_ctime = lva->va_ctime;
+
+ switch (ump->um_copymode) {
+ case UNIONFS_TRANSPARENT:
+ uva->va_mode = lva->va_mode;
+ uva->va_uid = lva->va_uid;
+ uva->va_gid = lva->va_gid;
+ break;
+ case UNIONFS_MASQUERADE:
+ if (ump->um_uid == lva->va_uid) {
+ uva->va_mode = lva->va_mode & 077077;
+ uva->va_mode |= (lva->va_type == VDIR ? ump->um_udir : ump->um_ufile) & 0700;
+ uva->va_uid = lva->va_uid;
+ uva->va_gid = lva->va_gid;
} else {
- /*
- * dvp == un->un_vnode, we are already locked.
- */
- error = 0;
- }
-
- if (error)
- goto loop;
-
- /*
- * At this point, the union_node is locked and referenced.
- *
- * uppervp is locked and referenced or NULL, lowervp is
- * referenced or NULL.
- */
- UDEBUG(("Modify existing un %p vn %p upper %p(refs %d) -> %p(refs %d)\n",
- un, un->un_vnode, un->un_uppervp,
- (un->un_uppervp ? vrefcnt(un->un_uppervp) : -99),
- uppervp,
- (uppervp ? vrefcnt(uppervp) : -99)
- ));
-
- if (uppervp != un->un_uppervp) {
- KASSERT(uppervp == NULL || vrefcnt(uppervp) > 0, ("union_allocvp: too few refs %d (at least 1 required) on uppervp", vrefcnt(uppervp)));
- union_newupper(un, uppervp);
- } else if (uppervp) {
- KASSERT(vrefcnt(uppervp) > 1, ("union_allocvp: too few refs %d (at least 2 required) on uppervp", vrefcnt(uppervp)));
- vrele(uppervp);
+ uva->va_mode = (lva->va_type == VDIR ? ump->um_udir : ump->um_ufile);
+ uva->va_uid = ump->um_uid;
+ uva->va_gid = ump->um_gid;
}
-
- /*
- * Save information about the lower layer.
- * This needs to keep track of pathname
- * and directory information which union_vn_create()
- * might need.
- */
- if (lowervp != un->un_lowervp) {
- union_newlower(un, lowervp);
- if (cnp && (lowervp != NULLVP)) {
- un->un_path = malloc(cnp->cn_namelen+1,
- M_UNPATH, M_WAITOK);
- bcopy(cnp->cn_nameptr, un->un_path,
- cnp->cn_namelen);
- un->un_path[cnp->cn_namelen] = '\0';
- }
- } else if (lowervp) {
- vrele(lowervp);
- }
-
- /*
- * and upperdvp
- */
- if (upperdvp != un->un_dirvp) {
- if (un->un_dirvp)
- vrele(un->un_dirvp);
- un->un_dirvp = upperdvp;
- } else if (upperdvp) {
- vrele(upperdvp);
- }
-
- *vpp = UNIONTOV(un);
- return (0);
+ break;
+ default: /* UNIONFS_TRADITIONAL */
+ FILEDESC_LOCK_FAST(td->td_proc->p_fd);
+ uva->va_mode = 0777 & ~td->td_proc->p_fd->fd_cmask;
+ FILEDESC_UNLOCK_FAST(td->td_proc->p_fd);
+ uva->va_uid = ump->um_uid;
+ uva->va_gid = ump->um_gid;
+ break;
}
+}
- if (docache) {
- /*
- * Otherwise lock the vp list while we call getnewvnode()
- * since that can block.
- */
- hash = UNION_HASH(uppervp, lowervp);
+/*
+ * Create upper node attr.
+ */
+int
+unionfs_create_uppervattr(struct unionfs_mount *ump,
+ struct vnode *lvp,
+ struct vattr *uva,
+ struct ucred *cred,
+ struct thread *td)
+{
+ int error;
+ struct vattr lva;
- if (union_list_lock(hash))
- goto loop;
- }
+ if ((error = VOP_GETATTR(lvp, &lva, cred, td)))
+ return (error);
- /*
- * Create new node rather than replace old node.
- */
+ unionfs_create_uppervattr_core(ump, &lva, uva, td);
- error = getnewvnode("union", mp, &union_vnodeops, vpp);
- if (error) {
- /*
- * If an error occurs, clear out vnodes.
- */
- if (lowervp)
- vrele(lowervp);
- if (uppervp)
- vrele(uppervp);
- if (upperdvp)
- vrele(upperdvp);
- *vpp = NULL;
- goto out;
- }
+ return (error);
+}
- MALLOC((*vpp)->v_data, void *, sizeof(struct union_node),
- M_TEMP, M_WAITOK);
+/*
+ * relookup
+ *
+ * dvp should be locked on entry and will be locked on return.
+ *
+ * If an error is returned, *vpp will be invalid, otherwise it will hold a
+ * locked, referenced vnode. If *vpp == dvp then remember that only one
+ * LK_EXCLUSIVE lock is held.
+ */
+static int
+unionfs_relookup(struct vnode *dvp, struct vnode **vpp,
+ struct componentname *cnp, struct componentname *cn,
+ struct thread *td, char *path, int pathlen, u_long nameiop)
+{
+ int error;
- (*vpp)->v_vflag |= vflag;
- if (uppervp)
- (*vpp)->v_type = uppervp->v_type;
- else
- (*vpp)->v_type = lowervp->v_type;
+ cn->cn_namelen = pathlen;
+ cn->cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK);
+ bcopy(path, cn->cn_pnbuf, pathlen);
+ cn->cn_pnbuf[pathlen] = '\0';
- un = VTOUNION(*vpp);
- bzero(un, sizeof(*un));
+ cn->cn_nameiop = nameiop;
+ cn->cn_flags = (LOCKPARENT | LOCKLEAF | HASBUF | SAVENAME | ISLASTCN);
+ cn->cn_lkflags = LK_EXCLUSIVE;
+ cn->cn_thread = td;
+ cn->cn_cred = cnp->cn_cred;
- vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY, td);
+ cn->cn_nameptr = cn->cn_pnbuf;
+ cn->cn_consume = cnp->cn_consume;
- un->un_vnode = *vpp;
- un->un_uppervp = uppervp;
- un->un_uppersz = VNOVAL;
- un->un_lowervp = lowervp;
- un->un_lowersz = VNOVAL;
- un->un_dirvp = upperdvp;
- un->un_pvp = dvp; /* only parent dir in new allocation */
- if (dvp != NULLVP)
- VREF(dvp);
- un->un_dircache = NULL;
- un->un_openl = 0;
-
- if (cnp && (lowervp != NULLVP)) {
- un->un_path = malloc(cnp->cn_namelen+1, M_UNPATH, M_WAITOK);
- bcopy(cnp->cn_nameptr, un->un_path, cnp->cn_namelen);
- un->un_path[cnp->cn_namelen] = '\0';
- } else {
- un->un_path = NULL;
- un->un_dirvp = NULL;
- }
+ if (nameiop == DELETE)
+ cn->cn_flags |= (cnp->cn_flags & (DOWHITEOUT | SAVESTART));
+ else if (RENAME == nameiop)
+ cn->cn_flags |= (cnp->cn_flags & SAVESTART);
- if (docache) {
- LIST_INSERT_HEAD(&unhead[hash], un, un_cache);
- un->un_flags |= UN_CACHED;
- }
+ vref(dvp);
+ VOP_UNLOCK(dvp, 0, td);
-out:
- if (docache)
- union_list_unlock(hash);
+ if ((error = relookup(dvp, vpp, cn))) {
+ uma_zfree(namei_zone, cn->cn_pnbuf);
+ cn->cn_flags &= ~HASBUF;
+ vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td);
+ } else
+ vrele(dvp);
return (error);
}
+/*
+ * relookup for CREATE namei operation.
+ *
+ * dvp is unionfs vnode. dvp should be locked.
+ *
+ * If it called 'unionfs_copyfile' function by unionfs_link etc,
+ * VOP_LOOKUP information is broken.
+ * So it need relookup in order to create link etc.
+ */
int
-union_freevp(vp)
- struct vnode *vp;
+unionfs_relookup_for_create(struct vnode *dvp, struct componentname *cnp,
+ struct thread *td)
{
- struct union_node *un = VTOUNION(vp);
+ int error;
+ struct vnode *udvp;
+ struct vnode *vp;
+ struct componentname cn;
- if (un->un_flags & UN_CACHED) {
- un->un_flags &= ~UN_CACHED;
- LIST_REMOVE(un, un_cache);
- }
+ udvp = UNIONFSVPTOUPPERVP(dvp);
+ vp = NULLVP;
- if (un->un_pvp != NULLVP) {
- vrele(un->un_pvp);
- un->un_pvp = NULL;
- }
- if (un->un_uppervp != NULLVP) {
- vrele(un->un_uppervp);
- un->un_uppervp = NULL;
- }
- if (un->un_lowervp != NULLVP) {
- vrele(un->un_lowervp);
- un->un_lowervp = NULL;
- }
- if (un->un_dirvp != NULLVP) {
- vrele(un->un_dirvp);
- un->un_dirvp = NULL;
+ error = unionfs_relookup(udvp, &vp, cnp, &cn, td, cnp->cn_nameptr,
+ strlen(cnp->cn_nameptr), CREATE);
+ if (error)
+ return (error);
+
+ if (vp != NULLVP) {
+ if (udvp == vp)
+ vrele(vp);
+ else
+ vput(vp);
+
+ error = EEXIST;
}
- if (un->un_path) {
- free(un->un_path, M_UNPATH);
- un->un_path = NULL;
+
+ if (cn.cn_flags & HASBUF) {
+ uma_zfree(namei_zone, cn.cn_pnbuf);
+ cn.cn_flags &= ~HASBUF;
}
- FREE(vp->v_data, M_TEMP);
- vp->v_data = 0;
- vp->v_object = NULL;
+ if (!error) {
+ cn.cn_flags |= (cnp->cn_flags & HASBUF);
+ cnp->cn_flags = cn.cn_flags;
+ }
- return (0);
+ return (error);
}
/*
- * copyfile. Copy the vnode (fvp) to the vnode (tvp)
- * using a sequence of reads and writes. Both (fvp)
- * and (tvp) are locked on entry and exit.
+ * relookup for DELETE namei operation.
*
- * fvp and tvp are both exclusive locked on call, but their refcount's
- * haven't been bumped at all.
+ * dvp is unionfs vnode. dvp should be locked.
*/
-static int
-union_copyfile(fvp, tvp, cred, td)
- struct vnode *fvp;
- struct vnode *tvp;
- struct ucred *cred;
- struct thread *td;
+int
+unionfs_relookup_for_delete(struct vnode *dvp, struct componentname *cnp,
+ struct thread *td)
{
- char *buf;
- struct uio uio;
- struct iovec iov;
- int error = 0;
-
- /*
- * strategy:
- * Allocate a buffer of size MAXBSIZE.
- * Loop doing reads and writes, keeping track
- * of the current uio offset.
- * Give up at the first sign of trouble.
- */
-
- bzero(&uio, sizeof(uio));
-
- uio.uio_td = td;
- uio.uio_segflg = UIO_SYSSPACE;
- uio.uio_offset = 0;
-
- VOP_LEASE(fvp, td, cred, LEASE_READ);
- VOP_LEASE(tvp, td, cred, LEASE_WRITE);
-
- buf = malloc(MAXBSIZE, M_TEMP, M_WAITOK);
+ int error;
+ struct vnode *udvp;
+ struct vnode *vp;
+ struct componentname cn;
- /* ugly loop follows... */
- do {
- off_t offset = uio.uio_offset;
- int count;
- int bufoffset;
+ udvp = UNIONFSVPTOUPPERVP(dvp);
+ vp = NULLVP;
- /*
- * Setup for big read.
- */
- uio.uio_iov = &iov;
- uio.uio_iovcnt = 1;
- iov.iov_base = buf;
- iov.iov_len = MAXBSIZE;
- uio.uio_resid = iov.iov_len;
- uio.uio_rw = UIO_READ;
-
- if ((error = VOP_READ(fvp, &uio, 0, cred)) != 0)
- break;
+ error = unionfs_relookup(udvp, &vp, cnp, &cn, td, cnp->cn_nameptr,
+ strlen(cnp->cn_nameptr), DELETE);
+ if (error)
+ return (error);
- /*
- * Get bytes read, handle read eof case and setup for
- * write loop.
- */
- if ((count = MAXBSIZE - uio.uio_resid) == 0)
- break;
- bufoffset = 0;
+ if (vp == NULLVP)
+ error = ENOENT;
+ else {
+ if (udvp == vp)
+ vrele(vp);
+ else
+ vput(vp);
+ }
- /*
- * Write until an error occurs or our buffer has been
- * exhausted, then update the offset for the next read.
- */
- while (bufoffset < count) {
- uio.uio_iov = &iov;
- uio.uio_iovcnt = 1;
- iov.iov_base = buf + bufoffset;
- iov.iov_len = count - bufoffset;
- uio.uio_offset = offset + bufoffset;
- uio.uio_rw = UIO_WRITE;
- uio.uio_resid = iov.iov_len;
+ if (cn.cn_flags & HASBUF) {
+ uma_zfree(namei_zone, cn.cn_pnbuf);
+ cn.cn_flags &= ~HASBUF;
+ }
- if ((error = VOP_WRITE(tvp, &uio, 0, cred)) != 0)
- break;
- bufoffset += (count - bufoffset) - uio.uio_resid;
- }
- uio.uio_offset = offset + bufoffset;
- } while (error == 0);
+ if (!error) {
+ cn.cn_flags |= (cnp->cn_flags & HASBUF);
+ cnp->cn_flags = cn.cn_flags;
+ }
- free(buf, M_TEMP);
return (error);
}
/*
+ * relookup for RENAME namei operation.
*
- * un's vnode is assumed to be locked on entry and remains locked on exit.
+ * dvp is unionfs vnode. dvp should be locked.
*/
-
int
-union_copyup(un, docopy, cred, td)
- struct union_node *un;
- int docopy;
- struct ucred *cred;
- struct thread *td;
+unionfs_relookup_for_rename(struct vnode *dvp, struct componentname *cnp,
+ struct thread *td)
{
int error;
- struct mount *mp;
- struct vnode *lvp, *uvp;
+ struct vnode *udvp;
+ struct vnode *vp;
+ struct componentname cn;
- /*
- * If the user does not have read permission, the vnode should not
- * be copied to upper layer.
- */
- vn_lock(un->un_lowervp, LK_EXCLUSIVE | LK_RETRY, td);
- error = VOP_ACCESS(un->un_lowervp, VREAD, cred, td);
- VOP_UNLOCK(un->un_lowervp, 0, td);
+ udvp = UNIONFSVPTOUPPERVP(dvp);
+ vp = NULLVP;
+
+ error = unionfs_relookup(udvp, &vp, cnp, &cn, td, cnp->cn_nameptr,
+ strlen(cnp->cn_nameptr), RENAME);
if (error)
return (error);
- if ((error = vn_start_write(un->un_dirvp, &mp, V_WAIT | PCATCH)) != 0)
- return (error);
- if ((error = union_vn_create(&uvp, un, td)) != 0) {
- vn_finished_write(mp);
- return (error);
+ if (vp != NULLVP) {
+ if (udvp == vp)
+ vrele(vp);
+ else
+ vput(vp);
}
- lvp = un->un_lowervp;
-
- KASSERT(vrefcnt(uvp) > 0, ("copy: uvp refcount 0: %d", vrefcnt(uvp)));
- if (docopy) {
- /*
- * XX - should not ignore errors
- * from VOP_CLOSE()
- */
- vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, td);
- error = VOP_OPEN(lvp, FREAD, cred, td, -1);
- if (error == 0) {
- error = union_copyfile(lvp, uvp, cred, td);
- VOP_UNLOCK(lvp, 0, td);
- (void) VOP_CLOSE(lvp, FREAD, cred, td);
- }
- if (error == 0)
- UDEBUG(("union: copied up %s\n", un->un_path));
-
+ if (cn.cn_flags & HASBUF) {
+ uma_zfree(namei_zone, cn.cn_pnbuf);
+ cn.cn_flags &= ~HASBUF;
}
- VOP_UNLOCK(uvp, 0, td);
- vn_finished_write(mp);
- union_newupper(un, uvp);
- KASSERT(vrefcnt(uvp) > 0, ("copy: uvp refcount 0: %d", vrefcnt(uvp)));
- union_vn_close(uvp, FWRITE, cred, td);
- KASSERT(vrefcnt(uvp) > 0, ("copy: uvp refcount 0: %d", vrefcnt(uvp)));
- /*
- * Subsequent IOs will go to the top layer, so
- * call close on the lower vnode and open on the
- * upper vnode to ensure that the filesystem keeps
- * its references counts right. This doesn't do
- * the right thing with (cred) and (FREAD) though.
- * Ignoring error returns is not right, either.
- */
- if (error == 0) {
- int i;
- for (i = 0; i < un->un_openl; i++) {
- (void) VOP_CLOSE(lvp, FREAD, cred, td);
- (void) VOP_OPEN(uvp, FREAD, cred, td, -1);
- }
- un->un_openl = 0;
+ if (!error) {
+ cn.cn_flags |= (cnp->cn_flags & HASBUF);
+ cnp->cn_flags = cn.cn_flags;
}
return (error);
@@ -819,550 +670,567 @@ union_copyup(un, docopy, cred, td)
}
/*
- * union_relookup:
- *
- * dvp should be locked on entry and will be locked on return. No
- * net change in the ref count will occur.
- *
- * If an error is returned, *vpp will be invalid, otherwise it
- * will hold a locked, referenced vnode. If *vpp == dvp then
- * remember that only one exclusive lock is held.
+ * Update the unionfs_node.
+ *
+ * uvp is new locked upper vnode. unionfs vnode's lock will be exchanged to the
+ * uvp's lock and lower's lock will be unlocked.
*/
-
-static int
-union_relookup(um, dvp, vpp, cnp, cn, path, pathlen)
- struct union_mount *um;
- struct vnode *dvp;
- struct vnode **vpp;
- struct componentname *cnp;
- struct componentname *cn;
- char *path;
- int pathlen;
+static void
+unionfs_node_update(struct unionfs_node *unp, struct vnode *uvp,
+ struct thread *td)
{
- int error;
-
- /*
- * A new componentname structure must be faked up because
- * there is no way to know where the upper level cnp came
- * from or what it is being used for. This must duplicate
- * some of the work done by NDINIT(), some of the work done
- * by namei(), some of the work done by lookup() and some of
- * the work done by VOP_LOOKUP() when given a CREATE flag.
- * Conclusion: Horrible.
- */
- cn->cn_namelen = pathlen;
- cn->cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK);
- bcopy(path, cn->cn_pnbuf, cn->cn_namelen);
- cn->cn_pnbuf[cn->cn_namelen] = '\0';
-
- cn->cn_nameiop = CREATE;
- cn->cn_flags = (LOCKPARENT|LOCKLEAF|HASBUF|SAVENAME|ISLASTCN);
- cn->cn_thread = cnp->cn_thread;
- if (um->um_op == UNMNT_ABOVE)
- cn->cn_cred = cnp->cn_cred;
- else
- cn->cn_cred = um->um_cred;
- cn->cn_nameptr = cn->cn_pnbuf;
- cn->cn_consume = cnp->cn_consume;
+ int count, lockcnt;
+ struct vnode *vp;
+ struct vnode *lvp;
- VREF(dvp);
- VOP_UNLOCK(dvp, 0, cnp->cn_thread);
+ vp = UNIONFSTOV(unp);
+ lvp = unp->un_lowervp;
/*
- * Pass dvp unlocked and referenced on call to relookup().
- *
- * If an error occurs, dvp will be returned unlocked and dereferenced.
+ * lock update
*/
-
- if ((error = relookup(dvp, vpp, cn)) != 0) {
- vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, cnp->cn_thread);
- return(error);
- }
+ VI_LOCK(vp);
+ unp->un_uppervp = uvp;
+ vp->v_vnlock = uvp->v_vnlock;
+ lockcnt = lvp->v_vnlock->lk_exclusivecount;
+ if (lockcnt <= 0)
+ panic("unionfs: no exclusive lock");
+ VI_UNLOCK(vp);
+ for (count = 1; count < lockcnt; count++)
+ vn_lock(uvp, LK_EXCLUSIVE | LK_CANRECURSE | LK_RETRY, td);
/*
- * If no error occurs, dvp will be returned locked with the reference
- * left as before, and vpp will be returned referenced and locked.
- *
- * We want to return with dvp as it was passed to us, so we get
- * rid of our reference.
+ * cache update
*/
- vrele(dvp);
- return (0);
+ mtx_lock(&unionfs_hashmtx);
+ if (unp->un_flag & UNIONFS_CACHED)
+ LIST_REMOVE(unp, un_hash);
+ LIST_INSERT_HEAD(UNIONFS_NHASH(uvp, lvp), unp, un_hash);
+ unp->un_flag |= UNIONFS_CACHED;
+ mtx_unlock(&unionfs_hashmtx);
}
/*
- * Create a shadow directory in the upper layer.
- * The new vnode is returned locked.
- *
- * (um) points to the union mount structure for access to the
- * the mounting process's credentials.
- * (dvp) is the directory in which to create the shadow directory,
- * It is locked (but not ref'd) on entry and return.
- * (cnp) is the component name to be created.
- * (vpp) is the returned newly created shadow directory, which
- * is returned locked and ref'd
+ * Create a new shadow dir.
+ *
+ * udvp should be locked on entry and will be locked on return.
+ *
+ * If no error returned, unp will be updated.
*/
int
-union_mkshadow(um, dvp, cnp, vpp)
- struct union_mount *um;
- struct vnode *dvp;
- struct componentname *cnp;
- struct vnode **vpp;
+unionfs_mkshadowdir(struct unionfs_mount *ump, struct vnode *udvp,
+ struct unionfs_node *unp, struct componentname *cnp,
+ struct thread *td)
{
- int error;
- struct vattr va;
- struct thread *td = cnp->cn_thread;
+ int error;
+ struct vnode *lvp;
+ struct vnode *uvp;
+ struct vattr va;
+ struct vattr lva;
struct componentname cn;
- struct mount *mp;
+ struct mount *mp;
+ struct ucred *cred;
+ struct ucred *credbk;
+ struct uidinfo *rootinfo;
- if ((error = vn_start_write(dvp, &mp, V_WAIT | PCATCH)) != 0)
- return (error);
- if ((error = union_relookup(um, dvp, vpp, cnp, &cn,
- cnp->cn_nameptr, cnp->cn_namelen)) != 0) {
- vn_finished_write(mp);
- return (error);
- }
+ if (unp->un_uppervp != NULLVP)
+ return (EEXIST);
- if (*vpp) {
- if (cn.cn_flags & HASBUF) {
- uma_zfree(namei_zone, cn.cn_pnbuf);
- cn.cn_flags &= ~HASBUF;
- }
- if (dvp == *vpp)
- vrele(*vpp);
+ lvp = unp->un_lowervp;
+ uvp = NULLVP;
+ credbk = cnp->cn_cred;
+
+ /* Authority change to root */
+ rootinfo = uifind((uid_t)0);
+ cred = crdup(cnp->cn_cred);
+ chgproccnt(cred->cr_ruidinfo, 1, 0);
+ change_euid(cred, rootinfo);
+ change_ruid(cred, rootinfo);
+ change_svuid(cred, (uid_t)0);
+ uifree(rootinfo);
+ cnp->cn_cred = cred;
+
+ memset(&cn, 0, sizeof(cn));
+
+ if ((error = VOP_GETATTR(lvp, &lva, cnp->cn_cred, td)))
+ goto unionfs_mkshadowdir_abort;
+
+ if ((error = unionfs_relookup(udvp, &uvp, cnp, &cn, td, cnp->cn_nameptr, cnp->cn_namelen, CREATE)))
+ goto unionfs_mkshadowdir_abort;
+ if (uvp != NULLVP) {
+ if (udvp == uvp)
+ vrele(uvp);
else
- vput(*vpp);
+ vput(uvp);
+
+ error = EEXIST;
+ goto unionfs_mkshadowdir_free_out;
+ }
+
+ if ((error = vn_start_write(udvp, &mp, V_WAIT | PCATCH)))
+ goto unionfs_mkshadowdir_free_out;
+ if ((error = VOP_LEASE(udvp, td, cn.cn_cred, LEASE_WRITE))) {
vn_finished_write(mp);
- *vpp = NULLVP;
- return (EEXIST);
+ goto unionfs_mkshadowdir_free_out;
}
+ unionfs_create_uppervattr_core(ump, &lva, &va, td);
- /*
- * Policy: when creating the shadow directory in the
- * upper layer, create it owned by the user who did
- * the mount, group from parent directory, and mode
- * 777 modified by umask (ie mostly identical to the
- * mkdir syscall). (jsp, kb)
- */
+ error = VOP_MKDIR(udvp, &uvp, &cn, &va);
- VATTR_NULL(&va);
- va.va_type = VDIR;
- va.va_mode = um->um_cmode;
+ if (!error) {
+ unionfs_node_update(unp, uvp, td);
- /* VOP_LEASE: dvp is locked */
- VOP_LEASE(dvp, td, cn.cn_cred, LEASE_WRITE);
+ /*
+ * XXX The bug which cannot set uid/gid was corrected.
+ * Ignore errors.
+ */
+ va.va_type = VNON;
+ VOP_SETATTR(uvp, &va, cn.cn_cred, td);
+ }
+ vn_finished_write(mp);
- error = VOP_MKDIR(dvp, vpp, &cn, &va);
+unionfs_mkshadowdir_free_out:
if (cn.cn_flags & HASBUF) {
uma_zfree(namei_zone, cn.cn_pnbuf);
cn.cn_flags &= ~HASBUF;
}
- /*vput(dvp);*/
- vn_finished_write(mp);
+
+unionfs_mkshadowdir_abort:
+ cnp->cn_cred = credbk;
+ chgproccnt(cred->cr_ruidinfo, -1, 0);
+ crfree(cred);
+
return (error);
}
/*
- * Create a whiteout entry in the upper layer.
- *
- * (um) points to the union mount structure for access to the
- * the mounting process's credentials.
- * (dvp) is the directory in which to create the whiteout.
- * It is locked on entry and return.
- * (cnp) is the component name to be created.
+ * Create a new whiteout.
+ *
+ * dvp should be locked on entry and will be locked on return.
*/
int
-union_mkwhiteout(um, dvp, cnp, path)
- struct union_mount *um;
- struct vnode *dvp;
- struct componentname *cnp;
- char *path;
+unionfs_mkwhiteout(struct vnode *dvp, struct componentname *cnp,
+ struct thread *td, char *path)
{
- int error;
- struct thread *td = cnp->cn_thread;
- struct vnode *wvp;
+ int error;
+ struct vnode *wvp;
struct componentname cn;
- struct mount *mp;
+ struct mount *mp;
- if ((error = vn_start_write(dvp, &mp, V_WAIT | PCATCH)) != 0)
- return (error);
- error = union_relookup(um, dvp, &wvp, cnp, &cn, path, strlen(path));
- if (error) {
- vn_finished_write(mp);
- return (error);
- }
+ if (path == NULL)
+ path = cnp->cn_nameptr;
- if (wvp) {
+ wvp = NULLVP;
+ if ((error = unionfs_relookup(dvp, &wvp, cnp, &cn, td, path, strlen(path), CREATE)))
+ return (error);
+ if (wvp != NULLVP) {
if (cn.cn_flags & HASBUF) {
uma_zfree(namei_zone, cn.cn_pnbuf);
cn.cn_flags &= ~HASBUF;
}
- if (wvp == dvp)
+ if (dvp == wvp)
vrele(wvp);
else
vput(wvp);
- vn_finished_write(mp);
+
return (EEXIST);
}
- /* VOP_LEASE: dvp is locked */
- VOP_LEASE(dvp, td, td->td_ucred, LEASE_WRITE);
+ if ((error = vn_start_write(dvp, &mp, V_WAIT | PCATCH)))
+ goto unionfs_mkwhiteout_free_out;
+ if (!(error = VOP_LEASE(dvp, td, td->td_ucred, LEASE_WRITE)))
+ error = VOP_WHITEOUT(dvp, &cn, CREATE);
+
+ vn_finished_write(mp);
- error = VOP_WHITEOUT(dvp, &cn, CREATE);
+unionfs_mkwhiteout_free_out:
if (cn.cn_flags & HASBUF) {
uma_zfree(namei_zone, cn.cn_pnbuf);
cn.cn_flags &= ~HASBUF;
}
- vn_finished_write(mp);
+
return (error);
}
/*
- * union_vn_create: creates and opens a new shadow file
- * on the upper union layer. This function is similar
- * in spirit to calling vn_open() but it avoids calling namei().
- * The problem with calling namei() is that a) it locks too many
- * things, and b) it doesn't start at the "right" directory,
- * whereas relookup() is told where to start.
- *
- * On entry, the vnode associated with un is locked. It remains locked
- * on return.
- *
- * If no error occurs, *vpp contains a locked referenced vnode for your
- * use. If an error occurs *vpp iis undefined.
+ * Create a new vnode for create a new shadow file.
+ *
+ * If an error is returned, *vpp will be invalid, otherwise it will hold a
+ * locked, referenced and opened vnode.
+ *
+ * unp is never updated.
*/
static int
-union_vn_create(vpp, un, td)
- struct vnode **vpp;
- struct union_node *un;
- struct thread *td;
+unionfs_vn_create_on_upper(struct vnode **vpp, struct vnode *udvp,
+ struct unionfs_node *unp, struct vattr *uvap,
+ struct thread *td)
{
- struct vnode *vp;
- struct ucred *cred = td->td_ucred;
- struct vattr vat;
- struct vattr *vap = &vat;
- int fmode = FFLAGS(O_WRONLY|O_CREAT|O_TRUNC|O_EXCL);
- int error;
- int cmode;
+ struct unionfs_mount *ump;
+ struct vnode *vp;
+ struct vnode *lvp;
+ struct ucred *cred;
+ struct vattr lva;
+ int fmode;
+ int error;
struct componentname cn;
- *vpp = NULLVP;
- FILEDESC_LOCK_FAST(td->td_proc->p_fd);
- cmode = UN_FILEMODE & ~td->td_proc->p_fd->fd_cmask;
- FILEDESC_UNLOCK_FAST(td->td_proc->p_fd);
+ ump = MOUNTTOUNIONFSMOUNT(UNIONFSTOV(unp)->v_mount);
+ vp = NULLVP;
+ lvp = unp->un_lowervp;
+ cred = td->td_ucred;
+ fmode = FFLAGS(O_WRONLY | O_CREAT | O_TRUNC | O_EXCL);
+ error = 0;
- /*
- * Build a new componentname structure (for the same
- * reasons outlines in union_mkshadow()).
- * The difference here is that the file is owned by
- * the current user, rather than by the person who
- * did the mount, since the current user needs to be
- * able to write the file (that's why it is being
- * copied in the first place).
- */
- cn.cn_namelen = strlen(un->un_path);
+ if ((error = VOP_GETATTR(lvp, &lva, cred, td)) != 0)
+ return (error);
+ unionfs_create_uppervattr_core(ump, &lva, uvap, td);
+
+ if (unp->un_path == NULL)
+ panic("unionfs: un_path is null");
+
+ cn.cn_namelen = strlen(unp->un_path);
cn.cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK);
- bcopy(un->un_path, cn.cn_pnbuf, cn.cn_namelen+1);
+ bcopy(unp->un_path, cn.cn_pnbuf, cn.cn_namelen + 1);
cn.cn_nameiop = CREATE;
- cn.cn_flags = ISOPEN|LOCKPARENT|LOCKLEAF|HASBUF|SAVENAME|ISLASTCN;
+ cn.cn_flags = (LOCKPARENT | LOCKLEAF | HASBUF | SAVENAME | ISLASTCN);
+ cn.cn_lkflags = LK_EXCLUSIVE;
cn.cn_thread = td;
- cn.cn_cred = td->td_ucred;
+ cn.cn_cred = cred;
cn.cn_nameptr = cn.cn_pnbuf;
cn.cn_consume = 0;
- /*
- * Pass dvp unlocked and referenced on call to relookup().
- *
- * If an error occurs, dvp will be returned unlocked and dereferenced.
- */
- VREF(un->un_dirvp);
- error = relookup(un->un_dirvp, &vp, &cn);
- if (error)
- return (error);
+ vref(udvp);
+ if ((error = relookup(udvp, &vp, &cn)) != 0)
+ goto unionfs_vn_create_on_upper_free_out2;
+ vrele(udvp);
- /*
- * If no error occurs, dvp will be returned locked with the reference
- * left as before, and vpp will be returned referenced and locked.
- */
- if (vp) {
- vput(un->un_dirvp);
- if (cn.cn_flags & HASBUF) {
- uma_zfree(namei_zone, cn.cn_pnbuf);
- cn.cn_flags &= ~HASBUF;
- }
- if (vp == un->un_dirvp)
+ if (vp != NULLVP) {
+ if (vp == udvp)
vrele(vp);
else
vput(vp);
- return (EEXIST);
+ error = EEXIST;
+ goto unionfs_vn_create_on_upper_free_out1;
}
- /*
- * Good - there was no race to create the file
- * so go ahead and create it. The permissions
- * on the file will be 0666 modified by the
- * current user's umask. Access to the file, while
- * it is unioned, will require access to the top *and*
- * bottom files. Access when not unioned will simply
- * require access to the top-level file.
- * TODO: confirm choice of access permissions.
- */
- VATTR_NULL(vap);
- vap->va_type = VREG;
- vap->va_mode = cmode;
- VOP_LEASE(un->un_dirvp, td, cred, LEASE_WRITE);
- error = VOP_CREATE(un->un_dirvp, &vp, &cn, vap);
- if (cn.cn_flags & HASBUF) {
- uma_zfree(namei_zone, cn.cn_pnbuf);
- cn.cn_flags &= ~HASBUF;
- }
- vput(un->un_dirvp);
- if (error)
- return (error);
+ if ((error = VOP_LEASE(udvp, td, cred, LEASE_WRITE)) != 0)
+ goto unionfs_vn_create_on_upper_free_out1;
- error = VOP_OPEN(vp, fmode, cred, td, -1);
- if (error) {
+ if ((error = VOP_CREATE(udvp, &vp, &cn, uvap)) != 0)
+ goto unionfs_vn_create_on_upper_free_out1;
+
+ if ((error = VOP_OPEN(vp, fmode, cred, td, -1)) != 0) {
vput(vp);
- return (error);
+ goto unionfs_vn_create_on_upper_free_out1;
}
vp->v_writecount++;
*vpp = vp;
- return (0);
-}
-static int
-union_vn_close(vp, fmode, cred, td)
- struct vnode *vp;
- int fmode;
- struct ucred *cred;
- struct thread *td;
-{
+unionfs_vn_create_on_upper_free_out1:
+ VOP_UNLOCK(udvp, 0, td);
- if (fmode & FWRITE)
- --vp->v_writecount;
- return (VOP_CLOSE(vp, fmode, cred, td));
+unionfs_vn_create_on_upper_free_out2:
+ if (cn.cn_flags & HASBUF) {
+ uma_zfree(namei_zone, cn.cn_pnbuf);
+ cn.cn_flags &= ~HASBUF;
+ }
+
+ return (error);
}
/*
- * union_removed_upper:
- *
- * An upper-only file/directory has been removed; un-cache it so
- * that unionfs vnode gets reclaimed and the last uppervp reference
- * disappears.
- *
- * Called with union_node unlocked.
+ * Copy from lvp to uvp.
+ *
+ * lvp and uvp should be locked and opened on entry and will be locked and
+ * opened on return.
*/
-
-void
-union_removed_upper(un)
- struct union_node *un;
+static int
+unionfs_copyfile_core(struct vnode *lvp, struct vnode *uvp,
+ struct ucred *cred, struct thread *td)
{
- if (un->un_flags & UN_CACHED) {
- int hash = UNION_HASH(un->un_uppervp, un->un_lowervp);
-
- while (union_list_lock(hash))
- continue;
- un->un_flags &= ~UN_CACHED;
- LIST_REMOVE(un, un_cache);
- union_list_unlock(hash);
+ int error;
+ off_t offset;
+ int count;
+ int bufoffset;
+ char *buf;
+ struct uio uio;
+ struct iovec iov;
+
+ error = 0;
+ memset(&uio, 0, sizeof(uio));
+
+ uio.uio_td = td;
+ uio.uio_segflg = UIO_SYSSPACE;
+ uio.uio_offset = 0;
+
+ if ((error = VOP_LEASE(lvp, td, cred, LEASE_READ)) != 0)
+ return (error);
+ if ((error = VOP_LEASE(uvp, td, cred, LEASE_WRITE)) != 0)
+ return (error);
+ buf = malloc(MAXBSIZE, M_TEMP, M_WAITOK);
+
+ while (error == 0) {
+ offset = uio.uio_offset;
+
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ iov.iov_base = buf;
+ iov.iov_len = MAXBSIZE;
+ uio.uio_resid = iov.iov_len;
+ uio.uio_rw = UIO_READ;
+
+ if ((error = VOP_READ(lvp, &uio, 0, cred)) != 0)
+ break;
+ if ((count = MAXBSIZE - uio.uio_resid) == 0)
+ break;
+
+ bufoffset = 0;
+ while (bufoffset < count) {
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ iov.iov_base = buf + bufoffset;
+ iov.iov_len = count - bufoffset;
+ uio.uio_offset = offset + bufoffset;
+ uio.uio_resid = iov.iov_len;
+ uio.uio_rw = UIO_WRITE;
+
+ if ((error = VOP_WRITE(uvp, &uio, 0, cred)) != 0)
+ break;
+
+ bufoffset += (count - bufoffset) - uio.uio_resid;
+ }
+
+ uio.uio_offset = offset + bufoffset;
}
+
+ free(buf, M_TEMP);
+
+ return (error);
}
/*
- * Determine whether a whiteout is needed
- * during a remove/rmdir operation.
+ * Copy file from lower to upper.
+ *
+ * If you need copy of the contents, set 1 to docopy. Otherwise, set 0 to
+ * docopy.
+ *
+ * If no error returned, unp will be updated.
*/
int
-union_dowhiteout(un, cred, td)
- struct union_node *un;
- struct ucred *cred;
- struct thread *td;
+unionfs_copyfile(struct unionfs_node *unp, int docopy, struct ucred *cred,
+ struct thread *td)
{
- struct vattr va;
+ int error;
+ struct mount *mp;
+ struct vnode *udvp;
+ struct vnode *lvp;
+ struct vnode *uvp;
+ struct vattr uva;
+
+ lvp = unp->un_lowervp;
+ uvp = NULLVP;
+
+ if ((UNIONFSTOV(unp)->v_mount->mnt_flag & MNT_RDONLY))
+ return (EROFS);
+ if (unp->un_dvp == NULLVP)
+ return (EINVAL);
+ if (unp->un_uppervp != NULLVP)
+ return (EEXIST);
+ udvp = VTOUNIONFS(unp->un_dvp)->un_uppervp;
+ if (udvp == NULLVP)
+ return (EROFS);
+ if ((udvp->v_mount->mnt_flag & MNT_RDONLY))
+ return (EROFS);
+
+ error = VOP_ACCESS(lvp, VREAD, cred, td);
+ if (error != 0)
+ return (error);
- if (un->un_lowervp != NULLVP)
- return (1);
+ if ((error = vn_start_write(udvp, &mp, V_WAIT | PCATCH)) != 0)
+ return (error);
+ error = unionfs_vn_create_on_upper(&uvp, udvp, unp, &uva, td);
+ if (error != 0) {
+ vn_finished_write(mp);
+ return (error);
+ }
- if (VOP_GETATTR(un->un_uppervp, &va, cred, td) == 0 &&
- (va.va_flags & OPAQUE))
- return (1);
+ if (docopy != 0) {
+ error = VOP_OPEN(lvp, FREAD, cred, td, -1);
+ if (error == 0) {
+ error = unionfs_copyfile_core(lvp, uvp, cred, td);
+ VOP_CLOSE(lvp, FREAD, cred, td);
+ }
+ }
+ VOP_CLOSE(uvp, FWRITE, cred, td);
+ uvp->v_writecount--;
- return (0);
-}
+ vn_finished_write(mp);
-static void
-union_dircache_r(vp, vppp, cntp)
- struct vnode *vp;
- struct vnode ***vppp;
- int *cntp;
-{
- struct union_node *un;
-
- if (vp->v_op != &union_vnodeops) {
- if (vppp) {
- VREF(vp);
- *(*vppp)++ = vp;
- if (--(*cntp) == 0)
- panic("union: dircache table too small");
- } else {
- (*cntp)++;
- }
- } else {
- un = VTOUNION(vp);
- if (un->un_uppervp != NULLVP)
- union_dircache_r(un->un_uppervp, vppp, cntp);
- if (un->un_lowervp != NULLVP)
- union_dircache_r(un->un_lowervp, vppp, cntp);
+ if (error == 0) {
+ /* Reset the attributes. Ignore errors. */
+ uva.va_type = VNON;
+ VOP_SETATTR(uvp, &uva, cred, td);
}
+
+ unionfs_node_update(unp, uvp, td);
+
+ return (error);
}
-struct vnode *
-union_dircache_get(vp, td)
- struct vnode *vp;
- struct thread *td;
+/*
+ * It checks whether vp can rmdir. (check empty)
+ *
+ * vp is unionfs vnode.
+ * vp should be locked.
+ */
+int
+unionfs_check_rmdir(struct vnode *vp, struct ucred *cred, struct thread *td)
{
- int cnt;
- struct vnode *nvp;
- struct vnode **vpp;
- struct vnode **dircache, **newdircache;
- struct union_node *un;
- int error;
+ int error;
+ int eofflag;
+ int lookuperr;
+ struct vnode *uvp;
+ struct vnode *lvp;
+ struct vnode *tvp;
+ struct vattr va;
+ struct componentname cn;
+ /*
+ * The size of buf needs to be larger than DIRBLKSIZ.
+ */
+ char buf[256 * 6];
+ struct dirent *dp;
+ struct dirent *edp;
+ struct uio uio;
+ struct iovec iov;
- vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
- un = VTOUNION(vp);
- dircache = un->un_dircache;
- newdircache = NULL;
-
- nvp = NULLVP;
-
- if (dircache == NULL) {
- cnt = 0;
- union_dircache_r(vp, 0, &cnt);
- cnt++;
- newdircache = dircache = malloc(cnt * sizeof(struct vnode *),
- M_UNDCACHE, M_WAITOK);
- vpp = dircache;
- union_dircache_r(vp, &vpp, &cnt);
- *vpp = NULLVP;
- vpp = dircache + 1;
- } else {
- vpp = dircache;
- do {
- if (*vpp++ == un->un_uppervp)
- break;
- } while (*vpp != NULLVP);
- }
+ ASSERT_VOP_ELOCKED(vp, "unionfs_check_rmdir");
- if (*vpp == NULLVP)
- goto out;
+ eofflag = 0;
+ uvp = UNIONFSVPTOUPPERVP(vp);
+ lvp = UNIONFSVPTOLOWERVP(vp);
- /*vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY, td);*/
- UDEBUG(("ALLOCVP-3 %p ref %d\n", *vpp, (*vpp ? vrefcnt(*vpp) : -99)));
- VREF(*vpp);
- error = union_allocvp(&nvp, vp->v_mount, NULLVP, NULLVP, NULL, *vpp, NULLVP, 0);
- UDEBUG(("ALLOCVP-3B %p ref %d\n", nvp, (*vpp ? vrefcnt(*vpp) : -99)));
- if (error)
- goto out;
+ /* check opaque */
+ if ((error = VOP_GETATTR(uvp, &va, cred, td)) != 0)
+ return (error);
+ if (va.va_flags & OPAQUE)
+ return (0);
- un->un_dircache = NULL;
- VTOUNION(nvp)->un_dircache = dircache;
- newdircache = NULL;
+ /* open vnode */
+ if ((error = VOP_OPEN(vp, FREAD, cred, td, -1)) != 0)
+ return (error);
-out:
- /*
- * If we allocated a new dircache and couldn't attach
- * it to a new vp, free the resources we allocated.
- */
- if (newdircache) {
- for (vpp = newdircache; *vpp != NULLVP; vpp++)
- vrele(*vpp);
- free(newdircache, M_UNDCACHE);
- }
+ uio.uio_rw = UIO_READ;
+ uio.uio_segflg = UIO_SYSSPACE;
+ uio.uio_td = td;
+ uio.uio_offset = 0;
- VOP_UNLOCK(vp, 0, td);
- return (nvp);
-}
+#ifdef MAC
+ error = mac_check_vnode_readdir(td->td_ucred, lvp);
+#endif
+ while (!error && !eofflag) {
+ iov.iov_base = buf;
+ iov.iov_len = sizeof(buf);
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_resid = iov.iov_len;
-void
-union_dircache_free(struct union_node *un)
-{
- struct vnode **vpp;
+ error = VOP_READDIR(lvp, &uio, cred, &eofflag, NULL, NULL);
+ if (error)
+ break;
- for (vpp = un->un_dircache; *vpp != NULLVP; vpp++)
- vrele(*vpp);
- free(un->un_dircache, M_UNDCACHE);
- un->un_dircache = NULL;
-}
+ edp = (struct dirent*)&buf[sizeof(buf) - uio.uio_resid];
+ for (dp = (struct dirent*)buf; !error && dp < edp;
+ dp = (struct dirent*)((caddr_t)dp + dp->d_reclen)) {
+ if (dp->d_type == DT_WHT ||
+ (dp->d_namlen == 1 && dp->d_name[0] == '.') ||
+ (dp->d_namlen == 2 && !bcmp(dp->d_name, "..", 2)))
+ continue;
-/*
- * Module glue to remove #ifdef UNION from vfs_syscalls.c
- */
-static int
-union_dircheck(struct thread *td, struct vnode **vp, struct file *fp)
-{
- int error = 0;
+ cn.cn_namelen = dp->d_namlen;
+ cn.cn_pnbuf = NULL;
+ cn.cn_nameptr = dp->d_name;
+ cn.cn_nameiop = LOOKUP;
+ cn.cn_flags = (LOCKPARENT | LOCKLEAF | SAVENAME | RDONLY | ISLASTCN);
+ cn.cn_lkflags = LK_EXCLUSIVE;
+ cn.cn_thread = td;
+ cn.cn_cred = cred;
+ cn.cn_consume = 0;
- if ((*vp)->v_op == &union_vnodeops) {
- struct vnode *lvp;
+ /*
+ * check entry in lower.
+ * Sometimes, readdir function returns
+ * wrong entry.
+ */
+ lookuperr = VOP_LOOKUP(lvp, &tvp, &cn);
- lvp = union_dircache_get(*vp, td);
- if (lvp != NULLVP) {
- struct vattr va;
+ if (!lookuperr)
+ vput(tvp);
+ else
+ continue; /* skip entry */
/*
- * If the directory is opaque,
- * then don't show lower entries
+ * check entry
+ * If it has no exist/whiteout entry in upper,
+ * directory is not empty.
*/
- error = VOP_GETATTR(*vp, &va, fp->f_cred, td);
- if (va.va_flags & OPAQUE) {
- vput(lvp);
- lvp = NULLVP;
- }
- }
+ cn.cn_flags = (LOCKPARENT | LOCKLEAF | SAVENAME | RDONLY | ISLASTCN);
+ lookuperr = VOP_LOOKUP(uvp, &tvp, &cn);
- if (lvp != NULLVP) {
- error = VOP_OPEN(lvp, FREAD, fp->f_cred, td, -1);
- if (error) {
- vput(lvp);
- return (error);
- }
- VOP_UNLOCK(lvp, 0, td);
- FILE_LOCK(fp);
- fp->f_vnode = lvp;
- fp->f_data = lvp;
- fp->f_offset = 0;
- FILE_UNLOCK(fp);
- error = vn_close(*vp, FREAD, fp->f_cred, td);
- if (error)
- return (error);
- *vp = lvp;
- return -1; /* goto unionread */
+ if (!lookuperr)
+ vput(tvp);
+
+ /* ignore exist or whiteout entry */
+ if (!lookuperr ||
+ (lookuperr == ENOENT && (cn.cn_flags & ISWHITEOUT)))
+ continue;
+
+ error = ENOTEMPTY;
}
}
- return error;
+
+ /* close vnode */
+ VOP_CLOSE(vp, FREAD, cred, td);
+
+ return (error);
}
-static int
-union_modevent(module_t mod, int type, void *data)
+#ifdef DIAGNOSTIC
+
+struct vnode *
+unionfs_checkuppervp(struct vnode *vp, char *fil, int lno)
{
- switch (type) {
- case MOD_LOAD:
- union_dircheckp = union_dircheck;
- break;
- case MOD_UNLOAD:
- union_dircheckp = NULL;
- break;
- default:
- return EOPNOTSUPP;
- break;
- }
- return 0;
+ struct unionfs_node *unp;
+
+ unp = VTOUNIONFS(vp);
+
+#ifdef notyet
+ if (vp->v_op != unionfs_vnodeop_p) {
+ printf("unionfs_checkuppervp: on non-unionfs-node.\n");
+#ifdef KDB
+ kdb_enter("unionfs_checkuppervp: on non-unionfs-node.\n");
+#endif
+ panic("unionfs_checkuppervp");
+ };
+#endif
+ return (unp->un_uppervp);
}
-static moduledata_t union_mod = {
- "union_dircheck",
- union_modevent,
- NULL
-};
-
-DECLARE_MODULE(union_dircheck, union_mod, SI_SUB_VFS, SI_ORDER_ANY);
+struct vnode *
+unionfs_checklowervp(struct vnode *vp, char *fil, int lno)
+{
+ struct unionfs_node *unp;
+
+ unp = VTOUNIONFS(vp);
+
+#ifdef notyet
+ if (vp->v_op != unionfs_vnodeop_p) {
+ printf("unionfs_checklowervp: on non-unionfs-node.\n");
+#ifdef KDB
+ kdb_enter("unionfs_checklowervp: on non-unionfs-node.\n");
+#endif
+ panic("unionfs_checklowervp");
+ };
+#endif
+ return (unp->un_lowervp);
+}
+#endif
diff --git a/sys/fs/unionfs/union_vfsops.c b/sys/fs/unionfs/union_vfsops.c
index 1d0d58e..2aa7a9c 100644
--- a/sys/fs/unionfs/union_vfsops.c
+++ b/sys/fs/unionfs/union_vfsops.c
@@ -1,6 +1,8 @@
/*-
* Copyright (c) 1994, 1995 The Regents of the University of California.
* Copyright (c) 1994, 1995 Jan-Simon Pendry.
+ * Copyright (c) 2005, 2006 Masanori Ozawa <ozawa@ongs.co.jp>, ONGS Inc.
+ * Copyright (c) 2006 Daichi Goto <daichi@freebsd.org>
* All rights reserved.
*
* This code is derived from software donated to Berkeley by
@@ -34,437 +36,438 @@
* $FreeBSD$
*/
-/*
- * Union Layer
- */
-
#include <sys/param.h>
#include <sys/systm.h>
+#include <sys/kdb.h>
#include <sys/kernel.h>
#include <sys/lock.h>
-#include <sys/mutex.h>
-#include <sys/proc.h>
-#include <sys/vnode.h>
+#include <sys/malloc.h>
#include <sys/mount.h>
#include <sys/namei.h>
-#include <sys/malloc.h>
-#include <sys/filedesc.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/stat.h>
+
#include <fs/unionfs/union.h>
-static MALLOC_DEFINE(M_UNIONFSMNT, "union_mount", "UNION mount structure");
+static MALLOC_DEFINE(M_UNIONFSMNT, "UNIONFS mount", "UNIONFS mount structure");
-extern vfs_init_t union_init;
-static vfs_root_t union_root;
-static vfs_mount_t union_mount;
-static vfs_statfs_t union_statfs;
-static vfs_unmount_t union_unmount;
+static vfs_fhtovp_t unionfs_fhtovp;
+static vfs_checkexp_t unionfs_checkexp;
+static vfs_mount_t unionfs_domount;
+static vfs_quotactl_t unionfs_quotactl;
+static vfs_root_t unionfs_root;
+static vfs_sync_t unionfs_sync;
+static vfs_statfs_t unionfs_statfs;
+static vfs_unmount_t unionfs_unmount;
+static vfs_vget_t unionfs_vget;
+static vfs_vptofh_t unionfs_vptofh;
+static vfs_extattrctl_t unionfs_extattrctl;
+
+static struct vfsops unionfs_vfsops;
/*
- * Mount union filesystem.
+ * Exchange from userland file mode to vmode.
+ */
+static u_short
+mode2vmode(mode_t mode)
+{
+ u_short ret;
+
+ ret = 0;
+
+ /* other */
+ if (mode & S_IXOTH)
+ ret |= VEXEC >> 6;
+ if (mode & S_IWOTH)
+ ret |= VWRITE >> 6;
+ if (mode & S_IROTH)
+ ret |= VREAD >> 6;
+
+ /* group */
+ if (mode & S_IXGRP)
+ ret |= VEXEC >> 3;
+ if (mode & S_IWGRP)
+ ret |= VWRITE >> 3;
+ if (mode & S_IRGRP)
+ ret |= VREAD >> 3;
+
+ /* owner */
+ if (mode & S_IXUSR)
+ ret |= VEXEC;
+ if (mode & S_IWUSR)
+ ret |= VWRITE;
+ if (mode & S_IRUSR)
+ ret |= VREAD;
+
+ return (ret);
+}
+
+/*
+ * Mount unionfs layer.
*/
static int
-union_mount(mp, td)
- struct mount *mp;
- struct thread *td;
+unionfs_domount(struct mount *mp, struct thread *td)
{
- int error = 0;
- struct vfsoptlist *opts;
- struct vnode *lowerrootvp = NULLVP;
- struct vnode *upperrootvp = NULLVP;
- struct union_mount *um = 0;
- struct vattr va;
- char *cp = 0, *target;
- int op;
- int len;
- size_t size;
+ int error;
+ struct vnode *lowerrootvp;
+ struct vnode *upperrootvp;
+ struct unionfs_mount *ump;
+ char *target;
+ char *tmp;
+ char *ep;
+ int len;
+ size_t done;
+ int below;
+ uid_t uid;
+ gid_t gid;
+ u_short udir;
+ u_short ufile;
+ unionfs_copymode copymode;
struct componentname fakecn;
- struct nameidata nd, *ndp = &nd;
+ struct nameidata nd, *ndp;
+ struct vattr va;
- UDEBUG(("union_mount(mp = %p)\n", (void *)mp));
+ UNIONFSDEBUG("unionfs_mount(mp = %p)\n", (void *)mp);
- opts = mp->mnt_optnew;
- /*
- * Disable clustered write, otherwise system becomes unstable.
- */
- MNT_ILOCK(mp);
- mp->mnt_flag |= MNT_NOCLUSTERW;
- MNT_IUNLOCK(mp);
+ error = 0;
+ below = 0;
+ uid = 0;
+ gid = 0;
+ udir = 0;
+ ufile = 0;
+ copymode = UNIONFS_TRADITIONAL; /* default */
+ ndp = &nd;
if (mp->mnt_flag & MNT_ROOTFS)
return (EOPNOTSUPP);
+
/*
- * Update is a no-op
+ * Update is a no operation.
*/
if (mp->mnt_flag & MNT_UPDATE)
- /*
- * Need to provide:
- * 1. a way to convert between rdonly and rdwr mounts.
- * 2. support for nfs exports.
- */
return (EOPNOTSUPP);
/*
- * Get arguments.
+ * Get argument
*/
- error = vfs_getopt(opts, "target", (void **)&target, &len);
- if (error || target[len - 1] != '\0')
+ error = vfs_getopt(mp->mnt_optnew, "target", (void **)&target, &len);
+ if (error)
+ error = vfs_getopt(mp->mnt_optnew, "from", (void **)&target,
+ &len);
+ if (error || target[len - 1] != '\0') {
+ vfs_mount_error(mp, "Invalid target");
return (EINVAL);
-
- op = 0;
- if (vfs_getopt(opts, "below", NULL, NULL) == 0)
- op = UNMNT_BELOW;
- if (vfs_getopt(opts, "replace", NULL, NULL) == 0) {
- /* These options are mutually exclusive. */
- if (op)
+ }
+ if (vfs_getopt(mp->mnt_optnew, "below", NULL, NULL) == 0)
+ below = 1;
+ if (vfs_getopt(mp->mnt_optnew, "udir", (void **)&tmp, NULL) == 0) {
+ if (tmp != NULL)
+ udir = (mode_t)strtol(tmp, &ep, 8);
+ if (tmp == NULL || *ep) {
+ vfs_mount_error(mp, "Invalid udir");
return (EINVAL);
- op = UNMNT_REPLACE;
+ }
+ udir = mode2vmode(udir);
}
- /*
- * UNMNT_ABOVE is the default.
- */
- if (op == 0)
- op = UNMNT_ABOVE;
-
- /*
- * Obtain lower vnode. Vnode is stored in mp->mnt_vnodecovered.
- * We need to reference it but not lock it.
- */
- lowerrootvp = mp->mnt_vnodecovered;
- VREF(lowerrootvp);
- /*
- * Obtain upper vnode by calling namei() on the path. The
- * upperrootvp will be turned referenced and locked.
- */
- NDINIT(ndp, LOOKUP, FOLLOW|LOCKLEAF, UIO_SYSSPACE, target, td);
- error = namei(ndp);
+ if (vfs_getopt(mp->mnt_optnew, "ufile", (void **)&tmp, NULL) == 0) {
+ if (tmp != NULL)
+ ufile = (mode_t)strtol(tmp, &ep, 8);
+ if (tmp == NULL || *ep) {
+ vfs_mount_error(mp, "Invalid ufile");
+ return (EINVAL);
+ }
+ ufile = mode2vmode(ufile);
+ }
+ /* check umask, uid and gid */
+ if (udir == 0 && ufile != 0)
+ udir = ufile;
+ if (ufile == 0 && udir != 0)
+ ufile = udir;
+
+ vn_lock(mp->mnt_vnodecovered, LK_SHARED | LK_RETRY, td);
+ error = VOP_GETATTR(mp->mnt_vnodecovered, &va, mp->mnt_cred, td);
+ if (!error) {
+ if (udir == 0)
+ udir = va.va_mode;
+ if (ufile == 0)
+ ufile = va.va_mode;
+ uid = va.va_uid;
+ gid = va.va_gid;
+ }
+ VOP_UNLOCK(mp->mnt_vnodecovered, 0, td);
if (error)
- goto bad;
- NDFREE(ndp, NDF_ONLY_PNBUF);
- upperrootvp = ndp->ni_vp;
-
- UDEBUG(("mount_root UPPERVP %p locked = %d\n", upperrootvp,
- VOP_ISLOCKED(upperrootvp, NULL)));
+ return (error);
- /*
- * Check multi union mount to avoid `lock myself again' panic.
- * Also require that it be a directory.
- */
- if (upperrootvp == VTOUNION(lowerrootvp)->un_uppervp) {
-#ifdef DIAGNOSTIC
- printf("union_mount: multi union mount?\n");
-#endif
- error = EDEADLK;
- goto bad;
+ if (mp->mnt_cred->cr_ruid == 0) { /* root only */
+ if (vfs_getopt(mp->mnt_optnew, "uid", (void **)&tmp,
+ NULL) == 0) {
+ if (tmp != NULL)
+ uid = (uid_t)strtol(tmp, &ep, 10);
+ if (tmp == NULL || *ep) {
+ vfs_mount_error(mp, "Invalid uid");
+ return (EINVAL);
+ }
+ }
+ if (vfs_getopt(mp->mnt_optnew, "gid", (void **)&tmp,
+ NULL) == 0) {
+ if (tmp != NULL)
+ gid = (gid_t)strtol(tmp, &ep, 10);
+ if (tmp == NULL || *ep) {
+ vfs_mount_error(mp, "Invalid gid");
+ return (EINVAL);
+ }
+ }
+ if (vfs_getopt(mp->mnt_optnew, "copymode", (void **)&tmp,
+ NULL) == 0) {
+ if (tmp == NULL) {
+ vfs_mount_error(mp, "Invalid copymode");
+ return (EINVAL);
+ } else if (strcasecmp(tmp, "traditional") == 0)
+ copymode = UNIONFS_TRADITIONAL;
+ else if (strcasecmp(tmp, "transparent") == 0)
+ copymode = UNIONFS_TRANSPARENT;
+ else if (strcasecmp(tmp, "masquerade") == 0)
+ copymode = UNIONFS_MASQUERADE;
+ else {
+ vfs_mount_error(mp, "Invalid copymode");
+ return (EINVAL);
+ }
+ }
}
-
- if (upperrootvp->v_type != VDIR) {
- error = EINVAL;
- goto bad;
+ /* If copymode is UNIONFS_TRADITIONAL, uid/gid is mounted user. */
+ if (copymode == UNIONFS_TRADITIONAL) {
+ uid = mp->mnt_cred->cr_ruid;
+ gid = mp->mnt_cred->cr_rgid;
}
+ UNIONFSDEBUG("unionfs_mount: uid=%d, gid=%d\n", uid, gid);
+ UNIONFSDEBUG("unionfs_mount: udir=0%03o, ufile=0%03o\n", udir, ufile);
+ UNIONFSDEBUG("unionfs_mount: copymode=%d\n", copymode);
+
/*
- * Allocate our union_mount structure and populate the fields.
- * The vnode references are stored in the union_mount as held,
- * unlocked references. Depending on the _BELOW flag, the
- * filesystems are viewed in a different order. In effect this
- * is the same as providing a mount-under option to the mount
- * syscall.
+ * Find upper node
*/
+ NDINIT(ndp, LOOKUP, FOLLOW | WANTPARENT | LOCKLEAF, UIO_SYSSPACE, target, td);
+ if ((error = namei(ndp)))
+ return (error);
- um = (struct union_mount *) malloc(sizeof(struct union_mount),
- M_UNIONFSMNT, M_WAITOK | M_ZERO);
-
- um->um_op = op;
+ NDFREE(ndp, NDF_ONLY_PNBUF);
- error = VOP_GETATTR(upperrootvp, &va, td->td_ucred, td);
- if (error)
- goto bad;
+ /* get root vnodes */
+ lowerrootvp = mp->mnt_vnodecovered;
+ upperrootvp = ndp->ni_vp;
- um->um_upperdev = va.va_fsid;
+ vrele(ndp->ni_dvp);
+ ndp->ni_dvp = NULLVP;
- switch (um->um_op) {
- case UNMNT_ABOVE:
- um->um_lowervp = lowerrootvp;
- um->um_uppervp = upperrootvp;
- upperrootvp = NULL;
- lowerrootvp = NULL;
- break;
+ /* create unionfs_mount */
+ ump = (struct unionfs_mount *)malloc(sizeof(struct unionfs_mount),
+ M_UNIONFSMNT, M_WAITOK | M_ZERO);
- case UNMNT_BELOW:
+ /*
+ * Save reference
+ */
+ if (below) {
VOP_UNLOCK(upperrootvp, 0, td);
- vn_lock(lowerrootvp, LK_RETRY|LK_EXCLUSIVE, td);
- um->um_lowervp = upperrootvp;
- um->um_uppervp = lowerrootvp;
- upperrootvp = NULL;
- lowerrootvp = NULL;
- break;
-
- case UNMNT_REPLACE:
- vrele(lowerrootvp);
- lowerrootvp = NULL;
- um->um_uppervp = upperrootvp;
- um->um_lowervp = lowerrootvp;
- upperrootvp = NULL;
- break;
-
- default:
- error = EINVAL;
- goto bad;
+ vn_lock(lowerrootvp, LK_EXCLUSIVE | LK_RETRY, td);
+ ump->um_lowervp = upperrootvp;
+ ump->um_uppervp = lowerrootvp;
+ } else {
+ ump->um_lowervp = lowerrootvp;
+ ump->um_uppervp = upperrootvp;
}
+ ump->um_rootvp = NULLVP;
+ ump->um_uid = uid;
+ ump->um_gid = gid;
+ ump->um_udir = udir;
+ ump->um_ufile = ufile;
+ ump->um_copymode = copymode;
+
+ mp->mnt_data = (qaddr_t)ump;
/*
- * Unless the mount is readonly, ensure that the top layer
- * supports whiteout operations.
+ * Copy upper layer's RDONLY flag.
+ */
+ mp->mnt_flag |= ump->um_uppervp->v_mount->mnt_flag & MNT_RDONLY;
+
+ /*
+ * Check whiteout
*/
if ((mp->mnt_flag & MNT_RDONLY) == 0) {
- /*
- * XXX Fake up a struct componentname with only cn_nameiop
- * and cn_thread valid; union_whiteout() needs to use the
- * thread pointer to lock the vnode.
- */
- bzero(&fakecn, sizeof(fakecn));
+ memset(&fakecn, 0, sizeof(fakecn));
fakecn.cn_nameiop = LOOKUP;
fakecn.cn_thread = td;
- error = VOP_WHITEOUT(um->um_uppervp, &fakecn, LOOKUP);
- if (error)
- goto bad;
+ error = VOP_WHITEOUT(ump->um_uppervp, &fakecn, LOOKUP);
+ if (error) {
+ if (below) {
+ VOP_UNLOCK(ump->um_uppervp, 0, td);
+ vrele(upperrootvp);
+ } else
+ vput(ump->um_uppervp);
+ free(ump, M_UNIONFSMNT);
+ mp->mnt_data = NULL;
+ return (error);
+ }
}
- VOP_UNLOCK(um->um_uppervp, 0, td);
- um->um_cred = crhold(td->td_ucred);
- FILEDESC_LOCK_FAST(td->td_proc->p_fd);
- um->um_cmode = UN_DIRMODE &~ td->td_proc->p_fd->fd_cmask;
- FILEDESC_UNLOCK_FAST(td->td_proc->p_fd);
+ /*
+ * Unlock the node
+ */
+ VOP_UNLOCK(ump->um_uppervp, 0, td);
/*
- * Depending on what you think the MNT_LOCAL flag might mean,
- * you may want the && to be || on the conditional below.
- * At the moment it has been defined that the filesystem is
- * only local if it is all local, ie the MNT_LOCAL flag implies
- * that the entire namespace is local. If you think the MNT_LOCAL
- * flag implies that some of the files might be stored locally
- * then you will want to change the conditional.
+ * Get the unionfs root vnode.
*/
- if (um->um_op == UNMNT_ABOVE) {
- if (((um->um_lowervp == NULLVP) ||
- (um->um_lowervp->v_mount->mnt_flag & MNT_LOCAL)) &&
- (um->um_uppervp->v_mount->mnt_flag & MNT_LOCAL)) {
- MNT_ILOCK(mp);
- mp->mnt_flag |= MNT_LOCAL;
- MNT_IUNLOCK(mp);
- }
+ error = unionfs_nodeget(mp, ump->um_uppervp, ump->um_lowervp,
+ NULLVP, &(ump->um_rootvp), NULL, td);
+ if (error) {
+ vrele(upperrootvp);
+ free(ump, M_UNIONFSMNT);
+ mp->mnt_data = NULL;
+ return (error);
}
/*
- * Copy in the upper layer's RDONLY flag. This is for the benefit
- * of lookup() which explicitly checks the flag, rather than asking
- * the filesystem for its own opinion. This means, that an update
- * mount of the underlying filesystem to go from rdonly to rdwr
- * will leave the unioned view as read-only.
+ * Check mnt_flag
*/
- MNT_ILOCK(mp);
- mp->mnt_flag |= (um->um_uppervp->v_mount->mnt_flag & MNT_RDONLY);
- MNT_IUNLOCK(mp);
+ if ((ump->um_lowervp->v_mount->mnt_flag & MNT_LOCAL) &&
+ (ump->um_uppervp->v_mount->mnt_flag & MNT_LOCAL))
+ mp->mnt_flag |= MNT_LOCAL;
- mp->mnt_data = (qaddr_t) um;
+ /*
+ * Get new fsid
+ */
vfs_getnewfsid(mp);
- switch (um->um_op) {
- case UNMNT_ABOVE:
- cp = "<above>:";
- break;
- case UNMNT_BELOW:
- cp = "<below>:";
- break;
- case UNMNT_REPLACE:
- cp = "";
- break;
- }
- len = strlen(cp);
- bcopy(cp, mp->mnt_stat.f_mntfromname, len);
+ len = MNAMELEN - 1;
+ tmp = mp->mnt_stat.f_mntfromname;
+ copystr((below ? "<below>:" : "<above>:"), tmp, len, &done);
+ len -= done - 1;
+ tmp += done - 1;
+ copystr(target, tmp, len, NULL);
- cp = mp->mnt_stat.f_mntfromname + len;
- len = MNAMELEN - len;
+ UNIONFSDEBUG("unionfs_mount: from %s, on %s\n",
+ mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
- (void) copystr(target, cp, len - 1, &size);
- bzero(cp + size, len - size);
-
- UDEBUG(("union_mount: from %s, on %s\n",
- mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname));
return (0);
-
-bad:
- if (um) {
- if (um->um_uppervp)
- vput(um->um_uppervp);
- if (um->um_lowervp)
- vrele(um->um_lowervp);
- /* XXX other fields */
- free(um, M_UNIONFSMNT);
- }
- if (upperrootvp)
- vput(upperrootvp);
- if (lowerrootvp)
- vrele(lowerrootvp);
- return (error);
}
/*
- * Free reference to union layer.
+ * Free reference to unionfs layer
*/
static int
-union_unmount(mp, mntflags, td)
- struct mount *mp;
- int mntflags;
- struct thread *td;
+unionfs_unmount(struct mount *mp, int mntflags, struct thread *td)
{
- struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
- int error;
- int freeing;
- int flags = 0;
+ struct unionfs_mount *ump;
+ int error;
+ int num;
+ int freeing;
+ int flags;
+
+ UNIONFSDEBUG("unionfs_unmount: mp = %p\n", (void *)mp);
- UDEBUG(("union_unmount(mp = %p)\n", (void *)mp));
+ ump = MOUNTTOUNIONFSMOUNT(mp);
+ flags = 0;
if (mntflags & MNT_FORCE)
flags |= FORCECLOSE;
- /*
- * Keep flushing vnodes from the mount list.
- * This is needed because of the un_pvp held
- * reference to the parent vnode.
- * If more vnodes have been freed on a given pass,
- * the try again. The loop will iterate at most
- * (d) times, where (d) is the maximum tree depth
- * in the filesystem.
- */
- for (freeing = 0; (error = vflush(mp, 0, flags, td)) != 0;) {
- int n;
-
- /* count #vnodes held on mount list */
- n = mp->mnt_nvnodelistsize;
-
- /* if this is unchanged then stop */
- if (n == freeing)
+ /* vflush (no need to call vrele) */
+ for (freeing = 0; (error = vflush(mp, 1, flags, td)) != 0;) {
+ num = mp->mnt_nvnodelistsize;
+ if (num == freeing)
break;
-
- /* otherwise try once more time */
- freeing = n;
+ freeing = num;
}
- /*
- * If the most recent vflush failed, the filesystem is still busy.
- */
if (error)
return (error);
- /*
- * Discard references to upper and lower target vnodes.
- */
- if (um->um_lowervp)
- vrele(um->um_lowervp);
- vrele(um->um_uppervp);
- crfree(um->um_cred);
- /*
- * Finally, throw away the union_mount structure.
- */
- free(mp->mnt_data, M_UNIONFSMNT); /* XXX */
+ free(ump, M_UNIONFSMNT);
mp->mnt_data = 0;
+
return (0);
}
static int
-union_root(mp, flags, vpp, td)
- struct mount *mp;
- int flags;
- struct vnode **vpp;
- struct thread *td;
+unionfs_root(struct mount *mp, int flags, struct vnode **vpp, struct thread *td)
{
- struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
- int error;
+ struct unionfs_mount *ump;
+ struct unionfs_node *unp;
+ struct vnode *vp;
- /*
- * Supply an unlocked reference to um_uppervp and to um_lowervp. It
- * is possible for um_uppervp to be locked without the associated
- * root union_node being locked. We let union_allocvp() deal with
- * it.
- */
- UDEBUG(("union_root UPPERVP %p locked = %d\n", um->um_uppervp,
- VOP_ISLOCKED(um->um_uppervp, NULL)));
+ ump = MOUNTTOUNIONFSMOUNT(mp);
+ vp = ump->um_rootvp;
+ unp = VTOUNIONFS(vp);
+
+ UNIONFSDEBUG("unionfs_root: rootvp=%p locked=%x\n",
+ vp, VOP_ISLOCKED(vp, td));
- VREF(um->um_uppervp);
- if (um->um_lowervp)
- VREF(um->um_lowervp);
+ vref(vp);
+ if (flags & LK_TYPE_MASK)
+ vn_lock(vp, flags, td);
- error = union_allocvp(vpp, mp, NULLVP, NULLVP, NULL,
- um->um_uppervp, um->um_lowervp, 1);
- UDEBUG(("error %d\n", error));
- UDEBUG(("union_root2 UPPERVP %p locked = %d\n", um->um_uppervp,
- VOP_ISLOCKED(um->um_uppervp, NULL)));
+ *vpp = vp;
- return (error);
+ return (0);
}
static int
-union_statfs(mp, sbp, td)
- struct mount *mp;
- struct statfs *sbp;
- struct thread *td;
+unionfs_quotactl(struct mount *mp, int cmd, uid_t uid, void *arg,
+ struct thread *td)
{
- int error;
- struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
- struct statfs mstat;
- int lbsize;
+ struct unionfs_mount *ump;
- UDEBUG(("union_statfs(mp = %p, lvp = %p, uvp = %p)\n",
- (void *)mp, (void *)um->um_lowervp, (void *)um->um_uppervp));
+ ump = MOUNTTOUNIONFSMOUNT(mp);
+
+ /*
+ * Writing is always performed to upper vnode.
+ */
+ return (VFS_QUOTACTL(ump->um_uppervp->v_mount, cmd, uid, arg, td));
+}
+
+static int
+unionfs_statfs(struct mount *mp, struct statfs *sbp, struct thread *td)
+{
+ struct unionfs_mount *ump;
+ int error;
+ struct statfs mstat;
+ uint64_t lbsize;
+
+ ump = MOUNTTOUNIONFSMOUNT(mp);
+
+ UNIONFSDEBUG("unionfs_statfs(mp = %p, lvp = %p, uvp = %p)\n",
+ (void *)mp, (void *)ump->um_lowervp, (void *)ump->um_uppervp);
bzero(&mstat, sizeof(mstat));
- if (um->um_lowervp) {
- error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, td);
- if (error)
- return (error);
- }
+ error = VFS_STATFS(ump->um_lowervp->v_mount, &mstat, td);
+ if (error)
+ return (error);
- /*
- * Now copy across the "interesting" information and fake the rest.
- */
-#if 0
- sbp->f_type = mstat.f_type;
- sbp->f_flags = mstat.f_flags;
- sbp->f_bsize = mstat.f_bsize;
- sbp->f_iosize = mstat.f_iosize;
-#endif
- lbsize = mstat.f_bsize;
+ /* now copy across the "interesting" information and fake the rest */
sbp->f_blocks = mstat.f_blocks;
- sbp->f_bfree = mstat.f_bfree;
- sbp->f_bavail = mstat.f_bavail;
sbp->f_files = mstat.f_files;
- sbp->f_ffree = mstat.f_ffree;
- error = VFS_STATFS(um->um_uppervp->v_mount, &mstat, td);
+ lbsize = mstat.f_bsize;
+
+ error = VFS_STATFS(ump->um_uppervp->v_mount, &mstat, td);
if (error)
return (error);
+ /*
+ * The FS type etc is copy from upper vfs.
+ * (write able vfs have priority)
+ */
+ sbp->f_type = mstat.f_type;
sbp->f_flags = mstat.f_flags;
sbp->f_bsize = mstat.f_bsize;
sbp->f_iosize = mstat.f_iosize;
- /*
- * If the lower and upper blocksizes differ, then frig the
- * block counts so that the sizes reported by df make some
- * kind of sense. None of this makes sense though.
- */
-
if (mstat.f_bsize != lbsize)
- sbp->f_blocks = ((off_t) sbp->f_blocks * lbsize) / mstat.f_bsize;
+ sbp->f_blocks = ((off_t)sbp->f_blocks * lbsize) / mstat.f_bsize;
- /*
- * The "total" fields count total resources in all layers,
- * the "free" fields count only those resources which are
- * free in the upper layer (since only the upper layer
- * is writeable).
- */
sbp->f_blocks += mstat.f_blocks;
sbp->f_bfree = mstat.f_bfree;
sbp->f_bavail = mstat.f_bavail;
@@ -473,12 +476,71 @@ union_statfs(mp, sbp, td)
return (0);
}
-static struct vfsops union_vfsops = {
- .vfs_init = union_init,
- .vfs_mount = union_mount,
- .vfs_root = union_root,
- .vfs_statfs = union_statfs,
- .vfs_unmount = union_unmount,
+static int
+unionfs_sync(struct mount *mp, int waitfor, struct thread *td)
+{
+ /* nothing to do */
+ return (0);
+}
+
+static int
+unionfs_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp)
+{
+ return (EOPNOTSUPP);
+}
+
+static int
+unionfs_fhtovp(struct mount *mp, struct fid *fidp, struct vnode **vpp)
+{
+ return (EOPNOTSUPP);
+}
+
+static int
+unionfs_checkexp(struct mount *mp, struct sockaddr *nam, int *extflagsp,
+ struct ucred **credanonp)
+{
+ return (EOPNOTSUPP);
+}
+
+static int
+unionfs_vptofh(struct vnode *vp, struct fid *fhp)
+{
+ return (EOPNOTSUPP);
+}
+
+static int
+unionfs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp,
+ int namespace, const char *attrname, struct thread *td)
+{
+ struct unionfs_mount *ump;
+ struct unionfs_node *unp;
+
+ ump = MOUNTTOUNIONFSMOUNT(mp);
+ unp = VTOUNIONFS(filename_vp);
+
+ if (unp->un_uppervp != NULLVP) {
+ return (VFS_EXTATTRCTL(ump->um_uppervp->v_mount, cmd,
+ unp->un_uppervp, namespace, attrname, td));
+ } else {
+ return (VFS_EXTATTRCTL(ump->um_lowervp->v_mount, cmd,
+ unp->un_lowervp, namespace, attrname, td));
+ }
+}
+
+static struct vfsops unionfs_vfsops = {
+ .vfs_checkexp = unionfs_checkexp,
+ .vfs_extattrctl = unionfs_extattrctl,
+ .vfs_fhtovp = unionfs_fhtovp,
+ .vfs_init = unionfs_init,
+ .vfs_mount = unionfs_domount,
+ .vfs_quotactl = unionfs_quotactl,
+ .vfs_root = unionfs_root,
+ .vfs_statfs = unionfs_statfs,
+ .vfs_sync = unionfs_sync,
+ .vfs_uninit = unionfs_uninit,
+ .vfs_unmount = unionfs_unmount,
+ .vfs_vget = unionfs_vget,
+ .vfs_vptofh = unionfs_vptofh,
};
-VFS_SET(union_vfsops, unionfs, VFCF_LOOPBACK);
+VFS_SET(unionfs_vfsops, unionfs, VFCF_LOOPBACK);
diff --git a/sys/fs/unionfs/union_vnops.c b/sys/fs/unionfs/union_vnops.c
index 30dbc53..b69baba 100644
--- a/sys/fs/unionfs/union_vnops.c
+++ b/sys/fs/unionfs/union_vnops.c
@@ -1,7 +1,10 @@
/*-
* Copyright (c) 1992, 1993, 1994, 1995 Jan-Simon Pendry.
* Copyright (c) 1992, 1993, 1994, 1995
- * The Regents of the University of California. All rights reserved.
+ * The Regents of the University of California.
+ * Copyright (c) 2005, 2006 Masanori Ozawa <ozawa@ongs.co.jp>, ONGS Inc.
+ * Copyright (c) 2006 Daichi Goto <daichi@freebsd.org>
+ * All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Jan-Simon Pendry.
@@ -32,795 +35,630 @@
*
* @(#)union_vnops.c 8.32 (Berkeley) 6/23/95
* $FreeBSD$
+ *
*/
#include <sys/param.h>
#include <sys/systm.h>
-#include <sys/fcntl.h>
-#include <sys/stat.h>
+#include <sys/conf.h>
#include <sys/kernel.h>
-#include <sys/vnode.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
#include <sys/mount.h>
+#include <sys/mutex.h>
#include <sys/namei.h>
-#include <sys/malloc.h>
-#include <sys/bio.h>
-#include <sys/buf.h>
-#include <sys/lock.h>
#include <sys/sysctl.h>
-#include <sys/unistd.h>
-#include <sys/acl.h>
-#include <sys/event.h>
-#include <sys/extattr.h>
+#include <sys/vnode.h>
+#include <sys/kdb.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/dirent.h>
+#include <sys/proc.h>
+
#include <fs/unionfs/union.h>
#include <vm/vm.h>
-#include <vm/vnode_pager.h>
-
-#include <vm/vm_page.h>
+#include <vm/vm_extern.h>
#include <vm/vm_object.h>
+#include <vm/vnode_pager.h>
-int uniondebug = 0;
-
-#if UDEBUG_ENABLED
-SYSCTL_INT(_vfs, OID_AUTO, uniondebug, CTLFLAG_RW, &uniondebug, 0, "");
+#if 0
+#define UNIONFS_INTERNAL_DEBUG(msg, args...) printf(msg, ## args)
+#define UNIONFS_IDBG_RENAME
#else
-SYSCTL_INT(_vfs, OID_AUTO, uniondebug, CTLFLAG_RD, &uniondebug, 0, "");
+#define UNIONFS_INTERNAL_DEBUG(msg, args...)
#endif
-static vop_access_t union_access;
-static vop_aclcheck_t union_aclcheck;
-static vop_advlock_t union_advlock;
-static vop_close_t union_close;
-static vop_closeextattr_t union_closeextattr;
-static vop_create_t union_create;
-static vop_deleteextattr_t union_deleteextattr;
-static vop_fsync_t union_fsync;
-static vop_getacl_t union_getacl;
-static vop_getattr_t union_getattr;
-static vop_getextattr_t union_getextattr;
-static vop_inactive_t union_inactive;
-static vop_ioctl_t union_ioctl;
-static vop_lease_t union_lease;
-static vop_link_t union_link;
-static vop_listextattr_t union_listextattr;
-static vop_lookup_t union_lookup;
-static int union_lookup1(struct vnode *udvp, struct vnode **dvp,
- struct vnode **vpp,
- struct componentname *cnp);
-static vop_mkdir_t union_mkdir;
-static vop_mknod_t union_mknod;
-static vop_open_t union_open;
-static vop_openextattr_t union_openextattr;
-static vop_pathconf_t union_pathconf;
-static vop_print_t union_print;
-static vop_read_t union_read;
-static vop_readdir_t union_readdir;
-static vop_readlink_t union_readlink;
-static vop_getwritemount_t union_getwritemount;
-static vop_reclaim_t union_reclaim;
-static vop_remove_t union_remove;
-static vop_rename_t union_rename;
-static vop_rmdir_t union_rmdir;
-static vop_poll_t union_poll;
-static vop_setacl_t union_setacl;
-static vop_setattr_t union_setattr;
-static vop_setlabel_t union_setlabel;
-static vop_setextattr_t union_setextattr;
-static vop_strategy_t union_strategy;
-static vop_symlink_t union_symlink;
-static vop_whiteout_t union_whiteout;
-static vop_write_t union_write;
-
-static __inline
-struct vnode *
-union_lock_upper(struct union_node *un, struct thread *td)
-{
- struct vnode *uppervp;
-
- if ((uppervp = un->un_uppervp) != NULL) {
- VREF(uppervp);
- vn_lock(uppervp, LK_EXCLUSIVE | LK_CANRECURSE | LK_RETRY, td);
- }
- KASSERT((uppervp == NULL || vrefcnt(uppervp) > 0), ("uppervp usecount is 0"));
- return(uppervp);
-}
-
-static __inline
-void
-union_unlock_upper(struct vnode *uppervp, struct thread *td)
-{
- vput(uppervp);
-}
-
-static __inline
-struct vnode *
-union_lock_other(struct union_node *un, struct thread *td)
-{
- struct vnode *vp;
-
- if (un->un_uppervp != NULL) {
- vp = union_lock_upper(un, td);
- } else if ((vp = un->un_lowervp) != NULL) {
- VREF(vp);
- vn_lock(vp, LK_EXCLUSIVE | LK_CANRECURSE | LK_RETRY, td);
- }
- return(vp);
-}
+/* lockmgr lock <-> reverse table */
+struct lk_lr_table {
+ int lock;
+ int revlock;
+};
-static __inline
-void
-union_unlock_other(struct vnode *vp, struct thread *td)
-{
- vput(vp);
-}
+static struct lk_lr_table un_llt[] = {
+ {LK_SHARED, LK_RELEASE},
+ {LK_EXCLUSIVE, LK_RELEASE},
+ {LK_UPGRADE, LK_DOWNGRADE},
+ {LK_EXCLUPGRADE, LK_DOWNGRADE},
+ {LK_DOWNGRADE, LK_UPGRADE},
+ {0, 0}
+};
-/*
- * union_lookup:
- *
- * udvp must be exclusively locked on call and will remain
- * exclusively locked on return. This is the mount point
- * for our filesystem.
- *
- * dvp Our base directory, locked and referenced.
- * The passed dvp will be dereferenced and unlocked on return
- * and a new dvp will be returned which is locked and
- * referenced in the same variable.
- *
- * vpp is filled in with the result if no error occured,
- * locked and ref'd.
- *
- * If an error is returned, *vpp is set to NULLVP. If no
- * error occurs, *vpp is returned with a reference and an
- * exclusive lock.
- */
static int
-union_lookup1(udvp, pdvp, vpp, cnp)
- struct vnode *udvp;
- struct vnode **pdvp;
- struct vnode **vpp;
- struct componentname *cnp;
+unionfs_lookup(struct vop_lookup_args *ap)
{
- int error;
- struct thread *td = cnp->cn_thread;
- struct vnode *dvp = *pdvp;
- struct vnode *tdvp;
- struct mount *mp;
+ int iswhiteout;
+ int lockflag;
+ int error , uerror, lerror;
+ u_long nameiop;
+ u_long cnflags, cnflagsbk;
+ struct unionfs_node *dunp;
+ struct vnode *dvp, *udvp, *ldvp, *vp, *uvp, *lvp, *dtmpvp;
+ struct vattr va;
+ struct componentname *cnp;
+ struct thread *td;
- /*
- * If stepping up the directory tree, check for going
- * back across the mount point, in which case do what
- * lookup would do by stepping back down the mount
- * hierarchy.
- */
- if (cnp->cn_flags & ISDOTDOT) {
- while ((dvp != udvp) && (dvp->v_vflag & VV_ROOT)) {
- /*
- * Don't do the NOCROSSMOUNT check
- * at this level. By definition,
- * union fs deals with namespaces, not
- * filesystems.
- */
- tdvp = dvp;
- dvp = dvp->v_mount->mnt_vnodecovered;
- VREF(dvp);
- vput(tdvp);
- vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td);
- }
- }
+ iswhiteout = 0;
+ lockflag = 0;
+ error = uerror = lerror = ENOENT;
+ cnp = ap->a_cnp;
+ nameiop = cnp->cn_nameiop;
+ cnflags = cnp->cn_flags;
+ dvp = ap->a_dvp;
+ dunp = VTOUNIONFS(dvp);
+ udvp = dunp->un_uppervp;
+ ldvp = dunp->un_lowervp;
+ vp = uvp = lvp = NULLVP;
+ td = curthread;
+ *(ap->a_vpp) = NULLVP;
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_lookup: enter: nameiop=%ld, flags=%lx, path=%s\n", nameiop, cnflags, cnp->cn_nameptr);
+
+ if (dvp->v_type != VDIR)
+ return (ENOTDIR);
/*
- * Set return dvp to be the upperdvp 'parent directory.
+ * If read-only and op is not LOOKUP, will return EROFS.
*/
- *pdvp = dvp;
+ if ((cnflags & ISLASTCN) &&
+ (dvp->v_mount->mnt_flag & MNT_RDONLY) &&
+ LOOKUP != nameiop)
+ return (EROFS);
/*
- * If the VOP_LOOKUP() call generates an error, tdvp is invalid and
- * no changes will have been made to dvp, so we are set to return.
+ * lookup dotdot
*/
+ if (cnflags & ISDOTDOT) {
+ if (LOOKUP != nameiop && udvp == NULLVP)
+ return (EROFS);
- error = VOP_LOOKUP(dvp, &tdvp, cnp);
- if (error) {
- UDEBUG(("dvp %p error %d flags %lx\n", dvp, error, cnp->cn_flags));
- *vpp = NULL;
- return (error);
- }
- UDEBUG(("parentdir %p result %p flag %lx\n", dvp, tdvp, cnp->cn_flags));
-
- /*
- * Lastly check if the current node is a mount point in
- * which case walk up the mount hierarchy making sure not to
- * bump into the root of the mount tree (ie. dvp != udvp).
- *
- * We use dvp as a temporary variable here, it is no longer related
- * to the dvp above. However, we have to ensure that both *pdvp and
- * tdvp are locked on return.
- */
+ if (udvp != NULLVP) {
+ dtmpvp = udvp;
+ if (ldvp != NULLVP)
+ VOP_UNLOCK(ldvp, 0, td);
+ }
+ else
+ dtmpvp = ldvp;
- dvp = tdvp;
- while (
- dvp != udvp &&
- (dvp->v_type == VDIR) &&
- (mp = dvp->v_mountedhere)
- ) {
- int relock_pdvp = 0;
+ error = VOP_LOOKUP(dtmpvp, &vp, cnp);
- if (vfs_busy(mp, 0, 0, td))
- continue;
+ if (dtmpvp == udvp && ldvp != NULLVP) {
+ VOP_UNLOCK(udvp, 0, td);
+ vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td);
+ }
- if (dvp == *pdvp)
- relock_pdvp = 1;
- vput(dvp);
- dvp = NULL;
- error = VFS_ROOT(mp, LK_EXCLUSIVE, &dvp, td);
+ if (error == 0) {
+ /*
+ * Exchange lock and reference from vp to
+ * dunp->un_dvp. vp is upper/lower vnode, but it
+ * will need to return the unionfs vnode.
+ */
+ if (nameiop == DELETE || nameiop == RENAME ||
+ (cnp->cn_lkflags & LK_TYPE_MASK))
+ VOP_UNLOCK(vp, 0, td);
+ vrele(vp);
- vfs_unbusy(mp, td);
+ VOP_UNLOCK(dvp, 0, td);
+ *(ap->a_vpp) = dunp->un_dvp;
+ vref(dunp->un_dvp);
- if (relock_pdvp)
- vn_lock(*pdvp, LK_EXCLUSIVE | LK_RETRY, td);
+ if (nameiop == DELETE || nameiop == RENAME)
+ vn_lock(dunp->un_dvp, LK_EXCLUSIVE | LK_RETRY, td);
+ else if (cnp->cn_lkflags & LK_TYPE_MASK)
+ vn_lock(dunp->un_dvp, cnp->cn_lkflags | LK_RETRY, td);
- if (error) {
- *vpp = NULL;
- return (error);
+ vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td);
}
- }
- *vpp = dvp;
- return (0);
-}
-static int
-union_lookup(ap)
- struct vop_lookup_args /* {
- struct vnodeop_desc *a_desc;
- struct vnode *a_dvp;
- struct vnode **a_vpp;
- struct componentname *a_cnp;
- } */ *ap;
-{
- int error;
- int uerror, lerror;
- struct vnode *uppervp, *lowervp;
- struct vnode *upperdvp, *lowerdvp;
- struct vnode *dvp = ap->a_dvp; /* starting dir */
- struct union_node *dun = VTOUNION(dvp); /* associated union node */
- struct componentname *cnp = ap->a_cnp;
- struct thread *td = cnp->cn_thread;
- struct union_mount *um = MOUNTTOUNIONMOUNT(dvp->v_mount);
- struct ucred *saved_cred = NULL;
- int iswhiteout;
- struct vattr va;
-
- *ap->a_vpp = NULLVP;
+ UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n", error);
- /*
- * Disallow write attempts to the filesystem mounted read-only.
- */
- if ((cnp->cn_flags & ISLASTCN) &&
- (dvp->v_mount->mnt_flag & MNT_RDONLY) &&
- (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {
- return (EROFS);
+ return (error);
}
/*
- * For any lookups we do, always return with the parent locked.
+ * lookup upper layer
*/
- cnp->cn_flags |= LOCKPARENT;
-
- lowerdvp = dun->un_lowervp;
- uppervp = NULLVP;
- lowervp = NULLVP;
- iswhiteout = 0;
+ if (udvp != NULLVP) {
+ uerror = VOP_LOOKUP(udvp, &uvp, cnp);
- uerror = ENOENT;
- lerror = ENOENT;
+ if (uerror == 0) {
+ if (udvp == uvp) { /* is dot */
+ vrele(uvp);
+ *(ap->a_vpp) = dvp;
+ vref(dvp);
- /*
- * Get a private lock on uppervp and a reference, effectively
- * taking it out of the union_node's control.
- *
- * We must lock upperdvp while holding our lock on dvp
- * to avoid a deadlock.
- */
- upperdvp = union_lock_upper(dun, td);
+ UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n", uerror);
- /*
- * Do the lookup in the upper level.
- * If that level consumes additional pathnames,
- * then assume that something special is going
- * on and just return that vnode.
- */
- if (upperdvp != NULLVP) {
- /*
- * We do not have to worry about the DOTDOT case, we've
- * already unlocked dvp.
- */
- UDEBUG(("A %p\n", upperdvp));
-
- /*
- * Do the lookup. We must supply a locked and referenced
- * upperdvp to the function and will get a new locked and
- * referenced upperdvp back, with the old having been
- * dereferenced.
- *
- * If an error is returned, uppervp will be NULLVP. If no
- * error occurs, uppervp will be the locked and referenced.
- * Return vnode, or possibly NULL, depending on what is being
- * requested. It is possible that the returned uppervp
- * will be the same as upperdvp.
- */
- uerror = union_lookup1(um->um_uppervp, &upperdvp, &uppervp, cnp);
- UDEBUG((
- "uerror %d upperdvp %p %d/%d, uppervp %p ref=%d/lck=%d\n",
- uerror,
- upperdvp,
- vrefcnt(upperdvp),
- VOP_ISLOCKED(upperdvp, NULL),
- uppervp,
- (uppervp ? vrefcnt(uppervp) : -99),
- (uppervp ? VOP_ISLOCKED(uppervp, NULL) : -99)
- ));
-
- /*
- * Disallow write attempts to the filesystem mounted read-only.
- */
- if (uerror == EJUSTRETURN && (cnp->cn_flags & ISLASTCN) &&
- (dvp->v_mount->mnt_flag & MNT_RDONLY) &&
- (cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME)) {
- error = EROFS;
- goto out;
- }
-
- /*
- * Special case: If cn_consume != 0 then skip out. The result
- * of the lookup is transfered to our return variable. If
- * an error occured we have to throw away the results.
- */
-
- if (cnp->cn_consume != 0) {
- if ((error = uerror) == 0) {
- *ap->a_vpp = uppervp;
- uppervp = NULL;
+ return (uerror);
}
- goto out;
+ if (nameiop == DELETE || nameiop == RENAME ||
+ (cnp->cn_lkflags & LK_TYPE_MASK))
+ VOP_UNLOCK(uvp, 0, td);
}
- /*
- * Calculate whiteout, fall through.
- */
-
- if (uerror == ENOENT || uerror == EJUSTRETURN) {
- if (cnp->cn_flags & ISWHITEOUT) {
- iswhiteout = 1;
- } else if (lowerdvp != NULLVP) {
- int terror;
-
- terror = VOP_GETATTR(upperdvp, &va,
- cnp->cn_cred, cnp->cn_thread);
- if (terror == 0 && (va.va_flags & OPAQUE))
- iswhiteout = 1;
- }
- }
+ /* check whiteout */
+ if (uerror == ENOENT || uerror == EJUSTRETURN)
+ if (cnp->cn_flags & ISWHITEOUT)
+ iswhiteout = 1; /* don't lookup lower */
+ if (iswhiteout == 0 && ldvp != NULLVP)
+ if (VOP_GETATTR(udvp, &va, cnp->cn_cred, td) == 0 &&
+ (va.va_flags & OPAQUE))
+ iswhiteout = 1; /* don't lookup lower */
+#if 0
+ UNIONFS_INTERNAL_DEBUG("unionfs_lookup: debug: whiteout=%d, path=%s\n", iswhiteout, cnp->cn_nameptr);
+#endif
}
/*
- * In a similar way to the upper layer, do the lookup
- * in the lower layer. This time, if there is some
- * component magic going on, then vput whatever we got
- * back from the upper layer and return the lower vnode
- * instead.
+ * lookup lower layer
*/
-
- if (lowerdvp != NULLVP && !iswhiteout) {
- int nameiop;
-
- UDEBUG(("B %p\n", lowerdvp));
-
- /*
- * Force only LOOKUPs on the lower node, since
- * we won't be making changes to it anyway.
- */
- nameiop = cnp->cn_nameiop;
+ if (ldvp != NULLVP && !(cnflags & DOWHITEOUT) && iswhiteout == 0) {
+ /* always op is LOOKUP */
cnp->cn_nameiop = LOOKUP;
- if (um->um_op == UNMNT_BELOW) {
- saved_cred = cnp->cn_cred;
- cnp->cn_cred = um->um_cred;
- }
+ cnflagsbk = cnp->cn_flags;
+ cnp->cn_flags = cnflags;
- /*
- * We shouldn't have to worry about locking interactions
- * between the lower layer and our union layer (w.r.t.
- * `..' processing) because we don't futz with lowervp
- * locks in the union-node instantiation code path.
- *
- * union_lookup1() requires lowervp to be locked on entry,
- * and it will be unlocked on return. The ref count will
- * not change. On return lowervp doesn't represent anything
- * to us so we NULL it out.
- */
- VREF(lowerdvp);
- vn_lock(lowerdvp, LK_EXCLUSIVE | LK_RETRY, td);
- lerror = union_lookup1(um->um_lowervp, &lowerdvp, &lowervp, cnp);
- if (lowerdvp == lowervp)
- vrele(lowerdvp);
- else
- vput(lowerdvp);
- lowerdvp = NULL; /* lowerdvp invalid after vput */
+ lerror = VOP_LOOKUP(ldvp, &lvp, cnp);
- if (um->um_op == UNMNT_BELOW)
- cnp->cn_cred = saved_cred;
cnp->cn_nameiop = nameiop;
+ if (udvp != NULLVP && (uerror == 0 || uerror == EJUSTRETURN))
+ cnp->cn_flags = cnflagsbk;
- if (cnp->cn_consume != 0 || lerror == EACCES) {
- if ((error = lerror) == 0) {
- *ap->a_vpp = lowervp;
- lowervp = NULL;
- }
- goto out;
- }
- } else {
- UDEBUG(("C %p\n", lowerdvp));
- if ((cnp->cn_flags & ISDOTDOT) && dun->un_pvp != NULLVP) {
- if ((lowervp = LOWERVP(dun->un_pvp)) != NULL) {
- VREF(lowervp);
- vn_lock(lowervp, LK_EXCLUSIVE | LK_RETRY, td);
- lerror = 0;
+ if (lerror == 0) {
+ if (ldvp == lvp) { /* is dot */
+ if (uvp != NULLVP)
+ vrele(uvp); /* no need? */
+ vrele(lvp);
+ *(ap->a_vpp) = dvp;
+ vref(dvp);
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n", lerror);
+
+ return (lerror);
}
+ if (cnp->cn_lkflags & LK_TYPE_MASK)
+ VOP_UNLOCK(lvp, 0, td);
}
}
/*
- * Ok. Now we have uerror, uppervp, upperdvp, lerror, and lowervp.
- *
- * 1. If both layers returned an error, select the upper layer.
- *
- * 2. If the upper layer failed and the bottom layer succeeded,
- * two subcases occur:
- *
- * a. The bottom vnode is not a directory, in which case
- * just return a new union vnode referencing an
- * empty top layer and the existing bottom layer.
- *
- * b. The bottom vnode is a directory, in which case
- * create a new directory in the top layer and
- * and fall through to case 3.
- *
- * 3. If the top layer succeeded, then return a new union
- * vnode referencing whatever the new top layer and
- * whatever the bottom layer returned.
+ * check lookup result
*/
-
- /* case 1. */
- if ((uerror != 0) && (lerror != 0)) {
- error = uerror;
- goto out;
+ if (uvp == NULLVP && lvp == NULLVP) {
+ UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n",
+ (udvp != NULLVP ? uerror : lerror));
+ return (udvp != NULLVP ? uerror : lerror);
}
- /* case 2. */
- if (uerror != 0 /* && (lerror == 0) */ ) {
- if (lowervp->v_type == VDIR) { /* case 2b. */
- KASSERT(uppervp == NULL, ("uppervp unexpectedly non-NULL"));
- /*
- * Oops, uppervp has a problem, we may have to shadow.
- */
- uerror = union_mkshadow(um, upperdvp, cnp, &uppervp);
- if (uerror) {
- error = uerror;
- goto out;
- }
- }
+ /*
+ * check vnode type
+ */
+ if (uvp != NULLVP && lvp != NULLVP && uvp->v_type != lvp->v_type) {
+ vrele(lvp);
+ lvp = NULLVP;
}
/*
- * Must call union_allocvp() with both the upper and lower vnodes
- * referenced and the upper vnode locked. ap->a_vpp is returned
- * referenced and locked. lowervp, uppervp, and upperdvp are
- * absorbed by union_allocvp() whether it succeeds or fails.
- *
- * upperdvp is the parent directory of uppervp which may be
- * different, depending on the path, from dvp->un_uppervp. That's
- * why it is a separate argument. Note that it must be unlocked.
- *
- * dvp must be locked on entry to the call and will be locked on
- * return.
+ * check shadow dir
*/
-
- if (uppervp && uppervp != upperdvp)
- VOP_UNLOCK(uppervp, 0, td);
- if (lowervp)
- VOP_UNLOCK(lowervp, 0, td);
- if (upperdvp)
- VOP_UNLOCK(upperdvp, 0, td);
-
- error = union_allocvp(ap->a_vpp, dvp->v_mount, dvp, upperdvp, cnp,
- uppervp, lowervp, 1);
-
- UDEBUG(("Create %p = %p %p refs=%d\n", *ap->a_vpp, uppervp, lowervp, (*ap->a_vpp) ? vrefcnt(*ap->a_vpp) : -99));
-
- uppervp = NULL;
- upperdvp = NULL;
- lowervp = NULL;
-
- /*
- * Termination Code
- *
- * - put away any extra junk laying around. Note that lowervp
- * (if not NULL) will never be the same as *ap->a_vp and
- * neither will uppervp, because when we set that state we
- * NULL-out lowervp or uppervp. On the otherhand, upperdvp
- * may match uppervp or *ap->a_vpp.
- *
- * - relock/unlock dvp if appropriate.
+ if (uerror != 0 && uerror != EJUSTRETURN && udvp != NULLVP &&
+ lerror == 0 && lvp != NULLVP && lvp->v_type == VDIR &&
+ !(dvp->v_mount->mnt_flag & MNT_RDONLY) &&
+ (1 < cnp->cn_namelen || '.' != *(cnp->cn_nameptr))) {
+ /* get unionfs vnode in order to create a new shadow dir. */
+ error = unionfs_nodeget(dvp->v_mount, NULLVP, lvp, dvp, &vp,
+ cnp, td);
+ if (error != 0)
+ goto unionfs_lookup_out;
+
+ if (LK_SHARED == (cnp->cn_lkflags & LK_TYPE_MASK))
+ VOP_UNLOCK(vp, 0, td);
+ if (LK_EXCLUSIVE != VOP_ISLOCKED(vp, td)) {
+ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
+ lockflag = 1;
+ }
+ error = unionfs_mkshadowdir(MOUNTTOUNIONFSMOUNT(dvp->v_mount),
+ udvp, VTOUNIONFS(vp), cnp, td);
+ if (lockflag != 0)
+ VOP_UNLOCK(vp, 0, td);
+ if (error != 0) {
+ UNIONFSDEBUG("unionfs_lookup: Unable to create shadow dir.");
+ if ((cnp->cn_lkflags & LK_TYPE_MASK) == LK_EXCLUSIVE)
+ vput(vp);
+ else
+ vrele(vp);
+ goto unionfs_lookup_out;
+ }
+ if ((cnp->cn_lkflags & LK_TYPE_MASK) == LK_SHARED)
+ vn_lock(vp, LK_SHARED | LK_RETRY, td);
+ }
+ /*
+ * get unionfs vnode.
*/
-
-out:
- if (upperdvp) {
- if (upperdvp == uppervp || upperdvp == *ap->a_vpp)
- vrele(upperdvp);
+ else {
+ if (uvp != NULLVP)
+ error = uerror;
else
- vput(upperdvp);
+ error = lerror;
+ if (error != 0)
+ goto unionfs_lookup_out;
+ error = unionfs_nodeget(dvp->v_mount, uvp, lvp, dvp, &vp,
+ cnp, td);
+ if (error != 0) {
+ UNIONFSDEBUG("unionfs_lookup: Unable to create unionfs vnode.");
+ goto unionfs_lookup_out;
+ }
+ if ((nameiop == DELETE || nameiop == RENAME) &&
+ (cnp->cn_lkflags & LK_TYPE_MASK) == 0)
+ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
}
- if (uppervp)
- vput(uppervp);
+ *(ap->a_vpp) = vp;
- if (lowervp)
- vput(lowervp);
+unionfs_lookup_out:
+ if (uvp != NULLVP)
+ vrele(uvp);
+ if (lvp != NULLVP)
+ vrele(lvp);
- UDEBUG(("Out %d vpp %p/%d lower %p upper %p\n", error, *ap->a_vpp,
- ((*ap->a_vpp) ? vrefcnt(*ap->a_vpp) : -99),
- lowervp, uppervp));
-
- if (error == 0 || error == EJUSTRETURN) {
- if (cnp->cn_namelen == 1 &&
- cnp->cn_nameptr[0] == '.' &&
- *ap->a_vpp != dvp) {
-#ifdef DIAGNOSTIC
- vprint("union_lookup: vp", *ap->a_vpp);
- vprint("union_lookup: dvp", dvp);
-#endif
- panic("union_lookup returning . (%p) != startdir (%p)",
- *ap->a_vpp, dvp);
- }
- }
+ UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n", error);
return (error);
}
-/*
- * union_create:
- *
- * a_dvp is locked on entry and remains locked on return. a_vpp is returned
- * locked if no error occurs, otherwise it is garbage.
- */
-
static int
-union_create(ap)
- struct vop_create_args /* {
- struct vnode *a_dvp;
- struct vnode **a_vpp;
- struct componentname *a_cnp;
- struct vattr *a_vap;
- } */ *ap;
+unionfs_create(struct vop_create_args *ap)
{
- struct union_node *dun = VTOUNION(ap->a_dvp);
- struct componentname *cnp = ap->a_cnp;
- struct thread *td = cnp->cn_thread;
- struct vnode *dvp;
- int error = EROFS;
+ struct unionfs_node *dunp;
+ struct componentname *cnp;
+ struct thread *td;
+ struct vnode *udvp;
+ struct vnode *vp;
+ int error;
- if ((dvp = union_lock_upper(dun, td)) != NULL) {
- struct vnode *vp;
- struct mount *mp;
+ UNIONFS_INTERNAL_DEBUG("unionfs_create: enter\n");
- error = VOP_CREATE(dvp, &vp, cnp, ap->a_vap);
- if (error == 0) {
- mp = ap->a_dvp->v_mount;
+ dunp = VTOUNIONFS(ap->a_dvp);
+ cnp = ap->a_cnp;
+ td = curthread;
+ udvp = dunp->un_uppervp;
+ error = EROFS;
+
+ if (udvp != NULLVP) {
+ if ((error = VOP_CREATE(udvp, &vp, cnp, ap->a_vap)) == 0) {
VOP_UNLOCK(vp, 0, td);
- UDEBUG(("ALLOCVP-1 FROM %p REFS %d\n", vp, vrefcnt(vp)));
- error = union_allocvp(ap->a_vpp, mp, NULLVP, NULLVP,
- cnp, vp, NULLVP, 1);
- UDEBUG(("ALLOCVP-2B FROM %p REFS %d\n", *ap->a_vpp, vrefcnt(vp)));
+ error = unionfs_nodeget(ap->a_dvp->v_mount, vp, NULLVP,
+ ap->a_dvp, ap->a_vpp, cnp, td);
+ vrele(vp);
}
- union_unlock_upper(dvp, td);
}
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_create: leave (%d)\n", error);
+
return (error);
}
static int
-union_whiteout(ap)
- struct vop_whiteout_args /* {
- struct vnode *a_dvp;
- struct componentname *a_cnp;
- int a_flags;
- } */ *ap;
+unionfs_whiteout(struct vop_whiteout_args *ap)
{
- struct union_node *un = VTOUNION(ap->a_dvp);
- struct componentname *cnp = ap->a_cnp;
- struct vnode *uppervp;
- int error;
-
- switch (ap->a_flags) {
- case CREATE:
- case DELETE:
- uppervp = union_lock_upper(un, cnp->cn_thread);
- if (uppervp != NULLVP) {
- error = VOP_WHITEOUT(un->un_uppervp, cnp, ap->a_flags);
- union_unlock_upper(uppervp, cnp->cn_thread);
- } else
- error = EOPNOTSUPP;
- break;
- case LOOKUP:
- error = EOPNOTSUPP;
- break;
- default:
- panic("union_whiteout: unknown op");
+ struct unionfs_node *dunp;
+ struct componentname *cnp;
+ struct vnode *udvp;
+ int error;
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_whiteout: enter\n");
+
+ dunp = VTOUNIONFS(ap->a_dvp);
+ cnp = ap->a_cnp;
+ udvp = dunp->un_uppervp;
+ error = EOPNOTSUPP;
+
+ if (udvp != NULLVP) {
+ switch (ap->a_flags) {
+ case CREATE:
+ case DELETE:
+ case LOOKUP:
+ error = VOP_WHITEOUT(udvp, cnp, ap->a_flags);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
}
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_whiteout: leave (%d)\n", error);
+
return (error);
}
-/*
- * union_mknod:
- *
- * a_dvp is locked on entry and should remain locked on return.
- * a_vpp is garbage whether an error occurs or not.
- */
-
static int
-union_mknod(ap)
- struct vop_mknod_args /* {
- struct vnode *a_dvp;
- struct vnode **a_vpp;
- struct componentname *a_cnp;
- struct vattr *a_vap;
- } */ *ap;
+unionfs_mknod(struct vop_mknod_args *ap)
{
- struct union_node *dun = VTOUNION(ap->a_dvp);
- struct componentname *cnp = ap->a_cnp;
- struct vnode *dvp;
- int error = EROFS;
-
- if ((dvp = union_lock_upper(dun, cnp->cn_thread)) != NULL) {
- error = VOP_MKNOD(dvp, ap->a_vpp, cnp, ap->a_vap);
- union_unlock_upper(dvp, cnp->cn_thread);
+ struct unionfs_node *dunp;
+ struct componentname *cnp;
+ struct thread *td;
+ struct vnode *udvp;
+ struct vnode *vp;
+ int error;
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_mknod: enter\n");
+
+ dunp = VTOUNIONFS(ap->a_dvp);
+ cnp = ap->a_cnp;
+ td = curthread;
+ udvp = dunp->un_uppervp;
+ error = EROFS;
+
+ if (udvp != NULLVP) {
+ if ((error = VOP_MKNOD(udvp, &vp, cnp, ap->a_vap)) == 0) {
+ VOP_UNLOCK(vp, 0, td);
+ error = unionfs_nodeget(ap->a_dvp->v_mount, vp, NULLVP,
+ ap->a_dvp, ap->a_vpp, cnp, td);
+ vrele(vp);
+ }
}
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_mknod: leave (%d)\n", error);
+
return (error);
}
-/*
- * union_open:
- *
- * run open VOP. When opening the underlying vnode we have to mimic
- * vn_open(). What we *really* need to do to avoid screwups if the
- * open semantics change is to call vn_open(). For example, ufs blows
- * up if you open a file but do not vmio it prior to writing.
- */
-
static int
-union_open(ap)
- struct vop_open_args /* {
- struct vnodeop_desc *a_desc;
- struct vnode *a_vp;
- int a_mode;
- struct ucred *a_cred;
- struct thread *a_td;
- } */ *ap;
+unionfs_open(struct vop_open_args *ap)
{
- struct union_node *un = VTOUNION(ap->a_vp);
- struct vnode *tvp;
- int mode = ap->a_mode;
- struct ucred *cred = ap->a_cred;
- struct thread *td = ap->a_td;
- int error = 0;
- int tvpisupper = 1;
+ int error;
+ struct unionfs_node *unp;
+ struct unionfs_node_status *unsp;
+ struct vnode *uvp;
+ struct vnode *lvp;
+ struct vnode *targetvp;
+ struct ucred *cred;
+ struct thread *td;
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_open: enter\n");
+
+ error = 0;
+ unp = VTOUNIONFS(ap->a_vp);
+ uvp = unp->un_uppervp;
+ lvp = unp->un_lowervp;
+ targetvp = NULLVP;
+ cred = ap->a_cred;
+ td = ap->a_td;
+
+ unionfs_get_node_status(unp, td, &unsp);
+
+ if (unsp->uns_lower_opencnt > 0 || unsp->uns_upper_opencnt > 0) {
+ /* vnode is already opend. */
+ if (unsp->uns_upper_opencnt > 0)
+ targetvp = uvp;
+ else
+ targetvp = lvp;
- /*
- * If there is an existing upper vp then simply open that.
- * The upper vp takes precedence over the lower vp. When opening
- * a lower vp for writing copy it to the uppervp and then open the
- * uppervp.
- *
- * At the end of this section tvp will be left locked.
- */
- if ((tvp = union_lock_upper(un, td)) == NULLVP) {
- /*
- * If the lower vnode is being opened for writing, then
- * copy the file contents to the upper vnode and open that,
- * otherwise can simply open the lower vnode.
- */
- tvp = un->un_lowervp;
- if ((ap->a_mode & FWRITE) && (tvp->v_type == VREG)) {
- int docopy = !(mode & O_TRUNC);
- error = union_copyup(un, docopy, cred, td);
- tvp = union_lock_upper(un, td);
+ if (targetvp == lvp &&
+ (ap->a_mode & FWRITE) && lvp->v_type == VREG)
+ targetvp = NULLVP;
+ }
+ if (targetvp == NULLVP) {
+ if (uvp == NULLVP) {
+ if ((ap->a_mode & FWRITE) && lvp->v_type == VREG) {
+ error = unionfs_copyfile(unp,
+ !(ap->a_mode & O_TRUNC), cred, td);
+ if (error != 0)
+ goto unionfs_open_abort;
+ targetvp = uvp = unp->un_uppervp;
+ } else
+ targetvp = lvp;
+ } else
+ targetvp = uvp;
+ }
+
+ error = VOP_OPEN(targetvp, ap->a_mode, cred, td, ap->a_fdidx);
+ if (error == 0) {
+ if (targetvp == uvp) {
+ if (uvp->v_type == VDIR && lvp != NULLVP &&
+ unsp->uns_lower_opencnt <= 0) {
+ /* open lower for readdir */
+ error = VOP_OPEN(lvp, FREAD, cred, td, -1);
+ if (error != 0) {
+ VOP_CLOSE(uvp, ap->a_mode, cred, td);
+ goto unionfs_open_abort;
+ }
+ unsp->uns_node_flag |= UNS_OPENL_4_READDIR;
+ unsp->uns_lower_opencnt++;
+ }
+ unsp->uns_upper_opencnt++;
} else {
- un->un_openl++;
- VREF(tvp);
- vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY, td);
- tvpisupper = 0;
+ unsp->uns_lower_opencnt++;
+ unsp->uns_lower_openmode = ap->a_mode;
+ unsp->uns_lower_fdidx = ap->a_fdidx;
}
+ ap->a_vp->v_object = targetvp->v_object;
}
- /*
- * We are holding the correct vnode, open it.
- */
+unionfs_open_abort:
+ if (error != 0)
+ unionfs_tryrem_node_status(unp, td, unsp);
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_open: leave (%d)\n", error);
- if (error == 0)
- error = VOP_OPEN(tvp, mode, cred, td, -1);
- if (error == 0)
- ap->a_vp->v_object = tvp->v_object;
- /*
- * Release any locks held.
- */
- if (tvpisupper) {
- if (tvp)
- union_unlock_upper(tvp, td);
- } else {
- vput(tvp);
- }
return (error);
}
-/*
- * union_close:
- *
- * It is unclear whether a_vp is passed locked or unlocked. Whatever
- * the case we do not change it.
- */
-
static int
-union_close(ap)
- struct vop_close_args /* {
- struct vnode *a_vp;
- int a_fflag;
- struct ucred *a_cred;
- struct thread *a_td;
- } */ *ap;
+unionfs_close(struct vop_close_args *ap)
{
- struct union_node *un = VTOUNION(ap->a_vp);
- struct vnode *vp;
+ int error;
+ int locked;
+ struct unionfs_node *unp;
+ struct unionfs_node_status *unsp;
+ struct ucred *cred;
+ struct thread *td;
+ struct vnode *vp;
+ struct vnode *ovp;
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_close: enter\n");
+
+ locked = 0;
+ vp = ap->a_vp;
+ unp = VTOUNIONFS(vp);
+ cred = ap->a_cred;
+ td = ap->a_td;
+
+ if (VOP_ISLOCKED(vp, td) != LK_EXCLUSIVE) {
+ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
+ locked = 1;
+ }
+ unionfs_get_node_status(unp, td, &unsp);
- if ((vp = un->un_uppervp) == NULLVP) {
-#ifdef UNION_DIAGNOSTIC
- if (un->un_openl <= 0)
- panic("union: un_openl cnt");
+ if (unsp->uns_lower_opencnt <= 0 && unsp->uns_upper_opencnt <= 0) {
+#ifdef DIAGNOSTIC
+ printf("unionfs_close: warning: open count is 0\n");
#endif
- --un->un_openl;
- vp = un->un_lowervp;
- }
- ap->a_vp = vp;
- return (VOP_CLOSE_AP(ap));
+ if (unp->un_uppervp != NULLVP)
+ ovp = unp->un_uppervp;
+ else
+ ovp = unp->un_lowervp;
+ } else if (unsp->uns_upper_opencnt > 0)
+ ovp = unp->un_uppervp;
+ else
+ ovp = unp->un_lowervp;
+
+ error = VOP_CLOSE(ovp, ap->a_fflag, cred, td);
+
+ if (error != 0)
+ goto unionfs_close_abort;
+
+ ap->a_vp->v_object = ovp->v_object;
+
+ if (ovp == unp->un_uppervp) {
+ unsp->uns_upper_opencnt--;
+ if (unsp->uns_upper_opencnt == 0) {
+ if (unsp->uns_node_flag & UNS_OPENL_4_READDIR) {
+ VOP_CLOSE(unp->un_lowervp, FREAD, cred, td);
+ unsp->uns_node_flag &= ~UNS_OPENL_4_READDIR;
+ unsp->uns_lower_opencnt--;
+ }
+ if (unsp->uns_lower_opencnt > 0)
+ ap->a_vp->v_object = unp->un_lowervp->v_object;
+ }
+ } else
+ unsp->uns_lower_opencnt--;
+
+unionfs_close_abort:
+ unionfs_tryrem_node_status(unp, td, unsp);
+
+ if (locked != 0)
+ VOP_UNLOCK(vp, 0, td);
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_close: leave (%d)\n", error);
+
+ return (error);
}
/*
- * Check access permission on the union vnode.
- * The access check being enforced is to check
- * against both the underlying vnode, and any
- * copied vnode. This ensures that no additional
- * file permissions are given away simply because
- * the user caused an implicit file copy.
+ * Check the access mode toward shadow file/dir.
*/
static int
-union_access(ap)
- struct vop_access_args /* {
- struct vnodeop_desc *a_desc;
- struct vnode *a_vp;
- int a_mode;
- struct ucred *a_cred;
- struct thread *a_td;
- } */ *ap;
+unionfs_check_corrected_access(u_short mode,
+ struct vattr *va,
+ struct ucred *cred)
{
- struct union_node *un = VTOUNION(ap->a_vp);
- struct thread *td = ap->a_td;
- int error = EACCES;
- struct vnode *vp;
+ int count;
+ uid_t uid; /* upper side vnode's uid */
+ gid_t gid; /* upper side vnode's gid */
+ u_short vmode; /* upper side vnode's mode */
+ gid_t *gp;
+ u_short mask;
+
+ mask = 0;
+ uid = va->va_uid;
+ gid = va->va_gid;
+ vmode = va->va_mode;
+
+ /* check owner */
+ if (cred->cr_uid == uid) {
+ if (mode & VEXEC)
+ mask |= S_IXUSR;
+ if (mode & VREAD)
+ mask |= S_IRUSR;
+ if (mode & VWRITE)
+ mask |= S_IWUSR;
+ return ((vmode & mask) == mask ? 0 : EACCES);
+ }
- /*
- * Disallow write attempts on filesystems mounted read-only.
- */
- if ((ap->a_mode & VWRITE) &&
+ /* check group */
+ count = 0;
+ gp = cred->cr_groups;
+ for (; count < cred->cr_ngroups; count++, gp++) {
+ if (gid == *gp) {
+ if (mode & VEXEC)
+ mask |= S_IXGRP;
+ if (mode & VREAD)
+ mask |= S_IRGRP;
+ if (mode & VWRITE)
+ mask |= S_IWGRP;
+ return ((vmode & mask) == mask ? 0 : EACCES);
+ }
+ }
+
+ /* check other */
+ if (mode & VEXEC)
+ mask |= S_IXOTH;
+ if (mode & VREAD)
+ mask |= S_IROTH;
+ if (mode & VWRITE)
+ mask |= S_IWOTH;
+
+ return ((vmode & mask) == mask ? 0 : EACCES);
+}
+
+static int
+unionfs_access(struct vop_access_args *ap)
+{
+ struct unionfs_mount *ump;
+ struct unionfs_node *unp;
+ struct vnode *uvp;
+ struct vnode *lvp;
+ struct thread *td;
+ struct vattr va;
+ int mode;
+ int error;
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_access: enter\n");
+
+ ump = MOUNTTOUNIONFSMOUNT(ap->a_vp->v_mount);
+ unp = VTOUNIONFS(ap->a_vp);
+ uvp = unp->un_uppervp;
+ lvp = unp->un_lowervp;
+ td = ap->a_td;
+ mode = ap->a_mode;
+ error = EACCES;
+
+ if ((mode & VWRITE) &&
(ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)) {
switch (ap->a_vp->v_type) {
- case VREG:
+ case VREG:
case VDIR:
case VLNK:
return (EROFS);
@@ -829,1177 +667,1590 @@ union_access(ap)
}
}
- if ((vp = union_lock_upper(un, td)) != NULLVP) {
- ap->a_vp = vp;
- error = VOP_ACCESS_AP(ap);
- union_unlock_upper(vp, td);
- return(error);
- }
-
- if ((vp = un->un_lowervp) != NULLVP) {
- vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
- ap->a_vp = vp;
+ if (uvp != NULLVP) {
+ error = VOP_ACCESS(uvp, mode, ap->a_cred, td);
- /*
- * Remove VWRITE from a_mode if our mount point is RW, because
- * we want to allow writes and lowervp may be read-only.
- */
- if ((un->un_vnode->v_mount->mnt_flag & MNT_RDONLY) == 0)
- ap->a_mode &= ~VWRITE;
+ UNIONFS_INTERNAL_DEBUG("unionfs_access: leave (%d)\n", error);
- error = VOP_ACCESS_AP(ap);
- if (error == 0) {
- struct union_mount *um;
-
- um = MOUNTTOUNIONMOUNT(un->un_vnode->v_mount);
+ return (error);
+ }
- if (um->um_op == UNMNT_BELOW) {
- ap->a_cred = um->um_cred;
- error = VOP_ACCESS_AP(ap);
+ if (lvp != NULLVP) {
+ if (mode & VWRITE) {
+ if (ump->um_uppervp->v_mount->mnt_flag & MNT_RDONLY) {
+ switch (ap->a_vp->v_type) {
+ case VREG:
+ case VDIR:
+ case VLNK:
+ return (EROFS);
+ default:
+ break;
+ }
+ } else if (ap->a_vp->v_type == VREG || ap->a_vp->v_type == VDIR) {
+ /* check shadow file/dir */
+ if (ump->um_copymode != UNIONFS_TRANSPARENT) {
+ error = unionfs_create_uppervattr(ump,
+ lvp, &va, ap->a_cred, td);
+ if (error != 0)
+ return (error);
+
+ error = unionfs_check_corrected_access(
+ mode, &va, ap->a_cred);
+ if (error != 0)
+ return (error);
+ }
}
+ mode &= ~VWRITE;
+ mode |= VREAD; /* will copy to upper */
}
- VOP_UNLOCK(vp, 0, td);
+ error = VOP_ACCESS(lvp, mode, ap->a_cred, td);
}
- return(error);
-}
-/*
- * We handle getattr only to change the fsid and
- * track object sizes
- *
- * It's not clear whether VOP_GETATTR is to be
- * called with the vnode locked or not. stat() calls
- * it with (vp) locked, and fstat() calls it with
- * (vp) unlocked.
- *
- * Because of this we cannot use our normal locking functions
- * if we do not intend to lock the main a_vp node. At the moment
- * we are running without any specific locking at all, but beware
- * to any programmer that care must be taken if locking is added
- * to this function.
- */
+ UNIONFS_INTERNAL_DEBUG("unionfs_access: leave (%d)\n", error);
+
+ return (error);
+}
static int
-union_getattr(ap)
- struct vop_getattr_args /* {
- struct vnode *a_vp;
- struct vattr *a_vap;
- struct ucred *a_cred;
- struct thread *a_td;
- } */ *ap;
+unionfs_getattr(struct vop_getattr_args *ap)
{
- int error;
- struct union_node *un = VTOUNION(ap->a_vp);
- struct union_mount *um = MOUNTTOUNIONMOUNT(ap->a_vp->v_mount);
- struct vnode *vp;
- struct vattr *vap;
- struct vattr va;
-
- /*
- * Some programs walk the filesystem hierarchy by counting
- * links to directories to avoid stat'ing all the time.
- * This means the link count on directories needs to be "correct".
- * The only way to do that is to call getattr on both layers
- * and fix up the link count. The link count will not necessarily
- * be accurate but will be large enough to defeat the tree walkers.
- */
-
- vap = ap->a_vap;
+ int error;
+ struct unionfs_node *unp;
+ struct unionfs_mount *ump;
+ struct vnode *uvp;
+ struct vnode *lvp;
+ struct thread *td;
+ struct vattr va;
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_getattr: enter\n");
+
+ unp = VTOUNIONFS(ap->a_vp);
+ ump = MOUNTTOUNIONFSMOUNT(ap->a_vp->v_mount);
+ uvp = unp->un_uppervp;
+ lvp = unp->un_lowervp;
+ td = ap->a_td;
+
+ if (uvp != NULLVP) {
+ if ((error = VOP_GETATTR(uvp, ap->a_vap, ap->a_cred, td)) == 0)
+ ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_getattr: leave mode=%o, uid=%d, gid=%d (%d)\n",
+ ap->a_vap->va_mode, ap->a_vap->va_uid,
+ ap->a_vap->va_gid, error);
- if ((vp = un->un_uppervp) != NULLVP) {
- error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_td);
- if (error)
- return (error);
- /* XXX isn't this dangerous without a lock? */
- union_newsize(ap->a_vp, vap->va_size, VNOVAL);
+ return (error);
}
- if (vp == NULLVP) {
- vp = un->un_lowervp;
- } else if (vp->v_type == VDIR && un->un_lowervp != NULLVP) {
- vp = un->un_lowervp;
- vap = &va;
- } else {
- vp = NULLVP;
- }
+ error = VOP_GETATTR(lvp, ap->a_vap, ap->a_cred, td);
- if (vp != NULLVP) {
- error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_td);
- if (error)
- return (error);
- /* XXX isn't this dangerous without a lock? */
- union_newsize(ap->a_vp, VNOVAL, vap->va_size);
+ if (error == 0 && !(ump->um_uppervp->v_mount->mnt_flag & MNT_RDONLY)) {
+ /* correct the attr toward shadow file/dir. */
+ if (ap->a_vp->v_type == VREG || ap->a_vp->v_type == VDIR) {
+ unionfs_create_uppervattr_core(ump, ap->a_vap, &va, td);
+ ap->a_vap->va_mode = va.va_mode;
+ ap->a_vap->va_uid = va.va_uid;
+ ap->a_vap->va_gid = va.va_gid;
+ }
}
- if (ap->a_vap->va_fsid == um->um_upperdev)
+ if (error == 0)
ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
- if ((vap != ap->a_vap) && (vap->va_type == VDIR))
- ap->a_vap->va_nlink += vap->va_nlink;
- return (0);
+ UNIONFS_INTERNAL_DEBUG("unionfs_getattr: leave mode=%o, uid=%d, gid=%d (%d)\n",
+ ap->a_vap->va_mode, ap->a_vap->va_uid, ap->a_vap->va_gid, error);
+
+ return (error);
}
static int
-union_setattr(ap)
- struct vop_setattr_args /* {
- struct vnode *a_vp;
- struct vattr *a_vap;
- struct ucred *a_cred;
- struct thread *a_td;
- } */ *ap;
+unionfs_setattr(struct vop_setattr_args *ap)
{
- struct union_node *un = VTOUNION(ap->a_vp);
- struct thread *td = ap->a_td;
- struct vattr *vap = ap->a_vap;
- struct vnode *uppervp;
- int error;
+ int error;
+ struct unionfs_node *unp;
+ struct vnode *uvp;
+ struct vnode *lvp;
+ struct thread *td;
+ struct vattr *vap;
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_setattr: enter\n");
+
+ error = EROFS;
+ unp = VTOUNIONFS(ap->a_vp);
+ uvp = unp->un_uppervp;
+ lvp = unp->un_lowervp;
+ td = ap->a_td;
+ vap = ap->a_vap;
- /*
- * Disallow write attempts on filesystems mounted read-only.
- */
if ((ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) &&
(vap->va_flags != VNOVAL || vap->va_uid != (uid_t)VNOVAL ||
vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL ||
- vap->va_mtime.tv_sec != VNOVAL ||
- vap->va_mode != (mode_t)VNOVAL)) {
+ vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL))
return (EROFS);
- }
- /*
- * Handle case of truncating lower object to zero size
- * by creating a zero length upper object. This is to
- * handle the case of open with O_TRUNC and O_CREAT.
- */
- if (un->un_uppervp == NULLVP && (un->un_lowervp->v_type == VREG)) {
- error = union_copyup(un, (ap->a_vap->va_size != 0),
- ap->a_cred, ap->a_td);
- if (error)
+ if (uvp == NULLVP && lvp->v_type == VREG) {
+ error = unionfs_copyfile(unp, (vap->va_size != 0),
+ ap->a_cred, td);
+ if (error != 0)
return (error);
+ uvp = unp->un_uppervp;
}
- /*
- * Try to set attributes in upper layer,
- * otherwise return read-only filesystem error.
- */
- error = EROFS;
- if ((uppervp = union_lock_upper(un, td)) != NULLVP) {
- error = VOP_SETATTR(un->un_uppervp, ap->a_vap,
- ap->a_cred, ap->a_td);
- if ((error == 0) && (ap->a_vap->va_size != VNOVAL))
- union_newsize(ap->a_vp, ap->a_vap->va_size, VNOVAL);
- union_unlock_upper(uppervp, td);
- }
+ if (uvp != NULLVP)
+ error = VOP_SETATTR(uvp, vap, ap->a_cred, td);
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_setattr: leave (%d)\n", error);
+
return (error);
}
static int
-union_read(ap)
- struct vop_read_args /* {
- struct vnode *a_vp;
- struct uio *a_uio;
- int a_ioflag;
- struct ucred *a_cred;
- } */ *ap;
+unionfs_read(struct vop_read_args *ap)
{
- struct union_node *un = VTOUNION(ap->a_vp);
- struct thread *td = ap->a_uio->uio_td;
- struct vnode *uvp;
- int error;
+ int error;
+ struct unionfs_node *unp;
+ struct vnode *tvp;
- uvp = union_lock_other(un, td);
- KASSERT(uvp != NULL, ("union_read: backing vnode missing!"));
+ /* UNIONFS_INTERNAL_DEBUG("unionfs_read: enter\n"); */
- error = VOP_READ(uvp, ap->a_uio, ap->a_ioflag, ap->a_cred);
- union_unlock_other(uvp, td);
+ unp = VTOUNIONFS(ap->a_vp);
+ tvp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp);
- /*
- * XXX
- * Perhaps the size of the underlying object has changed under
- * our feet. Take advantage of the offset information present
- * in the uio structure.
- */
- if (error == 0) {
- struct union_node *un = VTOUNION(ap->a_vp);
- off_t cur = ap->a_uio->uio_offset;
+ error = VOP_READ(tvp, ap->a_uio, ap->a_ioflag, ap->a_cred);
+
+ /* UNIONFS_INTERNAL_DEBUG("unionfs_read: leave (%d)\n", error); */
- if (uvp == un->un_uppervp) {
- if (cur > un->un_uppersz)
- union_newsize(ap->a_vp, cur, VNOVAL);
- } else {
- if (cur > un->un_lowersz)
- union_newsize(ap->a_vp, VNOVAL, cur);
- }
- }
return (error);
}
static int
-union_write(ap)
- struct vop_write_args /* {
- struct vnode *a_vp;
- struct uio *a_uio;
- int a_ioflag;
- struct ucred *a_cred;
- } */ *ap;
+unionfs_write(struct vop_write_args *ap)
{
- struct union_node *un = VTOUNION(ap->a_vp);
- struct thread *td = ap->a_uio->uio_td;
- struct vnode *uppervp;
- int error;
+ int error;
+ struct unionfs_node *unp;
+ struct vnode *tvp;
- if ((uppervp = union_lock_upper(un, td)) == NULLVP)
- panic("union: missing upper layer in write");
+ /* UNIONFS_INTERNAL_DEBUG("unionfs_write: enter\n"); */
- error = VOP_WRITE(uppervp, ap->a_uio, ap->a_ioflag, ap->a_cred);
+ unp = VTOUNIONFS(ap->a_vp);
+ tvp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp);
- /*
- * The size of the underlying object may be changed by the
- * write.
- */
- if (error == 0) {
- off_t cur = ap->a_uio->uio_offset;
+ error = VOP_WRITE(tvp, ap->a_uio, ap->a_ioflag, ap->a_cred);
+
+ /* UNIONFS_INTERNAL_DEBUG("unionfs_write: leave (%d)\n", error); */
- if (cur > un->un_uppersz)
- union_newsize(ap->a_vp, cur, VNOVAL);
- }
- union_unlock_upper(uppervp, td);
return (error);
}
static int
-union_lease(ap)
- struct vop_lease_args /* {
- struct vnode *a_vp;
- struct thread *a_td;
- struct ucred *a_cred;
- int a_flag;
- } */ *ap;
+unionfs_lease(struct vop_lease_args *ap)
{
- struct vnode *ovp = OTHERVP(ap->a_vp);
+ int error;
+ struct unionfs_node *unp;
+ struct vnode *vp;
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_lease: enter\n");
- ap->a_vp = ovp;
- return (VOP_LEASE_AP(ap));
+ unp = VTOUNIONFS(ap->a_vp);
+ vp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp);
+
+ error = VOP_LEASE(vp, ap->a_td, ap->a_cred, ap->a_flag);
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_lease: lease (%d)\n", error);
+
+ return (error);
}
static int
-union_ioctl(ap)
- struct vop_ioctl_args /* {
- struct vnode *a_vp;
- u_long a_command;
- caddr_t a_data;
- int a_fflag;
- struct ucred *a_cred;
- struct thread *a_td;
- } */ *ap;
+unionfs_ioctl(struct vop_ioctl_args *ap)
{
- struct vnode *ovp = OTHERVP(ap->a_vp);
+ int error;
+ struct unionfs_node *unp;
+ struct unionfs_node_status *unsp;
+ struct vnode *ovp;
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_ioctl: enter\n");
- ap->a_vp = ovp;
- return (VOP_IOCTL_AP(ap));
+ unp = VTOUNIONFS(ap->a_vp);
+ unionfs_get_node_status(unp, ap->a_td, &unsp);
+ ovp = (unsp->uns_upper_opencnt ? unp->un_uppervp : unp->un_lowervp);
+
+ if (ovp == NULLVP)
+ return (EBADF);
+
+ error = VOP_IOCTL(ovp, ap->a_command, ap->a_data, ap->a_fflag,
+ ap->a_cred, ap->a_td);
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_ioctl: lease (%d)\n", error);
+
+ return (error);
}
static int
-union_poll(ap)
- struct vop_poll_args /* {
- struct vnode *a_vp;
- int a_events;
- struct ucred *a_cred;
- struct thread *a_td;
- } */ *ap;
+unionfs_poll(struct vop_poll_args *ap)
{
- struct vnode *ovp = OTHERVP(ap->a_vp);
+ struct unionfs_node *unp;
+ struct unionfs_node_status *unsp;
+ struct vnode *ovp;
- ap->a_vp = ovp;
- return (VOP_POLL_AP(ap));
+ unp = VTOUNIONFS(ap->a_vp);
+ unionfs_get_node_status(unp, ap->a_td, &unsp);
+ ovp = (unsp->uns_upper_opencnt ? unp->un_uppervp : unp->un_lowervp);
+
+ if (ovp == NULLVP)
+ return (EBADF);
+
+ return (VOP_POLL(ovp, ap->a_events, ap->a_cred, ap->a_td));
}
static int
-union_fsync(ap)
- struct vop_fsync_args /* {
- struct vnode *a_vp;
- struct ucred *a_cred;
- int a_waitfor;
- struct thread *a_td;
- } */ *ap;
+unionfs_fsync(struct vop_fsync_args *ap)
{
- int error = 0;
- struct thread *td = ap->a_td;
- struct vnode *targetvp;
- struct union_node *un = VTOUNION(ap->a_vp);
-
- if ((targetvp = union_lock_other(un, td)) != NULLVP) {
- error = VOP_FSYNC(targetvp, ap->a_waitfor, td);
- union_unlock_other(targetvp, td);
- }
+ struct unionfs_node *unp;
+ struct unionfs_node_status *unsp;
+ struct vnode *ovp;
- return (error);
-}
+ unp = VTOUNIONFS(ap->a_vp);
+ unionfs_get_node_status(unp, ap->a_td, &unsp);
+ ovp = (unsp->uns_upper_opencnt ? unp->un_uppervp : unp->un_lowervp);
-/*
- * union_remove:
- *
- * Remove the specified cnp. The dvp and vp are passed to us locked
- * and must remain locked on return.
- */
+ if (ovp == NULLVP)
+ return (EBADF);
+
+ return (VOP_FSYNC(ovp, ap->a_waitfor, ap->a_td));
+}
static int
-union_remove(ap)
- struct vop_remove_args /* {
- struct vnode *a_dvp;
- struct vnode *a_vp;
- struct componentname *a_cnp;
- } */ *ap;
+unionfs_remove(struct vop_remove_args *ap)
{
- struct union_node *dun = VTOUNION(ap->a_dvp);
- struct union_node *un = VTOUNION(ap->a_vp);
- struct componentname *cnp = ap->a_cnp;
- struct thread *td = cnp->cn_thread;
- struct vnode *uppervp;
- struct vnode *upperdvp;
- int error;
+ int error;
+ struct unionfs_node *dunp;
+ struct unionfs_node *unp;
+ struct vnode *udvp;
+ struct vnode *uvp;
+ struct vnode *lvp;
+ struct componentname *cnp;
+ struct thread *td;
- if ((upperdvp = union_lock_upper(dun, td)) == NULLVP)
- panic("union remove: null upper vnode");
+ UNIONFS_INTERNAL_DEBUG("unionfs_remove: enter\n");
+
+ error = 0;
+ dunp = VTOUNIONFS(ap->a_dvp);
+ unp = VTOUNIONFS(ap->a_vp);
+ udvp = dunp->un_uppervp;
+ uvp = unp->un_uppervp;
+ lvp = unp->un_lowervp;
+ cnp = ap->a_cnp;
+ td = curthread;
+
+ if (udvp == NULLVP)
+ return (EROFS);
+
+ if (uvp != NULLVP) {
+ cnp->cn_flags |= DOWHITEOUT;
+ error = VOP_REMOVE(udvp, uvp, cnp);
+ } else if (lvp != NULLVP)
+ error = unionfs_mkwhiteout(udvp, cnp, td, unp->un_path);
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_remove: leave (%d)\n", error);
- if ((uppervp = union_lock_upper(un, td)) != NULLVP) {
- if (union_dowhiteout(un, cnp->cn_cred, td))
- cnp->cn_flags |= DOWHITEOUT;
- if (cnp->cn_flags & DOWHITEOUT) /* XXX fs corruption */
- error = EOPNOTSUPP;
- else
- error = VOP_REMOVE(upperdvp, uppervp, cnp);
- if (!error)
- union_removed_upper(un);
- union_unlock_upper(uppervp, td);
- } else {
- error = union_mkwhiteout(
- MOUNTTOUNIONMOUNT(ap->a_dvp->v_mount),
- upperdvp, ap->a_cnp, un->un_path);
- }
- union_unlock_upper(upperdvp, td);
return (error);
}
-/*
- * union_link:
- *
- * tdvp and vp will be locked on entry.
- * tdvp and vp should remain locked on return.
- */
-
static int
-union_link(ap)
- struct vop_link_args /* {
- struct vnode *a_tdvp;
- struct vnode *a_vp;
- struct componentname *a_cnp;
- } */ *ap;
+unionfs_link(struct vop_link_args *ap)
{
- struct componentname *cnp = ap->a_cnp;
- struct thread *td = cnp->cn_thread;
- struct union_node *dun = VTOUNION(ap->a_tdvp);
- struct vnode *vp;
- struct vnode *tdvp;
- int error = 0;
-
- if (ap->a_tdvp->v_op != ap->a_vp->v_op) {
- vp = ap->a_vp;
- } else {
- struct union_node *tun = VTOUNION(ap->a_vp);
-
- if (tun->un_uppervp == NULLVP) {
-#if 0
- if (dun->un_uppervp == tun->un_dirvp) {
- if (dun->un_flags & UN_ULOCK) {
- dun->un_flags &= ~UN_ULOCK;
- VOP_UNLOCK(dun->un_uppervp, 0, td);
- }
- }
-#endif
- error = union_copyup(tun, 1, cnp->cn_cred, td);
-#if 0
- if (dun->un_uppervp == tun->un_dirvp) {
- vn_lock(dun->un_uppervp,
- LK_EXCLUSIVE | LK_RETRY, td);
- dun->un_flags |= UN_ULOCK;
- }
-#endif
- if (error)
+ int error;
+ int needrelookup;
+ struct unionfs_node *dunp;
+ struct unionfs_node *unp;
+ struct vnode *udvp;
+ struct vnode *uvp;
+ struct componentname *cnp;
+ struct thread *td;
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_link: enter\n");
+
+ error = 0;
+ needrelookup = 0;
+ dunp = VTOUNIONFS(ap->a_tdvp);
+ unp = NULL;
+ udvp = dunp->un_uppervp;
+ uvp = NULLVP;
+ cnp = ap->a_cnp;
+ td = curthread;
+
+ if (udvp == NULLVP)
+ return (EROFS);
+
+ if (ap->a_vp->v_op != &unionfs_vnodeops)
+ uvp = ap->a_vp;
+ else {
+ unp = VTOUNIONFS(ap->a_vp);
+
+ if (unp->un_uppervp == NULLVP) {
+ if (ap->a_vp->v_type != VREG)
+ return (EOPNOTSUPP);
+
+ error = unionfs_copyfile(unp, 1, cnp->cn_cred, td);
+ if (error != 0)
return (error);
+ needrelookup = 1;
}
- vp = tun->un_uppervp;
- vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
+ uvp = unp->un_uppervp;
}
- /*
- * Make sure upper is locked, then unlock the union directory we were
- * called with to avoid a deadlock while we are calling VOP_LINK() on
- * the upper (with tdvp locked and vp not locked). Our ap->a_tdvp
- * is expected to be locked on return.
- */
+ if (needrelookup != 0)
+ error = unionfs_relookup_for_create(ap->a_tdvp, cnp, td);
- if ((tdvp = union_lock_upper(dun, td)) == NULLVP)
- return (EROFS);
+ if (error == 0)
+ error = VOP_LINK(udvp, uvp, cnp);
- VOP_UNLOCK(ap->a_tdvp, 0, td); /* unlock calling node */
- error = VOP_LINK(tdvp, vp, cnp); /* call link on upper */
+ UNIONFS_INTERNAL_DEBUG("unionfs_link: leave (%d)\n", error);
- /*
- * Unlock tun->un_uppervp if we locked it above.
- */
- if (ap->a_tdvp->v_op == ap->a_vp->v_op)
- VOP_UNLOCK(vp, 0, td);
- /*
- * We have to unlock tdvp prior to relocking our calling node in
- * order to avoid a deadlock. We also have to unlock ap->a_vp
- * before relocking the directory, but then we have to relock
- * ap->a_vp as our caller expects.
- */
- VOP_UNLOCK(ap->a_vp, 0, td);
- union_unlock_upper(tdvp, td);
- vn_lock(ap->a_tdvp, LK_EXCLUSIVE | LK_RETRY, td);
- vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY, td);
return (error);
}
static int
-union_rename(ap)
- struct vop_rename_args /* {
- struct vnode *a_fdvp;
- struct vnode *a_fvp;
- struct componentname *a_fcnp;
- struct vnode *a_tdvp;
- struct vnode *a_tvp;
- struct componentname *a_tcnp;
- } */ *ap;
+unionfs_rename(struct vop_rename_args *ap)
{
- int error;
- struct vnode *fdvp = ap->a_fdvp;
- struct vnode *fvp = ap->a_fvp;
- struct vnode *tdvp = ap->a_tdvp;
- struct vnode *tvp = ap->a_tvp;
+ int error;
+ struct vnode *fdvp;
+ struct vnode *fvp;
+ struct componentname *fcnp;
+ struct vnode *tdvp;
+ struct vnode *tvp;
+ struct componentname *tcnp;
+ struct vnode *ltdvp;
+ struct vnode *ltvp;
+ struct thread *td;
+
+ /* rename target vnodes */
+ struct vnode *rfdvp;
+ struct vnode *rfvp;
+ struct vnode *rtdvp;
+ struct vnode *rtvp;
+
+ int needrelookup;
+ struct unionfs_mount *ump;
+ struct unionfs_node *unp;
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_rename: enter\n");
+
+ error = 0;
+ fdvp = ap->a_fdvp;
+ fvp = ap->a_fvp;
+ fcnp = ap->a_fcnp;
+ tdvp = ap->a_tdvp;
+ tvp = ap->a_tvp;
+ tcnp = ap->a_tcnp;
+ ltdvp = NULLVP;
+ ltvp = NULLVP;
+ td = curthread;
+ rfdvp = fdvp;
+ rfvp = fvp;
+ rtdvp = tdvp;
+ rtvp = tvp;
+ needrelookup = 0;
- /*
- * Figure out what fdvp to pass to our upper or lower vnode. If we
- * replace the fdvp, release the original one and ref the new one.
- */
+#ifdef DIAGNOSTIC
+ if (!(fcnp->cn_flags & HASBUF) || !(tcnp->cn_flags & HASBUF))
+ panic("unionfs_rename: no name");
+#endif
- if (fdvp->v_op == &union_vnodeops) { /* always true */
- struct union_node *un = VTOUNION(fdvp);
- if (un->un_uppervp == NULLVP) {
- /*
- * this should never happen in normal
- * operation but might if there was
- * a problem creating the top-level shadow
- * directory.
- */
- error = EXDEV;
- goto bad;
- }
- fdvp = un->un_uppervp;
- VREF(fdvp);
- vrele(ap->a_fdvp);
+ /* check for cross device rename */
+ if (fvp->v_mount != tdvp->v_mount ||
+ (tvp != NULLVP && fvp->v_mount != tvp->v_mount)) {
+ error = EXDEV;
+ goto unionfs_rename_abort;
}
+ /* Renaming a file to itself has no effect. */
+ if (fvp == tvp)
+ goto unionfs_rename_abort;
+
/*
- * Figure out what fvp to pass to our upper or lower vnode. If we
- * replace the fvp, release the original one and ref the new one.
+ * from/to vnode is unionfs node.
*/
- if (fvp->v_op == &union_vnodeops) { /* always true */
- struct union_node *un = VTOUNION(fvp);
-#if 0
- struct union_mount *um = MOUNTTOUNIONMOUNT(fvp->v_mount);
+ unp = VTOUNIONFS(fdvp);
+#ifdef UNIONFS_IDBG_RENAME
+ UNIONFS_INTERNAL_DEBUG("fdvp=%p, ufdvp=%p, lfdvp=%p\n", fdvp, unp->un_uppervp, unp->un_lowervp);
#endif
+ if (unp->un_uppervp == NULLVP) {
+ error = ENODEV;
+ goto unionfs_rename_abort;
+ }
+ rfdvp = unp->un_uppervp;
+ vref(rfdvp);
- if (un->un_uppervp == NULLVP) {
- switch(fvp->v_type) {
- case VREG:
- vn_lock(un->un_vnode, LK_EXCLUSIVE | LK_RETRY, ap->a_fcnp->cn_thread);
- error = union_copyup(un, 1, ap->a_fcnp->cn_cred, ap->a_fcnp->cn_thread);
- VOP_UNLOCK(un->un_vnode, 0, ap->a_fcnp->cn_thread);
- if (error)
- goto bad;
- break;
- case VDIR:
- /*
- * XXX not yet.
- *
- * There is only one way to rename a directory
- * based in the lowervp, and that is to copy
- * the entire directory hierarchy. Otherwise
- * it would not last across a reboot.
- */
-#if 0
- vrele(fvp);
- fvp = NULL;
- vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY, ap->a_fcnp->cn_thread);
- error = union_mkshadow(um, fdvp,
- ap->a_fcnp, &un->un_uppervp);
- VOP_UNLOCK(fdvp, 0, ap->a_fcnp->cn_thread);
- if (un->un_uppervp)
- VOP_UNLOCK(un->un_uppervp, 0, ap->a_fcnp->cn_thread);
- if (error)
- goto bad;
- break;
+ unp = VTOUNIONFS(fvp);
+#ifdef UNIONFS_IDBG_RENAME
+ UNIONFS_INTERNAL_DEBUG("fvp=%p, ufvp=%p, lfvp=%p\n", fvp, unp->un_uppervp, unp->un_lowervp);
#endif
- default:
- error = EXDEV;
- goto bad;
- }
+ ump = MOUNTTOUNIONFSMOUNT(fvp->v_mount);
+ if (unp->un_uppervp == NULLVP) {
+ switch (fvp->v_type) {
+ case VREG:
+ if ((error = vn_lock(fvp, LK_EXCLUSIVE, td)) != 0)
+ goto unionfs_rename_abort;
+ error = unionfs_copyfile(unp, 1, fcnp->cn_cred, td);
+ VOP_UNLOCK(fvp, 0, td);
+ if (error != 0)
+ goto unionfs_rename_abort;
+ break;
+ case VDIR:
+ if ((error = vn_lock(fvp, LK_EXCLUSIVE, td)) != 0)
+ goto unionfs_rename_abort;
+ error = unionfs_mkshadowdir(ump, rfdvp, unp, fcnp, td);
+ VOP_UNLOCK(fvp, 0, td);
+ if (error != 0)
+ goto unionfs_rename_abort;
+ break;
+ default:
+ error = ENODEV;
+ goto unionfs_rename_abort;
}
- if (un->un_lowervp != NULLVP)
- ap->a_fcnp->cn_flags |= DOWHITEOUT;
- fvp = un->un_uppervp;
- VREF(fvp);
- vrele(ap->a_fvp);
+ needrelookup = 1;
}
- /*
- * Figure out what tdvp (destination directory) to pass to the
- * lower level. If we replace it with uppervp, we need to vput the
- * old one. The exclusive lock is transfered to what we will pass
- * down in the VOP_RENAME() and we replace uppervp with a simple
- * reference.
- */
-
- if (tdvp->v_op == &union_vnodeops) {
- struct union_node *un = VTOUNION(tdvp);
+ if (unp->un_lowervp != NULLVP)
+ fcnp->cn_flags |= DOWHITEOUT;
+ rfvp = unp->un_uppervp;
+ vref(rfvp);
- if (un->un_uppervp == NULLVP) {
- /*
- * This should never happen in normal
- * operation but might if there was
- * a problem creating the top-level shadow
- * directory.
- */
- error = EXDEV;
- goto bad;
+ unp = VTOUNIONFS(tdvp);
+#ifdef UNIONFS_IDBG_RENAME
+ UNIONFS_INTERNAL_DEBUG("tdvp=%p, utdvp=%p, ltdvp=%p\n", tdvp, unp->un_uppervp, unp->un_lowervp);
+#endif
+ if (unp->un_uppervp == NULLVP) {
+ error = ENODEV;
+ goto unionfs_rename_abort;
+ }
+ rtdvp = unp->un_uppervp;
+ ltdvp = unp->un_lowervp;
+ vref(rtdvp);
+
+ if (tdvp == tvp) {
+ rtvp = rtdvp;
+ vref(rtvp);
+ } else if (tvp != NULLVP) {
+ unp = VTOUNIONFS(tvp);
+#ifdef UNIONFS_IDBG_RENAME
+ UNIONFS_INTERNAL_DEBUG("tvp=%p, utvp=%p, ltvp=%p\n", tvp, unp->un_uppervp, unp->un_lowervp);
+#endif
+ if (unp->un_uppervp == NULLVP)
+ rtvp = NULLVP;
+ else {
+ if (tvp->v_type == VDIR) {
+ error = EINVAL;
+ goto unionfs_rename_abort;
+ }
+ rtvp = unp->un_uppervp;
+ ltvp = unp->un_lowervp;
+ vref(rtvp);
}
-
- /*
- * New tdvp is a lock and reference on uppervp.
- * Put away the old tdvp.
- */
- tdvp = union_lock_upper(un, ap->a_tcnp->cn_thread);
- vput(ap->a_tdvp);
}
- /*
- * Figure out what tvp (destination file) to pass to the
- * lower level.
- *
- * If the uppervp file does not exist, put away the (wrong)
- * file and change tvp to NULL.
- */
+ if (needrelookup != 0) {
+ if ((error = vn_lock(fdvp, LK_EXCLUSIVE, td)) != 0)
+ goto unionfs_rename_abort;
+ error = unionfs_relookup_for_delete(fdvp, fcnp, td);
+ VOP_UNLOCK(fdvp, 0, td);
+ if (error != 0)
+ goto unionfs_rename_abort;
+
+ /* Locke of tvp is canceled in order to avoid recursive lock. */
+ if (tvp != NULLVP && tvp != tdvp)
+ VOP_UNLOCK(tvp, 0, td);
+ error = unionfs_relookup_for_rename(tdvp, tcnp, td);
+ if (tvp != NULLVP && tvp != tdvp)
+ vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY, td);
+ if (error != 0)
+ goto unionfs_rename_abort;
+ }
- if (tvp != NULLVP && tvp->v_op == &union_vnodeops) {
- struct union_node *un = VTOUNION(tvp);
+ error = VOP_RENAME(rfdvp, rfvp, fcnp, rtdvp, rtvp, tcnp);
- tvp = union_lock_upper(un, ap->a_tcnp->cn_thread);
- vput(ap->a_tvp);
- /* note: tvp may be NULL */
+ if (fdvp != rfdvp)
+ vrele(fdvp);
+ if (fvp != rfvp)
+ vrele(fvp);
+ if (tdvp != rtdvp)
+ vrele(tdvp);
+ if (tvp != rtvp && tvp != NULLVP) {
+ if (rtvp == NULLVP)
+ vput(tvp);
+ else
+ vrele(tvp);
}
+ if (ltdvp != NULLVP)
+ VOP_UNLOCK(ltdvp, 0, td);
+ if (ltvp != NULLVP)
+ VOP_UNLOCK(ltvp, 0, td);
- /*
- * VOP_RENAME() releases/vputs prior to returning, so we have no
- * cleanup to do.
- */
-
- return (VOP_RENAME(fdvp, fvp, ap->a_fcnp, tdvp, tvp, ap->a_tcnp));
+ UNIONFS_INTERNAL_DEBUG("unionfs_rename: leave (%d)\n", error);
- /*
- * Error. We still have to release / vput the various elements.
- */
+ return (error);
-bad:
- vrele(fdvp);
- if (fvp)
- vrele(fvp);
+unionfs_rename_abort:
+ if (fdvp != rfdvp)
+ vrele(rfdvp);
+ if (fvp != rfvp)
+ vrele(rfvp);
+ if (tdvp != rtdvp)
+ vrele(rtdvp);
vput(tdvp);
+ if (tvp != rtvp && rtvp != NULLVP)
+ vrele(rtvp);
if (tvp != NULLVP) {
- if (tvp != tdvp)
+ if (tdvp != tvp)
vput(tvp);
else
vrele(tvp);
}
+ vrele(fdvp);
+ vrele(fvp);
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_rename: leave (%d)\n", error);
+
return (error);
}
static int
-union_mkdir(ap)
- struct vop_mkdir_args /* {
- struct vnode *a_dvp;
- struct vnode **a_vpp;
- struct componentname *a_cnp;
- struct vattr *a_vap;
- } */ *ap;
+unionfs_mkdir(struct vop_mkdir_args *ap)
{
- struct union_node *dun = VTOUNION(ap->a_dvp);
- struct componentname *cnp = ap->a_cnp;
- struct thread *td = cnp->cn_thread;
- struct vnode *upperdvp;
- int error = EROFS;
+ int error;
+ struct unionfs_node *dunp;
+ struct componentname *cnp;
+ struct thread *td;
+ struct vnode *udvp;
+ struct vnode *uvp;
+ struct vattr va;
- if ((upperdvp = union_lock_upper(dun, td)) != NULLVP) {
- struct vnode *vp;
+ UNIONFS_INTERNAL_DEBUG("unionfs_mkdir: enter\n");
- error = VOP_MKDIR(upperdvp, &vp, cnp, ap->a_vap);
- union_unlock_upper(upperdvp, td);
+ error = EROFS;
+ dunp = VTOUNIONFS(ap->a_dvp);
+ cnp = ap->a_cnp;
+ td = curthread;
+ udvp = dunp->un_uppervp;
+
+ if (udvp != NULLVP) {
+ /* check opaque */
+ if (!(cnp->cn_flags & ISWHITEOUT)) {
+ error = VOP_GETATTR(udvp, &va, cnp->cn_cred, td);
+ if (error != 0)
+ return (error);
+ if (va.va_flags & OPAQUE)
+ cnp->cn_flags |= ISWHITEOUT;
+ }
- if (error == 0) {
- VOP_UNLOCK(vp, 0, td);
- UDEBUG(("ALLOCVP-2 FROM %p REFS %d\n", vp, vrefcnt(vp)));
- error = union_allocvp(ap->a_vpp, ap->a_dvp->v_mount,
- ap->a_dvp, NULLVP, cnp, vp, NULLVP, 1);
- UDEBUG(("ALLOCVP-2B FROM %p REFS %d\n", *ap->a_vpp, vrefcnt(vp)));
+ if ((error = VOP_MKDIR(udvp, &uvp, cnp, ap->a_vap)) == 0) {
+ VOP_UNLOCK(uvp, 0, td);
+ error = unionfs_nodeget(ap->a_dvp->v_mount, uvp, NULLVP,
+ ap->a_dvp, ap->a_vpp, cnp, td);
+ vrele(uvp);
}
}
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_mkdir: leave (%d)\n", error);
+
return (error);
}
static int
-union_rmdir(ap)
- struct vop_rmdir_args /* {
- struct vnode *a_dvp;
- struct vnode *a_vp;
- struct componentname *a_cnp;
- } */ *ap;
+unionfs_rmdir(struct vop_rmdir_args *ap)
{
- struct union_node *dun = VTOUNION(ap->a_dvp);
- struct union_node *un = VTOUNION(ap->a_vp);
- struct componentname *cnp = ap->a_cnp;
- struct thread *td = cnp->cn_thread;
- struct vnode *upperdvp;
- struct vnode *uppervp;
- int error;
+ int error;
+ struct unionfs_node *dunp;
+ struct unionfs_node *unp;
+ struct componentname *cnp;
+ struct thread *td;
+ struct vnode *udvp;
+ struct vnode *uvp;
+ struct vnode *lvp;
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_rmdir: enter\n");
+
+ error = 0;
+ dunp = VTOUNIONFS(ap->a_dvp);
+ unp = VTOUNIONFS(ap->a_vp);
+ cnp = ap->a_cnp;
+ td = curthread;
+ udvp = dunp->un_uppervp;
+ uvp = unp->un_uppervp;
+ lvp = unp->un_lowervp;
+
+ if (udvp == NULLVP)
+ return (EROFS);
- if ((upperdvp = union_lock_upper(dun, td)) == NULLVP)
- panic("union rmdir: null upper vnode");
+ if (udvp == uvp)
+ return (EOPNOTSUPP);
- if ((uppervp = union_lock_upper(un, td)) != NULLVP) {
- if (union_dowhiteout(un, cnp->cn_cred, td))
- cnp->cn_flags |= DOWHITEOUT;
- if (cnp->cn_flags & DOWHITEOUT) /* XXX fs corruption */
- error = EOPNOTSUPP;
- else
- error = VOP_RMDIR(upperdvp, uppervp, ap->a_cnp);
- if (!error)
- union_removed_upper(un);
- union_unlock_upper(uppervp, td);
- } else {
- error = union_mkwhiteout(
- MOUNTTOUNIONMOUNT(ap->a_dvp->v_mount),
- dun->un_uppervp, ap->a_cnp, un->un_path);
+ if (uvp != NULLVP) {
+ if (lvp != NULLVP) {
+ error = unionfs_check_rmdir(ap->a_vp, cnp->cn_cred, td);
+ if (error != 0)
+ return (error);
+ }
+ cnp->cn_flags |= DOWHITEOUT;
+ error = VOP_RMDIR(udvp, uvp, cnp);
}
- union_unlock_upper(upperdvp, td);
+ else if (lvp != NULLVP)
+ error = unionfs_mkwhiteout(udvp, cnp, td, unp->un_path);
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_rmdir: leave (%d)\n", error);
+
return (error);
}
-/*
- * union_symlink:
- *
- * dvp is locked on entry and remains locked on return. a_vpp is garbage
- * (unused).
- */
-
static int
-union_symlink(ap)
- struct vop_symlink_args /* {
- struct vnode *a_dvp;
- struct vnode **a_vpp;
- struct componentname *a_cnp;
- struct vattr *a_vap;
- char *a_target;
- } */ *ap;
+unionfs_symlink(struct vop_symlink_args *ap)
{
- struct union_node *dun = VTOUNION(ap->a_dvp);
- struct componentname *cnp = ap->a_cnp;
- struct thread *td = cnp->cn_thread;
- struct vnode *dvp;
- int error = EROFS;
-
- if ((dvp = union_lock_upper(dun, td)) != NULLVP) {
- error = VOP_SYMLINK(dvp, ap->a_vpp, cnp, ap->a_vap,
- ap->a_target);
- union_unlock_upper(dvp, td);
+ int error;
+ struct unionfs_node *dunp;
+ struct componentname *cnp;
+ struct thread *td;
+ struct vnode *udvp;
+ struct vnode *uvp;
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_symlink: enter\n");
+
+ error = EROFS;
+ dunp = VTOUNIONFS(ap->a_dvp);
+ cnp = ap->a_cnp;
+ td = curthread;
+ udvp = dunp->un_uppervp;
+
+ if (udvp != NULLVP) {
+ error = VOP_SYMLINK(udvp, &uvp, cnp, ap->a_vap, ap->a_target);
+ if (error == 0) {
+ VOP_UNLOCK(uvp, 0, td);
+ error = unionfs_nodeget(ap->a_dvp->v_mount, uvp, NULLVP,
+ ap->a_dvp, ap->a_vpp, cnp, td);
+ vrele(uvp);
+ }
}
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_symlink: leave (%d)\n", error);
+
return (error);
}
-/*
- * union_readdir ()works in concert with getdirentries() and
- * readdir(3) to provide a list of entries in the unioned
- * directories. getdirentries() is responsible for walking
- * down the union stack. readdir(3) is responsible for
- * eliminating duplicate names from the returned data stream.
- */
static int
-union_readdir(ap)
- struct vop_readdir_args /* {
- struct vnode *a_vp;
- struct uio *a_uio;
- struct ucred *a_cred;
- int *a_eofflag;
- u_long *a_cookies;
- int a_ncookies;
- } */ *ap;
+unionfs_readdir(struct vop_readdir_args *ap)
{
- struct union_node *un = VTOUNION(ap->a_vp);
- struct thread *td = ap->a_uio->uio_td;
- struct vnode *uvp;
- int error = 0;
-
- if ((uvp = union_lock_upper(un, td)) != NULLVP) {
- ap->a_vp = uvp;
- error = VOP_READDIR_AP(ap);
- union_unlock_upper(uvp, td);
+ int error;
+ int eofflag;
+ struct unionfs_node *unp;
+ struct unionfs_node_status *unsp;
+ struct uio *uio;
+ struct vnode *uvp;
+ struct vnode *lvp;
+ struct thread *td;
+ struct vattr va;
+
+ int ncookies_bk;
+ u_long *cookies_bk;
+
+ ASSERT_VOP_ELOCKED(ap->a_vp, "unionfs_readdir");
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_readdir: enter\n");
+
+ error = 0;
+ eofflag = 0;
+ unp = VTOUNIONFS(ap->a_vp);
+ uio = ap->a_uio;
+ uvp = unp->un_uppervp;
+ lvp = unp->un_lowervp;
+ td = uio->uio_td;
+ ncookies_bk = 0;
+ cookies_bk = NULL;
+
+ if (ap->a_vp->v_type != VDIR)
+ return (ENOTDIR);
+
+ /* check opaque */
+ if (uvp != NULLVP && lvp != NULLVP) {
+ if ((error = VOP_GETATTR(uvp, &va, ap->a_cred, td)) != 0)
+ return (error);
+ if (va.va_flags & OPAQUE)
+ lvp = NULLVP;
+ }
+
+ unionfs_get_node_status(unp, curthread, &unsp);
+
+ /* upper only */
+ if (uvp != NULLVP && lvp == NULLVP) {
+ if (unsp->uns_upper_opencnt <= 0)
+ error = EBADF;
+ else {
+ error = VOP_READDIR(uvp, uio, ap->a_cred, ap->a_eofflag,
+ ap->a_ncookies, ap->a_cookies);
+ unsp->uns_readdir_status = 0;
+ }
+
+ goto unionfs_readdir_exit;
+ }
+
+ /* lower only */
+ if (uvp == NULLVP && lvp != NULLVP) {
+ if (unsp->uns_lower_opencnt <= 0)
+ error = EBADF;
+ else {
+ error = VOP_READDIR(lvp, uio, ap->a_cred, ap->a_eofflag,
+ ap->a_ncookies, ap->a_cookies);
+ unsp->uns_readdir_status = 2;
+ }
+
+ goto unionfs_readdir_exit;
}
- return(error);
+
+ /*
+ * readdir upper and lower
+ */
+ if (unsp->uns_lower_opencnt <= 0 || unsp->uns_upper_opencnt <= 0) {
+ error = EBADF;
+ goto unionfs_readdir_exit;
+ }
+
+ if (uio->uio_offset == 0)
+ unsp->uns_readdir_status = 0;
+
+ if (unsp->uns_readdir_status == 0) {
+ /* read upper */
+ error = VOP_READDIR(uvp, uio, ap->a_cred, &eofflag,
+ ap->a_ncookies, ap->a_cookies);
+
+ if (error != 0 || eofflag == 0) {
+ UNIONFS_INTERNAL_DEBUG("unionfs_readdir: leave (%d)\n", error);
+ return (error);
+ }
+ unsp->uns_readdir_status = 1;
+
+ /*
+ * ufs(and other fs) needs size of uio_resid larger than
+ * DIRBLKSIZ.
+ * size of DIRBLKSIZ equals DEV_BSIZE.
+ * (see: ufs/ufs/ufs_vnops.c ufs_readdir func , ufs/ufs/dir.h)
+ */
+ if (uio->uio_resid <= (uio->uio_resid & (DEV_BSIZE -1))) {
+ UNIONFS_INTERNAL_DEBUG("unionfs_readdir: leave (%d)\n", error);
+ return (0);
+ }
+
+ /*
+ * backup cookies
+ * It prepares to readdir in lower.
+ */
+ if (ap->a_ncookies != NULL) {
+ ncookies_bk = *(ap->a_ncookies);
+ *(ap->a_ncookies) = 0;
+ }
+ if (ap->a_cookies != NULL) {
+ cookies_bk = *(ap->a_cookies);
+ *(ap->a_cookies) = NULL;
+ }
+ }
+
+ /* initialize for readdir in lower */
+ if (unsp->uns_readdir_status == 1) {
+ unsp->uns_readdir_status = 2;
+ uio->uio_offset = 0;
+ }
+
+ /* read lower */
+ error = VOP_READDIR(lvp, uio, ap->a_cred, ap->a_eofflag,
+ ap->a_ncookies, ap->a_cookies);
+
+ if (cookies_bk != NULL) {
+ /* merge cookies */
+ int size;
+ u_long *newcookies, *pos;
+
+ size = *(ap->a_ncookies) + ncookies_bk;
+ newcookies = (u_long *) malloc(size * sizeof(u_long),
+ M_TEMP, M_WAITOK);
+ pos = newcookies;
+
+ memcpy(pos, cookies_bk, ncookies_bk * sizeof(u_long));
+ pos += ncookies_bk * sizeof(u_long);
+ memcpy(pos, *(ap->a_cookies), *(ap->a_ncookies) * sizeof(u_long));
+ free(cookies_bk, M_TEMP);
+ free(*(ap->a_cookies), M_TEMP);
+ *(ap->a_ncookies) = size;
+ *(ap->a_cookies) = newcookies;
+ }
+
+unionfs_readdir_exit:
+ UNIONFS_INTERNAL_DEBUG("unionfs_readdir: leave (%d)\n", error);
+
+ return (error);
}
static int
-union_readlink(ap)
- struct vop_readlink_args /* {
- struct vnode *a_vp;
- struct uio *a_uio;
- struct ucred *a_cred;
- } */ *ap;
+unionfs_readlink(struct vop_readlink_args *ap)
{
int error;
- struct union_node *un = VTOUNION(ap->a_vp);
- struct uio *uio = ap->a_uio;
- struct thread *td = uio->uio_td;
- struct vnode *vp;
+ struct unionfs_node *unp;
+ struct vnode *vp;
- vp = union_lock_other(un, td);
- KASSERT(vp != NULL, ("union_readlink: backing vnode missing!"));
+ UNIONFS_INTERNAL_DEBUG("unionfs_readlink: enter\n");
- ap->a_vp = vp;
- error = VOP_READLINK_AP(ap);
- union_unlock_other(vp, td);
+ unp = VTOUNIONFS(ap->a_vp);
+ vp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp);
+
+ error = VOP_READLINK(vp, ap->a_uio, ap->a_cred);
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_readlink: leave (%d)\n", error);
return (error);
}
static int
-union_getwritemount(ap)
- struct vop_getwritemount_args /* {
- struct vnode *a_vp;
- struct mount **a_mpp;
- } */ *ap;
+unionfs_getwritemount(struct vop_getwritemount_args *ap)
{
- struct vnode *vp = ap->a_vp;
- struct vnode *uvp = UPPERVP(vp);
+ int error;
+ struct vnode *uvp;
+ struct vnode *vp;
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_getwritemount: enter\n");
+
+ error = 0;
+ vp = ap->a_vp;
+
+ if (vp == NULLVP || (vp->v_mount->mnt_flag & MNT_RDONLY))
+ return (EACCES);
+
+ uvp = UNIONFSVPTOUPPERVP(vp);
+ if (uvp == NULLVP && VREG == vp->v_type)
+ uvp = UNIONFSVPTOUPPERVP(VTOUNIONFS(vp)->un_dvp);
- if (uvp == NULL) {
+ if (uvp != NULLVP)
+ error = VOP_GETWRITEMOUNT(uvp, ap->a_mpp);
+ else {
VI_LOCK(vp);
- if (vp->v_iflag & VI_FREE) {
- VI_UNLOCK(vp);
- return (EOPNOTSUPP);
- }
+ if (vp->v_iflag & VI_FREE)
+ error = EOPNOTSUPP;
+ else
+ error = EACCES;
VI_UNLOCK(vp);
- return (EACCES);
}
- return(VOP_GETWRITEMOUNT(uvp, ap->a_mpp));
-}
-/*
- * union_inactive:
- *
- * Called with the vnode locked. We are expected to unlock the vnode.
- */
+ UNIONFS_INTERNAL_DEBUG("unionfs_getwritemount: leave (%d)\n", error);
+
+ return (error);
+}
static int
-union_inactive(ap)
- struct vop_inactive_args /* {
- struct vnode *a_vp;
- struct thread *a_td;
- } */ *ap;
+unionfs_inactive(struct vop_inactive_args *ap)
{
- struct vnode *vp = ap->a_vp;
- struct union_node *un = VTOUNION(vp);
+ struct unionfs_node *unp;
- /*
- * Do nothing (and _don't_ bypass).
- * Wait to vrele lowervp until reclaim,
- * so that until then our union_node is in the
- * cache and reusable.
- *
- */
+ unp = VTOUNIONFS(ap->a_vp);
- if (un->un_dircache != NULL)
- union_dircache_free(un);
+ if (unp == NULL || !(unp->un_flag & UNIONFS_CACHED))
+ vgone(ap->a_vp);
-#if 0
- if ((un->un_flags & UN_ULOCK) && un->un_uppervp) {
- un->un_flags &= ~UN_ULOCK;
- VOP_UNLOCK(un->un_uppervp, 0, td);
- }
-#endif
+ return (0);
+}
+
+static int
+unionfs_reclaim(struct vop_reclaim_args *ap)
+{
+ /* UNIONFS_INTERNAL_DEBUG("unionfs_reclaim: enter\n"); */
+
+ unionfs_hashrem(ap->a_vp, ap->a_td);
- if ((un->un_flags & UN_CACHED) == 0)
- vgone(vp);
+ /* UNIONFS_INTERNAL_DEBUG("unionfs_reclaim: leave\n"); */
return (0);
}
static int
-union_reclaim(ap)
- struct vop_reclaim_args /* {
- struct vnode *a_vp;
- } */ *ap;
+unionfs_print(struct vop_print_args *ap)
{
- union_freevp(ap->a_vp);
+ struct unionfs_node *unp;
+ /* struct unionfs_node_status *unsp; */
+
+ unp = VTOUNIONFS(ap->a_vp);
+ /* unionfs_get_node_status(unp, curthread, &unsp); */
+
+ printf("unionfs_vp=%p, uppervp=%p, lowervp=%p\n",
+ ap->a_vp, unp->un_uppervp, unp->un_lowervp);
+ /*
+ printf("unionfs opencnt: uppervp=%d, lowervp=%d\n",
+ unsp->uns_upper_opencnt, unsp->uns_lower_opencnt);
+ */
+
+ if (unp->un_uppervp != NULLVP)
+ vprint("unionfs: upper", unp->un_uppervp);
+ if (unp->un_lowervp != NULLVP)
+ vprint("unionfs: lower", unp->un_lowervp);
return (0);
}
static int
-union_print(ap)
- struct vop_print_args /* {
- struct vnode *a_vp;
- } */ *ap;
+unionfs_get_llt_revlock(int flags)
{
- struct vnode *vp = ap->a_vp;
+ int count;
- printf("\tvp=%p, uppervp=%p, lowervp=%p\n",
- vp, UPPERVP(vp), LOWERVP(vp));
- if (UPPERVP(vp) != NULLVP)
- vprint("union: upper", UPPERVP(vp));
- if (LOWERVP(vp) != NULLVP)
- vprint("union: lower", LOWERVP(vp));
+ flags &= LK_TYPE_MASK;
+ for (count = 0; un_llt[count].lock != 0; count++) {
+ if (flags == un_llt[count].lock) {
+ return un_llt[count].revlock;
+ }
+ }
- return (0);
+ return 0;
}
static int
-union_pathconf(ap)
- struct vop_pathconf_args /* {
- struct vnode *a_vp;
- int a_name;
- int *a_retval;
- } */ *ap;
+unionfs_lock(struct _vop_lock_args *ap)
{
- int error;
- struct thread *td = curthread; /* XXX */
- struct union_node *un = VTOUNION(ap->a_vp);
- struct vnode *vp;
+ int error;
+ int flags;
+ int revlock;
+ int uhold;
+ struct unionfs_mount *ump;
+ struct unionfs_node *unp;
+ struct vnode *vp;
+ struct vnode *uvp;
+ struct vnode *lvp;
+ struct thread *td;
+
+ error = 0;
+ uhold = 0;
+ flags = ap->a_flags;
+ vp = ap->a_vp;
+ td = ap->a_td;
+
+ if (LK_RELEASE == (flags & LK_TYPE_MASK) || !(flags & LK_TYPE_MASK))
+ return (VOP_UNLOCK(vp, flags, td));
+
+ if ((revlock = unionfs_get_llt_revlock(flags)) == 0)
+ panic("unknown lock type: 0x%x", flags & LK_TYPE_MASK);
+
+ if (!(flags & LK_INTERLOCK))
+ VI_LOCK(vp);
+
+ ump = MOUNTTOUNIONFSMOUNT(vp->v_mount);
+ unp = VTOUNIONFS(vp);
+ if (NULL == unp)
+ goto unionfs_lock_null_vnode;
+ lvp = unp->un_lowervp;
+ uvp = unp->un_uppervp;
+
+ /*
+ * Sometimes, lower or upper is already exclusive locked.
+ * (ex. vfs_domount: mounted vnode is already locked.)
+ */
+ if ((flags & LK_TYPE_MASK) == LK_EXCLUSIVE &&
+ vp == ump->um_rootvp)
+ flags |= LK_CANRECURSE;
+
+ if (lvp != NULLVP) {
+ VI_LOCK_FLAGS(lvp, MTX_DUPOK);
+ flags |= LK_INTERLOCK;
+ vholdl(lvp);
+
+ VI_UNLOCK(vp);
+ ap->a_flags &= ~LK_INTERLOCK;
+
+ error = VOP_LOCK(lvp, flags, td);
+
+ VI_LOCK(vp);
+ unp = VTOUNIONFS(vp);
+ if (unp == NULL) {
+ if (error == 0)
+ VOP_UNLOCK(lvp, 0, td);
+ VI_UNLOCK(vp);
+ vdrop(lvp);
+ return (vop_stdlock(ap));
+ }
+ }
+
+ if (error == 0 && uvp != NULLVP) {
+ VI_LOCK_FLAGS(uvp, MTX_DUPOK);
+ flags |= LK_INTERLOCK;
+ vholdl(uvp);
+ uhold = 1;
+
+ VI_UNLOCK(vp);
+ ap->a_flags &= ~LK_INTERLOCK;
+
+ error = VOP_LOCK(uvp, flags, td);
- vp = union_lock_other(un, td);
- KASSERT(vp != NULL, ("union_pathconf: backing vnode missing!"));
+ VI_LOCK(vp);
+ unp = VTOUNIONFS(vp);
+ if (unp == NULL) {
+ if (error == 0) {
+ VOP_UNLOCK(uvp, 0, td);
+ if (lvp != NULLVP)
+ VOP_UNLOCK(lvp, 0, td);
+ }
+ VI_UNLOCK(vp);
+ if (lvp != NULLVP)
+ vdrop(lvp);
+ vdrop(uvp);
+ return (vop_stdlock(ap));
+ }
+
+ if (error != 0 && lvp != NULLVP)
+ vn_lock(lvp, revlock | LK_RETRY, td);
+ }
- ap->a_vp = vp;
- error = VOP_PATHCONF_AP(ap);
- union_unlock_other(vp, td);
+ VI_UNLOCK(vp);
+ if (lvp != NULLVP)
+ vdrop(lvp);
+ if (uhold != 0)
+ vdrop(uvp);
return (error);
+
+unionfs_lock_null_vnode:
+ ap->a_flags |= LK_INTERLOCK;
+ return (vop_stdlock(ap));
}
static int
-union_advlock(ap)
- struct vop_advlock_args /* {
- struct vnode *a_vp;
- caddr_t a_id;
- int a_op;
- struct flock *a_fl;
- int a_flags;
- } */ *ap;
+unionfs_unlock(struct vop_unlock_args *ap)
{
- register struct vnode *ovp = OTHERVP(ap->a_vp);
+ int error;
+ int flags;
+ int mtxlkflag;
+ int uhold;
+ struct vnode *vp;
+ struct vnode *lvp;
+ struct vnode *uvp;
+ struct unionfs_node *unp;
+
+ error = 0;
+ mtxlkflag = 0;
+ uhold = 0;
+ flags = ap->a_flags | LK_RELEASE;
+ vp = ap->a_vp;
+
+ if (flags & LK_INTERLOCK)
+ mtxlkflag = 1;
+ else if (mtx_owned(VI_MTX(vp)) == 0) {
+ VI_LOCK(vp);
+ mtxlkflag = 2;
+ }
+
+ unp = VTOUNIONFS(vp);
+ if (unp == NULL)
+ goto unionfs_unlock_null_vnode;
+ lvp = unp->un_lowervp;
+ uvp = unp->un_uppervp;
+
+ if (lvp != NULLVP) {
+ VI_LOCK_FLAGS(lvp, MTX_DUPOK);
+ flags |= LK_INTERLOCK;
+ vholdl(lvp);
- ap->a_vp = ovp;
- return (VOP_ADVLOCK_AP(ap));
+ VI_UNLOCK(vp);
+ ap->a_flags &= ~LK_INTERLOCK;
+
+ error = VOP_UNLOCK(lvp, flags, ap->a_td);
+
+ VI_LOCK(vp);
+ }
+
+ if (error == 0 && uvp != NULLVP) {
+ VI_LOCK_FLAGS(uvp, MTX_DUPOK);
+ flags |= LK_INTERLOCK;
+ vholdl(uvp);
+ uhold = 1;
+
+ VI_UNLOCK(vp);
+ ap->a_flags &= ~LK_INTERLOCK;
+
+ error = VOP_UNLOCK(uvp, flags, ap->a_td);
+
+ VI_LOCK(vp);
+ }
+
+ VI_UNLOCK(vp);
+ if (lvp != NULLVP)
+ vdrop(lvp);
+ if (uhold != 0)
+ vdrop(uvp);
+ if (mtxlkflag == 0)
+ VI_LOCK(vp);
+
+ return error;
+
+unionfs_unlock_null_vnode:
+ if (mtxlkflag == 2)
+ VI_UNLOCK(vp);
+ return (vop_stdunlock(ap));
}
+static int
+unionfs_pathconf(struct vop_pathconf_args *ap)
+{
+ struct unionfs_node *unp;
+ struct vnode *vp;
+
+ unp = VTOUNIONFS(ap->a_vp);
+ vp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp);
+
+ return (VOP_PATHCONF(vp, ap->a_name, ap->a_retval));
+}
+
+static int
+unionfs_advlock(struct vop_advlock_args *ap)
+{
+ int error;
+ struct unionfs_node *unp;
+ struct unionfs_node_status *unsp;
+ struct vnode *vp;
+ struct vnode *uvp;
+ struct thread *td;
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_advlock: enter\n");
+
+ vp = ap->a_vp;
+ td = curthread;
+
+ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
+
+ unp = VTOUNIONFS(ap->a_vp);
+ uvp = unp->un_uppervp;
+
+ if (uvp == NULLVP) {
+ error = unionfs_copyfile(unp, 1, td->td_ucred, td);
+ if (error != 0)
+ goto unionfs_advlock_abort;
+ uvp = unp->un_uppervp;
+
+ unionfs_get_node_status(unp, td, &unsp);
+ if (unsp->uns_lower_opencnt > 0) {
+ /* try reopen the vnode */
+ error = VOP_OPEN(uvp, unsp->uns_lower_openmode, td->td_ucred, td, unsp->uns_lower_fdidx);
+ if (error)
+ goto unionfs_advlock_abort;
+ unsp->uns_upper_opencnt++;
+ VOP_CLOSE(unp->un_lowervp, unsp->uns_lower_openmode, td->td_ucred, td);
+ unsp->uns_lower_opencnt--;
+ }
+ }
+
+ VOP_UNLOCK(vp, 0, td);
+
+ error = VOP_ADVLOCK(uvp, ap->a_id, ap->a_op, ap->a_fl, ap->a_flags);
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_advlock: leave (%d)\n", error);
+
+ return error;
+
+unionfs_advlock_abort:
+ VOP_UNLOCK(vp, 0, td);
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_advlock: leave (%d)\n", error);
+
+ return error;
+}
-/*
- * XXX - vop_strategy must be hand coded because it has no
- * YYY - and it is not coherent with anything
- *
- * vnode in its arguments.
- * This goes away with a merged VM/buffer cache.
- */
static int
-union_strategy(ap)
- struct vop_strategy_args /* {
- struct vnode *a_vp;
- struct buf *a_bp;
- } */ *ap;
+unionfs_strategy(struct vop_strategy_args *ap)
{
- struct buf *bp = ap->a_bp;
- struct vnode *othervp = OTHERVP(ap->a_vp);
+ struct unionfs_node *unp;
+ struct vnode *vp;
+
+ unp = VTOUNIONFS(ap->a_vp);
+ vp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp);
#ifdef DIAGNOSTIC
- if (othervp == NULLVP)
- panic("union_strategy: nil vp");
- if ((bp->b_iocmd == BIO_WRITE) &&
- (othervp == LOWERVP(ap->a_vp)))
- panic("union_strategy: writing to lowervp");
+ if (vp == NULLVP)
+ panic("unionfs_strategy: nullvp");
+
+ if (ap->a_bp->b_iocmd == BIO_WRITE && vp == unp->un_lowervp)
+ panic("unionfs_strategy: writing to lowervp");
#endif
- return (VOP_STRATEGY(othervp, bp));
+
+ return (VOP_STRATEGY(vp, ap->a_bp));
}
static int
-union_getacl(ap)
- struct vop_getacl_args /* {
- struct vnode *a_vp;
- acl_type_t a_type;
- struct acl *a_aclp;
- struct ucred *a_cred;
- struct thread *a_td;
- } */ *ap;
+unionfs_getacl(struct vop_getacl_args *ap)
{
- int error;
- struct union_node *un = VTOUNION(ap->a_vp);
- struct vnode *vp;
+ int error;
+ struct unionfs_node *unp;
+ struct vnode *vp;
+
+ unp = VTOUNIONFS(ap->a_vp);
+ vp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp);
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_getacl: enter\n");
- vp = union_lock_other(un, ap->a_td);
- ap->a_vp = vp;
- error = VOP_GETACL_AP(ap);
- union_unlock_other(vp, ap->a_td);
+ error = VOP_GETACL(vp, ap->a_type, ap->a_aclp, ap->a_cred, ap->a_td);
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_getacl: leave (%d)\n", error);
return (error);
}
static int
-union_setacl(ap)
- struct vop_setacl_args /* {
- struct vnode *a_vp;
- acl_type_t a_type;
- struct acl *a_aclp;
- struct ucred *a_cred;
- struct thread *a_td;
- } */ *ap;
+unionfs_setacl(struct vop_setacl_args *ap)
{
- int error;
- struct union_node *un = VTOUNION(ap->a_vp);
- struct vnode *vp;
+ int error;
+ struct unionfs_node *unp;
+ struct vnode *uvp;
+ struct vnode *lvp;
+ struct thread *td;
- vp = union_lock_other(un, ap->a_td);
- ap->a_vp = vp;
- error = VOP_SETACL_AP(ap);
- union_unlock_other(vp, ap->a_td);
+ UNIONFS_INTERNAL_DEBUG("unionfs_setacl: enter\n");
+
+ error = EROFS;
+ unp = VTOUNIONFS(ap->a_vp);
+ uvp = unp->un_uppervp;
+ lvp = unp->un_lowervp;
+ td = ap->a_td;
+
+ if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)
+ return (EROFS);
+
+ if (uvp == NULLVP && lvp->v_type == VREG) {
+ if ((error = unionfs_copyfile(unp, 1, ap->a_cred, td)) != 0)
+ return (error);
+ uvp = unp->un_uppervp;
+ }
+
+ if (uvp != NULLVP)
+ error = VOP_SETACL(uvp, ap->a_type, ap->a_aclp, ap->a_cred, td);
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_setacl: leave (%d)\n", error);
return (error);
}
static int
-union_aclcheck(ap)
- struct vop_aclcheck_args /* {
- struct vnode *a_vp;
- acl_type_t a_type;
- struct acl *a_aclp;
- struct ucred *a_cred;
- struct thread *a_td;
- } */ *ap;
+unionfs_aclcheck(struct vop_aclcheck_args *ap)
{
- struct vnode *ovp = OTHERVP(ap->a_vp);
+ int error;
+ struct unionfs_node *unp;
+ struct vnode *vp;
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_aclcheck: enter\n");
+
+ unp = VTOUNIONFS(ap->a_vp);
+ vp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp);
+
+ error = VOP_ACLCHECK(vp, ap->a_type, ap->a_aclp, ap->a_cred, ap->a_td);
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_aclcheck: leave (%d)\n", error);
- ap->a_vp = ovp;
- return (VOP_ACLCHECK_AP(ap));
+ return (error);
}
static int
-union_closeextattr(ap)
- struct vop_closeextattr_args /* {
- struct vnode *a_vp;
- int a_commit;
- struct ucred *a_cred;
- struct thread *a_td;
- } */ *ap;
+unionfs_openextattr(struct vop_openextattr_args *ap)
{
- int error;
- struct union_node *un = VTOUNION(ap->a_vp);
- struct vnode *vp;
+ int error;
+ struct unionfs_node *unp;
+ struct vnode *vp;
+
+ unp = VTOUNIONFS(ap->a_vp);
+ vp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp);
- vp = union_lock_other(un, ap->a_td);
- ap->a_vp = vp;
- error = VOP_CLOSEEXTATTR_AP(ap);
- union_unlock_other(vp, ap->a_td);
+ if ((vp == unp->un_uppervp && (unp->un_flag & UNIONFS_OPENEXTU)) ||
+ (vp == unp->un_lowervp && (unp->un_flag & UNIONFS_OPENEXTL)))
+ return (EBUSY);
+
+ error = VOP_OPENEXTATTR(vp, ap->a_cred, ap->a_td);
+
+ if (error == 0) {
+ if (vp == unp->un_uppervp)
+ unp->un_flag |= UNIONFS_OPENEXTU;
+ else
+ unp->un_flag |= UNIONFS_OPENEXTL;
+ }
return (error);
}
static int
-union_getextattr(ap)
- struct vop_getextattr_args /* {
- struct vnode *a_vp;
- int a_attrnamespace;
- const char *a_name;
- struct uio *a_uio;
- size_t *a_size;
- struct ucred *a_cred;
- struct thread *a_td;
- } */ *ap;
+unionfs_closeextattr(struct vop_closeextattr_args *ap)
{
- int error;
- struct union_node *un = VTOUNION(ap->a_vp);
- struct vnode *vp;
+ int error;
+ struct unionfs_node *unp;
+ struct vnode *vp;
+
+ unp = VTOUNIONFS(ap->a_vp);
+ vp = NULLVP;
+
+ if (unp->un_flag & UNIONFS_OPENEXTU)
+ vp = unp->un_uppervp;
+ else if (unp->un_flag & UNIONFS_OPENEXTL)
+ vp = unp->un_lowervp;
- vp = union_lock_other(un, ap->a_td);
- ap->a_vp = vp;
- error = VOP_GETEXTATTR_AP(ap);
- union_unlock_other(vp, ap->a_td);
+ if (vp == NULLVP)
+ return (EOPNOTSUPP);
+
+ error = VOP_CLOSEEXTATTR(vp, ap->a_commit, ap->a_cred, ap->a_td);
+
+ if (error == 0) {
+ if (vp == unp->un_uppervp)
+ unp->un_flag &= ~UNIONFS_OPENEXTU;
+ else
+ unp->un_flag &= ~UNIONFS_OPENEXTL;
+ }
return (error);
}
static int
-union_listextattr(ap)
- struct vop_listextattr_args /* {
- struct vnode *a_vp;
- int a_attrnamespace;
- struct uio *a_uio;
- size_t *a_size;
- struct ucred *a_cred;
- struct thread *a_td;
- } */ *ap;
+unionfs_getextattr(struct vop_getextattr_args *ap)
{
- int error;
- struct union_node *un = VTOUNION(ap->a_vp);
- struct vnode *vp;
+ struct unionfs_node *unp;
+ struct vnode *vp;
- vp = union_lock_other(un, ap->a_td);
- ap->a_vp = vp;
- error = VOP_LISTEXTATTR_AP(ap);
- union_unlock_other(vp, ap->a_td);
+ unp = VTOUNIONFS(ap->a_vp);
+ vp = NULLVP;
- return (error);
+ if (unp->un_flag & UNIONFS_OPENEXTU)
+ vp = unp->un_uppervp;
+ else if (unp->un_flag & UNIONFS_OPENEXTL)
+ vp = unp->un_lowervp;
+
+ if (vp == NULLVP)
+ return (EOPNOTSUPP);
+
+ return (VOP_GETEXTATTR(vp, ap->a_attrnamespace, ap->a_name,
+ ap->a_uio, ap->a_size, ap->a_cred, ap->a_td));
}
static int
-union_openextattr(ap)
- struct vop_openextattr_args /* {
- struct vnode *a_vp;
- struct ucred *a_cred;
- struct thread *a_td;
- } */ *ap;
+unionfs_setextattr(struct vop_setextattr_args *ap)
{
- int error;
- struct union_node *un = VTOUNION(ap->a_vp);
- struct vnode *vp;
+ int error;
+ struct unionfs_node *unp;
+ struct vnode *uvp;
+ struct vnode *lvp;
+ struct vnode *ovp;
+ struct ucred *cred;
+ struct thread *td;
- vp = union_lock_other(un, ap->a_td);
- ap->a_vp = vp;
- error = VOP_OPENEXTATTR_AP(ap);
- union_unlock_other(vp, ap->a_td);
+ error = EROFS;
+ unp = VTOUNIONFS(ap->a_vp);
+ uvp = unp->un_uppervp;
+ lvp = unp->un_lowervp;
+ ovp = NULLVP;
+ cred = ap->a_cred;
+ td = ap->a_td;
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_setextattr: enter (un_flag=%x)\n", unp->un_flag);
+
+ if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)
+ return (EROFS);
+
+ if (unp->un_flag & UNIONFS_OPENEXTU)
+ ovp = unp->un_uppervp;
+ else if (unp->un_flag & UNIONFS_OPENEXTL)
+ ovp = unp->un_lowervp;
+
+ if (ovp == NULLVP)
+ return (EOPNOTSUPP);
+
+ if (ovp == lvp && lvp->v_type == VREG) {
+ VOP_CLOSEEXTATTR(lvp, 0, cred, td);
+ if (uvp == NULLVP &&
+ (error = unionfs_copyfile(unp, 1, cred, td)) != 0) {
+unionfs_setextattr_reopen:
+ if ((unp->un_flag & UNIONFS_OPENEXTL) &&
+ VOP_OPENEXTATTR(lvp, cred, td)) {
+#ifdef DIAGNOSTIC
+ panic("unionfs: VOP_OPENEXTATTR failed");
+#endif
+ unp->un_flag &= ~UNIONFS_OPENEXTL;
+ }
+ goto unionfs_setextattr_abort;
+ }
+ uvp = unp->un_uppervp;
+ if ((error = VOP_OPENEXTATTR(uvp, cred, td)) != 0)
+ goto unionfs_setextattr_reopen;
+ unp->un_flag &= ~UNIONFS_OPENEXTL;
+ unp->un_flag |= UNIONFS_OPENEXTU;
+ ovp = uvp;
+ }
+
+ if (ovp == uvp)
+ error = VOP_SETEXTATTR(ovp, ap->a_attrnamespace, ap->a_name,
+ ap->a_uio, cred, td);
+
+unionfs_setextattr_abort:
+ UNIONFS_INTERNAL_DEBUG("unionfs_setextattr: leave (%d)\n", error);
return (error);
}
static int
-union_deleteextattr(ap)
- struct vop_deleteextattr_args /* {
- struct vnode *a_vp;
- int a_attrnamespace;
- const char *a_name;
- struct ucred *a_cred;
- struct thread *a_td;
- } */ *ap;
+unionfs_listextattr(struct vop_listextattr_args *ap)
{
- int error;
- struct union_node *un = VTOUNION(ap->a_vp);
- struct vnode *vp;
+ struct unionfs_node *unp;
+ struct vnode *vp;
- vp = union_lock_other(un, ap->a_td);
- ap->a_vp = vp;
- error = VOP_DELETEEXTATTR_AP(ap);
- union_unlock_other(vp, ap->a_td);
+ unp = VTOUNIONFS(ap->a_vp);
+ vp = NULLVP;
- return (error);
+ if (unp->un_flag & UNIONFS_OPENEXTU)
+ vp = unp->un_uppervp;
+ else if (unp->un_flag & UNIONFS_OPENEXTL)
+ vp = unp->un_lowervp;
+
+ if (vp == NULLVP)
+ return (EOPNOTSUPP);
+
+ return (VOP_LISTEXTATTR(vp, ap->a_attrnamespace, ap->a_uio,
+ ap->a_size, ap->a_cred, ap->a_td));
}
static int
-union_setextattr(ap)
- struct vop_setextattr_args /* {
- struct vnode *a_vp;
- int a_attrnamespace;
- const char *a_name;
- struct uio *a_uio;
- struct ucred *a_cred;
- struct thread *a_td;
- } */ *ap;
+unionfs_deleteextattr(struct vop_deleteextattr_args *ap)
{
- int error;
- struct union_node *un = VTOUNION(ap->a_vp);
- struct vnode *vp;
+ int error;
+ struct unionfs_node *unp;
+ struct vnode *uvp;
+ struct vnode *lvp;
+ struct vnode *ovp;
+ struct ucred *cred;
+ struct thread *td;
+
+ error = EROFS;
+ unp = VTOUNIONFS(ap->a_vp);
+ uvp = unp->un_uppervp;
+ lvp = unp->un_lowervp;
+ ovp = NULLVP;
+ cred = ap->a_cred;
+ td = ap->a_td;
- vp = union_lock_other(un, ap->a_td);
- ap->a_vp = vp;
- error = VOP_SETEXTATTR_AP(ap);
- union_unlock_other(vp, ap->a_td);
+ UNIONFS_INTERNAL_DEBUG("unionfs_deleteextattr: enter (un_flag=%x)\n", unp->un_flag);
+
+ if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)
+ return (EROFS);
+
+ if (unp->un_flag & UNIONFS_OPENEXTU)
+ ovp = unp->un_uppervp;
+ else if (unp->un_flag & UNIONFS_OPENEXTL)
+ ovp = unp->un_lowervp;
+
+ if (ovp == NULLVP)
+ return (EOPNOTSUPP);
+
+ if (ovp == lvp && lvp->v_type == VREG) {
+ VOP_CLOSEEXTATTR(lvp, 0, cred, td);
+ if (uvp == NULLVP &&
+ (error = unionfs_copyfile(unp, 1, cred, td)) != 0) {
+unionfs_deleteextattr_reopen:
+ if ((unp->un_flag & UNIONFS_OPENEXTL) &&
+ VOP_OPENEXTATTR(lvp, cred, td)) {
+#ifdef DIAGNOSTIC
+ panic("unionfs: VOP_OPENEXTATTR failed");
+#endif
+ unp->un_flag &= ~UNIONFS_OPENEXTL;
+ }
+ goto unionfs_deleteextattr_abort;
+ }
+ uvp = unp->un_uppervp;
+ if ((error = VOP_OPENEXTATTR(uvp, cred, td)) != 0)
+ goto unionfs_deleteextattr_reopen;
+ unp->un_flag &= ~UNIONFS_OPENEXTL;
+ unp->un_flag |= UNIONFS_OPENEXTU;
+ ovp = uvp;
+ }
+
+ if (ovp == uvp)
+ error = VOP_DELETEEXTATTR(ovp, ap->a_attrnamespace, ap->a_name,
+ ap->a_cred, ap->a_td);
+
+unionfs_deleteextattr_abort:
+ UNIONFS_INTERNAL_DEBUG("unionfs_deleteextattr: leave (%d)\n", error);
return (error);
}
static int
-union_setlabel(ap)
- struct vop_setlabel_args /* {
- struct vnode *a_vp;
- struct label *a_label;
- struct ucred *a_cred;
- struct thread *a_td;
- } */ *ap;
+unionfs_setlabel(struct vop_setlabel_args *ap)
{
- int error;
- struct union_node *un = VTOUNION(ap->a_vp);
- struct vnode *vp;
+ int error;
+ struct unionfs_node *unp;
+ struct vnode *uvp;
+ struct vnode *lvp;
+ struct thread *td;
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_setlabel: enter\n");
- vp = union_lock_other(un, ap->a_td);
- ap->a_vp = vp;
- error = VOP_SETLABEL_AP(ap);
- union_unlock_other(vp, ap->a_td);
+ error = EROFS;
+ unp = VTOUNIONFS(ap->a_vp);
+ uvp = unp->un_uppervp;
+ lvp = unp->un_lowervp;
+ td = ap->a_td;
+
+ if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)
+ return (EROFS);
+
+ if (uvp == NULLVP && lvp->v_type == VREG) {
+ if ((error = unionfs_copyfile(unp, 1, ap->a_cred, td)) != 0)
+ return (error);
+ uvp = unp->un_uppervp;
+ }
+
+ if (uvp != NULLVP)
+ error = VOP_SETLABEL(uvp, ap->a_label, ap->a_cred, td);
+
+ UNIONFS_INTERNAL_DEBUG("unionfs_setlabel: leave (%d)\n", error);
return (error);
}
-/*
- * Global vfs data structures
- */
-struct vop_vector union_vnodeops = {
+struct vop_vector unionfs_vnodeops = {
.vop_default = &default_vnodeops,
- .vop_access = union_access,
- .vop_aclcheck = union_aclcheck,
- .vop_advlock = union_advlock,
+ .vop_access = unionfs_access,
+ .vop_aclcheck = unionfs_aclcheck,
+ .vop_advlock = unionfs_advlock,
.vop_bmap = VOP_EOPNOTSUPP,
- .vop_close = union_close,
- .vop_closeextattr = union_closeextattr,
- .vop_create = union_create,
- .vop_deleteextattr = union_deleteextattr,
- .vop_fsync = union_fsync,
- .vop_getacl = union_getacl,
- .vop_getattr = union_getattr,
- .vop_getextattr = union_getextattr,
- .vop_getwritemount = union_getwritemount,
- .vop_inactive = union_inactive,
- .vop_ioctl = union_ioctl,
- .vop_lease = union_lease,
- .vop_link = union_link,
- .vop_listextattr = union_listextattr,
- .vop_lookup = union_lookup,
- .vop_mkdir = union_mkdir,
- .vop_mknod = union_mknod,
- .vop_open = union_open,
- .vop_openextattr = union_openextattr,
- .vop_pathconf = union_pathconf,
- .vop_poll = union_poll,
- .vop_print = union_print,
- .vop_read = union_read,
- .vop_readdir = union_readdir,
- .vop_readlink = union_readlink,
- .vop_reclaim = union_reclaim,
- .vop_remove = union_remove,
- .vop_rename = union_rename,
- .vop_rmdir = union_rmdir,
- .vop_setacl = union_setacl,
- .vop_setattr = union_setattr,
- .vop_setextattr = union_setextattr,
- .vop_setlabel = union_setlabel,
- .vop_strategy = union_strategy,
- .vop_symlink = union_symlink,
- .vop_whiteout = union_whiteout,
- .vop_write = union_write,
+ .vop_close = unionfs_close,
+ .vop_closeextattr = unionfs_closeextattr,
+ .vop_create = unionfs_create,
+ .vop_deleteextattr = unionfs_deleteextattr,
+ .vop_fsync = unionfs_fsync,
+ .vop_getacl = unionfs_getacl,
+ .vop_getattr = unionfs_getattr,
+ .vop_getextattr = unionfs_getextattr,
+ .vop_getwritemount = unionfs_getwritemount,
+ .vop_inactive = unionfs_inactive,
+ .vop_ioctl = unionfs_ioctl,
+ .vop_lease = unionfs_lease,
+ .vop_link = unionfs_link,
+ .vop_listextattr = unionfs_listextattr,
+ ._vop_lock = unionfs_lock,
+ .vop_lookup = unionfs_lookup,
+ .vop_mkdir = unionfs_mkdir,
+ .vop_mknod = unionfs_mknod,
+ .vop_open = unionfs_open,
+ .vop_openextattr = unionfs_openextattr,
+ .vop_pathconf = unionfs_pathconf,
+ .vop_poll = unionfs_poll,
+ .vop_print = unionfs_print,
+ .vop_read = unionfs_read,
+ .vop_readdir = unionfs_readdir,
+ .vop_readlink = unionfs_readlink,
+ .vop_reclaim = unionfs_reclaim,
+ .vop_remove = unionfs_remove,
+ .vop_rename = unionfs_rename,
+ .vop_rmdir = unionfs_rmdir,
+ .vop_setacl = unionfs_setacl,
+ .vop_setattr = unionfs_setattr,
+ .vop_setextattr = unionfs_setextattr,
+ .vop_setlabel = unionfs_setlabel,
+ .vop_strategy = unionfs_strategy,
+ .vop_symlink = unionfs_symlink,
+ .vop_unlock = unionfs_unlock,
+ .vop_whiteout = unionfs_whiteout,
+ .vop_write = unionfs_write,
};
OpenPOWER on IntegriCloud