summaryrefslogtreecommitdiffstats
path: root/sys/fs/unionfs/union_vfsops.c
diff options
context:
space:
mode:
authorrodrigc <rodrigc@FreeBSD.org>2006-12-02 19:35:56 +0000
committerrodrigc <rodrigc@FreeBSD.org>2006-12-02 19:35:56 +0000
commitc4618bacd39ad5ce8f2fb96309c79e7c9983bdb6 (patch)
tree9c239765475d2d432117d23b880498481836ab65 /sys/fs/unionfs/union_vfsops.c
parent113db579f072d3851609d3a7bc172bd558241f7a (diff)
downloadFreeBSD-src-c4618bacd39ad5ce8f2fb96309c79e7c9983bdb6.zip
FreeBSD-src-c4618bacd39ad5ce8f2fb96309c79e7c9983bdb6.tar.gz
Many, many thanks to Masanori OZAWA <ozawa@ongs.co.jp>
and Daichi GOTO <daichi@FreeBSD.org> for submitting this major rewrite of unionfs. This rewrite was done to try to solve many of the longstanding crashing and locking issues in the existing unionfs implementation. This implementation also adds a 'MASQUERADE mode', which allows the user to set different user, group, and file permission modes in the upper layer. Submitted by: daichi, Masanori OZAWA Reviewed by: rodrigc (modified for minor style issues)
Diffstat (limited to 'sys/fs/unionfs/union_vfsops.c')
-rw-r--r--sys/fs/unionfs/union_vfsops.c712
1 files changed, 387 insertions, 325 deletions
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);
OpenPOWER on IntegriCloud