#include <sys/param.h>
#include <sys/systm.h>
#include <sys/namei.h>
#include <sys/filedesc.h>
#include <sys/proc_internal.h>
#include <sys/kernel.h>
#include <mach/machine/vm_types.h>
#include <sys/vnode_internal.h>
#include <sys/socket.h>
#include <sys/mount_internal.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 <sys/uio_internal.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,
NULL,
synthfs_vfs_getattr,
synthfs_sync,
synthfs_vget,
synthfs_fhtovp,
synthfs_vptofh,
synthfs_init,
synthfs_sysctl
};
#define ROOTMPMODE 0755
#define ROOTPLACEHOLDERMODE 0700
static char synthfs_fs_name[MFSTYPENAMELEN] = "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) {
}
int synthfs_unload(void) {
return 0;
}
#endif
int
synthfs_mount_fs(struct mount *mp, vnode_t devvp, __unused user_addr_t data, 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_vfsstat.f_fstypename, synthfs_fs_name, sizeof(mp->mnt_vfsstat.f_fstypename));
strncpy(mp->mnt_vfsstat.f_mntfromname, synthfs_fake_mntfromname, sizeof(mp->mnt_vfsstat.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;
vnode_put(priv_mnt_data->synthfs_rootvp);
return (0);
}
int
synthfs_mount(mp, devvp, data, context)
register struct mount *mp;
vnode_t devvp;
user_addr_t data;
vfs_context_t context;
{
size_t size;
return (synthfs_mount_fs(mp, devvp, data, vfs_context_proc(context)));
}
int
synthfs_init(vfsp)
struct vfsconf *vfsp;
{
DBG_VOP(("synthfs_init called.\n"));
return 0;
}
int
synthfs_start(mp, flags, context)
struct mount * mp;
int flags;
vfs_context_t context;
{
DBG_VOP(("synthfs_start called.\n"));
return 0;
}
int
synthfs_root(mp, vpp, context)
struct mount *mp;
struct vnode **vpp;
vfs_context_t context;
{
unsigned long root_nodeid = ROOT_DIRID;
DBG_VOP(("synthfs_root called.\n"));
*vpp = VFSTOSFS(mp)->synthfs_rootvp;
return vnode_get(VFSTOSFS(mp)->synthfs_rootvp);
}
int
synthfs_unmount(mp, mntflags, context)
struct mount *mp;
int mntflags;
vfs_context_t context;
{
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) {
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));
retval = EBUSY;
goto Err_Exit;
};
};
synth->synthfs_rootvp = NULL;
if (retval == 0) {
vnode_get(root_vp);
vnode_rele(root_vp);
vnode_recycle(root_vp);
vnode_put(root_vp);
};
};
mp->mnt_data = NULL;
FREE(synth, M_SYNTHFS);
Err_Exit:
if (mntflags & MNT_FORCE) retval = 0;
return(retval);
}
int
synthfs_vfs_getattr(mount_t mp, struct vfs_attr *fsap, vfs_context_t context)
{
struct synthfs_mntdata *synthfs_mp = VFSTOSFS(mp);
DBG_VOP(("synthfs_vfs_getattr called.\n"));
VFSATTR_RETURN(fsap, f_bsize, 512);
VFSATTR_RETURN(fsap, f_iosize, 512);
VFSATTR_RETURN(fsap, f_blocks, 1024);
VFSATTR_RETURN(fsap, f_bfree, 0);
VFSATTR_RETURN(fsap, f_bavail, 0);
VFSATTR_RETURN(fsap, f_bused, 1024);
VFSATTR_RETURN(fsap, f_files, synthfs_mp->synthfs_filecount + synthfs_mp->synthfs_dircount);
VFSATTR_RETURN(fsap, f_ffree, 0);
VFSATTR_RETURN(fsap, f_fssubtype, 0);
return 0;
}
int
synthfs_sync(mp, waitfor, context)
struct mount *mp;
int waitfor;
vfs_context_t context;
{
return 0;
}
int
synthfs_vget(mp, ino, vpp, context)
struct mount *mp;
ino64_t ino;
struct vnode **vpp;
vfs_context_t context;
{
struct vnode *vp;
int vid = 0;
if (mp->mnt_kern_flag & MNTK_UNMOUNT) {
*vpp = NULL;
return (EPERM);
}
loop:
TAILQ_FOREACH(vp, &mp->mnt_vnodelist, v_mntvnodes) {
if (VTOS(vp)->s_nodeid == (unsigned long)ino) {
vid = vnode_vid(vp);
if (vnode_getwithvid(vp, vid) != 0) {
goto loop;
};
*vpp = vp;
return 0;
};
};
*vpp = NULL;
return -1;
}
int
synthfs_sysctl(int *name, u_int namelen, user_addr_t oldp, size_t *oldlenp,
user_addr_t newp, size_t newlen, vfs_context_t context)
{
DBG_VOP(("synthfs_sysctl called.\n"));
return (ENOTSUP);
}
int
synthfs_fhtovp(mp, fhlen, fhp, vpp, context)
register struct mount *mp;
int fhlen;
unsigned char *fhp;
struct vnode **vpp;
vfs_context_t context;
{
DBG_VOP(("synthfs_fhtovp called.\n"));
return ENOTSUP;
}
int
synthfs_vptofh(vp, fhlenp, fhp, context)
struct vnode *vp;
int *fhlenp;
unsigned char *fhp;
vfs_context_t context;
{
DBG_VOP(("synthfs_vptofh called.\n"));
return ENOTSUP;
}
int
vn_mkdir(struct proc *p, char *path, int mode)
{
struct nameidata nd;
struct vnode *vp;
struct vnode_attr va;
struct vfs_context context;
int error;
context.vc_proc = p;
context.vc_ucred = kauth_cred_proc_ref(p);
NDINIT(&nd, CREATE, LOCKPARENT, UIO_SYSSPACE32, CAST_USER_ADDR_T(path), &context);
error = namei(&nd);
if (error) {
DBG_VOP(("vn_mkdir: error from namei, error = %d.\n", error));
kauth_cred_unref(&context.vc_ucred);
return (error);
};
vp = nd.ni_vp;
if (vp == NULL) {
VATTR_INIT(&va);
VATTR_SET(&va, va_type, VDIR);
VATTR_SET(&va, va_mode, (mode & ACCESSPERMS) &~ p->p_fd->fd_cmask);
error = vn_create(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &va, 0, &context);
if (error)
DBG_VOP(("vn_mkdir: error from vnop_mkdir (%d).\n", error));
} else {
DBG_VOP(("vn_mkdir: target already exists; returning EEXIST.\n"));
error = EEXIST;
}
vnode_put(nd.ni_dvp);
if (nd.ni_vp)
vnode_put(nd.ni_vp);
nameidone(&nd);
kauth_cred_unref(&context.vc_ucred);
return (error);
}
int
vn_symlink(struct proc *p, char *path, char *link) {
struct nameidata nd;
struct vnode_attr va;
struct vfs_context context;
int error;
context.vc_proc = p;
context.vc_ucred = kauth_cred_proc_ref(p);
NDINIT(&nd, CREATE, LOCKPARENT, UIO_SYSSPACE32, CAST_USER_ADDR_T(link), &context);
if ((error = namei(&nd))) {
kauth_cred_unref(&context.vc_ucred);
return error;
}
if (nd.ni_vp == NULL) {
VATTR_INIT(&va);
VATTR_SET(&va, va_type, VLNK);
VATTR_SET(&va, va_mode, ACCESSPERMS &~ p->p_fd->fd_cmask);
error = VNOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &va, path, &context);
} else
error = EEXIST;
vnode_put(nd.ni_dvp);
if (nd.ni_vp)
vnode_put(nd.ni_vp);
nameidone(&nd);
kauth_cred_unref(&context.vc_ucred);
return (error);
}