/* * Copyright (c) 2000-2001 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* Copyright (c) 1998 Apple Computer, Inc. All Rights Reserved */ /* * Change History: * * 17-Aug-1999 Pat Dirks New today. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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[] = ""; extern struct vnodeopv_desc synthfs_vnodeop_opv_desc; /* The following refer to kernel global variables used in the loading/initialization: */ extern int maxvfsslots; /* Total number of slots in the system's vfsconf table */ extern int maxvfsconf; /* The highest fs type number [old-style ID] in use [dispite its name] */ extern int vfs_opv_numops; /* The total number of defined vnode operations */ 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) /* * This routine is responsible for all the initialization that would * ordinarily be done as part of the system startup; it calls synthfs_init * to do the initialization that is strictly synthfs-specific. */ 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; /* Can't mount root of file system [yet] */ newvfsconf->vfc_next = NULL; /* Based on vfs_op_init and ... */ opv_desc_vector_p = synthfs_vnodeop_opv_desc.opv_desc_vector_p; DBG_VOP(("load_synthfs: Allocating and initializing VNode ops vector...\n")); /* * Allocate and init the vector. * Also handle backwards compatibility. */ 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]); /* * Sanity check: is this operation listed * in the list of operations? We check this * by seeing if its offest is zero. Since * the default routine should always be listed * first, it should be the only one with a zero * offset. Any other operation with a zero * offset is probably not listed in * vfs_op_descs, and so is probably an error. * * A panic here means the layer programmer * has committed the all-too common bug * of adding a new operation to the layer's * list of vnode operations but * not adding the operation to the system-wide * list of supported operations. */ 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"); } /* * Fill in this entry. */ opv_desc_vector[opve_descp->opve_op->vdesc_offset] = opve_descp->opve_impl; } /* * Finally, go back and replace unfilled routines * with their default. (Sigh, an O(n^3) algorithm. I * could make it better, but that'd be work, and n is small.) */ opv_desc_vector_p = synthfs_vnodeop_opv_desc.opv_desc_vector_p; /* * Force every operations vector to have a default routine. */ 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;jmnt_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; /* Set up the root vnode for fast reference in the future. Note that synthfs_new_directory() returns the vnode with a refcount of +2. The root vnode's refcount is maintained unlocked but with a pos. ref count until unmount. */ 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; /* Drop the freshly acquired reference on the root, leaving v_usecount=1 to prevent the vnode from beeing freed: */ 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)); } /* * Initialize the filesystem */ 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; } /* * Return the root of a filesystem. */ 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); } /* * unmount system call */ 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; /* Free the root vnode. Note that there's no need to vget() or vref() it before locking it here: the ref. count has been maintained at +1 ever since mount time. */ 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); /* This drops synthfs's own refcount */ vgone(root_vp); }; }; /* All vnodes should be gone, and no errors, clean up the last */ mp->mnt_data = NULL; FREE(synth, M_SYNTHFS); Err_Exit: if (mntflags & MNT_FORCE) retval = 0; return(retval); } /* * Get file system statistics. */ 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; // lies, darn lies and virtual file systems sbp->f_bfree = 0; // Nope, can't write here! 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); } /* * synthfs doesn't have any data or backing store and you can't write into any of the synthfs * structures, so don't do anything */ int synthfs_sync(mp, waitfor, cred, p) struct mount *mp; int waitfor; struct ucred *cred; struct proc *p; { // DBG_VOP(("synthfs_sync called\n")); return 0; } /* * Look up a synthfs node by node number. */ int synthfs_vget(mp, ino, vpp) struct mount *mp; void *ino; struct vnode **vpp; { struct vnode *vp; // DBG_VOP(("synthfs_vget called\n")); /* Check for unmount in progress */ 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; } /* * fast filesystem related variables. */ 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); } /* * File handle to vnode * */ 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; } /* * Vnode pointer to File handle */ /* ARGSUSED */ 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); }