#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/malloc.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/namecache.h>
#include <sys/nlookup.h>
#include <sys/mountctl.h>
#include <sys/sysctl.h>
#include "null.h"
extern struct vop_ops null_vnode_vops;
static MALLOC_DEFINE(M_NULLFSMNT, "NULLFS mount", "NULLFS mount structure");
SYSCTL_NODE(_vfs, OID_AUTO, nullfs, CTLFLAG_RD, 0, "NULLFS filesystem");
static int nullfs_debug;
TUNABLE_INT("vfs.nullfs.debug", &nullfs_debug);
SYSCTL_INT(_vfs_nullfs, OID_AUTO, debug, CTLFLAG_RW,
&nullfs_debug, 0, "nullfs debugging");
static int nullfs_root(struct mount *mp, struct vnode **vpp);
static int nullfs_statfs(struct mount *mp, struct statfs *sbp,
struct ucred *cred);
static int
nullfs_mount(struct mount *mp, char *path, caddr_t data, struct ucred *cred)
{
int error = 0;
struct null_args args;
struct vnode *rootvp;
struct null_mount *xmp;
size_t size;
struct nlookupdata nd;
fhandle_t fh;
NULLFSDEBUG("nullfs_mount(mp = %p)\n", (void *)mp);
error = copyin(data, (caddr_t)&args, sizeof(struct null_args));
if (error)
return (error);
if (mp->mnt_flag & MNT_UPDATE) {
xmp = MOUNTTONULLMOUNT(mp);
error = vfs_export(mp, &xmp->export, &args.export);
return (error);
}
rootvp = NULL;
error = nlookup_init(&nd, args.target, UIO_USERSPACE, NLC_FOLLOW);
if (error)
goto fail1;
error = nlookup(&nd);
if (error)
goto fail2;
error = cache_vget(&nd.nl_nch, nd.nl_cred, LK_EXCLUSIVE, &rootvp);
if (error)
goto fail2;
xmp = (struct null_mount *) kmalloc(sizeof(struct null_mount),
M_NULLFSMNT, M_WAITOK | M_ZERO);
xmp->nullm_vfs = nd.nl_nch.mount;
if (xmp->nullm_vfs != rootvp->v_mount) {
if (xmp->nullm_vfs->mnt_flag & MNT_RDONLY)
mp->mnt_flag |= MNT_RDONLY;
if (xmp->nullm_vfs->mnt_flag & MNT_NOEXEC)
mp->mnt_flag |= MNT_NOEXEC;
xmp->nullm_vfs = rootvp->v_mount;
}
mp->mnt_ncmountpt = nd.nl_nch;
cache_changemount(&mp->mnt_ncmountpt, mp);
mp->mnt_ncmountpt.ncp->nc_flag |= NCF_ISMOUNTPT;
cache_unlock(&mp->mnt_ncmountpt);
cache_zero(&nd.nl_nch);
nlookup_done(&nd);
vfs_add_vnodeops(mp, &null_vnode_vops, &mp->mnt_vn_norm_ops);
vn_unlock(rootvp);
xmp->nullm_rootvp = rootvp;
if (xmp->nullm_vfs->mnt_flag & MNT_LOCAL)
mp->mnt_flag |= MNT_LOCAL;
mp->mnt_data = (qaddr_t) xmp;
copyinstr(args.target, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size);
bzero(&fh, sizeof(fh));
fh.fh_fsid = rootvp->v_mount->mnt_stat.f_fsid;
if (VFS_VPTOFH(rootvp, &fh.fh_fid) == 0) {
if (nullfs_debug)
kprintf("(A)");
vfs_setfsid(mp, &fh.fh_fsid);
} else {
if (nullfs_debug)
kprintf("(B)");
vfs_getnewfsid(mp);
}
fh.fh_fsid.val[1] ^= crc32(mp->mnt_stat.f_mntfromname, size);
if (nullfs_debug) {
kprintf("NULLFS FSID %08x.%08x -> %08x.%08x (%*.*s)\n",
rootvp->v_mount->mnt_stat.f_fsid.val[0],
rootvp->v_mount->mnt_stat.f_fsid.val[1],
fh.fh_fsid.val[0],
fh.fh_fsid.val[1],
(int)size, (int)size, mp->mnt_stat.f_mntfromname);
}
bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
nullfs_statfs(mp, &mp->mnt_stat, cred);
NULLFSDEBUG("nullfs_mount: lower %s, alias at %s\n",
mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntfromname);
bzero(mp->mnt_stat.f_mntonname, MNAMELEN);
if (path != NULL) {
(void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1,
&size);
}
mp->mnt_kern_flag |= MNTK_NCALIASED | MNTK_ALL_MPSAFE;
return (0);
fail2:
nlookup_done(&nd);
fail1:
return (error);
}
static int
nullfs_unmount(struct mount *mp, int mntflags)
{
struct null_mount *xmp;
NULLFSDEBUG("nullfs_unmount: mp = %p\n", (void *)mp);
xmp = (void *)mp->mnt_data;
mp->mnt_data = 0;
if (xmp->nullm_rootvp) {
vrele(xmp->nullm_rootvp);
xmp->nullm_rootvp = NULL;
}
kfree(xmp, M_NULLFSMNT);
return 0;
}
static int
nullfs_root(struct mount *mp, struct vnode **vpp)
{
struct vnode *vp;
int error;
NULLFSDEBUG("nullfs_root(mp = %p, vp = %p)\n", (void *)mp,
(void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp);
vp = MOUNTTONULLMOUNT(mp)->nullm_rootvp;
error = vget(vp, LK_EXCLUSIVE | LK_RETRY);
if (error == 0)
*vpp = vp;
return (error);
}
static int
nullfs_quotactl(struct mount *mp, int cmd, uid_t uid, caddr_t arg,
struct ucred *cred)
{
return VFS_QUOTACTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, uid, arg, cred);
}
static int
nullfs_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred)
{
int error;
struct statfs mstat;
NULLFSDEBUG("nullfs_statfs(mp = %p, vp = %p)\n", (void *)mp,
(void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp);
bzero(&mstat, sizeof(mstat));
error = VFS_STATFS(MOUNTTONULLMOUNT(mp)->nullm_vfs, &mstat, cred);
if (error)
return (error);
sbp->f_type = mstat.f_type;
sbp->f_flags = mstat.f_flags;
sbp->f_bsize = mstat.f_bsize;
sbp->f_iosize = mstat.f_iosize;
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) {
bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
}
return (0);
}
static int
nullfs_checkexp(struct mount *mp, struct sockaddr *nam, int *extflagsp,
struct ucred **credanonp)
{
struct null_mount *xmp = (void *)mp->mnt_data;
struct netcred *np;
int error;
np = vfs_export_lookup(mp, &xmp->export, nam);
if (np) {
*extflagsp = np->netc_exflags;
*credanonp = &np->netc_anon;
error = 0;
} else {
error = EACCES;
}
return(error);
#if 0
return VFS_CHECKEXP(MOUNTTONULLMOUNT(mp)->nullm_vfs, nam,
extflagsp, credanonp);
#endif
}
int
nullfs_export(struct mount *mp, int op, const struct export_args *export)
{
struct null_mount *xmp = (void *)mp->mnt_data;
int error;
switch(op) {
case MOUNTCTL_SET_EXPORT:
error = vfs_export(mp, &xmp->export, export);
break;
default:
error = EOPNOTSUPP;
break;
}
return(error);
}
static int
nullfs_vptofh(struct vnode *vp, struct fid *fhp)
{
return VFS_VPTOFH(vp, fhp);
}
static int
nullfs_fhtovp(struct mount *mp, struct vnode *rootvp,
struct fid *fhp, struct vnode **vpp)
{
struct null_mount *xmp = MOUNTTONULLMOUNT(mp);
return VFS_FHTOVP(xmp->nullm_vfs, xmp->nullm_rootvp, fhp, vpp);
}
static int
nullfs_extattrctl(struct mount *mp, int cmd, struct vnode *vp,
int attrnamespace, const char *attrname, struct ucred *cred)
{
return VFS_EXTATTRCTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd,
vp, attrnamespace, attrname, cred);
}
static void
nullfs_ncpgen_set(struct mount *mp, struct namecache *ncp)
{
struct null_mount *xmp = MOUNTTONULLMOUNT(mp);
VFS_NCPGEN_SET(xmp->nullm_vfs, ncp);
}
static int
nullfs_ncpgen_test(struct mount *mp, struct namecache *ncp)
{
struct null_mount *xmp = MOUNTTONULLMOUNT(mp);
return VFS_NCPGEN_TEST(xmp->nullm_vfs, ncp);
}
static int
nullfs_modifying(struct mount *mp)
{
struct null_mount *xmp = MOUNTTONULLMOUNT(mp);
int error;
if (mp->mnt_flag & MNT_RDONLY)
error = EROFS;
else if (xmp->nullm_vfs)
error = VFS_MODIFYING(xmp->nullm_vfs);
else
error = 0;
return error;
}
static struct vfsops null_vfsops = {
.vfs_flags = VFSOPSF_NOSYNCERTHR,
.vfs_mount = nullfs_mount,
.vfs_unmount = nullfs_unmount,
.vfs_root = nullfs_root,
.vfs_quotactl = nullfs_quotactl,
.vfs_statfs = nullfs_statfs,
.vfs_extattrctl = nullfs_extattrctl,
.vfs_fhtovp = nullfs_fhtovp,
.vfs_vptofh = nullfs_vptofh,
.vfs_ncpgen_set = nullfs_ncpgen_set,
.vfs_ncpgen_test = nullfs_ncpgen_test,
.vfs_checkexp = nullfs_checkexp,
.vfs_modifying = nullfs_modifying
};
VFS_SET(null_vfsops, null, VFCF_LOOPBACK | VFCF_MPSAFE);
MODULE_VERSION(null, 1);