#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/malloc.h>
#include <sys/filedesc.h>
#include <sys/queue.h>
#include <miscfs/union/union.h>
int
union_mount(mp, path, data, ndp, p)
struct mount *mp;
char *path;
caddr_t data;
struct nameidata *ndp;
struct proc *p;
{
int error = 0;
struct union_args args;
struct vnode *lowerrootvp = NULLVP;
struct vnode *upperrootvp = NULLVP;
struct union_mount *um = 0;
struct ucred *cred = 0;
struct ucred *scred;
struct vattr va;
char *cp;
int len;
u_int size;
#ifdef UNION_DIAGNOSTIC
printf("union_mount(mp = %x)\n", mp);
#endif
if (mp->mnt_flag & MNT_UPDATE) {
error = EOPNOTSUPP;
goto bad;
}
if (error = copyin(data, (caddr_t)&args, sizeof(struct union_args)))
goto bad;
lowerrootvp = mp->mnt_vnodecovered;
VREF(lowerrootvp);
NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT,
UIO_USERSPACE, args.target, p);
if (error = namei(ndp))
goto bad;
upperrootvp = ndp->ni_vp;
vrele(ndp->ni_dvp);
ndp->ni_dvp = NULL;
if (upperrootvp->v_type != VDIR) {
error = EINVAL;
goto bad;
}
MALLOC(um, struct union_mount *, sizeof(struct union_mount),
M_UFSMNT, M_WAITOK);
um->um_op = args.mntflags & UNMNT_OPMASK;
switch (um->um_op) {
case UNMNT_ABOVE:
um->um_lowervp = lowerrootvp;
um->um_uppervp = upperrootvp;
break;
case UNMNT_BELOW:
um->um_lowervp = upperrootvp;
um->um_uppervp = lowerrootvp;
break;
case UNMNT_REPLACE:
vrele(lowerrootvp);
lowerrootvp = NULLVP;
um->um_uppervp = upperrootvp;
um->um_lowervp = lowerrootvp;
break;
default:
error = EINVAL;
goto bad;
}
if ((mp->mnt_flag & MNT_RDONLY) == 0) {
error = VOP_WHITEOUT(um->um_uppervp, (struct componentname *) 0, LOOKUP);
if (error)
goto bad;
}
um->um_cred = p->p_ucred;
crhold(um->um_cred);
um->um_cmode = UN_DIRMODE &~ p->p_fd->fd_cmask;
if (um->um_op == UNMNT_ABOVE) {
if (((um->um_lowervp == NULLVP) ||
(um->um_lowervp->v_mount->mnt_flag & MNT_LOCAL)) &&
(um->um_uppervp->v_mount->mnt_flag & MNT_LOCAL))
mp->mnt_flag |= MNT_LOCAL;
}
mp->mnt_flag |= (um->um_uppervp->v_mount->mnt_flag & MNT_RDONLY);
mp->mnt_data = (qaddr_t) um;
vfs_getnewfsid(mp);
(void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
switch (um->um_op) {
case UNMNT_ABOVE:
cp = "<above>:";
break;
case UNMNT_BELOW:
cp = "<below>:";
break;
case UNMNT_REPLACE:
cp = "";
break;
}
len = strlen(cp);
bcopy(cp, mp->mnt_stat.f_mntfromname, len);
cp = mp->mnt_stat.f_mntfromname + len;
len = MNAMELEN - len;
(void) copyinstr(args.target, cp, len - 1, &size);
bzero(cp + size, len - size);
#ifdef UNION_DIAGNOSTIC
printf("union_mount: from %s, on %s\n",
mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
#endif
return (0);
bad:
if (um)
_FREE(um, M_UFSMNT);
if (cred != NOCRED)
crfree(cred);
if (upperrootvp)
vrele(upperrootvp);
if (lowerrootvp)
vrele(lowerrootvp);
return (error);
}
int
union_start(mp, flags, p)
struct mount *mp;
int flags;
struct proc *p;
{
return (0);
}
int
union_unmount(mp, mntflags, p)
struct mount *mp;
int mntflags;
struct proc *p;
{
struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
struct vnode *um_rootvp;
int error;
int freeing;
int flags = 0;
struct ucred *cred;
#ifdef UNION_DIAGNOSTIC
printf("union_unmount(mp = %x)\n", mp);
#endif
if (mntflags & MNT_FORCE)
flags |= FORCECLOSE;
if (error = union_root(mp, &um_rootvp))
return (error);
for (freeing = 0; vflush(mp, um_rootvp, flags) != 0;) {
struct vnode *vp;
int n;
for (n = 0, vp = mp->mnt_vnodelist.lh_first;
vp != NULLVP;
vp = vp->v_mntvnodes.le_next)
n++;
if (n == freeing)
break;
freeing = n;
}
if (um_rootvp->v_usecount > 1) {
vput(um_rootvp);
return (EBUSY);
}
#ifdef UNION_DIAGNOSTIC
vprint("union root", um_rootvp);
#endif
if (um->um_lowervp)
vrele(um->um_lowervp);
vrele(um->um_uppervp);
cred = um->um_cred;
if (cred != NOCRED) {
um->um_cred = NOCRED;
crfree(cred);
}
vput(um_rootvp);
vgone(um_rootvp);
_FREE(mp->mnt_data, M_UFSMNT);
mp->mnt_data = 0;
return (0);
}
int
union_root(mp, vpp)
struct mount *mp;
struct vnode **vpp;
{
struct proc *p = current_proc();
struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
int error;
int loselock;
VREF(um->um_uppervp);
if ((um->um_op == UNMNT_BELOW) &&
VOP_ISLOCKED(um->um_uppervp)) {
loselock = 1;
} else {
vn_lock(um->um_uppervp, LK_EXCLUSIVE | LK_RETRY, p);
loselock = 0;
}
if (um->um_lowervp)
VREF(um->um_lowervp);
error = union_allocvp(vpp, mp,
(struct vnode *) 0,
(struct vnode *) 0,
(struct componentname *) 0,
um->um_uppervp,
um->um_lowervp,
1);
if (error) {
if (loselock)
vrele(um->um_uppervp);
else
vput(um->um_uppervp);
if (um->um_lowervp)
vrele(um->um_lowervp);
} else {
if (loselock)
VTOUNION(*vpp)->un_flags &= ~UN_ULOCK;
}
return (error);
}
int
union_statfs(mp, sbp, p)
struct mount *mp;
struct statfs *sbp;
struct proc *p;
{
int error;
struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
struct statfs mstat;
int lbsize;
#ifdef UNION_DIAGNOSTIC
printf("union_statfs(mp = %x, lvp = %x, uvp = %x)\n", mp,
um->um_lowervp,
um->um_uppervp);
#endif
bzero(&mstat, sizeof(mstat));
if (um->um_lowervp) {
error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, p);
if (error)
return (error);
}
#if 0
sbp->f_type = mstat.f_type;
sbp->f_flags = mstat.f_flags;
sbp->f_bsize = mstat.f_bsize;
sbp->f_iosize = mstat.f_iosize;
#endif
lbsize = mstat.f_bsize;
sbp->f_blocks = mstat.f_blocks;
sbp->f_bfree = mstat.f_bfree;
sbp->f_bavail = mstat.f_bavail;
sbp->f_files = mstat.f_files;
sbp->f_ffree = mstat.f_ffree;
error = VFS_STATFS(um->um_uppervp->v_mount, &mstat, p);
if (error)
return (error);
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 = 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;
if (sbp != &mp->mnt_stat) {
sbp->f_type = mp->mnt_vfc->vfc_typenum;
bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
}
return (0);
}
#define union_sync ((int (*) __P((struct mount *, int, struct ucred *, \
struct proc *)))nullop)
#define union_fhtovp ((int (*) __P((struct mount *, struct fid *, \
struct mbuf *, struct vnode **, int *, struct ucred **)))eopnotsupp)
int union_init __P((struct vfsconf *));
#define union_quotactl ((int (*) __P((struct mount *, int, uid_t, caddr_t, \
struct proc *)))eopnotsupp)
#define union_sysctl ((int (*) __P((int *, u_int, void *, size_t *, void *, \
size_t, struct proc *)))eopnotsupp)
#define union_vget ((int (*) __P((struct mount *, ino_t, struct vnode **))) \
eopnotsupp)
#define union_vptofh ((int (*) __P((struct vnode *, struct fid *)))eopnotsupp)
struct vfsops union_vfsops = {
union_mount,
union_start,
union_unmount,
union_root,
union_quotactl,
union_statfs,
union_sync,
union_vget,
union_fhtovp,
union_vptofh,
union_init,
union_sysctl,
};