#include <sys/vfsops.h>
#include <sys/mount.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/param.h>
#include <sys/module.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/sysctl.h>
#include <sys/queue.h>
#include <sys/spinlock2.h>
#include <sys/ktr.h>
#include <string.h>
#include "dirfs.h"
MALLOC_DEFINE(M_DIRFS, "dirfs", "dirfs mount allocation");
MALLOC_DEFINE(M_DIRFS_NODE, "dirfs nodes", "dirfs nodes memory allocation");
MALLOC_DEFINE(M_DIRFS_MISC, "dirfs misc", "dirfs miscellaneous allocation");
KTR_INFO_MASTER(dirfs);
KTR_INFO(KTR_DIRFS, dirfs, root, 20,
"DIRFS(root dnp=%p vnode=%p hostdir=%s fd=%d error=%d)",
dirfs_node_t dnp, struct vnode *vp, char *hostdir, int fd, int error);
KTR_INFO(KTR_DIRFS, dirfs, mount, 21,
"DIRFS(mount path=%s dmp=%p mp=%p error=%d)",
char *path, dirfs_mount_t dmp, struct mount *mp, int error);
KTR_INFO(KTR_DIRFS, dirfs, unmount, 22,
"DIRFS(unmount dmp=%p mp=%p error=%d)",
dirfs_mount_t dmp, struct mount *mp, int error);
int debuglvl = 0;
int dirfs_fd_limit = 100;
int dirfs_fd_used = 0;
long passive_fd_list_miss = 0;
long passive_fd_list_hits = 0;
SYSCTL_NODE(_vfs, OID_AUTO, dirfs, CTLFLAG_RW, 0,
"dirfs filesystem for vkernels");
SYSCTL_INT(_vfs_dirfs, OID_AUTO, debug, CTLFLAG_RW,
&debuglvl, 0, "dirfs debug level");
SYSCTL_INT(_vfs_dirfs, OID_AUTO, fd_limit, CTLFLAG_RW,
&dirfs_fd_limit, 0, "Maximum number of passive nodes to cache");
SYSCTL_INT(_vfs_dirfs, OID_AUTO, fd_used, CTLFLAG_RD,
&dirfs_fd_used, 0, "Current number of passive nodes cached");
SYSCTL_LONG(_vfs_dirfs, OID_AUTO, passive_fd_list_miss, CTLFLAG_RD,
&passive_fd_list_miss, 0, "Passive fd list cache misses");
SYSCTL_LONG(_vfs_dirfs, OID_AUTO, passive_fd_list_hits, CTLFLAG_RD,
&passive_fd_list_hits, 0, "Passive fd list cache misses");
static int dirfs_statfs(struct mount *, struct statfs *, struct ucred *);
static int
dirfs_mount(struct mount *mp, char *path, caddr_t data, struct ucred *cred)
{
dirfs_mount_t dmp;
struct stat st;
size_t done, nlen;
int error;
dbg(1, "called\n");
if (mp->mnt_flag & MNT_UPDATE) {
dmp = VFS_TO_DIRFS(mp);
if (dmp->dm_rdonly == 0 && (mp->mnt_flag & MNT_RDONLY)) {
dmp->dm_rdonly = 1;
debug(2, "dirfs read-write -> read-only\n");
}
if (dmp->dm_rdonly && (mp->mnt_kern_flag & MNTK_WANTRDWR)) {
debug(2, "dirfs read-only -> read-write\n");
dmp->dm_rdonly = 0;
}
return 0;
}
dmp = kmalloc(sizeof(*dmp), M_DIRFS, M_WAITOK | M_ZERO);
mp->mnt_data = (qaddr_t)dmp;
dmp->dm_mount = mp;
error = copyinstr(data, &dmp->dm_path, MAXPATHLEN, &done);
if (error) {
error = copystr(data, &dmp->dm_path, MAXPATHLEN, &done);
if (error) {
kfree(dmp, M_DIRFS);
goto failure;
}
}
nlen = strnlen(dmp->dm_path, MAXPATHLEN);
if (dmp->dm_path[nlen-1] == '/')
dmp->dm_path[nlen-1] = 0;
if ((stat(dmp->dm_path, &st)) == 0) {
if (!S_ISDIR(st.st_mode)) {
kfree(dmp, M_DIRFS);
error = EINVAL;
goto failure;
}
} else {
error = errno;
goto failure;
}
lockinit(&dmp->dm_lock, "dfsmnt", 0, LK_CANRECURSE);
vfs_add_vnodeops(mp, &dirfs_vnode_vops, &mp->mnt_vn_norm_ops);
vfs_getnewfsid(mp);
dmp->dm_uid = getuid();
dmp->dm_gid = getgid();
TAILQ_INIT(&dmp->dm_fdlist);
RB_INIT(&dmp->dm_inotree);
kmalloc_raise_limit(M_DIRFS_NODE, 0);
dirfs_statfs(mp, &mp->mnt_stat, cred);
failure:
KTR_LOG(dirfs_mount, (dmp->dm_path) ? dmp->dm_path : "NULL",
dmp, mp, error);
return error;
}
static int
dirfs_unmount(struct mount *mp, int mntflags)
{
dirfs_mount_t dmp;
dirfs_node_t dnp;
int cnt;
int error;
dbg(1, "called\n");
cnt = 0;
dmp = VFS_TO_DIRFS(mp);
error = vflush(mp, 0, 0);
if (error)
goto failure;
while ((dnp = TAILQ_FIRST(&dmp->dm_fdlist)) != NULL) {
dirfs_node_setpassive(dmp, dnp, 0);
}
dnp = dmp->dm_root;
if (dnp != NULL) {
dirfs_close_helper(dnp);
debug_node2(dnp);
dirfs_node_drop(dmp, dnp);
}
kfree(dmp, M_DIRFS);
mp->mnt_data = (qaddr_t) 0;
failure:
KTR_LOG(dirfs_unmount, dmp, mp, error);
return error;
}
static int
dirfs_root(struct mount *mp, struct vnode **vpp)
{
dirfs_mount_t dmp;
dirfs_node_t dnp;
int fd;
int error;
dbg(1, "called\n");
dmp = VFS_TO_DIRFS(mp);
KKASSERT(dmp != NULL);
if (dmp->dm_root == NULL) {
dnp = dirfs_node_alloc(mp);
error = dirfs_node_stat(DIRFS_NOFD, dmp->dm_path, dnp);
if (error != 0) {
dirfs_node_free(dmp, dnp);
return error;
}
dirfs_node_ref(dnp);
dnp->dn_parent = NULL;
dmp->dm_root = dnp;
dirfs_node_setflags(dnp, DIRFS_ROOT);
fd = open(dmp->dm_path, O_DIRECTORY);
if (fd == -1) {
dbg(9, "failed to open ROOT node\n");
dirfs_free_vp(dmp, dnp);
dirfs_node_free(dmp, dnp);
return errno;
}
dnp->dn_fd = fd;
dnp->dn_type = VDIR;
} else {
dnp = dmp->dm_root;
}
dirfs_alloc_vp(mp, vpp, LK_CANRECURSE, dnp);
KTR_LOG(dirfs_root, dnp, *vpp, dmp->dm_path, dnp->dn_fd, error);
return 0;
}
static int
dirfs_fhtovp(struct mount *mp, struct vnode *rootvp, struct fid *fhp, struct vnode **vpp)
{
dbg(1, "called\n");
return EOPNOTSUPP;
}
static int
dirfs_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred)
{
dirfs_mount_t dmp = VFS_TO_DIRFS(mp);
struct statfs st;
dbg(1, "called\n");
if((statfs(dmp->dm_path, &st)) == -1)
return errno;
ksnprintf(st.f_mntfromname, MNAMELEN - 1, "dirfs@%s", dmp->dm_path);
bcopy(&st, sbp, sizeof(st));
strlcpy(sbp->f_fstypename, mp->mnt_vfc->vfc_name, MFSNAMELEN);
dbg(5, "iosize = %zd\n", sbp->f_iosize);
return 0;
}
static int
dirfs_statvfs(struct mount *mp, struct statvfs *sbp, struct ucred *cred)
{
dirfs_mount_t dmp = VFS_TO_DIRFS(mp);
struct statvfs st;
dbg(1, "called\n");
if ((statvfs(dmp->dm_path, &st)) == -1)
return errno;
bcopy(&st, sbp, sizeof(st));
return 0;
}
static int
dirfs_vptofh(struct vnode *vp, struct fid *fhp)
{
dirfs_node_t dnp;
dnp = VP_TO_NODE(vp);
debug_node2(dnp);
dbg(1, "called\n");
return EOPNOTSUPP;
}
static int
dirfs_checkexp(struct mount *mp, struct sockaddr *nam, int *exflagsp,
struct ucred **credanonp)
{
dbg(1, "called\n");
return EOPNOTSUPP;
}
static struct vfsops dirfs_vfsops = {
.vfs_flags = 0,
.vfs_mount = dirfs_mount,
.vfs_unmount = dirfs_unmount,
.vfs_root = dirfs_root,
.vfs_vget = vfs_stdvget,
.vfs_statfs = dirfs_statfs,
.vfs_statvfs = dirfs_statvfs,
.vfs_fhtovp = dirfs_fhtovp,
.vfs_vptofh = dirfs_vptofh,
.vfs_checkexp = dirfs_checkexp
};
VFS_SET(dirfs_vfsops, dirfs, 0);
MODULE_VERSION(dirfs, 1);