예제 #1
0
/*
 * Free reference to unionfs layer
 */
static int
unionfs_unmount(struct mount *mp, int mntflags)
{
    struct unionfs_mount *ump;
    int		error;
    int		num;
    int		freeing;
    int		flags;

    UNIONFSDEBUG("unionfs_unmount: mp = %p\n", (void *)mp);

    ump = MOUNTTOUNIONFSMOUNT(mp);
    flags = 0;

    if (mntflags & MNT_FORCE)
        flags |= FORCECLOSE;

    /* vflush (no need to call vrele) */
    for (freeing = 0; (error = vflush(mp, 1, flags, curthread)) != 0;) {
        num = mp->mnt_nvnodelistsize;
        if (num == freeing)
            break;
        freeing = num;
    }

    if (error)
        return (error);

    free(ump, M_UNIONFSMNT);
    mp->mnt_data = 0;

    return (0);
}
예제 #2
0
static int
unionfs_statfs(struct mount *mp, struct statfs *sbp)
{
	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);

	mstat = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK | M_ZERO);

	error = VFS_STATFS(ump->um_lowervp->v_mount, mstat);
	if (error) {
		free(mstat, M_STATFS);
		return (error);
	}

	/* now copy across the "interesting" information and fake the rest */
	sbp->f_blocks = mstat->f_blocks;
	sbp->f_files = mstat->f_files;

	lbsize = mstat->f_bsize;

	error = VFS_STATFS(ump->um_uppervp->v_mount, mstat);
	if (error) {
		free(mstat, M_STATFS);
		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 (mstat->f_bsize != lbsize)
		sbp->f_blocks = ((off_t)sbp->f_blocks * lbsize) /
		    mstat->f_bsize;

	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;

	free(mstat, M_STATFS);
	return (0);
}
예제 #3
0
static int
unionfs_root(struct mount *mp, int flags, struct vnode **vpp)
{
    struct unionfs_mount *ump;
    struct vnode   *vp;

    ump = MOUNTTOUNIONFSMOUNT(mp);
    vp = ump->um_rootvp;

    UNIONFSDEBUG("unionfs_root: rootvp=%p locked=%x\n",
                 vp, VOP_ISLOCKED(vp));

    vref(vp);
    if (flags & LK_TYPE_MASK)
        vn_lock(vp, flags);

    *vpp = vp;

    return (0);
}
예제 #4
0
static int
unionfs_lookup(void *v)
{
	struct vop_lookup_args *ap = v;
	int		iswhiteout;
	int		lockflag;
	int		error , uerror, lerror;
	u_long		nameiop;
	u_long		cnflags, cnflagsbk;
	struct unionfs_node *dunp;
	struct vnode   *dvp, *udvp, *ldvp, *vp, *uvp, *lvp, *dtmpvp;
	struct vattr	va;
	struct componentname *cnp;

	iswhiteout = 0;
	lockflag = 0;
	error = uerror = lerror = ENOENT;
	cnp = ap->a_cnp;
	nameiop = cnp->cn_nameiop;
	cnflags = cnp->cn_flags;
	dvp = ap->a_dvp;
	dunp = VTOUNIONFS(dvp);
	udvp = dunp->un_uppervp;
	ldvp = dunp->un_lowervp;
	vp = uvp = lvp = NULLVP;
	*(ap->a_vpp) = NULLVP;

	UNIONFS_INTERNAL_DEBUG("unionfs_lookup: enter: nameiop=%ld, flags=%lx, path=%s\n", nameiop, cnflags, cnp->cn_nameptr);

	if (dvp->v_type != VDIR)
		return (ENOTDIR);

	/*
	 * If read-only and op is not LOOKUP, will return EROFS.
	 */
	if ((cnflags & ISLASTCN) &&
	    (dvp->v_mount->mnt_flag & MNT_RDONLY) &&
	    LOOKUP != nameiop)
		return (EROFS);

	/*
	 * lookup dotdot
	 */
	if (cnflags & ISDOTDOT) {
		if (LOOKUP != nameiop && udvp == NULLVP)
			return (EROFS);

		if (udvp != NULLVP) {
			dtmpvp = udvp;
			if (ldvp != NULLVP)
				VOP_UNLOCK(ldvp);
		}
		else
			dtmpvp = ldvp;

		error = VOP_LOOKUP(dtmpvp, &vp, cnp);

		if (dtmpvp == udvp && ldvp != NULLVP) {
			VOP_UNLOCK(udvp);
			vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
		}

		if (error == 0) {
			/*
			 * Exchange lock and reference from vp to
			 * dunp->un_dvp. vp is upper/lower vnode, but it
			 * will need to return the unionfs vnode.
			 */
			if (nameiop == DELETE  || nameiop == RENAME)
				VOP_UNLOCK(vp);
			vrele(vp);

			VOP_UNLOCK(dvp);
			*(ap->a_vpp) = dunp->un_dvp;
			vref(dunp->un_dvp);

			vn_lock(dunp->un_dvp, LK_EXCLUSIVE | LK_RETRY);
			vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
		} else if (error == ENOENT && nameiop != CREATE)
			cache_enter(dvp, NULLVP, cnp->cn_nameptr,
				    cnp->cn_namelen, cnp->cn_flags);

		UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n", error);

		return (error);
	}

	/*
	 * lookup upper layer
	 */
	if (udvp != NULLVP) {
		uerror = VOP_LOOKUP(udvp, &uvp, cnp);

		if (uerror == 0) {
			if (udvp == uvp) {	/* is dot */
				vrele(uvp);
				*(ap->a_vpp) = dvp;
				vref(dvp);

				UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n", uerror);

				return (uerror);
			}
		}

		/* check whiteout */
		if (uerror == ENOENT || uerror == EJUSTRETURN)
			if (cnp->cn_flags & ISWHITEOUT)
				iswhiteout = 1;	/* don't lookup lower */
		if (iswhiteout == 0 && ldvp != NULLVP)
			if (VOP_GETATTR(udvp, &va, cnp->cn_cred) == 0 &&
			    (va.va_flags & OPAQUE))
				iswhiteout = 1;	/* don't lookup lower */
#if 0
		UNIONFS_INTERNAL_DEBUG("unionfs_lookup: debug: whiteout=%d, path=%s\n", iswhiteout, cnp->cn_nameptr);
#endif
	}

	/*
	 * lookup lower layer
	 */
	if (ldvp != NULLVP && !(cnflags & DOWHITEOUT) && iswhiteout == 0) {
		/* always op is LOOKUP */
		cnp->cn_nameiop = LOOKUP;
		cnflagsbk = cnp->cn_flags;
		cnp->cn_flags = cnflags;

		lerror = VOP_LOOKUP(ldvp, &lvp, cnp);

		cnp->cn_nameiop = nameiop;
		if (udvp != NULLVP && (uerror == 0 || uerror == EJUSTRETURN))
			cnp->cn_flags = cnflagsbk;

		if (lerror == 0) {
			if (ldvp == lvp) {	/* is dot */
				if (uvp != NULLVP)
					vrele(uvp);	/* no need? */
				vrele(lvp);
				*(ap->a_vpp) = dvp;
				vref(dvp);

				UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n", lerror);
				if (uvp != NULL)
					VOP_UNLOCK(uvp);
				return (lerror);
			}
		}
	}

	/*
	 * check lookup result
	 */
	if (uvp == NULLVP && lvp == NULLVP) {
		UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n",
		    (udvp != NULLVP ? uerror : lerror));
		return (udvp != NULLVP ? uerror : lerror);
	}

	/*
	 * check vnode type
	 */
	if (uvp != NULLVP && lvp != NULLVP && uvp->v_type != lvp->v_type) {
		vput(lvp);
		lvp = NULLVP;
	}

	/*
	 * check shadow dir
	 */
	if (uerror != 0 && uerror != EJUSTRETURN && udvp != NULLVP &&
	    lerror == 0 && lvp != NULLVP && lvp->v_type == VDIR &&
	    !(dvp->v_mount->mnt_flag & MNT_RDONLY) &&
	    (1 < cnp->cn_namelen || '.' != *(cnp->cn_nameptr))) {
		/* get unionfs vnode in order to create a new shadow dir. */
		error = unionfs_nodeget(dvp->v_mount, NULLVP, lvp, dvp, &vp,
		    cnp);
		if (error != 0)
			goto unionfs_lookup_out;
		error = unionfs_mkshadowdir(MOUNTTOUNIONFSMOUNT(dvp->v_mount),
		    udvp, VTOUNIONFS(vp), cnp);
		if (error != 0) {
			UNIONFSDEBUG("unionfs_lookup: Unable to create shadow dir.");
			vput(vp);
			goto unionfs_lookup_out;
		}
	}
	/*
	 * get unionfs vnode.
	 */
	else {
		if (uvp != NULLVP)
			error = uerror;
		else
			error = lerror;
		if (error != 0)
			goto unionfs_lookup_out;
		error = unionfs_nodeget(dvp->v_mount, uvp, lvp, dvp, &vp, cnp);
		if (error != 0) {
			UNIONFSDEBUG("unionfs_lookup: Unable to create unionfs vnode.");
			goto unionfs_lookup_out;
		}
	}

	*(ap->a_vpp) = vp;

	cache_enter(dvp, vp, cnp->cn_nameptr, cnp->cn_namelen,
		    cnp->cn_flags);

	/* XXXAD lock status on error */
