Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 28 Dec 2005 10:12:31 GMT
From:      Masanori OZAWA <ozawa@ongs.co.jp>
To:        freebsd-gnats-submit@FreeBSD.org
Subject:   kern/91010: [unionfs] new source code and some changes
Message-ID:  <200512281012.jBSACVct027091@www.freebsd.org>
Resent-Message-ID: <200512281020.jBSAK3WC005634@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         91010
>Category:       kern
>Synopsis:       [unionfs] new source code and some changes
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Wed Dec 28 10:20:02 GMT 2005
>Closed-Date:
>Last-Modified:
>Originator:     Masanori OZAWA
>Release:        FreeBSD 6.0-RELEASE
>Organization:
ONGS Inc.
>Environment:
FreeBSD mithos.ongs.co.jp 6.0-RELEASE FreeBSD 6.0-RELEASE #0: Thu Nov 10 14:06:38 JST 2005     root@mithos.ongs.co.jp:/usr/obj/usr/src/sys/MITHOS  i386
>Description:
filesystem:
 - new unionfs source code (for 7-current)

mount_unionfs:
 - added "-c" option
       Set copy strategy for copying from raw layer to upper layer around 
     files and directories. The strategies are "old", "fullcopy" and 
     "useful" setting by root only.

      old:       the same strategy as established unionfs. (default)
      fullcopy:  upper layer gets full copy of raw layer status.
      useful:    user can get access controlling by -m, -M, -u and -g.
                 only owner's access permission get chagne when target 
                 owner is the same as original owner.

 - added "-m" option
       Set access permission for file with "useful" strategy only.
     mount target directory gives default permission. permission
     given by "-M" gives default permission when "-m" is not given.

  - added "-M" option
       Set access permission for directory with "useful" strategy only.
     mount target directory gives default permission. permission
     given by "-m" gives default permission when "-M" is not given.

  - added "-u" option
       Set owner with "useful" strategy only. mount target directory 
     gives default owner.

  - added "-g" option
       Set group with "useful" strategy only. mount target directory 
     gives default group.

 - deleted "-r" option
     "-r" option is not needed because mount_nullfs gets instead.

>How-To-Repeat:
              
>Fix:
Established unionfs implementaion is weak around thier functions.
Some systems that use unionfs like FreeSBIE need more rich function for unionfs.
The following patch gives the unionfs rich feature to get it on.
I get suggest for unionfs's maintainer to merge my patch as follow.

So sorry my English is not enough to get communication with you.
My boss, daichi@freebsd.org gives us help of communication.
Please gives daichi@freebsd.org as CC: for my mail.

Thanks. :)

# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	unionfs
#	unionfs/mount_unionfs
#	unionfs/mount_unionfs/mount_unionfs.c
#	unionfs/fs
#	unionfs/fs/union_vnops.c
#	unionfs/fs/union_vfsops.c
#	unionfs/fs/union.h
#	unionfs/fs/union_subr.c
#
echo c - unionfs
mkdir -p unionfs > /dev/null 2>&1
echo c - unionfs/mount_unionfs
mkdir -p unionfs/mount_unionfs > /dev/null 2>&1
echo x - unionfs/mount_unionfs/mount_unionfs.c
sed 's/^X//' >unionfs/mount_unionfs/mount_unionfs.c << 'END-of-unionfs/mount_unionfs/mount_unionfs.c'
X/*
X * Copyright (c) 1992, 1993, 1994
X *	The Regents of the University of California.  All rights reserved.
X *
X * This code is derived from software donated to Berkeley by
X * Jan-Simon Pendry.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 4. Neither the name of the University nor the names of its contributors
X *    may be used to endorse or promote products derived from this software
X *    without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
X * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X * SUCH DAMAGE.
X */
X
X#ifndef lint
Xstatic const char copyright[] =
X"@(#) Copyright (c) 1992, 1993, 1994\n\
X	The Regents of the University of California.  All rights reserved.";
X#endif /* not lint */
X
X#ifndef lint
X#if 0
Xstatic char sccsid[] = "@(#)mount_union.c	8.5 (Berkeley) 3/27/94";
X#else
Xstatic const char rcsid[] =
X  "$FreeBSD: src/sbin/mount_unionfs/mount_unionfs.c,v 1.24 2005/06/10 09:51:43 delphij Exp $\n";
X#endif
X#endif /* not lint */
X
X#include <sys/param.h>
X#include <sys/mount.h>
X#include <sys/uio.h>
X#include <sys/errno.h>
X
X#include <err.h>
X#include <stdio.h>
X#include <stdlib.h>
X#include <string.h>
X#include <sysexits.h>
X#include <unistd.h>
X#include <grp.h>
X#include <pwd.h>
X
X#include <fs/unionfs/union.h>
X
X#include "mntopts.h"
X
Xstatic struct mntopt mopts[] = {
X	MOPT_STDOPTS,
X	MOPT_END
X};
X
Xstatic int	subdir(const char *, const char *);
Xstatic void	usage (void) __dead2;
Xstatic void checkRoot();
Xstatic gid_t parseGID(const char *s);
Xstatic uid_t parseUID(const char *s);
Xstatic mode_t parseMask(const char *s);
X
Xint main(int argc, char *argv[])
X{
X	struct iovec iov[18];
X	int ch, mntflags;
X	char source[MAXPATHLEN];
X	char target[MAXPATHLEN];
X	int iovcnt;
X	union_copymode copymode;
X	uid_t uid;
X	gid_t gid;
X	mode_t ufile;
X	mode_t udir;
X
X	iovcnt = 6;
X	mntflags = 0;
X	while ((ch = getopt(argc, argv, "bc:o:m:M:u:g:")) != -1) {
X		switch (ch) {
X		case 'b':
X			iov[iovcnt].iov_base = "below";
X			iov[iovcnt].iov_len = strlen(iov[iovcnt].iov_base) + 1;
X			iov[iovcnt+1].iov_base = NULL;
X			iov[iovcnt+1].iov_len = 0;
X			iovcnt += 2;
X			break;
X		case 'c':
X			if (!optarg) {
X				usage();
X			}
X			else if (!strcasecmp("old", optarg)) {
X				copymode = UNION_OLD;
X			}
X			else if (!strcasecmp("useful", optarg)) {
X				copymode = UNION_USEFUL;
X			}
X			else if (!strcasecmp("fullcopy", optarg)) {
X				copymode = UNION_FULL_COPY;
X			}
X			else {
X				usage();
X			}
X			checkRoot();
X			iov[iovcnt].iov_base = "copymode";
X			iov[iovcnt].iov_len = strlen(iov[iovcnt].iov_base) + 1;
X			iov[iovcnt+1].iov_base = &copymode;
X			iov[iovcnt+1].iov_len = sizeof(copymode);
X			iovcnt += 2;
X			break;
X		case 'o':
X			getmntopts(optarg, mopts, &mntflags, 0);
X			break;
X		case 'm':
X			if (!optarg) usage();
X			ufile = parseMask(optarg);
X			iov[iovcnt].iov_base = "ufile";
X			iov[iovcnt].iov_len = strlen(iov[iovcnt].iov_base) + 1;
X			iov[iovcnt+1].iov_base = &ufile;
X			iov[iovcnt+1].iov_len = sizeof(ufile);
X			iovcnt += 2;
X			break;
X		case 'M':
X			if (!optarg) usage();
X			udir = parseMask(optarg);
X			iov[iovcnt].iov_base = "udir";
X			iov[iovcnt].iov_len = strlen(iov[iovcnt].iov_base) + 1;
X			iov[iovcnt+1].iov_base = &udir;
X			iov[iovcnt+1].iov_len = sizeof(udir);
X			iovcnt += 2;
X			break;
X		case 'u':
X			checkRoot();
X			if (!optarg) usage();
X			uid = parseUID(optarg);
X			iov[iovcnt].iov_base = "uid";
X			iov[iovcnt].iov_len = strlen(iov[iovcnt].iov_base) + 1;
X			iov[iovcnt+1].iov_base = &uid;
X			iov[iovcnt+1].iov_len = sizeof(uid);
X			iovcnt += 2;
X			break;
X		case 'g':
X			checkRoot();
X			if (!optarg) usage();
X			gid = parseGID(optarg);
X			iov[iovcnt].iov_base = "gid";
X			iov[iovcnt].iov_len = strlen(iov[iovcnt].iov_base) + 1;
X			iov[iovcnt+1].iov_base = &gid;
X			iov[iovcnt+1].iov_len = sizeof(gid);
X			iovcnt += 2;
X			break;
X		case '?':
X		default:
X			usage();
X			/* NOTREACHED */
X		}
X	}
X	argc -= optind;
X	argv += optind;
X
X	if (argc != 2)
X		usage();
X
X	/* resolve both target and source with realpath(3) */
X	(void)checkpath(argv[0], target);
X	(void)checkpath(argv[1], source);
X
X	if (subdir(target, source) || subdir(source, target))
X		errx(EX_USAGE, "%s (%s) and %s (%s) are not distinct paths",
X		    argv[0], target, argv[1], source);
X
X	iov[0].iov_base = "fstype";
X	iov[0].iov_len = strlen(iov[0].iov_base) + 1;
X	iov[1].iov_base = "unionfs";
X	iov[1].iov_len = strlen(iov[1].iov_base) + 1;
X	iov[2].iov_base = "fspath";
X	iov[2].iov_len = strlen(iov[2].iov_base) + 1;
X	iov[3].iov_base = source;
X	iov[3].iov_len = strlen(source) + 1;
X	iov[4].iov_base = "target";
X	iov[4].iov_len = strlen(iov[4].iov_base) + 1;
X	iov[5].iov_base = target;
X	iov[5].iov_len = strlen(target) + 1;
X
X	if (nmount(iov, iovcnt, mntflags))
X		err(EX_OSERR, "%s", target);
X
X	exit(0);
X}
X
Xstatic int subdir(const char *p, const char *dir)
X{
X	int l;
X
X	l = strlen(dir);
X	if (l <= 1)
X		return (1);
X
X	if ((strncmp(p, dir, l) == 0) && (p[l] == '/' || p[l] == '\0'))
X		return (1);
X
X	return (0);
X}
X
Xstatic void usage()
X{
X	(void)fprintf(stderr,
X		"usage: mount_unionfs [-b] [-c copymode] [-m mask] [-M mask]\n"
X		"                     [-u uid] [-g gid] [-o options]\n"
X		"                     directory uniondir\n");
X	exit(EX_USAGE);
X}
X
Xstatic void checkRoot()
X{
X	if (getuid()) {
X		errno = EACCES;
X		perror("you need the root account.");
X	}
X}
X
Xstatic gid_t parseGID(const char *s)
X{
X	struct group *gr;
X	const char *gname;
X	gid_t gid;
X
X	if ((gr = getgrnam(s)) != NULL) {
X		gid = gr->gr_gid;
X	}
X	else {
X		for (gname = s; *s && isdigit((int)*s); s++);
X		if (!*s) {
X			gid = atoi(gname);
X		}
X		else {
X			errx(EX_NOUSER, "unknown group id: %s", gname);
X			usage();
X		}
X	}
X
X	return gid;
X}
X
Xstatic uid_t parseUID(const char *s)
X{
X	struct passwd *pw;
X	const char *uname;
X	uid_t uid;
X
X	if ((pw = getpwnam(s)) != NULL) {
X		uid = pw->pw_uid;
X	}
X	else {
X		for (uname = s; *s && isdigit((int)*s); s++);
X		if (!*s) {
X			uid = atoi(uname);
X		}
X		else {
X			errx(EX_NOUSER, "unknown user id: %s", uname);
X			usage();
X		}
X	}
X
X	return uid;
X}
X
Xstatic mode_t parseMask(const char *s)
X{
X	int done, rv;
X	char *ep;
X
X	done = 0;
X	rv = -1;
X	if (*s >= '0' && *s <= '7') {
X		done = 1;
X		rv = strtol(optarg, &ep, 8);
X	}
X	if (!done || rv < 0 || *ep) {
X		errx(EX_USAGE, "invalid file mode: %s", s);
X		usage();
X	}
X
X	return rv;
X}
END-of-unionfs/mount_unionfs/mount_unionfs.c
echo c - unionfs/fs
mkdir -p unionfs/fs > /dev/null 2>&1
echo x - unionfs/fs/union_vnops.c
sed 's/^X//' >unionfs/fs/union_vnops.c << 'END-of-unionfs/fs/union_vnops.c'
X/*
X * union_vnops.c
X *
X * Copyright (c) 2005 ONGS Inc. All rights reserved.
X * Copyright (c) 2005 Masanori OZAWA. All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X *
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. Neither the name of the ONGS Inc. nor the names of its contributors
X *    may be used to endorse or promote products derived from this software
X *    without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
X * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
X * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
X * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
X * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
X * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
X * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
X * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
X * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
X * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
X * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
X */
X
X#include <sys/param.h>
X#include <sys/systm.h>
X#include <sys/conf.h>
X#include <sys/kernel.h>
X#include <sys/lock.h>
X#include <sys/malloc.h>
X#include <sys/mount.h>
X#include <sys/mutex.h>
X#include <sys/namei.h>
X#include <sys/sysctl.h>
X#include <sys/vnode.h>
X#include <sys/kdb.h>
X#include <sys/fcntl.h>
X#include <sys/stat.h>
X#include <sys/dirent.h>
X
X#include <fs/unionfs/union.h>
X
X#include <vm/vm.h>
X#include <vm/vm_extern.h>
X#include <vm/vm_object.h>
X#include <vm/vnode_pager.h>
X
X#if 0
X#define UNION_INTERNAL_DEBUG(msg)    printf(msg)
X#else
X#define UNION_INTERNAL_DEBUG(msg)
X#endif
X
Xstatic int
Xunion_lookup(struct vop_lookup_args *ap)
X{
X	struct componentname *cnp = ap->a_cnp;
X	struct vnode *dvp = ap->a_dvp;
X	struct thread *td = curthread;
X	u_long nameiop = cnp->cn_nameiop;
X
X	int iswhiteout = 0;
X	int error, uerror, lerror;
X	struct union_node *dun = VTOUNION(dvp);
X	struct vnode *udvp = dun->union_uppervp;
X	struct vnode *ldvp = dun->union_lowervp;
X	struct vnode *vp, *uvp, *lvp, *dtmpvp;
X	struct vattr va;
X
X	UNION_INTERNAL_DEBUG("union_lookup: enter\n");
X	/*printf("union_lookup: enter: td=%p, path=%s\n", td, cnp->cn_nameptr);*/
X
X	error = uerror = lerror = ENOENT;
X	vp = uvp = lvp = NULLVP;
X	*(ap->a_vpp) = NULLVP;
X
X	if (dvp->v_type != VDIR) {
X		return ENOTDIR;
X	}
X
X	if ((cnp->cn_flags & ISLASTCN) &&
X		(dvp->v_mount->mnt_flag & MNT_RDONLY) &&
X	    nameiop != LOOKUP) {
X		return EROFS;
X	}
X
X	/*
X	 * lookup dotdot
X	 */
X	if (cnp->cn_flags & ISDOTDOT) {
X		if (nameiop != LOOKUP && NULLVP == udvp) {
X			return EROFS;
X		}
X
X		if (NULLVP != udvp) {
X			dtmpvp = udvp;
X		}
X		else {
X			dtmpvp = ldvp;
X		}
X
X		error = VOP_LOOKUP(dtmpvp, &vp, cnp);
X
X		if (!error) {
X			if (nameiop == DELETE || nameiop == RENAME ||
X				(cnp->cn_lkflags & LK_TYPE_MASK)) {
X				VOP_UNLOCK(vp, 0, td);
X			}
X			vrele(vp);
X			VOP_UNLOCK(dvp, 0, td);
X			*(ap->a_vpp) = dun->union_dvp;
X			vref(dun->union_dvp);
X			if (nameiop == DELETE || nameiop == RENAME) {
X				vn_lock(dun->union_dvp, LK_EXCLUSIVE | LK_RETRY, td);
X			}
X			else if (cnp->cn_lkflags & LK_TYPE_MASK) {
X				vn_lock(dun->union_dvp, cnp->cn_lkflags | LK_RETRY, td);
X			}
X			vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td);
X		}
X
X		UNION_INTERNAL_DEBUG("union_lookup: leave\n");
X
X		return error;
X	}
X
X	/*
X	 * lookup upper layer
X	 */
X	if (NULLVP != udvp) {
X		uerror = VOP_LOOKUP(udvp, &uvp, cnp);
X
X		if (!uerror) {
X			if (udvp == uvp) {
X				vrele(uvp);
X				*(ap->a_vpp) = dvp;
X				vref(dvp);
X
X				UNION_INTERNAL_DEBUG("union_lookup: leave\n");
X
X				return uerror;
X			}
X			if (nameiop == DELETE || nameiop == RENAME ||
X				(cnp->cn_lkflags & LK_TYPE_MASK)) {
X				VOP_UNLOCK(uvp, 0, td);
X			}
X		}
X
X		if (uerror == ENOENT || uerror == EJUSTRETURN) {
X			if (cnp->cn_flags & ISWHITEOUT) {
X				iswhiteout = 1;
X			}
X			else if (NULLVP != ldvp) {
X				if (!VOP_GETATTR(udvp, &va, cnp->cn_cred, td) &&
X					(va.va_flags & OPAQUE)) {
X					iswhiteout = 1;
X				}
X			}
X		}
X	}
X
X	/*
X	 * lookup lower layer
X	 */
X	if (NULLVP != ldvp && !iswhiteout) {
X		cnp->cn_nameiop = LOOKUP;
X		if (NULLVP != udvp) vn_lock(ldvp, LK_EXCLUSIVE | LK_RETRY, td);
X		lerror = VOP_LOOKUP(ldvp, &lvp, cnp);
X		if (NULLVP != udvp) VOP_UNLOCK(ldvp, 0, td);
X		cnp->cn_nameiop = nameiop;
X
X		if (!lerror) {
X			if (ldvp == lvp) {
X				if (NULLVP != uvp) vrele(uvp); /* no need? */
X				vrele(lvp);
X				*(ap->a_vpp) = dvp;
X				vref(dvp);
X
X				UNION_INTERNAL_DEBUG("union_lookup: leave\n");
X
X				return lerror;
X			}
X			if (nameiop == DELETE || nameiop == RENAME ||
X				(cnp->cn_lkflags & LK_TYPE_MASK)) {
X				VOP_UNLOCK(lvp, 0, td);
X			}
X		}
X	}
X
X	/*
X	 * check lookup result
X	 */
X	if (NULLVP == uvp && NULLVP == lvp) {
X		UNION_INTERNAL_DEBUG("union_lookup: leave\n");
X		return (NULLVP != udvp ? uerror : lerror);
X	}
X
X	/*
X	 * check vnode type
X	 */
X	if (NULLVP != uvp && NULLVP != lvp && uvp->v_type != lvp->v_type) {
X		vrele(lvp);
X		lvp = NULLVP;
X	}
X
X	/*
X	 * check shadow dir
X	 */
X	if (uerror && uerror != EJUSTRETURN && NULLVP != udvp &&
X		!lerror && NULLVP != lvp && lvp->v_type == VDIR &&
X		(1 < cnp->cn_namelen || '.' != *(cnp->cn_nameptr))) {
X		error = union_nodeget(dvp->v_mount, NULLVP, lvp, dvp, &vp, cnp, td);
X		if (error) {
X			goto union_lookup_out;
X		}
X		switch (VOP_ISLOCKED(vp, td)) {
X		case 0:
X		case LK_EXCLOTHER:
X		case LK_SHARED:
X			vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); /* XXX */
X		default:
X			break;
X		}
X
X		error = union_mkshadowdir(MOUNTTOUNIONMOUNT(udvp->v_mount),
X								  udvp, VTOUNION(vp), cnp, td);
X		if (error) {
X			UNIONFSDEBUG("union_lookup: Unable to create shadow dir.");
X			goto union_lookup_out;
X		}
X		uerror = lerror;
X	}
X	/*
X	 * get union vnode.
X	 */
X	else {
X		error = union_nodeget(dvp->v_mount, uvp, lvp, dvp, &vp, cnp, td);
X		if (error) {
X			UNIONFSDEBUG("union_lookup: Unable to create union vnode.");
X			goto union_lookup_out;
X		}
X
X		if ((nameiop == DELETE || nameiop == RENAME) &&
X			!(cnp->cn_lkflags & LK_TYPE_MASK)) {
X			vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
X		}
X	}
X
X	*(ap->a_vpp) = vp;
X
X	if (NULLVP != uvp) error = uerror;
X	else error = lerror;
X
Xunion_lookup_out:
X	if (NULLVP != uvp) vrele(uvp);
X	if (NULLVP != lvp) vrele(lvp);
X
X#ifdef DIAGNOSTIC
X	if (!error || error == EJUSTRETURN) {
X		if (cnp->cn_namelen == 1 &&
X			cnp->cn_nameptr[0] == '.' &&
X			*(ap->a_vpp) != dvp) {
X			panic("union_lookup: returning '.' %p != %p",
X				  *(ap->a_vpp), dvp);
X		}
X	}
X#endif
X
X	UNION_INTERNAL_DEBUG("union_lookup: leave\n");
X
X	return error;
X}
X
Xstatic int
Xunion_create(struct vop_create_args *ap)
X{
X	struct union_node *dun = VTOUNION(ap->a_dvp);
X	struct componentname *cnp = ap->a_cnp;
X	struct thread *td = curthread;
X	struct vnode *udvp = dun->union_uppervp;
X	struct vnode *vp;
X	int error = EROFS;
X
X	UNION_INTERNAL_DEBUG("union_create: enter\n");
X
X	if (NULLVP != udvp) {
X		if (!(error = VOP_CREATE(udvp, &vp, cnp, ap->a_vap))) {
X			VOP_UNLOCK(vp, 0, td);
X			error = union_nodeget(ap->a_dvp->v_mount, vp, NULLVP, ap->a_dvp,
X								  ap->a_vpp, cnp, td);
X			vrele(vp);
X		}
X	}
X
X	UNION_INTERNAL_DEBUG("union_create: leave\n");
X
X	return error;
X}
X
Xstatic int
Xunion_whiteout(struct vop_whiteout_args *ap)
X{
X	struct union_node *dun = VTOUNION(ap->a_dvp);
X	struct componentname *cnp = ap->a_cnp;
X	struct vnode *udvp = dun->union_uppervp;
X	int error = EOPNOTSUPP;
X
X	UNION_INTERNAL_DEBUG("union_whiteout: enter\n");
X
X	if (NULLVP != udvp) {
X		switch (ap->a_flags) {
X		case CREATE:
X		case DELETE:
X		case LOOKUP:
X			error = VOP_WHITEOUT(udvp, cnp, ap->a_flags);
X			break;
X		default:
X			error = EINVAL;
X			break;
X		}
X	}
X
X	UNION_INTERNAL_DEBUG("union_whiteout: leave\n");
X
X	return error;
X}
X
Xstatic int
Xunion_mknod(struct vop_mknod_args *ap)
X{
X	struct union_node *dun = VTOUNION(ap->a_dvp);
X	struct componentname *cnp = ap->a_cnp;
X	struct thread *td = curthread;
X	struct vnode *udvp = dun->union_uppervp;
X	struct vnode *vp;
X	int error = EROFS;
X
X	UNION_INTERNAL_DEBUG("union_mknod: enter\n");
X
X	if (NULLVP != udvp) {
X		if (!(error = VOP_MKNOD(udvp, &vp, cnp, ap->a_vap))) {
X			VOP_UNLOCK(vp, 0, td);
X			error = union_nodeget(ap->a_dvp->v_mount, vp, NULLVP, ap->a_dvp,
X								  ap->a_vpp, cnp, td);
X			vrele(vp);
X		}
X	}
X
X	UNION_INTERNAL_DEBUG("union_mknod: leave\n");
X
X	return error;
X}
X
Xstatic int
Xunion_open(struct vop_open_args *ap)
X{
X	struct union_node *un = VTOUNION(ap->a_vp);
X	struct vnode *uvp = un->union_uppervp;
X	struct vnode *lvp = un->union_lowervp;
X	struct vnode *targetvp = NULLVP;
X	struct ucred *cred = ap->a_cred;
X	struct thread *td = ap->a_td;
X	int error = 0;
X
X	UNION_INTERNAL_DEBUG("union_open: enter\n");
X
X	if (un->lower_opencnt || un->upper_opencnt) {
X		/* vnode is already opend. */
X		if (un->upper_opencnt) {
X			targetvp = uvp;
X		}
X		else {
X			targetvp = lvp;
X		}
X
X		if (targetvp == lvp &&
X			(ap->a_mode & FWRITE) && (lvp->v_type == VREG)) {
X			targetvp = NULLVP;
X		}
X	}
X
X	if (NULLVP == targetvp) {
X		if (NULLVP == uvp) {
X			if ((ap->a_mode & FWRITE) && (lvp->v_type == VREG)) {
X				error = union_copyfile(un, !(ap->a_mode & O_TRUNC), cred, td);
X				if (error) {
X					return error;
X				}
X				targetvp = uvp = un->union_uppervp;
X			}
X		}
X		else {
X			targetvp = uvp;
X		}
X	}
X	if (NULLVP == targetvp) {
X		targetvp = lvp;
X	}
X
X	error = VOP_OPEN(targetvp, ap->a_mode, cred, td, -1);
X	if (!error) {
X		if (targetvp == uvp) un->upper_opencnt++;
X		else un->lower_opencnt++;
X		un->readdir_flag = 0;
X		ap->a_vp->v_object = targetvp->v_object;
X	}
X
X	UNION_INTERNAL_DEBUG("union_open: leave\n");
X
X	return error;
X}
X
Xstatic int
Xunion_close(struct vop_close_args *ap)
X{
X	struct union_node *un = VTOUNION(ap->a_vp);
X	struct ucred *cred = ap->a_cred;
X	struct thread *td = ap->a_td;
X	struct vnode *ovp;
X	int error;
X
X	UNION_INTERNAL_DEBUG("union_close: enter\n");
X
X	if (!(un->lower_opencnt) && !(un->upper_opencnt)) {
X#ifdef DIAGNOSTIC
X		panic("unionfs: open count is 0");
X#endif
X		return EINVAL;
X	}
X
X	if (un->upper_opencnt) {
X		ovp = un->union_uppervp;
X	}
X	else {
X		ovp = un->union_lowervp;
X	}
X
X	error = VOP_CLOSE(ovp, ap->a_fflag, cred, td);
X
X	if (!error) {
X		ap->a_vp->v_object = ovp->v_object;
X		un->readdir_flag = 0;
X
X		if (ovp == un->union_uppervp) {
X			un->upper_opencnt--;
X		}
X		else {
X			un->lower_opencnt--;
X		}
X	}
X
X	UNION_INTERNAL_DEBUG("union_close: leave\n");
X
X	return error;
X}
X
Xstatic int
Xunion_check_corrected_access(u_short mode,
X							 struct vattr *va,
X							 struct ucred *cred)
X{
X	int count = 0;
X	uid_t uid = va->va_uid;        /* upper side vnode's uid */
X	gid_t gid = va->va_gid;        /* upper side vnode's gid */
X	u_short vmode = va->va_mode;   /* upper side vnode's mode */
X
X	gid_t *gp;
X	u_short mask = 0;
X
X	if (cred->cr_uid == uid) {
X		if (mode & VEXEC) mask |= S_IXUSR;
X		if (mode & VREAD) mask |= S_IRUSR;
X		if (mode & VWRITE) mask |= S_IWUSR;
X		return ((vmode & mask) == mask ? 0 : EACCES);
X	}
X
X	for (gp = cred->cr_groups; count < cred->cr_ngroups; count++, gp++) {
X		if (gid == *gp) {
X			if (mode & VEXEC) mask |= S_IXGRP;
X			if (mode & VREAD) mask |= S_IRGRP;
X			if (mode & VWRITE) mask |= S_IWGRP;
X			return ((vmode & mask) == mask ? 0 : EACCES);
X		}
X	}
X
X	if (mode & VEXEC) mask |= S_IXOTH;
X	if (mode & VREAD) mask |= S_IROTH;
X	if (mode & VWRITE) mask |= S_IWOTH;
X
X	return ((vmode & mask) == mask ? 0 : EACCES);
X}
X
Xstatic int
Xunion_access(struct vop_access_args *ap)
X{
X	struct union_node *un = VTOUNION(ap->a_vp);
X	struct union_mount *um = MOUNTTOUNIONMOUNT(ap->a_vp->v_mount);
X	struct vnode *uvp = un->union_uppervp;
X	struct vnode *lvp = un->union_lowervp;
X	struct thread *td = ap->a_td;
X	struct vattr va;
X	int mode = ap->a_mode;
X	int error = EACCES;
X
X	UNION_INTERNAL_DEBUG("union_access: enter\n");
X
X	if ((mode & VWRITE) &&
X		(ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)) {
X		switch (ap->a_vp->v_type) {
X		case VREG:
X		case VDIR:
X		case VLNK:
X			return EROFS;
X		default:
X			break;
X		}
X	}
X
X	if (NULLVP != uvp) {
X		error = VOP_ACCESS(uvp, mode, ap->a_cred, td);
X
X		UNION_INTERNAL_DEBUG("union_access: leave\n");
X
X		return error;
X	}
X
X	if (NULLVP != lvp) {
X		if (mode & VWRITE) {
X			if (um->union_uppervp->v_mount->mnt_flag & MNT_RDONLY) {
X				switch (ap->a_vp->v_type) {
X				case VREG:
X				case VDIR:
X				case VLNK:
X					return EROFS;
X				default:
X					break;
X				}
X			}
X			else if (ap->a_vp->v_type == VREG || ap->a_vp->v_type == VDIR) {
X				/* check shadow file/dir */
X				if (UNION_FULL_COPY != um->copymode) {
X					error = union_create_uppervattr(um, lvp, &va,
X													ap->a_cred, td);
X					if (error) {
X						return error;
X					}
X					error = union_check_corrected_access(mode,
X														 &va, ap->a_cred);
X					if (error) {
X						return error;
X					}
X					mode &= ~VWRITE;
X				}
X			}
X		}
X
X		error = VOP_ACCESS(lvp, mode, ap->a_cred, td);
X	}
X
X	UNION_INTERNAL_DEBUG("union_access: leave\n");
X
X	return error;
X}
X
Xstatic int
Xunion_getattr(struct vop_getattr_args *ap)
X{
X	int error;
X	struct union_node *un = VTOUNION(ap->a_vp);
X	struct union_mount *um = MOUNTTOUNIONMOUNT(ap->a_vp->v_mount);
X	struct vnode *uvp = un->union_uppervp;
X	struct vnode *lvp = un->union_lowervp;
X	struct thread *td = ap->a_td;
X	struct vattr va;
X
X	UNION_INTERNAL_DEBUG("union_getattr: enter\n");
X
X	if (NULLVP != uvp) {
X		error = VOP_GETATTR(uvp, ap->a_vap, ap->a_cred, td);
X		if (!error) {
X			ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
X		}
X
X		UNION_INTERNAL_DEBUG("union_getattr: leave\n");
X
X		return error;
X	}
X
X	error = VOP_GETATTR(lvp, ap->a_vap, ap->a_cred, td);
X
X	if (!error && !(um->union_uppervp->v_mount->mnt_flag & MNT_RDONLY)) {
X		if ((ap->a_vp->v_type == VREG || ap->a_vp->v_type == VDIR)) {
X			union_create_uppervattr_core(um, ap->a_vap, &va, td);
X			ap->a_vap->va_mode = va.va_mode;
X			ap->a_vap->va_uid = va.va_uid;
X			ap->a_vap->va_gid = va.va_gid;
X		}
X	}
X
X	if (!error) {
X		ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
X	}
X
X	UNION_INTERNAL_DEBUG("union_getattr: leave\n");
X
X	return error;
X}
X
Xstatic int
Xunion_setattr(struct vop_setattr_args *ap)
X{
X	int error = EROFS;
X	struct union_node *un = VTOUNION(ap->a_vp);
X	struct vnode *uvp = un->union_uppervp;
X	struct vnode *lvp = un->union_lowervp;
X	struct thread *td = ap->a_td;
X	struct vattr *vap = ap->a_vap;
X
X	UNION_INTERNAL_DEBUG("union_setattr: enter\n");
X
X	if ((ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) &&
X		(vap->va_flags != VNOVAL || vap->va_uid != (uid_t)VNOVAL ||
X		 vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL ||
X		 vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL)) {
X		return EROFS;
X	}
X
X	if (NULLVP == uvp && lvp->v_type == VREG) {
X		error = union_copyfile(un, (vap->va_size != 0), ap->a_cred, td);
X		if (error) {
X			return error;
X		}
X		uvp = un->union_uppervp;
X	}
X
X	if (NULLVP != uvp) {
X		error = VOP_SETATTR(uvp, vap, ap->a_cred, td);
X	}
X
X	UNION_INTERNAL_DEBUG("union_setattr: leave\n");
X
X	return error;
X}
X
Xstatic int
Xunion_read(struct vop_read_args *ap)
X{
X	int error;
X	struct union_node *un = VTOUNION(ap->a_vp);
X	struct vnode *ovp = (un->upper_opencnt ?
X						 un->union_uppervp : un->union_lowervp);
X
X	/* UNION_INTERNAL_DEBUG("union_read: enter\n"); */
X
X	if (NULLVP == ovp) {
X#ifdef DIAGNOSTIC
X		panic("unionfs: no open vnode");
X#endif
X		return EBADF;
X	}
X
X	error = VOP_READ(ovp, ap->a_uio, ap->a_ioflag, ap->a_cred);
X
X	/* UNION_INTERNAL_DEBUG("union_read: leave\n"); */
X
X	return error;
X}
X
Xstatic int
Xunion_write(struct vop_write_args *ap)
X{
X	int error;
X	struct union_node *un = VTOUNION(ap->a_vp);
X	struct vnode *ovp = (un->upper_opencnt ?
X						 un->union_uppervp : un->union_lowervp);
X
X	/* UNION_INTERNAL_DEBUG("union_write: enter\n"); */
X
X	if (NULLVP == ovp) {
X#ifdef DIAGNOSTIC
X		panic("unionfs: no open vnode");
X#endif
X		return EBADF;
X	}
X	if (ovp != un->union_uppervp) {
X		return EROFS;
X	}
X
X	error = VOP_WRITE(ovp, ap->a_uio, ap->a_ioflag, ap->a_cred);
X
X	/* UNION_INTERNAL_DEBUG("union_write: leave\n"); */
X
X	return error;
X}
X
Xstatic int
Xunion_lease(struct vop_lease_args *ap)
X{
X	struct union_node *un = VTOUNION(ap->a_vp);
X	struct vnode *vp = (NULLVP != un->union_uppervp ? un->union_uppervp :
X						un->union_lowervp);
X
X	return VOP_LEASE(vp, ap->a_td, ap->a_cred, ap->a_flag);
X}
X
Xstatic int
Xunion_ioctl(struct vop_ioctl_args *ap)
X{
X	struct union_node *un = VTOUNION(ap->a_vp);
X	struct vnode *ovp = (un->upper_opencnt ?
X						 un->union_uppervp : un->union_lowervp);
X
X	if (NULLVP == ovp) {
X		return EBADF;
X	}
X
X	return VOP_IOCTL(ovp, ap->a_command, ap->a_data, ap->a_fflag,
X					 ap->a_cred, ap->a_td);
X}
X
Xstatic int
Xunion_poll(struct vop_poll_args *ap)
X{
X	struct union_node *un = VTOUNION(ap->a_vp);
X	struct vnode *ovp = (un->upper_opencnt ?
X						 un->union_uppervp : un->union_lowervp);
X
X	if (NULLVP == ovp) {
X		return EBADF;
X	}
X
X	return VOP_POLL(ovp, ap->a_events, ap->a_cred, ap->a_td);
X}
X
Xstatic int
Xunion_fsync(struct vop_fsync_args *ap)
X{
X	struct union_node *un = VTOUNION(ap->a_vp);
X	struct vnode *ovp = (un->upper_opencnt ?
X						 un->union_uppervp : un->union_lowervp);
X
X	if (NULLVP == ovp) {
X		return EBADF;
X	}
X
X	return VOP_FSYNC(ovp, ap->a_waitfor, ap->a_td);
X}
X
Xstatic int
Xunion_remove(struct vop_remove_args *ap)
X{
X	int error = 0;
X	struct union_node *dun = VTOUNION(ap->a_dvp);
X	struct union_node *un = VTOUNION(ap->a_vp);
X	struct union_mount *um = MOUNTTOUNIONMOUNT(ap->a_vp->v_mount);
X	struct vnode *udvp = dun->union_uppervp;
X	struct vnode *uvp = un->union_uppervp;
X	struct vnode *lvp = un->union_lowervp;
X	struct componentname *cnp = ap->a_cnp;
X	struct thread *td = curthread;
X
X	UNION_INTERNAL_DEBUG("union_remove: enter\n");
X
X	if (NULLVP == udvp) {
X		return EROFS;
X	}
X
X	if (NULLVP != uvp) {
X		error = VOP_REMOVE(udvp, uvp, cnp);
X		if (error) {
X			return error;
X		}
X	}
X	if (NULLVP != lvp) {
X		error = union_mkwhiteout(um, udvp, cnp, td, un->union_path);
X	}
X
X	UNION_INTERNAL_DEBUG("union_remove: leave\n");
X
X	return error;
X}
X
Xstatic int
Xunion_link(struct vop_link_args *ap)
X{
X	int error = 0;
X	struct union_node *dun = VTOUNION(ap->a_tdvp);
X	struct union_node *un = NULL;
X	struct vnode *udvp = dun->union_uppervp;
X	struct vnode *uvp = NULLVP;
X	struct componentname *cnp = ap->a_cnp;
X	struct thread *td = curthread;
X
X	UNION_INTERNAL_DEBUG("union_link: enter\n");
X
X	if (NULLVP == udvp) {
X		return EROFS;
X	}
X
X	if (ap->a_tdvp->v_op != ap->a_vp->v_op) {
X		uvp = ap->a_vp;
X	}
X	else {
X		un = VTOUNION(ap->a_vp);
X
X		if (NULLVP == un->union_uppervp) {
X			error = union_copyfile(un, 1, cnp->cn_cred, td);
X			if (error) {
X				return error;
X			}
X		}
X		uvp = un->union_uppervp;
X	}
X
X	error = VOP_LINK(udvp, uvp, cnp);
X
X	UNION_INTERNAL_DEBUG("union_link: leave\n");
X
X	return error;
X}
X
Xstatic int
Xunion_rename(struct vop_rename_args *ap)
X{
X	int error = 0;
X	struct vnode *fdvp = ap->a_fdvp;
X	struct vnode *fvp = ap->a_fvp;
X	struct componentname *fcnp = ap->a_fcnp;
X	struct vnode *tdvp = ap->a_tdvp;
X	struct vnode *tvp = ap->a_tvp;
X	struct componentname *tcnp = ap->a_tcnp;
X	struct thread *td = curthread;
X
X	struct vnode *rfdvp = fdvp;
X	struct vnode *rfvp = fvp;
X	struct vnode *rtdvp = tdvp;
X	struct vnode *rtvp = tvp;
X
X	int needwhiteout = 0;
X	struct union_mount *um;
X	struct union_node *un;
X
X	UNION_INTERNAL_DEBUG("union_rename: enter\n");
X
X#ifdef DIAGNOSTIC
X	if ((fcnp->cn_flags & HASBUF) == 0 ||
X	    (tcnp->cn_flags & HASBUF) == 0)
X		panic("union_rename: no name");
X#endif
X
X	/* check for cross device rename */
X	if ((fvp->v_mount != tdvp->v_mount) ||
X		(NULLVP != tvp && (fvp->v_mount != tvp->v_mount))) {
X		error = EXDEV;
X		goto union_rename_abort;
X	}
X
X	/* Renaming a file to itself has no effect. */
X	if (fvp == tvp) {
X		error = 0;
X		goto union_rename_abort;
X	}
X
X	if (fdvp->v_op == &union_vnodeops) {
X		un = VTOUNION(fdvp);
X		um = MOUNTTOUNIONMOUNT(fdvp->v_mount);
X		if (NULLVP == un->union_uppervp) {
X			error = ENODEV;
X			goto union_rename_abort;
X		}
X		rfdvp = un->union_uppervp;
X		vref(rfdvp);
X	}
X
X	if (fvp->v_op == &union_vnodeops) {
X		un = VTOUNION(fvp);
X		um = MOUNTTOUNIONMOUNT(fvp->v_mount);
X		if (NULLVP == un->union_uppervp) {
X			switch (fvp->v_type) {
X			case VREG:
X				error = union_copyfile(un, 1, fcnp->cn_cred, td);
X				if (error) {
X					goto union_rename_abort;
X				}
X				break;
X			case VDIR:
X				if (error) {
X					goto union_rename_abort;
X				}
X				error = union_mkshadowdir(um, rfdvp, un, fcnp, td);
X				if (error) {
X					goto union_rename_abort;
X				}
X				break;
X			default:
X				error = ENODEV;
X				goto union_rename_abort;
X			}
X		}
X		if (NULLVP != un->union_lowervp) {
X			needwhiteout = 1;
X		}
X		rfvp = un->union_uppervp;
X		vref(rfvp);
X	}
X
X	if (tdvp->v_op == &union_vnodeops) {
X		un = VTOUNION(tdvp);
X
X		if (NULLVP == un->union_uppervp) {
X			error = ENODEV;
X			goto union_rename_abort;
X		}
X		rtdvp = un->union_uppervp;
X		vref(rtdvp);
X	}
X
X	if (tdvp == tvp) {
X		rtvp = rtdvp;
X		vref(rtvp);
X	}
X	else if (NULLVP != tvp && tvp->v_op == &union_vnodeops) {
X		un = VTOUNION(tvp);
X
X		if (NULLVP == un->union_uppervp) {
X			rtvp = NULLVP;
X		}
X		else {
X			if (tvp->v_type == VDIR) {
X				error = EINVAL;
X				goto union_rename_abort;
X			}
X			rtvp = un->union_uppervp;
X			vref(rtvp);
X		}
X	}
X
X	error = VOP_RENAME(rfdvp, rfvp, fcnp, rtdvp, rtvp, tcnp);
X
X	if (!error) {
X		if (needwhiteout) {
X			un = VTOUNION(fvp);
X			um = MOUNTTOUNIONMOUNT(fvp->v_mount);
X			error = union_mkwhiteout(um, rfdvp, fcnp, td, un->union_path);
X		}
X	}
X
X	if (fdvp != rfdvp) vrele(fdvp);
X	if (fvp != rfvp) vrele(fvp);
X	if (tdvp != rtdvp) vrele(tdvp);
X	if (tvp != rtvp && NULLVP != rtvp) {
X		vrele(tvp);
X	}
X
X	UNION_INTERNAL_DEBUG("union_rename: leave\n");
X
X	return error;
X
Xunion_rename_abort:
X	if (fdvp != rfdvp) vrele(rfdvp);
X	if (fvp != rfvp) vrele(rfvp);
X	if (tdvp != rtdvp) vrele(rtdvp);
X	vput(tdvp);
X	if (tvp != rtvp && NULLVP != rtvp) vrele(rtvp);
X	if (NULLVP != tvp) vput(tvp);
X	vrele(fdvp);
X	vrele(fvp);
X
X	UNION_INTERNAL_DEBUG("union_rename: leave\n");
X
X	return error;
X}
X
Xstatic int
Xunion_mkdir(struct vop_mkdir_args *ap)
X{
X	int error = EROFS;
X	struct union_node *dun = VTOUNION(ap->a_dvp);
X	struct componentname *cnp = ap->a_cnp;
X	struct thread *td = curthread;
X	struct vnode *udvp = dun->union_uppervp;
X	struct vnode *uvp;
X
X	UNION_INTERNAL_DEBUG("union_mkdir: enter\n");
X
X	if (NULLVP != udvp) {
X		if (!(error = VOP_MKDIR(udvp, &uvp, cnp, ap->a_vap))) {
X			VOP_UNLOCK(uvp, 0, td);
X			error = union_nodeget(ap->a_dvp->v_mount, uvp, NULLVP,
X								  ap->a_dvp, ap->a_vpp, cnp, td);
X			vrele(uvp);
X		}
X	}
X
X	UNION_INTERNAL_DEBUG("union_mkdir: leave\n");
X
X	return error;
X}
X
Xstatic int
Xunion_rmdir(struct vop_rmdir_args *ap)
X{
X	int error = 0;
X	struct union_node *dun = VTOUNION(ap->a_dvp);
X	struct union_node *un = VTOUNION(ap->a_vp);
X	struct union_mount *um = MOUNTTOUNIONMOUNT(ap->a_dvp->v_mount);
X	struct componentname *cnp = ap->a_cnp;
X	struct thread *td = curthread;
X	struct vnode *udvp = dun->union_uppervp;
X	struct vnode *uvp = un->union_uppervp;
X	struct vnode *lvp = un->union_lowervp;
X
X	UNION_INTERNAL_DEBUG("union_rmdir: enter\n");
X
X	if (NULLVP == udvp) {
X		return EROFS;
X	}
X
X	if (NULLVP != uvp) {
X		error = VOP_RMDIR(udvp, uvp, cnp);
X		if (error) {
X			return error;
X		}
X	}
X	if (NULLVP != lvp) {
X		error = union_mkwhiteout(um, udvp, cnp, td, un->union_path);
X	}
X
X	UNION_INTERNAL_DEBUG("union_rmdir: leave\n");
X
X	return error;
X}
X
Xstatic int
Xunion_symlink(struct vop_symlink_args *ap)
X{
X	int error = EROFS;
X	struct union_node *dun = VTOUNION(ap->a_dvp);
X	struct componentname *cnp = ap->a_cnp;
X	struct thread *td = curthread;
X	struct vnode *udvp = dun->union_uppervp;
X	struct vnode *uvp;
X
X	UNION_INTERNAL_DEBUG("union_symlink: enter\n");
X
X	if (NULLVP != udvp) {
X		if (!(error = VOP_SYMLINK(udvp, &uvp, cnp, ap->a_vap, ap->a_target))) {
X			VOP_UNLOCK(uvp, 0, td);
X			error = union_nodeget(ap->a_dvp->v_mount, uvp, NULLVP,
X								  ap->a_dvp, ap->a_vpp, cnp, td);
X			vrele(uvp);
X		}
X	}
X
X	UNION_INTERNAL_DEBUG("union_symlink: leave\n");
X
X	return error;
X}
X
Xstatic int
Xunion_readdir(struct vop_readdir_args *ap)
X{
X	int error = 0;
X	int eofflag = 0;
X	struct union_node *un = VTOUNION(ap->a_vp);
X	struct uio *uio = ap->a_uio;
X	struct thread *td = uio->uio_td;
X	struct vnode *uvp = un->union_uppervp;
X	struct vnode *lvp = un->union_lowervp;
X
X	int ncookies_bk = 0;
X	u_long *cookies_bk = NULL;
X
X	UNION_INTERNAL_DEBUG("union_readdir: enter\n");
X
X	if (ap->a_vp->v_type != VDIR) {
X		return ENOTDIR;
X	}
X
X	if (NULLVP != uvp && NULLVP == lvp) {
X		error = VOP_READDIR(uvp, uio, ap->a_cred, ap->a_eofflag,
X							ap->a_ncookies, ap->a_cookies);
X
X		UNION_INTERNAL_DEBUG("union_readdir: leave\n");
X
X		return error;
X	}
X
X	if (NULLVP == uvp && NULLVP != lvp) {
X		error = VOP_READDIR(lvp, uio, ap->a_cred, ap->a_eofflag,
X							ap->a_ncookies, ap->a_cookies);
X
X		UNION_INTERNAL_DEBUG("union_readdir: leave\n");
X
X		return error;
X	}
X
X	/*
X	 * do merge
X	 */
X
X	if (0 == un->readdir_flag) {
X		error = VOP_READDIR(uvp, uio, ap->a_cred, &eofflag,
X							ap->a_ncookies, ap->a_cookies);
X
X		if (error || !eofflag) {
X			UNION_INTERNAL_DEBUG("union_readdir: leave\n");
X
X			return error;
X		}
X
X		un->readdir_flag = 1;
X
X		if (uio->uio_resid < sizeof(struct dirent)) {
X			return 0;
X		}
X
X		if (ap->a_ncookies) {
X			ncookies_bk = *(ap->a_ncookies);
X			*(ap->a_ncookies) = 0;
X		}
X		if (ap->a_cookies) {
X			cookies_bk = *(ap->a_cookies);
X			*(ap->a_cookies) = NULL;
X		}
X	}
X
X	if (1 == un->readdir_flag) {
X		uio->uio_offset = 0;
X		un->readdir_flag = 2;
X	}
X
X	vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, td);
X	error = VOP_READDIR(lvp, uio, ap->a_cred, ap->a_eofflag,
X						ap->a_ncookies, ap->a_cookies);
X	VOP_UNLOCK(lvp, 0, td);
X
X	if (cookies_bk) {
X		int size = *(ap->a_ncookies) + ncookies_bk;
X		u_long *newcookies = (u_long*)malloc(size * sizeof(u_long),
X											 M_TEMP, M_WAITOK);
X		u_long *pos = newcookies;
X
X		memcpy(pos, cookies_bk, ncookies_bk * sizeof(u_long));
X		pos += ncookies_bk * sizeof(u_long);
X		memcpy(pos, *(ap->a_cookies), *(ap->a_ncookies) * sizeof(u_long));
X		free(cookies_bk, M_TEMP);
X		free(*(ap->a_cookies), M_TEMP);
X		*(ap->a_ncookies) = size;
X		*(ap->a_cookies) = newcookies;
X	}
X
X	UNION_INTERNAL_DEBUG("union_readdir: leave\n");
X
X	return error;
X}
X
Xstatic int
Xunion_readlink(struct vop_readlink_args *ap)
X{
X	struct union_node *un = VTOUNION(ap->a_vp);
X	struct vnode *vp = (NULLVP != un->union_uppervp ? un->union_uppervp :
X						un->union_lowervp);
X
X	return VOP_READLINK(vp, ap->a_uio, ap->a_cred);
X}
X
Xstatic int
Xunion_inactive(struct vop_inactive_args *ap)
X{
X	struct union_node *un = VTOUNION(ap->a_vp);
X
X	if (!(un->union_flag & UNION_CACHED)) {
X		vgone(ap->a_vp);
X	}
X
X	return 0;
X}
X
Xstatic int
Xunion_reclaim(struct vop_reclaim_args *ap)
X{
X	UNION_INTERNAL_DEBUG("union_reclaim: enter\n");
X
X	union_hashrem(ap->a_vp, ap->a_td);
X
X	UNION_INTERNAL_DEBUG("union_reclaim: leave\n");
X
X	return 0;
X}
X
Xstatic int
Xunion_print(struct vop_print_args *ap)
X{
X	struct union_node *un = VTOUNION(ap->a_vp);
X
X	printf("union_vp=%p, uppervp=%p, lowervp=%p\n",
X		   ap->a_vp, un->union_uppervp, un->union_lowervp);
X
X	if (NULLVP != un->union_uppervp) {
X		vprint("union: upper", un->union_uppervp);
X	}
X	if (NULLVP != un->union_lowervp) {
X		vprint("union: lower", un->union_lowervp);
X	}
X
X	return 0;
X}
X
Xstatic int
Xunion_pathconf(struct vop_pathconf_args *ap)
X{
X	struct union_node *un = VTOUNION(ap->a_vp);
X	struct vnode *vp = (NULLVP != un->union_uppervp ? un->union_uppervp :
X						un->union_lowervp);
X
X	return VOP_PATHCONF(vp, ap->a_name, ap->a_retval);
X}
X
Xstatic int
Xunion_advlock(struct vop_advlock_args *ap)
X{
X	struct union_node *un = VTOUNION(ap->a_vp);
X	struct vnode *vp = (NULLVP != un->union_uppervp ? un->union_uppervp :
X						un->union_lowervp);
X
X	return VOP_ADVLOCK(vp, ap->a_id, ap->a_op, ap->a_fl, ap->a_flags);
X}
X
Xstatic int
Xunion_strategy(struct vop_strategy_args *ap)
X{
X	struct union_node *un = VTOUNION(ap->a_vp);
X	struct vnode *vp = (NULLVP != un->union_uppervp ? un->union_uppervp :
X						un->union_lowervp);
X
X#ifdef DIAGNOSTIC
X	if (NULLVP == vp) {
X		panic("union_strategy: nullvp");
X	}
X	if (ap->a_bp->b_iocmd == BIO_WRITE &&
X		vp == un->union_lowervp) {
X		panic("union_strategy: writing to lowervp");
X	}
X#endif
X
X	return VOP_STRATEGY(vp, ap->a_bp);
X}
X
Xstatic int
Xunion_getacl(struct vop_getacl_args *ap)
X{
X	struct union_node *un = VTOUNION(ap->a_vp);
X	struct vnode *vp = (NULLVP != un->union_uppervp ? un->union_uppervp :
X						un->union_lowervp);
X
X	return VOP_GETACL(vp, ap->a_type, ap->a_aclp, ap->a_cred, ap->a_td);
X}
X
Xstatic int
Xunion_setacl(struct vop_setacl_args *ap)
X{
X	struct union_node *un = VTOUNION(ap->a_vp);
X	struct vnode *vp = (NULLVP != un->union_uppervp ? un->union_uppervp :
X						un->union_lowervp);
X
X	return VOP_SETACL(vp, ap->a_type, ap->a_aclp, ap->a_cred, ap->a_td);
X}
X
Xstatic int
Xunion_aclcheck(struct vop_aclcheck_args *ap)
X{
X	struct union_node *un = VTOUNION(ap->a_vp);
X	struct vnode *vp = (NULLVP != un->union_uppervp ? un->union_uppervp :
X						un->union_lowervp);
X
X	return VOP_ACLCHECK(vp, ap->a_type, ap->a_aclp, ap->a_cred, ap->a_td);
X}
X
Xstatic int
Xunion_closeextattr(struct vop_closeextattr_args *ap)
X{
X	struct union_node *un = VTOUNION(ap->a_vp);
X	struct vnode *vp = (NULLVP != un->union_uppervp ? un->union_uppervp :
X						un->union_lowervp);
X
X	return VOP_CLOSEEXTATTR(vp, ap->a_commit, ap->a_cred, ap->a_td);
X}
X
Xstatic int
Xunion_getextattr(struct vop_getextattr_args *ap)
X{
X	struct union_node *un = VTOUNION(ap->a_vp);
X	struct vnode *vp = (NULLVP != un->union_uppervp ? un->union_uppervp :
X						un->union_lowervp);
X
X	return VOP_GETEXTATTR(vp, ap->a_attrnamespace, ap->a_name,
X						  ap->a_uio, ap->a_size, ap->a_cred, ap->a_td);
X}
X
Xstatic int
Xunion_setextattr(struct vop_setextattr_args *ap)
X{
X	struct union_node *un = VTOUNION(ap->a_vp);
X	struct vnode *vp = (NULLVP != un->union_uppervp ? un->union_uppervp :
X						un->union_lowervp);
X
X	return VOP_SETEXTATTR(vp, ap->a_attrnamespace, ap->a_name,
X						  ap->a_uio, ap->a_cred, ap->a_td);
X}
X
Xstatic int
Xunion_listextattr(struct vop_listextattr_args *ap)
X{
X	struct union_node *un = VTOUNION(ap->a_vp);
X	struct vnode *vp = (NULLVP != un->union_uppervp ? un->union_uppervp :
X						un->union_lowervp);
X
X	return VOP_LISTEXTATTR(vp, ap->a_attrnamespace, ap->a_uio,
X						   ap->a_size, ap->a_cred, ap->a_td);
X}
X
Xstatic int
Xunion_openextattr(struct vop_openextattr_args *ap)
X{
X	struct union_node *un = VTOUNION(ap->a_vp);
X	struct vnode *vp = (NULLVP != un->union_uppervp ? un->union_uppervp :
X						un->union_lowervp);
X
X	return VOP_OPENEXTATTR(vp, ap->a_cred, ap->a_td);
X}
X
Xstatic int
Xunion_deleteextattr(struct vop_deleteextattr_args *ap)
X{
X	struct union_node *un = VTOUNION(ap->a_vp);
X	struct vnode *vp = (NULLVP != un->union_uppervp ? un->union_uppervp :
X						un->union_lowervp);
X
X	return VOP_DELETEEXTATTR(vp, ap->a_attrnamespace, ap->a_name,
X							 ap->a_cred, ap->a_td);
X}
X
Xstatic int
Xunion_setlabel(struct vop_setlabel_args *ap)
X{
X	struct union_node *un = VTOUNION(ap->a_vp);
X	struct vnode *vp = (NULLVP != un->union_uppervp ? un->union_uppervp :
X						un->union_lowervp);
X
X	return VOP_SETLABEL(vp, ap->a_label, ap->a_cred, ap->a_td);
X}
X
Xstruct vop_vector union_vnodeops = {
X	.vop_default =      &default_vnodeops,
X
X	.vop_access =		union_access,
X	.vop_aclcheck =		union_aclcheck,
X	.vop_advlock =		union_advlock,
X	.vop_bmap =		VOP_EOPNOTSUPP,
X	.vop_close =		union_close,
X	.vop_closeextattr =	union_closeextattr,
X	.vop_create =		union_create,
X	.vop_deleteextattr =	union_deleteextattr,
X	.vop_fsync =		union_fsync,
X	.vop_getacl =		union_getacl,
X	.vop_getattr =		union_getattr,
X	.vop_getextattr =	union_getextattr,
X	.vop_inactive =		union_inactive,
X	.vop_ioctl =		union_ioctl,
X	.vop_lease =		union_lease,
X	.vop_link =		union_link,
X	.vop_listextattr =	union_listextattr,
X	.vop_lookup =		union_lookup,
X	.vop_mkdir =		union_mkdir,
X	.vop_mknod =		union_mknod,
X	.vop_open =		union_open,
X	.vop_openextattr =	union_openextattr,
X	.vop_pathconf =		union_pathconf,
X	.vop_poll =		union_poll,
X	.vop_print =		union_print,
X	.vop_read =		union_read,
X	.vop_readdir =		union_readdir,
X	.vop_readlink =		union_readlink,
X	.vop_reclaim =		union_reclaim,
X	.vop_remove =		union_remove,
X	.vop_rename =		union_rename,
X	.vop_rmdir =		union_rmdir,
X	.vop_setacl =		union_setacl,
X	.vop_setattr =		union_setattr,
X	.vop_setextattr =	union_setextattr,
X	.vop_setlabel =		union_setlabel,
X	.vop_strategy =		union_strategy,
X	.vop_symlink =		union_symlink,
X	.vop_whiteout =		union_whiteout,
X	.vop_write =		union_write,
X};
END-of-unionfs/fs/union_vnops.c
echo x - unionfs/fs/union_vfsops.c
sed 's/^X//' >unionfs/fs/union_vfsops.c << 'END-of-unionfs/fs/union_vfsops.c'
X/*
X * union_vfsops.c
X *
X * Copyright (c) 2005 ONGS Inc. All rights reserved.
X * Copyright (c) 2005 Masanori OZAWA. All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X *
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. Neither the name of the ONGS Inc. nor the names of its contributors
X *    may be used to endorse or promote products derived from this software
X *    without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
X * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
X * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
X * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
X * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
X * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
X * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
X * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
X * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
X * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
X * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
X */
X
X#include <sys/param.h>
X#include <sys/systm.h>
X#include <sys/kdb.h>
X#include <sys/kernel.h>
X#include <sys/lock.h>
X#include <sys/malloc.h>
X#include <sys/mount.h>
X#include <sys/namei.h>
X#include <sys/proc.h>
X#include <sys/vnode.h>
X#include <sys/stat.h>
X
X#include <fs/unionfs/union.h>
X
Xstatic MALLOC_DEFINE(M_UNIONFSMNT, "UNIONFS mount", "UNIONFS mount structure");
X
Xstatic vfs_fhtovp_t	unionfs_fhtovp;
Xstatic vfs_checkexp_t	unionfs_checkexp;
Xstatic vfs_mount_t	unionfs_mount;
Xstatic vfs_quotactl_t	unionfs_quotactl;
Xstatic vfs_root_t	unionfs_root;
Xstatic vfs_sync_t	unionfs_sync;
Xstatic vfs_statfs_t	unionfs_statfs;
Xstatic vfs_unmount_t	unionfs_unmount;
Xstatic vfs_vget_t	unionfs_vget;
Xstatic vfs_vptofh_t	unionfs_vptofh;
Xstatic vfs_extattrctl_t	unionfs_extattrctl;
X
Xstatic struct vfsops union_vfsops;
X
Xstatic u_short mode2vmode(mode_t mode)
X{
X	u_short ret = 0;
X
X	/* other */
X	if (mode & S_IXOTH) {
X		ret |= VEXEC >> 6;
X	}
X	if (mode & S_IWOTH) {
X		ret |= VWRITE >> 6;
X	}
X	if (mode & S_IROTH) {
X		ret |= VREAD >> 6;
X	}
X
X	/* group */
X	if (mode & S_IXGRP) {
X		ret |= VEXEC >> 3;
X	}
X	if (mode & S_IWGRP) {
X		ret |= VWRITE >> 3;
X	}
X	if (mode & S_IRGRP) {
X		ret |= VREAD >> 3;
X	}
X
X	/* owner */
X	if (mode & S_IXUSR) {
X		ret |= VEXEC;
X	}
X	if (mode & S_IWUSR) {
X		ret |= VWRITE;
X	}
X	if (mode & S_IRUSR) {
X		ret |= VREAD;
X	}
X
X	return ret;
X}
X
X/*
X * Mount union layer
X */
Xstatic int
Xunionfs_mount(struct mount *mp, struct thread *td)
X{
X	int error = 0;
X	struct vnode *lowerrootvp;
X	struct vnode *upperrootvp;
X	struct union_mount *xmp;
X	char *target;
X	char *tmp;
X	int len;
X	int done;
X	int below = 0;
X	int isvnunlocked = 0;
X	uid_t uid = 0;
X	gid_t gid = 0;
X	u_short udir = 0;
X	u_short ufile = 0;
X	union_copymode copymode = UNION_OLD; /* default */
X	struct componentname fakecn;
X	struct nameidata nd, *ndp = &nd;
X	struct vattr va;
X
X	UNIONFSDEBUG("unionfs_mount(mp = %p)\n", (void *)mp);
X
X	if (mp->mnt_flag & MNT_ROOTFS) {
X		return EOPNOTSUPP;
X	}
X
X	/*
X	 * Update is a no-op
X	 */
X	if (mp->mnt_flag & MNT_UPDATE) {
X		return EOPNOTSUPP;
X	}
X
X	/*
X	 * Check multi union mount to avoid `lock against myself' panic.
X	 */
X	lowerrootvp = mp->mnt_vnodecovered;
X	if (lowerrootvp->v_mount->mnt_op == &union_vfsops) {
X		UNIONFSDEBUG("unionfs_mount: multi union mount?\n");
X		return EDEADLK;
X	}
X
X	/*
X	 * Get argument (part 1)
X	 */
X	error = vfs_getopt(mp->mnt_optnew, "target", (void **)&target, &len);
X	if (error || target[len - 1] != '\0') {
X		return EINVAL;
X	}
X
X	if (0 == vfs_getopt(mp->mnt_optnew, "below", NULL, NULL)) {
X		below = 1;
X	}
X
X	if (0 == vfs_getopt(mp->mnt_optnew, "udir", (void **)&tmp, &len)) {
X		if (len != sizeof(mode_t)) {
X			return EINVAL;
X		}
X		udir = mode2vmode(*((mode_t*)tmp));
X	}
X	if (0 == vfs_getopt(mp->mnt_optnew, "ufile", (void **)&tmp, &len)) {
X		if (len != sizeof(mode_t)) {
X			return EINVAL;
X		}
X		ufile = mode2vmode(*((mode_t*)tmp));
X	}
X
X	/* check umask, uid and gid */
X	if (!udir && ufile) udir = ufile;
X	if (!ufile && udir) ufile = udir;
X
X	if (!VOP_ISLOCKED(mp->mnt_vnodecovered, NULL)) {
X		vn_lock(mp->mnt_vnodecovered, LK_SHARED | LK_RETRY, td);
X		isvnunlocked = 1;
X	}
X
X	error = VOP_GETATTR(mp->mnt_vnodecovered, &va, mp->mnt_cred, td);
X	if (!error) {
X		if (!udir) {
X			udir = va.va_mode;
X		}
X		if (!ufile) {
X			ufile = va.va_mode;
X		}
X		uid = va.va_uid;
X		gid = va.va_gid;
X	}
X
X	if (isvnunlocked) {
X		VOP_UNLOCK(mp->mnt_vnodecovered, 0, td);
X	}
X
X	if (error) {
X		return error;
X	}
X
X	/*
X	 * Get argument (part 2)
X	 */
X	if (0 == mp->mnt_cred->cr_ruid) { /* root only */
X		if (0 == vfs_getopt(mp->mnt_optnew, "uid", (void **)&tmp, &len)) {
X			if (len != sizeof(uid_t)) {
X				return EINVAL;
X			}
X			uid = *((uid_t*)tmp);
X		}
X		if (0 == vfs_getopt(mp->mnt_optnew, "gid", (void **)&tmp, &len)) {
X			if (len != sizeof(gid_t)) {
X				return EINVAL;
X			}
X			gid = *((gid_t*)tmp);
X		}
X		if (0 == vfs_getopt(mp->mnt_optnew, "copymode", (void **)&tmp, &len)) {
X			if (len != sizeof(union_copymode)) {
X				return EINVAL;
X			}
X			copymode = *((union_copymode*)tmp);
X		}
X	}
X
X	if (UNION_OLD == copymode) {
X		uid = mp->mnt_cred->cr_ruid;
X		gid = mp->mnt_cred->cr_rgid;
X	}
X
X	/*
X	 * Unlock lower node to avoid deadlock.
X	 * (XXX) VOP_ISLOCKED is needed?
X	 */
X	isvnunlocked = 0;
X	if (mp->mnt_vnodecovered->v_op == &union_vnodeops) {
X		switch (VOP_ISLOCKED(mp->mnt_vnodecovered, td)) {
X		case LK_SHARED:
X		case LK_EXCLUSIVE:
X			VOP_UNLOCK(mp->mnt_vnodecovered, 0, td);
X			isvnunlocked = 1;
X			break;
X		default:
X			break;
X		}
X	}
X
X	/*
X	 * Find upper node
X	 */
X	NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT|LOCKLEAF, UIO_SYSSPACE, target, td);
X	error = namei(ndp);
X
X	/*
X	 * Re-lock vnode.
X	 */
X	if (isvnunlocked && !VOP_ISLOCKED(mp->mnt_vnodecovered, NULL)) {
X		vn_lock(mp->mnt_vnodecovered, LK_EXCLUSIVE | LK_RETRY, td);
X	}
X
X	if (error) {
X		return error;
X	}
X	NDFREE(ndp, NDF_ONLY_PNBUF);
X
X	/*
X	 * get root vnodes
X	 */
X	upperrootvp = ndp->ni_vp;
X
X	vrele(ndp->ni_dvp);
X	ndp->ni_dvp = NULLVP;
X
X	/*
X	 * Check multi union mount to avoid `lock against myself' panic.
X	 */
X	/*
X	 * XXX: It is not being checked whether it operates safely.
X	 *
X	if (upperrootvp->v_mount->mnt_op == &union_vfsops) {
X		UNIONFSDEBUG("unionfs_mount: multi union mount?\n");
X		vput(upperrootvp);
X		return EDEADLK;
X	}
X	*/
X
X	xmp = (struct union_mount *) malloc(sizeof(struct union_mount),
X				M_UNIONFSMNT, M_WAITOK | M_ZERO);
X
X	/*
X	 * Save reference
X	 */
X	if (below) {
X		VOP_UNLOCK(upperrootvp, 0, td);
X		vn_lock(lowerrootvp, LK_EXCLUSIVE | LK_RETRY, td);
X		xmp->union_lowervp = upperrootvp;
X		xmp->union_uppervp = lowerrootvp;
X	}
X	else {
X		xmp->union_lowervp = lowerrootvp;
X		xmp->union_uppervp = upperrootvp;
X	}
X	xmp->union_rootvp = NULLVP;
X	xmp->uid = uid;
X	xmp->gid = gid;
X	xmp->udir = udir;
X	xmp->ufile = ufile;
X	xmp->copymode = copymode;
X
X	mp->mnt_data = (qaddr_t)xmp;
X
X	/*
X	 * Copy upper layer's RDONLY flag.
X	 */
X	mp->mnt_flag |= xmp->union_uppervp->v_mount->mnt_flag & MNT_RDONLY;
X
X	/*
X	 * Check whiteout
X	 */
X	if ((mp->mnt_flag & MNT_RDONLY) == 0) {
X		memset(&fakecn, 0, sizeof(fakecn));
X		fakecn.cn_nameiop = LOOKUP;
X		fakecn.cn_thread = td;
X		error = VOP_WHITEOUT(xmp->union_uppervp, &fakecn, LOOKUP);
X		if (error) {
X			vput(xmp->union_uppervp);
X			free(xmp, M_UNIONFSMNT);
X			mp->mnt_data = NULL;
X			return error;
X		}
X	}
X
X	/*
X	 * Get the union root node
X	 */
X	error = union_nodeget(mp, xmp->union_uppervp, xmp->union_lowervp,
X						  NULLVP, &(xmp->union_rootvp), NULL, td);
X	if (error) {
X		vput(xmp->union_uppervp);
X		free(xmp, M_UNIONFSMNT);
X		mp->mnt_data = NULL;
X		return error;
X	}
X
X	/*
X	 * Unlock the node
X	 */
X	VOP_UNLOCK(xmp->union_uppervp, 0, td);
X
X	if (!below) {
X		if (((xmp->union_lowervp == NULLVP) || 
X			 (xmp->union_lowervp->v_mount->mnt_flag & MNT_LOCAL)) &&
X			(xmp->union_uppervp->v_mount->mnt_flag & MNT_LOCAL)) {
X			mp->mnt_flag |= MNT_LOCAL;
X		}
X	}
X
X	vfs_getnewfsid(mp);
X
X	len = MNAMELEN -1;
X	tmp = mp->mnt_stat.f_mntfromname;
X	copystr((below ? "<below>:" : "<above>:"), tmp, len, &done);
X	len -= done -1;
X	tmp += done -1;
X	copystr(target, tmp, len, NULL);
X
X	UNIONFSDEBUG("unionfs_mount: from %s, on %s\n",
X		mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
X
X	return 0;
X}
X
X/*
X * Free reference to union layer
X */
Xstatic int
Xunionfs_unmount(struct mount *mp,
X				int mntflags,
X				struct thread *td)
X{
X	struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
X	int error;
X	int num;
X	int freeing;
X	int flags = 0;
X
X	UNIONFSDEBUG("unionfs_unmount: mp = %p\n", (void *)mp);
X
X	if (mntflags & MNT_FORCE) {
X		flags |= FORCECLOSE;
X	}
X
X	/* vflush (no need to call vrele) */
X	for (freeing = 0; (error = vflush(mp, 1, flags, td)) != 0;) {
X		num = mp->mnt_nvnodelistsize;
X		if (num == freeing) break;
X		freeing = num;
X	}
X
X	if (error) {
X		return error;
X	}
X
X	free(um, M_UNIONFSMNT);
X	mp->mnt_data = 0;
X
X	return 0;
X}
X
Xstatic int
Xunionfs_root(struct mount *mp,
X			 int flags,
X			 struct vnode **vpp,
X			 struct thread *td)
X{
X	struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
X	struct vnode *vp = um->union_rootvp;
X
X	UNIONFSDEBUG("union_root: rootvp=%p locked=%d\n",
X				 vp, VOP_ISLOCKED(vp, NULL));
X
X	vref(vp);
X	if (flags & LK_TYPE_MASK) {
X		vn_lock(vp, flags, td);
X	}
X
X	*vpp = vp;
X
X	return 0;
X}
X
Xstatic int
Xunionfs_quotactl(struct mount *mp,
X				 int cmd,
X				 uid_t uid,
X				 /* caddr_t arg, // for 6.0-R */
X				 void *arg, // for 7-current
X				 struct thread *td)
X{
X	struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
X
X	/*
X	 * Writing is always performed to upper vnode.
X	 */
X	return VFS_QUOTACTL(um->union_uppervp->v_mount, cmd, uid, arg, td);
X}
X
Xstatic int
Xunionfs_statfs(struct mount *mp,
X			   struct statfs *sbp,
X			   struct thread *td)
X{
X	struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
X	int error;
X	struct statfs mstat;
X	uint64_t lbsize;
X
X	UNIONFSDEBUG("unionfs_statfs(mp = %p, lvp = %p, uvp = %p)\n",
X				 (void *)mp,
X				 (void *)um->union_lowervp,
X				 (void *)um->union_uppervp);
X
X	bzero(&mstat, sizeof(mstat));
X
X	error = VFS_STATFS(um->union_lowervp->v_mount, &mstat, td);
X	if (error) {
X		return (error);
X	}
X
X	/* now copy across the "interesting" information and fake the rest */
X#if 0
X	sbp->f_type = mstat.f_type;
X	sbp->f_flags = mstat.f_flags;
X	sbp->f_bsize = mstat.f_bsize;
X	sbp->f_iosize = mstat.f_iosize;
X	sbp->f_bfree = mstat.f_bfree;
X	sbp->f_bavail = mstat.f_bavail;
X	sbp->f_ffree = mstat.f_ffree;
X#endif
X	sbp->f_blocks = mstat.f_blocks;
X	sbp->f_files = mstat.f_files;
X
X	lbsize = mstat.f_bsize;
X
X	error = VFS_STATFS(um->union_uppervp->v_mount, &mstat, td);
X	if (error) {
X		return (error);
X	}
X
X	sbp->f_type = mstat.f_type; /* humm... */
X	sbp->f_flags = mstat.f_flags;
X	sbp->f_bsize = mstat.f_bsize;
X	sbp->f_iosize = mstat.f_iosize;
X
X	if (mstat.f_bsize != lbsize) {
X		sbp->f_blocks = ((off_t)sbp->f_blocks * lbsize) / mstat.f_bsize;
X	}
X
X	sbp->f_blocks += mstat.f_blocks;
X	sbp->f_bfree = mstat.f_bfree;
X	sbp->f_bavail = mstat.f_bavail;
X	sbp->f_files += mstat.f_files;
X	sbp->f_ffree = mstat.f_ffree;
X
X	return 0;
X}
X
Xstatic int
Xunionfs_sync(struct mount *mp,
X			 int waitfor,
X			 struct thread *td)
X{
X	/* nothing to do */
X	return 0;
X}
X
Xstatic int
Xunionfs_vget(struct mount *mp,
X			 ino_t ino,
X			 int flags,
X			 struct vnode **vpp)
X{
X	/* humm... */
X	return EOPNOTSUPP;
X}
X
Xstatic int
Xunionfs_fhtovp(struct mount *mp,
X			   struct fid *fidp,
X			   struct vnode **vpp)
X{
X	/* humm... */
X	return EOPNOTSUPP;
X}
X
Xstatic int
Xunionfs_checkexp(struct mount *mp,
X				 struct sockaddr *nam,
X				 int *extflagsp,
X				 struct ucred **credanonp)
X{
X	/* humm... */
X	return EOPNOTSUPP;
X}
X
Xstatic int
Xunionfs_vptofh(struct vnode *vp,
X			   struct fid *fhp)
X{
X	/* humm... */
X	return EOPNOTSUPP;
X}
X
Xstatic int                        
Xunionfs_extattrctl(struct mount *mp,
X				   int cmd,
X				   struct vnode *filename_vp,
X				   int namespace,
X				   const char *attrname,
X				   struct thread *td)
X{
X	struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
X	struct union_node *un = VTOUNION(filename_vp);
X
X	if (NULLVP != un->union_uppervp) {
X		return VFS_EXTATTRCTL(um->union_uppervp->v_mount, cmd,
X							  un->union_uppervp,
X							  namespace, attrname, td);
X	}
X	else {
X		return VFS_EXTATTRCTL(um->union_lowervp->v_mount, cmd,
X							  un->union_lowervp,
X							  namespace, attrname, td);
X	}
X}
X
Xstatic struct vfsops union_vfsops = {
X	.vfs_checkexp =		unionfs_checkexp,
X	.vfs_extattrctl =	unionfs_extattrctl,
X	.vfs_fhtovp =		unionfs_fhtovp,
X	.vfs_init =		unionfs_init,
X	.vfs_mount =		unionfs_mount,
X	.vfs_quotactl =		unionfs_quotactl,
X	.vfs_root =		unionfs_root,
X	.vfs_statfs =		unionfs_statfs,
X	.vfs_sync =		unionfs_sync,
X	.vfs_uninit =		unionfs_uninit,
X	.vfs_unmount =		unionfs_unmount,
X	.vfs_vget =		unionfs_vget,
X	.vfs_vptofh =		unionfs_vptofh,
X};
X
XVFS_SET(union_vfsops, unionfs, VFCF_LOOPBACK);
END-of-unionfs/fs/union_vfsops.c
echo x - unionfs/fs/union.h
sed 's/^X//' >unionfs/fs/union.h << 'END-of-unionfs/fs/union.h'
X/*
X * union.h
X *
X * Copyright (c) 2005 ONGS Inc. All rights reserved.
X * Copyright (c) 2005 Masanori OZAWA. All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X *
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. Neither the name of the ONGS Inc. nor the names of its contributors
X *    may be used to endorse or promote products derived from this software
X *    without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
X * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
X * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
X * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
X * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
X * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
X * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
X * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
X * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
X * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
X * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
X */
X
Xtypedef enum _union_copymode {
X	UNION_OLD = 0,
X	UNION_FULL_COPY,
X	UNION_USEFUL
X} union_copymode;
X
Xstruct union_mount {
X	struct vnode	        *union_lowervp;	/* VREFed once */
X	struct vnode	        *union_uppervp;	/* VREFed once */
X	struct vnode	        *union_rootvp;	/* ROOT vnode */
X	union_copymode          copymode;
X	uid_t                   uid;
X	gid_t                   gid;
X	u_short	                udir;
X	u_short                 ufile;
X};
X
X#ifdef _KERNEL
X/*
X * A cache of vnode references
X */
Xstruct union_node {
X	LIST_ENTRY(union_node)	union_hash;	/* Hash list */
X	struct vnode	        *union_lowervp;	/* lower side vnode */
X	struct vnode	        *union_uppervp;	/* upper side vnode */
X	struct vnode            *union_dvp;     /* parent union vnode */
X	struct vnode	        *union_vnode;	/* Back pointer */
X	char                    *union_path;    /* path */
X	int                     *lower_opencnt; /* open count for lowervp */
X	int                     *upper_opencnt; /* open count for uppervp */
X	int                     readdir_flag;   /* status flag for readdir*/
X	int                     union_flag;     /* union node flag */
X};
X
X#define UNION_CACHED    0x01
X
X#define	MOUNTTOUNIONMOUNT(mp) ((struct union_mount *)((mp)->mnt_data))
X#define	VTOUNION(vp) ((struct union_node *)(vp)->v_data)
X#define	UNIONTOV(xp) ((xp)->union_vnode)
X
Xint unionfs_init(struct vfsconf *vfsp);
Xint unionfs_uninit(struct vfsconf *vfsp);
Xint union_nodeget(struct mount *mp, struct vnode *uppervp,
X				  struct vnode *lowervp, struct vnode *dvp,
X				  struct vnode **vpp, struct componentname *cnp,
X				  struct thread *td);
Xvoid union_hashrem(struct vnode *vp, struct thread *td);
Xvoid union_create_uppervattr_core(struct union_mount *um,
X								  struct vattr *lva,
X								  struct vattr *uva,
X								  struct thread *td);
Xint union_create_uppervattr(struct union_mount *um,
X							struct vnode *lvp,
X							struct vattr *uva,
X							struct ucred *cred,
X							struct thread* td);
Xint union_mkshadowdir(struct union_mount *um,
X					  struct vnode *duvp,
X					  struct union_node *un,
X					  struct componentname *cnp,
X					  struct thread *td);
Xint union_mkwhiteout(struct union_mount *um,
X					 struct vnode *dvp,
X					 struct componentname *cnp,
X					 struct thread *td,
X					 char *path);
Xint union_copyfile(struct union_node *un,
X				   int docopy,
X				   struct ucred *cred,
X				   struct thread *td);
X
X#ifdef DIAGNOSTIC
Xstruct vnode *union_checklowervp(struct vnode *vp, char *fil, int lno);
Xstruct vnode *union_checkuppervp(struct vnode *vp, char *fil, int lno);
X#define	UNIONVPTOLOWERVP(vp) union_checklowervp((vp), __FILE__, __LINE__)
X#define	UNIONVPTOUPPERVP(vp) union_checkuppervp((vp), __FILE__, __LINE__)
X#else
X#define	UNIONVPTOLOWERVP(vp) (VTOUNION(vp)->union_lowervp)
X#define	UNIONVPTOUPPERVP(vp) (VTOUNION(vp)->union_uppervp)
X#endif
X
Xextern struct vop_vector union_vnodeops;
X
X#ifdef MALLOC_DECLARE
XMALLOC_DECLARE(M_UNIONFSNODE);
X#endif
X
X#ifdef UNIONFS_DEBUG
X#define UNIONFSDEBUG(format, args...) printf(format ,## args)
X#else
X#define UNIONFSDEBUG(format, args...)
X#endif /* UNIONFS_DEBUG */
X
X#endif /* _KERNEL */
END-of-unionfs/fs/union.h
echo x - unionfs/fs/union_subr.c
sed 's/^X//' >unionfs/fs/union_subr.c << 'END-of-unionfs/fs/union_subr.c'
X/*
X * union_subr.c
X *
X * Copyright (c) 2005 ONGS Inc. All rights reserved.
X * Copyright (c) 2005 Masanori OZAWA. All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X *
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. Neither the name of the ONGS Inc. nor the names of its contributors
X *    may be used to endorse or promote products derived from this software
X *    without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
X * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
X * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
X * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
X * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
X * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
X * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
X * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
X * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
X * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
X * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
X */
X
X#include <sys/param.h>
X#include <sys/systm.h>
X#include <sys/kernel.h>
X#include <sys/lock.h>
X#include <sys/mutex.h>
X#include <sys/malloc.h>
X#include <sys/mount.h>
X#include <sys/namei.h>
X#include <sys/proc.h>
X#include <sys/vnode.h>
X#include <sys/fcntl.h>
X#include <sys/filedesc.h>
X
X#include <vm/uma.h>
X
X#include <fs/unionfs/union.h>
X
X#define	NUNIONNODECACHE 32
X
X#define	UNION_NHASH(upper, lower) \
X	(&union_node_hashtbl[(((uintptr_t)upper + (uintptr_t)lower) >> 8) & union_node_hash])
X
Xstatic LIST_HEAD(union_node_hashhead, union_node) *union_node_hashtbl;
Xstatic u_long union_node_hash;
Xstruct mtx union_hashmtx;
X
Xstatic MALLOC_DEFINE(M_UNIONFSHASH, "UNIONFS hash", "UNIONFS hash table");
XMALLOC_DEFINE(M_UNIONFSNODE, "UNIONFS node", "UNIONFS vnode private part");
XMALLOC_DEFINE(M_UNIONFSPATH, "UNIONFS path", "UNIONFS path private part");
X
Xstatic struct vnode * union_hashget(struct mount *, struct vnode *,
X									struct vnode *, int, struct thread *);
Xstatic struct vnode * union_hashins(struct mount *, struct union_node *,
X									int, struct thread *);
X
X/*
X * Initialise cache headers
X */
Xint unionfs_init(struct vfsconf *vfsp)
X{
X	UNIONFSDEBUG("unionfs_init\n");		/* printed during system boot */
X	union_node_hashtbl = hashinit(NUNIONNODECACHE, M_UNIONFSHASH, &union_node_hash);
X	mtx_init(&union_hashmtx, "unionfs", NULL, MTX_DEF);
X
X	return 0;
X}
X
Xint unionfs_uninit(struct vfsconf *vfsp)
X{
X	mtx_destroy(&union_hashmtx);
X	free(union_node_hashtbl, M_UNIONFSHASH);
X	return 0;
X}
X
X/*
X * Return a VREF'ed alias for union vnode if already exists, else 0.
X */
Xstatic struct vnode *
Xunion_hashget(struct mount *mp, struct vnode *uppervp,
X			  struct vnode *lowervp, int lkflags, struct thread *td)
X{
X	struct union_node_hashhead *hd;
X	struct union_node *a;
X	struct vnode *vp;
X
X	if (lkflags & LK_TYPE_MASK) {
X		lkflags |= LK_RETRY;
X	}
X
X	hd = UNION_NHASH(uppervp, lowervp);
X
Xloop:
X	mtx_lock(&union_hashmtx);
X	LIST_FOREACH(a, hd, union_hash) {
X		if (a->union_uppervp == uppervp &&
X			a->union_lowervp == lowervp &&
X			UNIONTOV(a)->v_mount == mp)
X		{
X			vp = UNIONTOV(a);
X			VI_LOCK(vp);
X
X			/*
X			 * If the unionfs node is being recycled we have
X			 * to wait until it finishes prior to scanning
X			 * again.
X			 */
X			mtx_unlock(&union_hashmtx);
X			if (vp->v_iflag & VI_DOOMED) {
X				/* Wait for recycling to finish. */
X				vn_lock(vp, LK_EXCLUSIVE | LK_INTERLOCK, td);
X				VOP_UNLOCK(vp, 0, td);
X				goto loop;
X			}
X
X			/*
X			 * We need to clear the OWEINACT flag here as this
X			 * may lead vget() to try to lock our vnode which
X			 * is already locked via vp.
X			 */
X			vp->v_iflag &= ~VI_OWEINACT;
X			vget(vp, lkflags | LK_INTERLOCK, td);
X
X			return vp;
X		}
X	}
X
X	mtx_unlock(&union_hashmtx);
X
X	return NULLVP;
X}
X
X/*
X * insert the new union node to hash
X */
Xstatic struct vnode * union_hashins(struct mount *mp, struct union_node *xp,
X									int lkflags, struct thread *td)
X{
X	struct union_node_hashhead *hd;
X	struct union_node *a;
X	struct vnode *vp;
X
X	if (lkflags & LK_TYPE_MASK) {
X		lkflags |= LK_RETRY;
X	}
X
X	hd = UNION_NHASH(xp->union_uppervp, xp->union_lowervp);
X
Xloop:
X	mtx_lock(&union_hashmtx);
X	LIST_FOREACH(a, hd, union_hash) {
X		if (a->union_uppervp == xp->union_uppervp &&
X			a->union_lowervp == xp->union_lowervp &&
X			UNIONTOV(a)->v_mount == mp)
X		{
X			vp = UNIONTOV(a);
X			VI_LOCK(vp);
X
X			mtx_unlock(&union_hashmtx);
X			if (vp->v_iflag & VI_DOOMED) {
X				/* Wait for recycling to finish. */
X				vn_lock(vp, LK_EXCLUSIVE | LK_INTERLOCK, td);
X				VOP_UNLOCK(vp, 0, td);
X				goto loop;
X			}
X
X			vp->v_iflag &= ~VI_OWEINACT;
X			vget(vp, lkflags | LK_INTERLOCK, td);
X
X			return vp;
X		}
X	}
X
X	LIST_INSERT_HEAD(hd, xp, union_hash);
X	xp->union_flag |= UNION_CACHED;
X	mtx_unlock(&union_hashmtx);
X
X	return NULLVP;
X}
X
X/*
X * Make a new or get existing unionfs node.
X */
Xint
Xunion_nodeget(struct mount *mp, struct vnode *uppervp,
X			  struct vnode *lowervp, struct vnode *dvp,
X			  struct vnode **vpp, struct componentname *cnp,
X			  struct thread *td)
X{
X	struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
X	struct union_node *xp;
X	struct vnode *vp;
X	int lkflags = (cnp ? cnp->cn_lkflags : 0);
X	int error;
X
X	/* Lookup the hash firstly */
X	*vpp = union_hashget(mp, uppervp, lowervp, lkflags, td);
X	if (*vpp != NULLVP) {
X		return 0;
X	}
X
X	if ((uppervp == NULLVP || um->union_uppervp != uppervp) ||
X		(lowervp == NULLVP || um->union_lowervp != lowervp)) {
X		if (NULLVP == dvp) {
X			return EINVAL;
X		}
X	}
X
X	/*
X	 * Do the MALLOC before the getnewvnode since doing so afterward
X	 * might cause a bogus v_data pointer to get dereferenced
X	 * elsewhere if MALLOC should block.
X	 */
X	MALLOC(xp, struct union_node *, sizeof(struct union_node),
X	    M_UNIONFSNODE, M_WAITOK | M_ZERO);
X
X	error = getnewvnode("unionfs", mp, &union_vnodeops, &vp);
X	if (error) {
X		FREE(xp, M_UNIONFSNODE);
X		return error;
X	}
X
X	if (NULLVP != dvp) {
X		vref(dvp);
X	}
X	if (NULLVP != uppervp) {
X		vref(uppervp);
X	}
X	if (NULLVP != lowervp) {
X		vref(lowervp);
X	}
X
X	xp->union_vnode = vp;
X	xp->union_uppervp = uppervp;
X	xp->union_lowervp = lowervp;
X	xp->union_dvp = dvp;
X	if (NULLVP != uppervp) {
X		vp->v_vnlock = uppervp->v_vnlock;
X	}
X	else {
X		vp->v_vnlock = lowervp->v_vnlock;
X	}
X	if (cnp) {
X		xp->union_path = (char *)
X			malloc(cnp->cn_namelen +1, M_UNIONFSPATH, M_WAITOK | M_ZERO);
X		bcopy(cnp->cn_nameptr, xp->union_path, cnp->cn_namelen);
X	}
X	vp->v_type = (uppervp != NULLVP ? uppervp->v_type : lowervp->v_type);
X	vp->v_data = xp;
X
X	if ((uppervp != NULLVP && um->union_uppervp == uppervp) &&
X		(lowervp != NULLVP && um->union_lowervp == lowervp)) {
X		vp->v_vflag |= VV_ROOT;
X	}
X
X	*vpp = union_hashins(mp, xp, lkflags, td);
X	if (NULLVP != *vpp) {
X		if (NULLVP != dvp) vrele(dvp);
X		if (NULLVP != uppervp) vrele(uppervp);
X		if (NULLVP != lowervp) vrele(lowervp);
X		xp->union_uppervp = xp->union_lowervp = xp->union_dvp = NULLVP;
X		vrele(vp);
X		return 0;
X	}
X
X	if (lkflags & LK_TYPE_MASK) {
X		vn_lock(vp, lkflags | LK_RETRY, td);
X	}
X
X	*vpp = vp;
X
X	return 0;
X}
X
X/*
X * Remove node from hash.
X */
Xvoid
Xunion_hashrem(struct vnode *vp,
X			  struct thread *td)
X{
X	struct union_node *un = VTOUNION(vp);
X	struct lock *vnlock;
X
X	VI_LOCK(vp);
X	vnlock = vp->v_vnlock;
X	vp->v_vnlock = &(vp->v_lock);
X	lockmgr(vp->v_vnlock, LK_EXCLUSIVE | LK_INTERLOCK, VI_MTX(vp), td);
X	lockmgr(vnlock, LK_RELEASE, NULL, td);
X	vp->v_data = NULL;
X
X	mtx_lock(&union_hashmtx);
X	if (un->union_flag & UNION_CACHED) {
X		LIST_REMOVE(un, union_hash);
X		un->union_flag &= ~UNION_CACHED;
X	}
X	mtx_unlock(&union_hashmtx);
X	vp->v_object = NULL;
X
X	if (NULLVP != un->union_lowervp) {
X		vrele(un->union_lowervp);
X		un->union_lowervp = NULLVP;
X	}
X	if (NULLVP != un->union_uppervp) {
X		vrele(un->union_uppervp);
X		un->union_uppervp = NULLVP;
X	}
X	if (NULLVP != un->union_dvp) {
X		vrele(un->union_dvp);
X		un->union_dvp = NULLVP;
X	}
X	if (un->union_path) {
X		free(un->union_path, M_UNIONFSPATH);
X		un->union_path = NULL;
X	}
X
X	FREE(un, M_UNIONFSNODE);
X}
X
Xvoid
Xunion_create_uppervattr_core(struct union_mount *um,
X							 struct vattr *lva,
X							 struct vattr *uva,
X							 struct thread *td)
X{
X	VATTR_NULL(uva);
X	uva->va_type = lva->va_type;
X
X	switch (um->copymode) {
X    case UNION_FULL_COPY:
X		uva->va_mode = lva->va_mode;
X		uva->va_uid = lva->va_uid;
X		uva->va_gid = lva->va_gid;
X		uva->va_atime = lva->va_atime;
X		uva->va_mtime = lva->va_mtime;
X		uva->va_ctime = lva->va_ctime;
X		break;
X	case UNION_USEFUL:
X		if (um->uid == lva->va_uid) {
X			uva->va_mode = lva->va_mode & 077077;
X			uva->va_mode |= um->ufile & 0700;
X			uva->va_uid = lva->va_uid;
X			uva->va_gid = lva->va_gid;
X			uva->va_atime = lva->va_atime;
X			uva->va_mtime = lva->va_mtime;
X			uva->va_ctime = lva->va_ctime;
X		}
X		else {
X			uva->va_mode = um->ufile;
X			uva->va_uid = um->uid;
X			uva->va_gid = um->gid;
X		}
X		break;
X	default: /* UNION_OLD */
X		FILEDESC_LOCK_FAST(td->td_proc->p_fd);
X		uva->va_mode = 0777 & ~td->td_proc->p_fd->fd_cmask;
X		FILEDESC_UNLOCK_FAST(td->td_proc->p_fd);
X		uva->va_uid = um->uid;
X		uva->va_gid = um->gid;
X		break;
X	}
X}
X
Xint
Xunion_create_uppervattr(struct union_mount *um,
X						struct vnode *lvp,
X						struct vattr *uva,
X						struct ucred *cred,
X						struct thread* td)
X{
X	int error = 0;
X	struct vattr lva;
X
X	if ((error = VOP_GETATTR(lvp, &lva, cred, td))) {
X		return error;
X	}
X
X	union_create_uppervattr_core(um, &lva, uva, td);
X
X	return error;
X}
X
Xstatic int
Xunion_relookup(struct union_mount *um,
X			   struct vnode *dvp,
X			   struct vnode **vpp,
X			   struct componentname *cnp,
X			   struct componentname *cn,
X			   struct thread *td,
X			   char *path,
X			   int pathlen)
X{
X	int error;
X
X	cn->cn_namelen = pathlen;
X	cn->cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK);
X	bcopy(path, cn->cn_pnbuf, pathlen);
X	cn->cn_pnbuf[pathlen] = '\0';
X
X	cn->cn_nameiop = CREATE;
X	cn->cn_flags = (LOCKPARENT | LOCKLEAF | HASBUF | SAVENAME | ISLASTCN);
X	cn->cn_thread = td;
X	cn->cn_cred = cnp->cn_cred;  /* humm... */
X
X	cn->cn_nameptr = cn->cn_pnbuf;
X	cn->cn_consume = cnp->cn_consume;
X
X	vref(dvp);
X	VOP_UNLOCK(dvp, 0, td);
X
X	if ((error = relookup(dvp, vpp, cn))) {
X		uma_zfree(namei_zone, cn->cn_pnbuf);
X		cn->cn_flags &= ~HASBUF;
X		vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td);
X	}
X	else {
X		vrele(dvp);
X	}
X
X	return error;
X}
X
Xstatic void
Xunion_node_update(struct union_node *un, struct vnode *uvp,
X				  struct thread *td)
X{
X	struct vnode *lvp = un->union_lowervp;
X
X	/*
X	 * lock update
X	 */
X	VI_LOCK(UNIONTOV(un));
X	un->union_uppervp = uvp;
X	UNIONTOV(un)->v_vnlock = uvp->v_vnlock;
X	VI_UNLOCK(UNIONTOV(un));
X	VOP_UNLOCK(lvp, 0, td);
X
X	/*
X	 * cache update
X	 */
X	mtx_lock(&union_hashmtx);
X	if (un->union_flag & UNION_CACHED) {
X		LIST_REMOVE(un, union_hash);
X	}
X	LIST_INSERT_HEAD(UNION_NHASH(uvp, lvp), un, union_hash);
X	un->union_flag |= UNION_CACHED;
X	mtx_unlock(&union_hashmtx);
X}
X
Xint
Xunion_mkshadowdir(struct union_mount *um,
X				  struct vnode *duvp,
X				  struct union_node *un,
X				  struct componentname *cnp,
X				  struct thread *td)
X{
X	int error;
X	struct vnode *lvp = un->union_lowervp;
X	struct vnode *uvp;
X	struct vattr va;
X	struct vattr lva;
X	struct componentname cn;
X	struct mount *mp;
X
X	if (NULLVP != un->union_uppervp) {
X		return EEXIST;
X	}
X
X	memset(&cn, 0, sizeof(cn));
X
X	if ((error = union_relookup(um, duvp, &uvp, cnp, &cn, td,
X								cnp->cn_nameptr, cnp->cn_namelen))) {
X		return error;
X	}
X
X	if ((error = VOP_GETATTR(lvp, &lva, cnp->cn_cred, td))) {
X		return error;
X	}
X
X	if (NULLVP != uvp) {
X		if (cn.cn_flags & HASBUF) {
X			uma_zfree(namei_zone, cn.cn_pnbuf);
X			cn.cn_flags &= ~HASBUF;
X		}
X		if (duvp == uvp) {
X			vrele(uvp);
X		}
X		else {
X			vput(uvp);
X		}
X
X		return EEXIST;
X	}
X
X	if ((error = vn_start_write(duvp, &mp, V_WAIT | PCATCH))) {
X		goto union_mkshadowdir_free_out;
X	}
X
X	if ((error = VOP_LEASE(duvp, td, cn.cn_cred, LEASE_WRITE))) {
X		vn_finished_write(mp);
X		goto union_mkshadowdir_free_out;
X	}
X
X	union_create_uppervattr_core(um, &lva, &va, td);
X
X	error = VOP_MKDIR(duvp, &uvp, &cn, &va);
X	vn_finished_write(mp);
X
X	if (!error) {
X		union_node_update(un, uvp, td);
X	}
X
Xunion_mkshadowdir_free_out:
X	if (cn.cn_flags & HASBUF) {
X		uma_zfree(namei_zone, cn.cn_pnbuf);
X		cn.cn_flags &= ~HASBUF;
X	}
X
X	return error;
X}
X
Xint
Xunion_mkwhiteout(struct union_mount *um,
X				 struct vnode *dvp,
X				 struct componentname *cnp,
X				 struct thread *td,
X				 char *path)
X{
X	int error;
X	struct vnode *wvp;
X	struct componentname cn;
X	struct mount *mp;
X
X	if ((error = union_relookup(um, dvp, &wvp, cnp, &cn, td,
X								path, strlen(path)))) {
X		return error;
X	}
X
X	if (wvp) {
X		if (cn.cn_flags & HASBUF) {
X			uma_zfree(namei_zone, cn.cn_pnbuf);
X			cn.cn_flags &= ~HASBUF;
X		}
X		if (dvp == wvp) {
X			vrele(wvp);
X		}
X		else {
X			vput(wvp);
X		}
X
X		return EEXIST;
X	}
X
X	if ((error = vn_start_write(dvp, &mp, V_WAIT | PCATCH))) {
X		goto union_mkwhiteout_free_out;
X	}
X
X	if ((error = VOP_LEASE(dvp, td, td->td_ucred, LEASE_WRITE))) {
X		vn_finished_write(mp);
X		goto union_mkwhiteout_free_out;
X	}
X
X	error = VOP_WHITEOUT(dvp, &cn, CREATE);
X	vn_finished_write(mp);
X
Xunion_mkwhiteout_free_out:
X	if (cn.cn_flags & HASBUF) {
X		uma_zfree(namei_zone, cn.cn_pnbuf);
X		cn.cn_flags &= ~HASBUF;
X	}
X
X	return error;
X}
X
Xstatic int
Xunion_vn_create_on_upper(struct vnode **vpp,
X						 struct union_node *un,
X						 struct thread *td)
X{
X	struct union_mount *um = MOUNTTOUNIONMOUNT(UNIONTOV(un)->v_mount);
X	struct vnode *vp = NULLVP;
X	struct vnode *lvp = un->union_lowervp;
X	struct vnode *udvp = VTOUNION(un->union_dvp)->union_uppervp;
X	struct ucred *cred = td->td_ucred;
X	struct vattr va;
X	struct vattr lva;
X	int fmode = FFLAGS(O_WRONLY | O_CREAT | O_TRUNC | O_EXCL);
X	int lockflag = 0;
X	int error = 0;
X	struct componentname cn;
X
X	if (NULLVP == udvp) {
X		return EINVAL;
X	}
X
X	if ((error = VOP_GETATTR(lvp, &lva, cred, td))) {
X		return error;
X	}
X
X	union_create_uppervattr_core(um, &lva, &va, td);
X
X	cn.cn_namelen = strlen(un->union_path);
X	cn.cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK);
X	bcopy(un->union_path, cn.cn_pnbuf, cn.cn_namelen +1);
X	cn.cn_nameiop = CREATE;
X	cn.cn_flags = ISOPEN | LOCKPARENT | LOCKLEAF | HASBUF | SAVENAME | ISLASTCN;
X	cn.cn_thread = td;
X	cn.cn_cred = cred;
X	cn.cn_nameptr = cn.cn_pnbuf;
X	cn.cn_consume = 0;
X
X	vref(udvp);
X	lockflag = VOP_ISLOCKED(udvp, td);
X	if (lockflag) {
X		switch (lockflag) {
X		case LK_EXCLUSIVE:
X		case LK_SHARED:
X			VOP_UNLOCK(udvp, 0, td);
X			break;
X		default:
X			lockflag = 0;
X			break;
X		}
X	}
X
X	if ((error = relookup(udvp, &vp, &cn))) {
X		if (lockflag) vn_lock(udvp, lockflag | LK_RETRY, td);
X		goto union_vn_create_on_upper_free_out;
X	}
X
X	vrele(udvp);
X
X	if (vp) {
X		if (vp == udvp) {
X			vrele(vp);
X		}
X		else {
X			vput(vp);
X		}
X		error = EEXIST;
X		goto union_vn_create_on_upper_free_out;
X	}
X
X	if ((error = VOP_LEASE(udvp, td, cred, LEASE_WRITE))) {
X		if (!lockflag) VOP_UNLOCK(udvp, 0, td);
X		goto union_vn_create_on_upper_free_out;
X	}
X
X	if ((error = VOP_CREATE(udvp, &vp, &cn, &va))) {
X		if (!lockflag) VOP_UNLOCK(udvp, 0, td);
X		goto union_vn_create_on_upper_free_out;
X	}
X
X	if (!lockflag) VOP_UNLOCK(udvp, 0, td);
X
X	if ((error = VOP_OPEN(vp, fmode, cred, td, -1))) {
X		vput(vp);
X		goto union_vn_create_on_upper_free_out;
X	}
X
X	vp->v_writecount++;
X	*vpp = vp;
X
Xunion_vn_create_on_upper_free_out:
X	if (cn.cn_flags & HASBUF) {
X		uma_zfree(namei_zone, cn.cn_pnbuf);
X		cn.cn_flags &= ~HASBUF;
X	}
X
X	return error;
X}
X
Xstatic int
Xunion_copyfile_core(struct vnode *lvp,
X					struct vnode *uvp,
X					struct ucred *cred,
X					struct thread *td)
X{
X	int error = 0;
X	off_t offset;
X	int count;
X	int bufoffset;
X	char *buf;
X	struct uio uio;
X	struct iovec iov;
X	
X	memset(&uio, 0, sizeof(uio));
X
X	uio.uio_td = td;
X	uio.uio_segflg = UIO_SYSSPACE;
X	uio.uio_offset = 0;
X
X	if ((error = VOP_LEASE(lvp, td, cred, LEASE_READ))) {
X		return error;
X	}
X	if ((error = VOP_LEASE(uvp, td, cred, LEASE_WRITE))) {
X		return error;
X	}
X
X	buf = malloc(MAXBSIZE, M_TEMP, M_WAITOK);
X
X	while (!error) {
X		offset = uio.uio_offset;
X
X		uio.uio_iov = &iov;
X		uio.uio_iovcnt = 1;
X		iov.iov_base = buf;
X		iov.iov_len = MAXBSIZE;
X		uio.uio_resid = iov.iov_len;
X		uio.uio_rw = UIO_READ;
X
X		if ((error = VOP_READ(lvp, &uio, 0, cred))) {
X			break;
X		}
X
X		if ((count = MAXBSIZE - uio.uio_resid) == 0) {
X			break;
X		}
X
X		bufoffset = 0;
X		while (bufoffset < count) {
X			uio.uio_iov = &iov;
X			uio.uio_iovcnt = 1;
X			iov.iov_base = buf + bufoffset;
X			iov.iov_len = count - bufoffset;
X			uio.uio_offset = offset + bufoffset;
X			uio.uio_resid = iov.iov_len;
X			uio.uio_rw = UIO_WRITE;
X
X			if ((error = VOP_WRITE(uvp, &uio, 0, cred))) {
X				break;
X			}
X			bufoffset += (count - bufoffset) - uio.uio_resid;
X		}
X
X		uio.uio_offset = offset + bufoffset;
X	}
X
X	free(buf, M_TEMP);
X
X	return error;
X}
X
Xint
Xunion_copyfile(struct union_node *un,
X			   int docopy,
X			   struct ucred *cred,
X			   struct thread *td)
X{
X	int error;
X	struct mount *mp;
X	struct vnode *lvp = un->union_lowervp;
X	struct vnode *uvp = NULLVP;
X
X	if (NULLVP != un->union_uppervp) {
X		return EEXIST;
X	}
X
X	error = VOP_ACCESS(lvp, VREAD, cred, td);
X	if (error) {
X		return error;
X	}
X
X	if ((error = vn_start_write(lvp, &mp, V_WAIT | PCATCH))) {
X		return error;
X	}
X
X	if ((error = union_vn_create_on_upper(&uvp, un, td))) {
X		vn_finished_write(mp);
X		return error;
X	}
X
X	if (docopy) {
X		error = VOP_OPEN(lvp, FREAD, cred, td, -1);
X		if (!error) {
X			error = union_copyfile_core(lvp, uvp, cred, td);
X			VOP_CLOSE(lvp, FREAD, cred, td);
X		}
X	}
X	VOP_CLOSE(uvp, FWRITE, cred, td);
X	uvp->v_writecount--;
X
X	vn_finished_write(mp);
X
X	union_node_update(un, uvp, td);
X
X	return error;
X}
X
X#ifdef DIAGNOSTIC
X
Xstruct vnode *
Xunion_checkuppervp(struct vnode *vp,
X				   char *fil,
X				   int lno)
X{
X	struct union_node *a = VTOUNION(vp);
X
X#ifdef notyet
X	if (vp->v_op != union_vnodeop_p) {
X		printf("union_checkuppervp: on non-union-node.\n");
X#ifdef KDB
X		kdb_enter("union_checkuppervp: on non-union-node.\n");
X#endif
X		panic("union_checkuppervp");
X	};
X#endif
X	return a->union_uppervp;
X}
X
Xstruct vnode *
Xunion_checklowervp(struct vnode *vp,
X				   char *fil,
X				   int lno)
X{
X	struct union_node *a = VTOUNION(vp);
X
X#ifdef notyet
X	if (vp->v_op != union_vnodeop_p) {
X		printf("union_checklowervp: on non-union-node.\n");
X#ifdef KDB
X		kdb_enter("union_checklowervp: on non-union-node.\n");
X#endif
X		panic("union_checklowervp");
X	};
X#endif
X	return a->union_lowervp;
X}
X#endif
END-of-unionfs/fs/union_subr.c
exit

>Release-Note:
>Audit-Trail:
>Unformatted:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200512281012.jBSACVct027091>