diff options
Diffstat (limited to 'rtemsbsd/fs/rootfs/rootvfs.c')
-rw-r--r-- | rtemsbsd/fs/rootfs/rootvfs.c | 656 |
1 files changed, 656 insertions, 0 deletions
diff --git a/rtemsbsd/fs/rootfs/rootvfs.c b/rtemsbsd/fs/rootfs/rootvfs.c new file mode 100644 index 00000000..a5489f94 --- /dev/null +++ b/rtemsbsd/fs/rootfs/rootvfs.c @@ -0,0 +1,656 @@ +/** + * @file + * + * @ingroup rtems_bsd_rtems + * + * @brief TODO. + */ + +/* + * Copyright (c) 2020 Chris Johns + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <machine/rtems-bsd-kernel-space.h> + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/ctype.h> +#include <sys/dirent.h> +#include <sys/fcntl.h> +#include <sys/limits.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/mount.h> +#include <sys/mutex.h> +#include <sys/namei.h> +#include <sys/proc.h> +#include <sys/sbuf.h> +#include <sys/sx.h> +#include <sys/sysctl.h> +#include <sys/vnode.h> +#include <sys/systm.h> + +/* + * Debugging + */ +#ifdef ROOTFS_TRACE +#define ROOTFS_TRACE(foo) \ +do { \ + printf("%s(): line %d: ", __func__, __LINE__); \ + printf foo ; \ + printf("\n"); \ +} while (0) +#define ROOTFS_RETURN(err) \ +do { \ + printf("%s(): line %d: returning %d\n", \ + __func__, __LINE__, err); \ + return (err); \ +} while (0) +#else +#define ROOTFS_TRACE(foo) \ + do { /* nothing */ } while (0) +#define ROOTFS_RETURN(err) \ + return (err) +#endif + +static struct vop_vector rootfs_vnodeops; + +/* + * The nodes are the file system's data. The vnode data comes and + * goes depending on the state of the vnode cache. + */ + +#define ROOTFS_NAMELEN (64) +#define ROOTFS_ROOT (1 << 0) + +struct rootfs_node { + char rn_name[ROOTFS_NAMELEN]; + int rn_flags; + struct rootfs_node *rn_parent; + struct rootfs_node *rn_children; + struct mtx rn_mutex; + struct rootfs_node *rn_prev, *rn_next; +}; + +struct rootfs_vdata { + struct rootfs_node *rvd_rn; + bool rvd_dead; + struct vnode *rvd_vnode; + struct rootfs_vdata *rvd_prev, *rvd_next; +}; + +struct rootfs_info { + struct rootfs_node *ri_root; + struct mtx ri_mutex; + struct unrhdr *ri_unrhdr; +}; + +static MALLOC_DEFINE(M_ROOTFSVNCACHE, "rootfs_vncache", "rootfs vnode cache"); + +static struct rootfs_info *rootfs; +static struct rootfs_vdata *rootfs_vncache; +static struct mtx rootfs_vncache_mutex; + +/* + * Vnode operations + */ +static vop_access_t rootfs_access; +static vop_cachedlookup_t rootfs_lookup; +static vop_mkdir_t rootfs_mkdir; +static vop_rmdir_t rootfs_rmdir; +static vop_vptocnp_t rootfs_vptocnp; + +static struct vop_vector rootfs_vnodeops = { + .vop_default = &default_vnodeops, + + .vop_access = rootfs_access, + .vop_cachedlookup = rootfs_lookup, + .vop_close = VOP_EOPNOTSUPP, + .vop_create = VOP_EOPNOTSUPP, + .vop_getattr = VOP_EOPNOTSUPP, + .vop_getextattr = VOP_EOPNOTSUPP, + .vop_ioctl = VOP_EOPNOTSUPP, + .vop_link = VOP_EOPNOTSUPP, + .vop_lookup = vfs_cache_lookup, + .vop_mkdir = VOP_EOPNOTSUPP, + .vop_mknod = VOP_EOPNOTSUPP, + .vop_open = VOP_EOPNOTSUPP, + .vop_read = VOP_EOPNOTSUPP, + .vop_readdir = VOP_EOPNOTSUPP, + .vop_readlink = VOP_EOPNOTSUPP, + .vop_remove = VOP_EOPNOTSUPP, + .vop_rename = VOP_EOPNOTSUPP, + .vop_rmdir = rootfs_rmdir, + .vop_setattr = VOP_EOPNOTSUPP, + .vop_symlink = VOP_EOPNOTSUPP, + .vop_vptocnp = rootfs_vptocnp, + .vop_write = VOP_EOPNOTSUPP, +}; + +static vfs_init_t rootfs_init; +static vfs_mount_t rootfs_mount; +static vfs_cmount_t rootfs_cmount; +static vfs_root_t rootfs_root; +static vfs_statfs_t rootfs_statfs; +static vfs_sync_t rootfs_sync; +static vfs_uninit_t rootfs_uninit; +static vfs_unmount_t rootfs_unmount; +static vfs_sysctl_t rootfs_sysctl; +static vfs_purge_t rootfs_purge; + +static struct vfsops rootfs_vfsops = { + .vfs_init = rootfs_init, + .vfs_mount = rootfs_mount, + .vfs_cmount = rootfs_cmount, + .vfs_root = rootfs_root, + .vfs_statfs = rootfs_statfs, + .vfs_sync = rootfs_sync, + .vfs_uninit = rootfs_uninit, + .vfs_unmount = rootfs_unmount, + .vfs_sysctl = rootfs_sysctl, + .vfs_purge = rootfs_purge, +}; +VFS_SET(rootfs_vfsops, rootfs, VFCF_STATIC); + +static void +rootfs_assert_not_owned(struct rootfs_node *rn) +{ + mtx_assert(&rn->rn_mutex, MA_NOTOWNED); +} + +static void +rootfs_lock(struct rootfs_node *rn) +{ + mtx_lock(&rn->rn_mutex); +} + +static void +rootfs_unlock(struct rootfs_node *rn) +{ + mtx_unlock(&rn->rn_mutex); +} + +static int +rootfs_vncache_alloc(struct mount *mp, + struct vnode **vpp, + struct rootfs_node *rn) +{ + struct rootfs_vdata *rvd; + struct vnode *vp; + int error; + + rvd = malloc(sizeof *rvd, M_ROOTFSVNCACHE, M_WAITOK); + rvd->rvd_next = rvd->rvd_prev = NULL; + error = getnewvnode("rootfs", mp, &rootfs_vnodeops, vpp); + if (error) { + free(rvd, M_ROOTFSVNCACHE); + return (error); + } + rvd->rvd_rn = rn; + (*vpp)->v_data = rvd; + (*vpp)->v_type = VDIR; + rvd->rvd_vnode = *vpp; + vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY); + VN_LOCK_AREC(*vpp); + error = insmntque(*vpp, mp); + if (error != 0) { + free(rvd, M_ROOTFSVNCACHE); + *vpp = NULLVP; + return (error); + } + while (true) { + struct rootfs_vdata *rvd2; + mtx_lock(&rootfs_vncache_mutex); + /* + * Other thread may race with us, creating the entry we are + * going to insert into the cache. Recheck after + * pfs_vncache_mutex is reacquired. + */ + for (rvd2 = rootfs_vncache; rvd2; rvd2 = rvd2->rvd_next) { + if (rvd2->rvd_rn == rn && rvd2->rvd_vnode->v_mount == mp) { + vp = rvd2->rvd_vnode; + VI_LOCK(vp); + mtx_unlock(&rootfs_vncache_mutex); + if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, curthread) == 0) { + vgone(*vpp); + vput(*vpp); + *vpp = vp; + cache_purge(vp); + return (0); + } + continue; + } + } + break; + } + rvd->rvd_prev = NULL; + rvd->rvd_next = rootfs_vncache; + if (rvd->rvd_next) + rvd->rvd_next->rvd_prev = rvd; + rootfs_vncache = rvd; + mtx_unlock(&rootfs_vncache_mutex); + return (0); +} + +int +pfs_vncache_free(struct vnode *vp) +{ + struct rootfs_vdata *rvd; + + mtx_lock(&rootfs_vncache_mutex); + rvd = (struct rootfs_vdata *)vp->v_data; + KASSERT(rvd != NULL, ("rootfs_vncache_free(): no vnode data\n")); + if (rvd->rvd_next) + rvd->rvd_next->rvd_prev = rvd->rvd_prev; + if (rvd->rvd_prev) { + rvd->rvd_prev->rvd_next = rvd->rvd_next; + } else if (rootfs_vncache == rvd) { + rootfs_vncache = rvd->rvd_next; + } + mtx_unlock(&rootfs_vncache_mutex); + free(rvd, M_ROOTFSVNCACHE); + vp->v_data = NULL; + return (0); +} + + +static void +pfs_purge(struct rootfs_node *rn) +{ + struct rootfs_vdata *rvd; + struct vnode *vnp; + mtx_lock(&rootfs_vncache_mutex); + rvd = rootfs_vncache; + while (rvd != NULL) { + if (rvd->rvd_dead || (rn != NULL && rvd->rvd_rn == rn)) { + vnp = rvd->rvd_vnode; + vhold(vnp); + mtx_unlock(&rootfs_vncache_mutex); + VOP_LOCK(vnp, LK_EXCLUSIVE); + vgone(vnp); + VOP_UNLOCK(vnp, 0); + mtx_lock(&rootfs_vncache_mutex); + vdrop(vnp); + rvd = rootfs_vncache; + } else { + rvd = rvd->rvd_next; + } + } + mtx_unlock(&rootfs_vncache_mutex); +} + + +/* + * Verify permissions + */ +static int +rootfs_access(struct vop_access_args *va) +{ + struct vnode *vn = va->a_vp; + struct rootfs_node *rn = vn->v_data; + struct vattr vattr; + int error; + + ROOTFS_TRACE(("%s", rn->rn_name)); + (void)rn; + + error = VOP_GETATTR(vn, &vattr, va->a_cred); + if (error) + ROOTFS_RETURN (error); + error = vaccess(vn->v_type, vattr.va_mode, vattr.va_uid, + vattr.va_gid, va->a_accmode, va->a_cred, NULL); + ROOTFS_RETURN (error); +} + +/* + * Get file attributes + */ +static int +rootfs_getattr(struct vop_getattr_args *va) +{ + struct vnode *vn = va->a_vp; + struct rootfs_node *rn = vn->v_data; + struct vattr *vap = va->a_vap; + int error = 0; + + ROOTFS_TRACE(("%s", rn->rn_name)); + rootfs_assert_not_owned(rn); + + vap->va_type = vn->v_type; + vap->va_fileid = alloc_unr(rootfs->ri_unrhdr); + vap->va_flags = 0; + vap->va_blocksize = PAGE_SIZE; + vap->va_bytes = vap->va_size = 0; + vap->va_filerev = 0; + vap->va_fsid = vn->v_mount->mnt_stat.f_fsid.val[0]; + vap->va_nlink = 1; + nanotime(&vap->va_ctime); + vap->va_atime = vap->va_mtime = vap->va_ctime; + + vap->va_mode = 0555; + vap->va_uid = 0; + vap->va_gid = 0; + + ROOTFS_RETURN (error); +} + +/* + * Make directory + */ +static int +rootfs_mkdir(struct vop_mkdir_args *va) +{ + struct vnode *dvn = va->a_dvp; + struct vnode **vpp = va->a_vpp; + struct vattr *vap = va->a_vap; + struct rootfs_node *rn; + int error = 0; + + ROOTFS_TRACE(("%s", rn->rn_name)); + + error = EOPNOTSUPP; + + ROOTFS_RETURN (error); +} + +/* + * Remove directory + */ +static int +rootfs_rmdir(struct vop_rmdir_args *va) +{ + struct vnode *vn = va->a_vp; + struct rootfs_node *rn = vn->v_data; + int error = 0; + + ROOTFS_TRACE(("%s", rn->rn_name)); + rootfs_assert_not_owned(rn); + + error = EOPNOTSUPP; + + ROOTFS_RETURN (error); +} + +/* + * Convert a vnode to its component name + */ +static int +rootfs_vptocnp(struct vop_vptocnp_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct vnode **dvp = ap->a_vpp; + struct rootfs_node *rn = vp->v_data; + struct mount *mp; + char *buf = ap->a_buf; + int *buflen = ap->a_buflen; + char pidbuf[ROOTFS_NAMELEN]; + int len, i, error, locked; + + i = *buflen; + error = 0; + + rootfs_lock(rn); + + if (vp->v_type == VDIR && ((rn->rn_flags & ROOTFS_ROOT) != 0)) { + *dvp = vp; + vhold(*dvp); + rootfs_unlock(rn); + ROOTFS_RETURN (0); + } else { + len = strlen(rn->rn_name); + i -= len; + if (i < 0) { + error = ENOMEM; + goto failed; + } + bcopy(rn->rn_name, buf + i, len); + } + + rootfs_unlock(rn); + rn = rn->rn_parent; + rootfs_lock(rn); + + mp = vp->v_mount; + error = vfs_busy(mp, 0); + if (error) + return (error); + + /* + * vp is held by caller. + */ + locked = VOP_ISLOCKED(vp); + VOP_UNLOCK(vp, 0); + + *buflen = i; + VOP_UNLOCK(*dvp, 0); + vn_lock(vp, locked | LK_RETRY); + vfs_unbusy(mp); + + ROOTFS_RETURN (0); +failed: + rootfs_unlock(rn); + ROOTFS_RETURN(error); +} + +/* + * Look up a file or directory + */ +static int +rootfs_lookup(struct vop_cachedlookup_args *va) +{ + struct vnode *vn = va->a_dvp; + struct vnode **vpp = va->a_vpp; + struct componentname *cnp = va->a_cnp; + struct rootfs_node *rvn = vn->v_data; + struct rootfs_node *rn, *pdn = NULL; + struct mount *mp; + char *rname; + int error, i, namelen, visible; + + ROOTFS_TRACE(("%.*s", (int)cnp->cn_namelen, cnp->cn_nameptr)); + rootfs_assert_not_owned(rvn); + + if (vn->v_type != VDIR) + ROOTFS_RETURN (ENOTDIR); + + if ((cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == RENAME) + ROOTFS_RETURN (EOPNOTSUPP); + + /* shortcut: check if the name is too long */ + if (cnp->cn_namelen >= ROOTFS_NAMELEN) + ROOTFS_RETURN (ENOENT); + + /* self */ + namelen = cnp->cn_namelen; + rname = cnp->cn_nameptr; + if (namelen == 1 && rname[0] == '.') { + *vpp = vn; + VREF(vn); + ROOTFS_RETURN (0); + } + + mp = vn->v_mount; + + /* parent */ + if (cnp->cn_flags & ISDOTDOT) { + if (rvn->rn_flags & ROOTFS_ROOT) + ROOTFS_RETURN (EIO); + error = vfs_busy(mp, MBF_NOWAIT); + if (error != 0) { + vfs_ref(mp); + VOP_UNLOCK(vn, 0); + error = vfs_busy(mp, 0); + vn_lock(vn, LK_EXCLUSIVE | LK_RETRY); + vfs_rel(mp); + if (error != 0) + ROOTFS_RETURN(ENOENT); + if (vn->v_iflag & VI_DOOMED) { + vfs_unbusy(mp); + ROOTFS_RETURN(ENOENT); + } + } + VOP_UNLOCK(vn, 0); + rootfs_lock(rvn); + rn = rvn->rn_parent; + rootfs_unlock(rvn); + goto got_rnode; + } + + rootfs_lock(rvn); + + /* named node */ + for (rn = rvn->rn_children; rn != NULL; rn = rn->rn_next) { + if (rn->rn_name[namelen] == '\0' && + bcmp(rname, rn->rn_name, namelen) == 0) { + rootfs_unlock(rvn); + goto got_rnode; + } + } + + rootfs_unlock(rvn); + + ROOTFS_RETURN (ENOENT); +got_rnode: + rootfs_assert_not_owned(rvn); + rootfs_assert_not_owned(rn); + error = rootfs_vncache_alloc(mp, vpp, rn); + if (error) + goto failed; + if (cnp->cn_flags & ISDOTDOT) { + vfs_unbusy(mp); + vn_lock(vn, LK_EXCLUSIVE | LK_RETRY); + if (vn->v_iflag & VI_DOOMED) { + vput(*vpp); + *vpp = NULL; + ROOTFS_RETURN(ENOENT); + } + } + if (cnp->cn_flags & MAKEENTRY && !(vn->v_iflag & VI_DOOMED)) + cache_enter(vn, *vpp, cnp); + ROOTFS_RETURN (0); +failed: + if (cnp->cn_flags & ISDOTDOT) { + vfs_unbusy(mp); + vn_lock(vn, LK_EXCLUSIVE | LK_RETRY); + *vpp = NULL; + } + ROOTFS_RETURN(error); +} + +static int +rootfs_init(struct vfsconf *vfsp) +{ + printf("rootfs: init\n"); + return (0); +} + +static int +rootfs_mount(struct mount *mp) +{ + printf("rootfs: mount\n"); + return (0); +} + +static int +rootfs_cmount(struct mntarg *ma, void *data, uint64_t flags) +{ + printf("rootfs: cmount\n"); + return (0); +} + +static int +rootfs_root(struct mount *mp, int flags, struct vnode **vpp) +{ + *vpp = rootfs->ri_root->rn_vnode; + return (0); +} + +static int +rootfs_statfs(struct mount *mp, struct statfs *sbp) +{ + printf("rootfs: statfs\n"); + return (0); +} + +static int +rootfs_sync(struct mount *mp, int waitfor) +{ + return (0); +} + +static int +rootfs_uninit(struct vfsconf *vfsp) +{ + printf("rootfs: uninit\n"); + return (EOPNOTSUPP); +} + +static int +nfs_unmount(struct mount *mp, int mntflags) +{ + printf("rootfs: unmount\n"); + return (EOPNOTSUPP); +} + +static int +rootfs_sysctl(struct mount *mp, fsctlop_t op, struct sysctl_req *req) +{ + return (0); +} + +static void +rootfs_purge(struct mount *mp) +{ +} + +static int +rootfs_modevent(module_t mod, int type, void *data) +{ + int error = 0; + static int loaded = 0; + switch (type) { + case MOD_LOAD: + if (loaded) + return (0); + mtx_init(&rootfs_vncache_mutex, "rootds_vncache_mutex", NULL, MTX_DEF); + loaded = 1; + break; + case MOD_UNLOAD: + error = EBUSY; + break; + default: + error = EOPNOTSUPP; + break; + } + return error; +} +static moduledata_t rootfs_mod = { + "rootfs", + rootfs_modevent, + NULL, +}; +DECLARE_MODULE(rootfs, rootfs_mod, SI_SUB_VFS, SI_ORDER_FIRST); +MODULE_VERSION(rootfs, 1); |