summaryrefslogtreecommitdiff
path: root/rtemsbsd/fs/rootfs/rootvfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'rtemsbsd/fs/rootfs/rootvfs.c')
-rw-r--r--rtemsbsd/fs/rootfs/rootvfs.c656
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);