#include <sys/param.h>
#include <sys/systm.h>
#include <sys/namei.h>
#include <sys/filedesc.h>
#include <sys/proc.h>
#include <sys/kernel.h>
#include <mach/machine/vm_types.h>
#include <sys/vnode.h>
#include <sys/socket.h>
#include <sys/mount.h>
#include <sys/buf.h>
#include <sys/mbuf.h>
#include <sys/file.h>
#include <sys/disk.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/attr.h>
#include <miscfs/specfs/specdev.h>
#include "synthfs.h"
#define LOADABLE_FS 0
typedef int (*PFI)();
struct vfsops synthfs_vfsops = {
synthfs_mount,
synthfs_start,
synthfs_unmount,
synthfs_root,
synthfs_quotactl,
synthfs_statfs,
synthfs_sync,
synthfs_vget,
synthfs_fhtovp,
synthfs_vptofh,
synthfs_init,
synthfs_sysctl
};
#define ROOTMPMODE 0755
#define ROOTPLACEHOLDERMODE 0700
static char synthfs_fs_name[MFSNAMELEN] = "synthfs";
static char synthfs_fake_mntfromname[] = "<synthfs>";
extern struct vnodeopv_desc synthfs_vnodeop_opv_desc;
extern int maxvfsslots;
extern int maxvfsconf;
extern int vfs_opv_numops;
int vn_mkdir(struct proc *p, char *path, int mode);
int vn_symlink(struct proc *p, char *path, char *link);
#if LOADABLE_FS
void
synthfs_load(int loadArgument) {
struct vfsconf *newvfsconf = NULL;
int j;
int (***opv_desc_vector_p)() = NULL;
int (**opv_desc_vector)();
struct vnodeopv_entry_desc *opve_descp;
int error = 0;
#pragma unused(loadArgument)
DBG_VOP(("load_synthfs: starting ...\n"));
MALLOC(newvfsconf, void *, sizeof(struct vfsconf), M_SYNTHFS, M_WAITOK);
DBG_VOP(("load_synthfs: Allocated new vfsconf list entry, newvfsconf = 0x%08lx.\n", (unsigned long)newvfsconf));
bzero(newvfsconf, sizeof(struct vfsconf));
if (newvfsconf) {
DBG_VOP(("load_synthfs: filling in newly allocated vfsconf entry at 0x%08lX.\n", (long)newvfsconf));
newvfsconf->vfc_vfsops = &synthfs_vfsops;
strncpy(&newvfsconf->vfc_name[0], synthfs_fs_name, MFSNAMELEN);
newvfsconf->vfc_typenum = maxvfsconf++;
newvfsconf->vfc_refcount = 0;
newvfsconf->vfc_flags = 0;
newvfsconf->vfc_mountroot = NULL;
newvfsconf->vfc_next = NULL;
opv_desc_vector_p = synthfs_vnodeop_opv_desc.opv_desc_vector_p;
DBG_VOP(("load_synthfs: Allocating and initializing VNode ops vector...\n"));
MALLOC(*opv_desc_vector_p, PFI *, vfs_opv_numops*sizeof(PFI), M_SYNTHFS, M_WAITOK);
bzero (*opv_desc_vector_p, vfs_opv_numops*sizeof(PFI));
opv_desc_vector = *opv_desc_vector_p;
for (j=0; synthfs_vnodeop_opv_desc.opv_desc_ops[j].opve_op; j++) {
opve_descp = &(synthfs_vnodeop_opv_desc.opv_desc_ops[j]);
if (opve_descp->opve_op->vdesc_offset == 0 &&
opve_descp->opve_op->vdesc_offset != VOFFSET(vop_default)) {
DBG_VOP(("load_synthfs: operation %s not listed in %s.\n",
opve_descp->opve_op->vdesc_name,
"vfs_op_descs"));
panic ("load_synthfs: bad operation");
}
opv_desc_vector[opve_descp->opve_op->vdesc_offset] =
opve_descp->opve_impl;
}
opv_desc_vector_p = synthfs_vnodeop_opv_desc.opv_desc_vector_p;
opv_desc_vector = *opv_desc_vector_p;
if (opv_desc_vector[VOFFSET(vop_default)]==NULL) {
panic("load_vp;fs: operation vector without default routine.");
}
for (j = 0;j<vfs_opv_numops; j++)
if (opv_desc_vector[j] == NULL)
opv_desc_vector[j] =
opv_desc_vector[VOFFSET(vop_default)];
if (error = vfsconf_add(newvfsconf)) {
goto ErrExit;
};
goto InitFS;
ErrExit: ;
if (opv_desc_vector_p && *opv_desc_vector_p) FREE(*opv_desc_vector_p, M_SYNTHFS);
if (newvfsconf) FREE (newvfsconf, M_SYNTHFS);
goto StdExit;
InitFS: ;
DBG_VOP(("load_synthfs: calling synthfs_init()...\n"));
synthfs_init(newvfsconf);
};
StdExit: ;
}
int synthfs_unload(void) {
DBG_VOP(("synthfs: Entering synthfs_unload...\n"));
return 0;
}
#endif
int
synthfs_mount_fs(struct mount *mp, char *path, caddr_t data, struct nameidata *ndp, struct proc *p)
{
struct synthfs_mntdata *priv_mnt_data;
int error;
size_t size;
DBG_VOP(("synthfs_mount_fs called.\n"));
MALLOC(priv_mnt_data, struct synthfs_mntdata *, sizeof(struct synthfs_mntdata), M_SYNTHFS, M_WAITOK);
DBG_VOP(("MALLOC succeeded...\n"));
strncpy(mp->mnt_stat.f_fstypename, synthfs_fs_name, sizeof(mp->mnt_stat.f_fstypename));
(void) copyinstr(path, mp->mnt_stat.f_mntonname, sizeof(mp->mnt_stat.f_mntonname) - 1, &size);
strncpy(mp->mnt_stat.f_mntfromname, synthfs_fake_mntfromname, sizeof(mp->mnt_stat.f_mntfromname));
priv_mnt_data->synthfs_mounteddev = (dev_t)0;
priv_mnt_data->synthfs_nextid = FIRST_SYNTHFS_ID;
priv_mnt_data->synthfs_filecount = 0;
priv_mnt_data->synthfs_dircount = 0;
priv_mnt_data->synthfs_encodingsused = 0x00000001;
error = synthfs_new_directory(mp, NULL, "", ROOT_DIRID, (S_IRWXU|S_IRWXG|S_IROTH|S_IXOTH), p, &priv_mnt_data->synthfs_rootvp);
if (error) {
DBG_VOP(("Attempt to create root directory failed with error %d.\n", error));
return error;
};
priv_mnt_data->synthfs_rootvp->v_flag |= VROOT;
priv_mnt_data->synthfs_mp = mp;
mp->mnt_data = (void *)priv_mnt_data;
vput(priv_mnt_data->synthfs_rootvp);
return (0);
}
int
synthfs_mount(mp, path, data, ndp, p)
register struct mount *mp;
char *path;
caddr_t data;
struct nameidata *ndp;
struct proc *p;
{
size_t size;
(void) copyinstr(path, mp->mnt_stat.f_mntonname, sizeof(mp->mnt_stat.f_mntonname) - 1, &size);
return (synthfs_mount_fs(mp, path, data, ndp, p));
}
int
synthfs_init(vfsp)
struct vfsconf *vfsp;
{
DBG_VOP(("synthfs_init called.\n"));
return 0;
}
int
synthfs_start(mp, flags, p)
struct mount * mp;
int flags;
struct proc * p;
{
DBG_VOP(("synthfs_start called.\n"));
return 0;
}
int
synthfs_root(mp, vpp)
struct mount *mp;
struct vnode **vpp;
{
unsigned long root_nodeid = ROOT_DIRID;
DBG_VOP(("synthfs_root called.\n"));
*vpp = VFSTOSFS(mp)->synthfs_rootvp;
return vget(VFSTOSFS(mp)->synthfs_rootvp, LK_EXCLUSIVE | LK_RETRY, current_proc());
}
int
synthfs_quotactl(mp, cmds, uid, arg, p)
struct mount *mp;
int cmds;
uid_t uid;
caddr_t arg;
struct proc * p;
{
DBG_VOP(("synthfs_quotactl called.\n"));
return (0);
}
int
synthfs_unmount(mp, mntflags, p)
struct mount *mp;
int mntflags;
struct proc *p;
{
struct synthfs_mntdata *synth;
struct vnode *root_vp;
int retval;
DBG_VOP(("synthfs_unmount called.\n"));
synth = (struct synthfs_mntdata *)mp->mnt_data;
root_vp = synth->synthfs_rootvp;
retval = vflush(mp, root_vp, (mntflags & MNT_FORCE) ? FORCECLOSE : 0);
if (retval && ((mntflags & MNT_FORCE) == 0)) goto Err_Exit;
if (root_vp) {
retval = vn_lock(root_vp, LK_EXCLUSIVE | LK_RETRY, p);
if ((mntflags & MNT_FORCE) == 0) {
if (retval) goto Err_Exit;
if (root_vp->v_usecount > 1) {
DBG_VOP(("synthfs ERROR: root vnode = %x, usecount = %d\n", (int)root_vp, synth->synthfs_rootvp->v_usecount));
VOP_UNLOCK(root_vp, 0, p);
retval = EBUSY;
goto Err_Exit;
};
};
synth->synthfs_rootvp = NULL;
if (retval == 0) {
vput(root_vp);
vgone(root_vp);
};
};
mp->mnt_data = NULL;
FREE(synth, M_SYNTHFS);
Err_Exit:
if (mntflags & MNT_FORCE) retval = 0;
return(retval);
}
int
synthfs_statfs(mp, sbp, p)
struct mount *mp;
register struct statfs *sbp;
struct proc *p;
{
DBG_VOP(("synthfs_statfs called.\n"));
sbp->f_bsize = 512;
sbp->f_iosize = 512;
sbp->f_blocks = 1024; sbp->f_bfree = 0; sbp->f_bavail = 0;
sbp->f_files = VFSTOSFS(mp)->synthfs_filecount + VFSTOSFS(mp)->synthfs_dircount;
sbp->f_ffree = 0;
strncpy(sbp->f_mntonname, mp->mnt_stat.f_mntonname, sizeof(sbp->f_mntonname));
strncpy(sbp->f_mntfromname, mp->mnt_stat.f_mntfromname, sizeof(sbp->f_mntfromname));
return (0);
}
int
synthfs_sync(mp, waitfor, cred, p)
struct mount *mp;
int waitfor;
struct ucred *cred;
struct proc *p;
{
return 0;
}
int
synthfs_vget(mp, ino, vpp)
struct mount *mp;
void *ino;
struct vnode **vpp;
{
struct vnode *vp;
if (mp->mnt_kern_flag & MNTK_UNMOUNT) {
*vpp = NULL;
return (EPERM);
}
loop:
simple_lock(&mntvnode_slock);
LIST_FOREACH(vp, &mp->mnt_vnodelist, v_mntvnodes) {
if (VTOS(vp)->s_nodeid == *((unsigned long *)ino)) {
if (vget(vp, LK_EXCLUSIVE, current_proc()) != 0) {
simple_unlock(&mntvnode_slock);
goto loop;
};
simple_unlock(&mntvnode_slock);
*vpp = vp;
return 0;
};
};
simple_unlock(&mntvnode_slock);
*vpp = NULL;
return -1;
}
int
synthfs_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p)
int *name;
u_int namelen;
void *oldp;
size_t *oldlenp;
void *newp;
size_t newlen;
struct proc *p;
{
DBG_VOP(("synthfs_sysctl called.\n"));
return (EOPNOTSUPP);
}
int
synthfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp)
register struct mount *mp;
struct fid *fhp;
struct mbuf *nam;
struct vnode **vpp;
int *exflagsp;
struct ucred **credanonp;
{
DBG_VOP(("synthfs_fhtovp called.\n"));
return EOPNOTSUPP;
}
int
synthfs_vptofh(vp, fhp)
struct vnode *vp;
struct fid *fhp;
{
DBG_VOP(("synthfs_vptofh called.\n"));
return EOPNOTSUPP;
}
int
vn_mkdir(struct proc *p, char *path, int mode) {
struct nameidata nd;
struct vnode *vp;
struct vattr vattr;
int error;
NDINIT(&nd, CREATE, LOCKPARENT, UIO_SYSSPACE, path, p);
if (error = namei(&nd)) {
DBG_VOP(("vn_mkdir: error from namei, error = %d.\n", error));
return (error);
};
vp = nd.ni_vp;
if (vp != NULL) {
VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
if (nd.ni_dvp == vp)
vrele(nd.ni_dvp);
else
vput(nd.ni_dvp);
vrele(vp);
DBG_VOP(("vn_mkdir: target already exists; returning EEXIST.\n"));
return (EEXIST);
}
VATTR_NULL(&vattr);
vattr.va_type = VDIR;
vattr.va_mode = (mode & ACCESSPERMS) &~ p->p_fd->fd_cmask;
VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr);
if (error) {
DBG_VOP(("vn_mkdir: error from VOP_MKDIR (%d).\n", error));
} else {
vput(nd.ni_vp);
};
return (error);
}
int
vn_symlink(struct proc *p, char *path, char *link) {
struct nameidata nd;
struct vattr vattr;
int error;
NDINIT(&nd, CREATE, LOCKPARENT, UIO_SYSSPACE, link, p);
if (error = namei(&nd)) return error;
if (nd.ni_vp) {
VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
if (nd.ni_dvp == nd.ni_vp)
vrele(nd.ni_dvp);
else
vput(nd.ni_dvp);
vrele(nd.ni_vp);
return EEXIST;
}
VATTR_NULL(&vattr);
vattr.va_mode = ACCESSPERMS &~ p->p_fd->fd_cmask;
VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
return VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr, path);
}