unionfs_lookup_out:
	if (uvp != NULLVP)
		vrele(uvp);
	if (lvp != NULLVP)
		vrele(lvp);

	if (error == ENOENT && nameiop != CREATE)
		cache_enter(dvp, NULLVP, cnp->cn_nameptr, cnp->cn_namelen,
			    cnp->cn_flags);

	UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n", error);

	return (error);
}
예제 #5
0
/*
 * Initialize
 */
int 
unionfs_init(struct vfsconf *vfsp)
{
	UNIONFSDEBUG("unionfs_init\n");	/* printed during system boot */
	return (0);
}
예제 #6
0
/*
 * Mount unionfs layer.
 */
static int
unionfs_domount(struct mount *mp)
{
    int		error;
    struct vnode   *lowerrootvp;
    struct vnode   *upperrootvp;
    struct unionfs_mount *ump;
    struct thread *td;
    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;
    unionfs_whitemode whitemode;
    struct componentname fakecn;
    struct nameidata nd, *ndp;
    struct vattr	va;

    UNIONFSDEBUG("unionfs_mount(mp = %p)\n", (void *)mp);

    error = 0;
    below = 0;
    uid = 0;
    gid = 0;
    udir = 0;
    ufile = 0;
    copymode = UNIONFS_TRANSPARENT;	/* default */
    whitemode = UNIONFS_WHITE_ALWAYS;
    ndp = &nd;
    td = curthread;

    if (mp->mnt_flag & MNT_ROOTFS) {
        vfs_mount_error(mp, "Cannot union mount root filesystem");
        return (EOPNOTSUPP);
    }

    /*
     * Update is a no operation.
     */
    if (mp->mnt_flag & MNT_UPDATE) {
        vfs_mount_error(mp, "unionfs does not support mount update");
        return (EOPNOTSUPP);
    }

    /*
     * Get argument
     */
    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);
    }
    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);
        }
        udir &= S_IRWXU | S_IRWXG | S_IRWXO;
    }
    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 &= S_IRWXU | S_IRWXG | S_IRWXO;
    }
    /* 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);
    error = VOP_GETATTR(mp->mnt_vnodecovered, &va, mp->mnt_cred);
    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);
    if (error)
        return (error);

    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 (vfs_getopt(mp->mnt_optnew, "whiteout", (void **)&tmp,
                       NULL) == 0) {
            if (tmp == NULL) {
                vfs_mount_error(mp, "Invalid whiteout mode");
                return (EINVAL);
            } else if (strcasecmp(tmp, "always") == 0)
                whitemode = UNIONFS_WHITE_ALWAYS;
            else if (strcasecmp(tmp, "whenneeded") == 0)
                whitemode = UNIONFS_WHITE_WHENNEEDED;
            else {
                vfs_mount_error(mp, "Invalid whiteout mode");
                return (EINVAL);
            }
        }
    }
    /* 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);

    /*
     * Find upper node
     */
    NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, target, td);
    if ((error = namei(ndp)))
        return (error);

    NDFREE(ndp, NDF_ONLY_PNBUF);

    /* get root vnodes */
    lowerrootvp = mp->mnt_vnodecovered;
    upperrootvp = ndp->ni_vp;

    /* create unionfs_mount */
    ump = (struct unionfs_mount *)malloc(sizeof(struct unionfs_mount),
                                         M_UNIONFSMNT, M_WAITOK | M_ZERO);

    /*
     * Save reference
     */
    if (below) {
        VOP_UNLOCK(upperrootvp, 0);
        vn_lock(lowerrootvp, LK_EXCLUSIVE | LK_RETRY);
        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;
    ump->um_whitemode = whitemode;

    MNT_ILOCK(mp);
    if ((lowerrootvp->v_mount->mnt_kern_flag & MNTK_MPSAFE) &&
            (upperrootvp->v_mount->mnt_kern_flag & MNTK_MPSAFE))
        mp->mnt_kern_flag |= MNTK_MPSAFE;
    MNT_IUNLOCK(mp);
    mp->mnt_data = ump;

    /*
     * 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) {
        memset(&fakecn, 0, sizeof(fakecn));
        fakecn.cn_nameiop = LOOKUP;
        fakecn.cn_thread = td;
        error = VOP_WHITEOUT(ump->um_uppervp, &fakecn, LOOKUP);
        if (error) {
            if (below) {
                VOP_UNLOCK(ump->um_uppervp, 0);
                vrele(upperrootvp);
            } else
                vput(ump->um_uppervp);
            free(ump, M_UNIONFSMNT);
            mp->mnt_data = NULL;
            return (error);
        }
    }

    /*
     * Unlock the node
     */
    VOP_UNLOCK(ump->um_uppervp, 0);

    /*
     * Get the unionfs root vnode.
     */
    error = unionfs_nodeget(mp, ump->um_uppervp, ump->um_lowervp,
                            NULLVP, &(ump->um_rootvp), NULL, td);
    vrele(upperrootvp);
    if (error) {
        free(ump, M_UNIONFSMNT);
        mp->mnt_data = NULL;
        return (error);
    }

    /*
     * Check mnt_flag
     */
    if ((ump->um_lowervp->v_mount->mnt_flag & MNT_LOCAL) &&
            (ump->um_uppervp->v_mount->mnt_flag & MNT_LOCAL))
        mp->mnt_flag |= MNT_LOCAL;

    /*
     * Get new fsid
     */
    vfs_getnewfsid(mp);

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

    UNIONFSDEBUG("unionfs_mount: from %s, on %s\n",
                 mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);

    return (0);
}