summaryrefslogtreecommitdiff
path: root/sys/fs
diff options
context:
space:
mode:
Diffstat (limited to 'sys/fs')
-rw-r--r--sys/fs/ctlfs.c389
-rw-r--r--sys/fs/devfs.c26
-rw-r--r--sys/fs/initramfs.c113
-rw-r--r--sys/fs/tmpfs.c390
4 files changed, 853 insertions, 65 deletions
diff --git a/sys/fs/ctlfs.c b/sys/fs/ctlfs.c
new file mode 100644
index 0000000..9225114
--- /dev/null
+++ b/sys/fs/ctlfs.c
@@ -0,0 +1,389 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * 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.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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 <sys/types.h>
+#include <sys/errno.h>
+#include <sys/vnode.h>
+#include <sys/device.h>
+#include <sys/syslog.h>
+#include <sys/mount.h>
+#include <sys/queue.h>
+#include <fs/ctlfs.h>
+#include <vm/dynalloc.h>
+#include <string.h>
+
+#define CTLFS_MPNAME "ctl"
+#define CTLFS_ENTRY_MAG 0x43454E54UL /* 'CENT' */
+#define CTLFS_NODE_MAG 0x43544C4EUL /* 'CTLN' */
+
+#define pr_trace(fmt, ...) kprintf("ctlfs: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+static const struct vops ctlfs_vops;
+
+struct ctlfs_hdr {
+ uint32_t magic;
+ char *name;
+};
+
+/*
+ * Control fs entry, represents a control
+ * file within a ctlfs node.
+ * -- HDR START --
+ * @magic: Magic number [MUST BE FIRST] (CTLFS_ENTRY_MAG)
+ * @name: Entry name [MUST BE SECOND]
+ * -- HDR END --
+ * @parent: Parent (ctlfs_node)
+ * @io: Ctlfs operations.
+ * @mode: Access flags.
+ * @link: TAILQ link.
+ */
+struct ctlfs_entry {
+ uint32_t magic;
+ char *name;
+ struct ctlfs_node *parent;
+ const struct ctlops *io;
+ mode_t mode;
+ TAILQ_ENTRY(ctlfs_entry) link;
+};
+
+/*
+ * Control fs node, represents a directory
+ * within ctlfs. These directories represent
+ * devices, each device directory contains
+ * control files.
+ *
+ * For example:
+ *
+ * /ctl/sd1/bsize # Block size
+ * /ctl/sd1/health # Health
+ * [et cetera]
+ *
+ * @magic: Magic number [MUST BE FIRST] (CTLFS_NODE_MAG)
+ * @name: Name of node [MUST BE SECOND]
+ * @mode: Access flags.
+ * @major: Device major number.
+ * @minor: Device major number.
+ * @eq: Entries for this ctlfs node.
+ */
+struct ctlfs_node {
+ uint32_t magic;
+ char *name;
+ mode_t mode;
+ TAILQ_HEAD(, ctlfs_entry) eq;
+ TAILQ_ENTRY(ctlfs_node) link;
+};
+
+static TAILQ_HEAD(, ctlfs_node) nodeq;
+
+/*
+ * Look up entries within a ctlfs
+ * node by name.
+ */
+static struct ctlfs_entry *
+entry_lookup(struct ctlfs_node *cnp, const char *name)
+{
+ struct ctlfs_entry *ep;
+
+ TAILQ_FOREACH(ep, &cnp->eq, link) {
+ if (strcmp(ep->name, name) == 0) {
+ return ep;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Lookup a ctlfs entry by name.
+ */
+static struct ctlfs_node *
+node_lookup(const char *name)
+{
+ struct ctlfs_node *cnp;
+
+ TAILQ_FOREACH(cnp, &nodeq, link) {
+ if (strcmp(cnp->name, name) == 0) {
+ return cnp;
+ }
+ }
+
+ return NULL;
+}
+
+static int
+ctlfs_init(struct fs_info *fip)
+{
+ struct vnode *vp;
+ struct mount *mp;
+ int error;
+
+ if ((error = vfs_alloc_vnode(&vp, VDIR)) != 0) {
+ pr_error("failed to alloc vnode\n");
+ return error;
+ }
+
+ vp->vops = &ctlfs_vops;
+ if ((mp = vfs_alloc_mount(vp, fip)) == NULL) {
+ pr_trace("failed to alloc mountpoint\n");
+ return -ENOMEM;
+ }
+
+ error = vfs_name_mount(mp, CTLFS_MPNAME);
+ if (error != 0) {
+ pr_trace("failed to mount @ /%s\n", CTLFS_MPNAME);
+ return error;
+ }
+
+ TAILQ_INSERT_TAIL(&g_mountlist, mp, mnt_list);
+ TAILQ_INIT(&nodeq);
+ return 0;
+}
+
+static int
+ctlfs_lookup(struct vop_lookup_args *args)
+{
+ int error;
+ const char *name = args->name;
+ struct vnode *vp, *dirvp;
+ struct ctlfs_node *cnp = NULL;
+ struct ctlfs_entry *enp = NULL;
+
+ if (name == NULL) {
+ return -EINVAL;
+ }
+
+ dirvp = args->dirvp;
+ if (dirvp == NULL) {
+ return -EIO;
+ }
+
+ /*
+ * If we already have data within this vnode
+ * it *might* be a control node but we'll have
+ * to verify its magic number...
+ */
+ if (dirvp->data != NULL) {
+ cnp = (struct ctlfs_node *)dirvp->data;
+ if (cnp->magic != CTLFS_NODE_MAG) {
+ pr_error("bad `cnp' magic (name=%s)\n", name);
+ return -EIO;
+ }
+ }
+
+ /*
+ * Handle cases where we are looking up
+ * relative to a control node.
+ */
+ if (cnp != NULL) {
+ enp = entry_lookup(cnp, name);
+ if (enp == NULL) {
+ return -ENOENT;
+ }
+
+ /* Create a vnode for this enp */
+ error = vfs_alloc_vnode(&vp, VCHR);
+ if (error != 0) {
+ return error;
+ }
+
+ vp->data = (void *)enp;
+ vp->vops = &ctlfs_vops;
+ *args->vpp = vp;
+ return 0;
+ }
+
+ /* Does this entry exist? */
+ if ((cnp = node_lookup(name)) == NULL) {
+ return -ENOENT;
+ }
+
+ if ((error = vfs_alloc_vnode(&vp, VDIR)) != 0) {
+ return error;
+ }
+
+ vp->data = cnp;
+ vp->vops = &ctlfs_vops;
+ *args->vpp = vp;
+ return 0;
+}
+
+/*
+ * Create a ctlfs node (directory) within the
+ * root fs.
+ *
+ * @name: Node name (e.g., "sd1" for "/ctl/sd1/")
+ * @dp: Device related arguments (see ctlfs_dev)
+ * Args used:
+ * - mode (access flags)
+ *
+ */
+int
+ctlfs_create_node(const char *name, const struct ctlfs_dev *dp)
+{
+ struct ctlfs_node *cnp;
+ size_t namelen;
+
+ if (name == NULL || dp == NULL) {
+ return -EINVAL;
+ }
+
+ cnp = dynalloc(sizeof(*cnp));
+ if (cnp == NULL) {
+ return -ENOMEM;
+ }
+
+ namelen = strlen(name);
+ cnp->name = dynalloc(namelen + 1);
+ if (cnp->name == NULL) {
+ dynfree(cnp);
+ return -ENOMEM;
+ }
+
+ memcpy(cnp->name, name, namelen);
+ cnp->name[namelen] = '\0';
+ cnp->mode = dp->mode;
+ cnp->magic = CTLFS_NODE_MAG;
+ TAILQ_INSERT_TAIL(&nodeq, cnp, link);
+ TAILQ_INIT(&cnp->eq);
+ return 0;
+}
+
+/*
+ * Create a ctlfs entry within a specific node.
+ *
+ * @name: Name e.g., "/health" for "/ctl/xxx/health".
+ * @dp: Device related arguments (see ctlfs_dev)
+ * Args used:
+ * - devname (name of device)
+ * - mode (access flags)
+ * - ops (operations vector)
+ */
+int
+ctlfs_create_entry(const char *name, const struct ctlfs_dev *dp)
+{
+ struct ctlfs_entry *enp;
+ struct ctlfs_node *parent;
+ size_t namelen;
+
+ if (name == NULL || dp == NULL) {
+ return -EINVAL;
+ }
+ if (dp->devname == NULL) {
+ return -EINVAL;
+ }
+ if (dp->ops == NULL) {
+ return -EINVAL;
+ }
+
+ parent = node_lookup(dp->devname);
+ if (parent == NULL) {
+ pr_trace("could not find %s\n", dp->devname);
+ return -ENOENT;
+ }
+
+ enp = dynalloc(sizeof(*enp));
+ if (enp == NULL) {
+ return -ENOMEM;
+ }
+
+ namelen = strlen(name);
+ enp->name = dynalloc(namelen + 1);
+ if (enp->name == NULL) {
+ dynfree(enp);
+ return -ENOMEM;
+ }
+
+ memcpy(enp->name, name, namelen);
+ enp->name[namelen] = '\0';
+ enp->io = dp->ops;
+ enp->magic = CTLFS_ENTRY_MAG;
+ enp->mode = dp->mode;
+ enp->parent = parent;
+ TAILQ_INSERT_TAIL(&parent->eq, enp, link);
+ return 0;
+}
+
+/*
+ * Read a control file
+ *
+ * Args passed to driver:
+ * - ctlfs_dev.ctlname
+ * - ctlfs_dev.iop
+ * - ctlfs_dev.mode
+ */
+static int
+ctlfs_read(struct vnode *vp, struct sio_txn *sio)
+{
+ const struct ctlops *iop;
+ struct ctlfs_entry *enp;
+ struct ctlfs_dev dev;
+
+ if ((enp = vp->data) == NULL) {
+ pr_error("no vnode data for ctlfs entry\n");
+ return -EIO;
+ }
+ if (enp->magic != CTLFS_ENTRY_MAG) {
+ pr_error("ctlfs entry has bad magic\n");
+ return -EIO;
+ }
+ if ((iop = enp->io) == NULL) {
+ pr_error("no i/o ops for ctlfs entry\n");
+ return -EIO;
+ }
+ if (iop->read == NULL) {
+ pr_trace("no read op for ctlfs entry\n");
+ return -EIO;
+ }
+
+ dev.ctlname = enp->name;
+ dev.ops = iop;
+ dev.mode = enp->mode;
+ return iop->read(&dev, sio);
+}
+
+static int
+ctlfs_reclaim(struct vnode *vp)
+{
+ vp->data = NULL;
+ return 0;
+}
+
+static const struct vops ctlfs_vops = {
+ .lookup = ctlfs_lookup,
+ .read = ctlfs_read,
+ .getattr = NULL,
+ .write = NULL,
+ .reclaim = ctlfs_reclaim,
+ .create = NULL
+};
+
+const struct vfsops g_ctlfs_vfsops = {
+ .init = ctlfs_init
+};
diff --git a/sys/fs/devfs.c b/sys/fs/devfs.c
index 024239d..293ee0a 100644
--- a/sys/fs/devfs.c
+++ b/sys/fs/devfs.c
@@ -30,6 +30,7 @@
#include <sys/types.h>
#include <sys/vnode.h>
#include <sys/errno.h>
+#include <sys/stat.h>
#include <sys/mount.h>
#include <sys/device.h>
#include <fs/devfs.h>
@@ -126,6 +127,8 @@ devfs_lookup(struct vop_lookup_args *args)
vp->data = dnp;
vp->vops = &g_devfs_vops;
+ vp->major = dnp->major;
+ vp->dev = dnp->dev;
*args->vpp = vp;
return 0;
}
@@ -136,6 +139,8 @@ devfs_getattr(struct vop_getattr_args *args)
struct vnode *vp;
struct vattr *attr;
struct devfs_node *dnp;
+ struct bdevsw *bdev;
+ size_t size = 0;
vp = args->vp;
if ((dnp = vp->data) == NULL) {
@@ -145,6 +150,13 @@ devfs_getattr(struct vop_getattr_args *args)
return -EIO;
}
+ if (dnp->is_block) {
+ bdev = dev_get(dnp->major, dnp->dev);
+ if (bdev->bsize != NULL) {
+ size = bdev->bsize(dnp->dev);
+ }
+ }
+
/*
* Set stat attributes from device node structure
* found within vnode data.
@@ -153,20 +165,13 @@ devfs_getattr(struct vop_getattr_args *args)
* size is hardwired to 0.
*/
attr->mode = dnp->mode;
- attr->size = 0;
+ attr->size = size;
return 0;
}
static int
devfs_reclaim(struct vnode *vp)
{
- struct devfs_node *dnp;
-
- if ((dnp = vp->data) != NULL) {
- dynfree(dnp->name);
- dynfree(vp->data);
- }
-
vp->data = NULL;
return 0;
}
@@ -255,7 +260,7 @@ devfs_create_entry(const char *name, devmajor_t major, dev_t dev, mode_t mode)
memcpy(dnp->name, name, name_len);
dnp->name[name_len] = '\0';
-
+ dnp->is_block = ISSET(mode, S_IFBLK) ? 1 : 0;
dnp->major = major;
dnp->dev = dev;
dnp->mode = mode;
@@ -268,7 +273,8 @@ const struct vops g_devfs_vops = {
.reclaim = devfs_reclaim,
.read = devfs_read,
.write = devfs_write,
- .getattr = devfs_getattr
+ .getattr = devfs_getattr,
+ .create = NULL
};
const struct vfsops g_devfs_vfsops = {
diff --git a/sys/fs/initramfs.c b/sys/fs/initramfs.c
index fd746ef..c41deb4 100644
--- a/sys/fs/initramfs.c
+++ b/sys/fs/initramfs.c
@@ -33,12 +33,16 @@
#include <sys/errno.h>
#include <sys/limine.h>
#include <sys/panic.h>
+#include <sys/param.h>
#include <sys/vnode.h>
#include <fs/initramfs.h>
#include <vm/dynalloc.h>
#include <string.h>
-#define CPIO_TRAILER "TRAILER!!!"
+#define OMAR_EOF "RAMO"
+#define OMAR_REG 0
+#define OMAR_DIR 1
+#define BLOCK_SIZE 512
/*
* File or directory.
@@ -51,20 +55,18 @@ struct initramfs_node {
};
/*
- * ODC CPIO header
+ * The OMAR file header, describes the basics
+ * of a file.
+ *
+ * @magic: Header magic ("OMAR")
+ * @len: Length of the file
+ * @namelen: Length of the filename
*/
-struct cpio_hdr {
- char c_magic[6];
- char c_dev[6];
- char c_ino[6];
- char c_mode[6];
- char c_uid[6];
- char c_gid[6];
- char c_nlink[6];
- char c_rdev[6];
- char c_mtime[11];
- char c_namesize[6];
- char c_filesize[11];
+struct __packed omar_hdr {
+ char magic[4];
+ uint8_t type;
+ uint8_t namelen;
+ uint32_t len;
};
static volatile struct limine_module_request mod_req = {
@@ -92,21 +94,6 @@ get_module(const char *path, uint64_t *size) {
}
/*
- * Convert octal to base 10
- */
-static uint32_t
-oct2dec(const char *in, size_t sz)
-{
- size_t val = 0;
-
- for (size_t i = 0; i < sz; ++i) {
- val = val * 8 + (in[i] - '0');
- }
-
- return val;
-}
-
-/*
* Get a file from initramfs
*
* @path: Path of file to get.
@@ -115,41 +102,54 @@ oct2dec(const char *in, size_t sz)
static int
initramfs_get_file(const char *path, struct initramfs_node *res)
{
- const struct cpio_hdr *hdr;
struct initramfs_node node;
- uintptr_t addr;
- size_t namesize, filesize;
- mode_t mode;
+ const struct omar_hdr *hdr;
+ const char *p, *name;
+ char namebuf[256];
+ off_t off;
- addr = (uintptr_t)initramfs;
+ p = initramfs;
for (;;) {
- hdr = (void *)addr;
- namesize = oct2dec(hdr->c_namesize, sizeof(hdr->c_namesize));
- filesize = oct2dec(hdr->c_filesize, sizeof(hdr->c_filesize));
- mode = oct2dec(hdr->c_mode, sizeof(hdr->c_mode));
+ hdr = (struct omar_hdr *)p;
+ if (strncmp(hdr->magic, OMAR_EOF, sizeof(OMAR_EOF)) == 0) {
+ break;
+ }
- /* Make sure the magic is correct */
- if (strncmp(hdr->c_magic, "070707", 6) != 0) {
+ /* Ensure the file is valid */
+ if (strncmp(hdr->magic, "OMAR", 4) != 0) {
+ /* Bad magic */
+ return -EINVAL;
+ }
+ if (hdr->namelen > sizeof(namebuf) - 1) {
return -EINVAL;
}
- addr += sizeof(struct cpio_hdr);
- node.path = (const char *)addr;
+ name = (char *)p + sizeof(struct omar_hdr);
+ memcpy(namebuf, name, hdr->namelen);
+ namebuf[hdr->namelen] = '\0';
- /* Is this the requested file? */
- if (strcmp(node.path, path) == 0) {
- node.data = (void *)(addr + namesize);
- node.size = filesize;
- node.mode = mode;
+ /* Compute offset to next block */
+ if (hdr->type == OMAR_DIR) {
+ off = 512;
+ } else {
+ off = ALIGN_UP(sizeof(*hdr) + hdr->namelen + hdr->len, BLOCK_SIZE);
+ }
+
+ /* Skip header and name, right to the data */
+ p = (char *)hdr + sizeof(struct omar_hdr);
+ p += hdr->namelen;
+
+ if (strcmp(namebuf, path) == 0) {
+ node.mode = 0700;
+ node.size = hdr->len;
+ node.data = (void *)p;
*res = node;
return 0;
}
- /* Get next header and see if we are at the end */
- addr += (namesize + filesize);
- if (strcmp(node.path, CPIO_TRAILER) == 0) {
- break;
- }
+ hdr = (struct omar_hdr *)((char *)hdr + off);
+ p = (char *)hdr;
+ memset(namebuf, 0, sizeof(namebuf));
}
return -ENOENT;
@@ -223,6 +223,8 @@ initramfs_read(struct vnode *vp, struct sio_txn *sio)
return -EIO;
if (sio->buf == NULL)
return -EIO;
+ if (sio->len > n->size)
+ sio->len = n->size;
src = n->data;
dest = sio->buf;
@@ -256,9 +258,9 @@ initramfs_init(struct fs_info *fip)
struct mount *mp;
int status;
- initramfs = get_module("/boot/ramfs.cpio", &initramfs_size);
+ initramfs = get_module("/boot/ramfs.omar", &initramfs_size);
if (initramfs == NULL) {
- panic("failed to open initramfs cpio image\n");
+ panic("failed to open initramfs OMAR image\n");
}
status = vfs_alloc_vnode(&g_root_vnode, VDIR);
@@ -277,7 +279,8 @@ const struct vops g_initramfs_vops = {
.read = initramfs_read,
.write = NULL,
.reclaim = initramfs_reclaim,
- .getattr = initramfs_getattr
+ .getattr = initramfs_getattr,
+ .create = NULL,
};
const struct vfsops g_initramfs_vfsops = {
diff --git a/sys/fs/tmpfs.c b/sys/fs/tmpfs.c
new file mode 100644
index 0000000..9dce89a
--- /dev/null
+++ b/sys/fs/tmpfs.c
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * 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.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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 <sys/mount.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/panic.h>
+#include <sys/vnode.h>
+#include <vm/dynalloc.h>
+#include <vm/vm_obj.h>
+#include <vm/vm_page.h>
+#include <vm/vm_pager.h>
+#include <fs/tmpfs.h>
+#include <string.h>
+
+#define ROOT_RPATH "/tmp"
+#define TMPFS_BSIZE DEFAULT_PAGESIZE
+
+#define pr_trace(fmt, ...) kprintf("tmpfs: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+static TAILQ_HEAD(, tmpfs_node) root;
+
+/*
+ * Generate a vnode for a specific tmpfs
+ * node.
+ */
+static int
+tmpfs_ref(struct tmpfs_node *np)
+{
+ struct vnode *vp = NULL;
+ int retval = 0;
+
+ if (np->vp == NULL) {
+ spinlock_acquire(&np->lock);
+ retval = vfs_alloc_vnode(&vp, np->type);
+ np->vp = vp;
+ spinlock_release(&np->lock);
+ }
+
+ if (vp != NULL) {
+ vp->data = np;
+ vp->vops = &g_tmpfs_vops;
+ }
+
+ return retval;
+}
+
+/*
+ * Perform lookup within the tmpfs namespace
+ *
+ * XXX: This operations is serialized
+ * TODO: Support multiple directories (only fs root now)
+ *
+ * @rpath: /tmp/ relative path to lookup
+ * @res: The result is written here (must NOT be NULL)
+ */
+static int
+tmpfs_do_lookup(const char *rpath, struct tmpfs_node **res)
+{
+ struct tmpfs_node *cnp;
+ struct tmpfs_node *dirent;
+ int error = 0;
+
+ /*
+ * If the directory is the node that we are
+ * looking for, return it. But if it is not
+ * and it is empty then there is nothing
+ * we can do.
+ */
+ cnp = TAILQ_FIRST(&root);
+ if (strcmp(cnp->rpath, rpath) == 0) {
+ *res = cnp;
+ return 0;
+ }
+ if (TAILQ_NELEM(&cnp->dirents) == 0) {
+ return -ENOENT;
+ }
+
+ /*
+ * Go through each tmpfs dirent to see if we can
+ * find the file we are looking for.
+ */
+ spinlock_acquire(&cnp->lock);
+ dirent = TAILQ_FIRST(&cnp->dirents);
+ while (dirent != NULL) {
+ if (strcmp(dirent->rpath, rpath) == 0) {
+ break;
+ }
+
+ dirent = TAILQ_NEXT(dirent, link);
+ }
+
+ spinlock_release(&cnp->lock);
+ if (res == NULL) {
+ return -ENOENT;
+ }
+
+ if ((error = tmpfs_ref(dirent)) != 0) {
+ return error;
+ }
+
+ *res = dirent;
+ return 0;
+}
+
+/*
+ * TMPFS lookup callback for the VFS
+ *
+ * Takes some arguments and returns a vnode
+ * in args->vpp
+ */
+static int
+tmpfs_lookup(struct vop_lookup_args *args)
+{
+ struct tmpfs_node *np;
+ int error;
+
+ if (args == NULL) {
+ return -EINVAL;
+ }
+ if (args->name == NULL) {
+ return -EINVAL;
+ }
+
+ /*
+ * Attempt to find the node we want, if it already
+ * has a vnode attached to it then that's something we
+ * want. However we should allocate a new vnode if we
+ * need to.
+ */
+ error = tmpfs_do_lookup(args->name, &np);
+ if (error != 0) {
+ return error;
+ }
+
+ *args->vpp = np->vp;
+ return 0;
+}
+
+/*
+ * TMPFS create callback for the VFS
+ *
+ * Creates a new TMPFS node
+ */
+static int
+tmpfs_create(struct vop_create_args *args)
+{
+ const char *pcp = args->path; /* Stay away from boat, kids */
+ struct vnode *dirvp;
+ struct tmpfs_node *np;
+ struct tmpfs_node *root_np;
+ int error;
+
+ /* Validate inputs */
+ if (args == NULL)
+ return -EINVAL;
+ if (pcp == NULL)
+ return -EIO;
+ if ((dirvp = args->dirvp) == NULL)
+ return -EIO;
+
+ /* Remove the leading "/tmp/" */
+ pcp += sizeof(ROOT_RPATH);
+ if (*pcp == '\0') {
+ return -ENOENT;
+ }
+
+ np = dynalloc(sizeof(*np));
+ if (np == NULL) {
+ return -ENOMEM;
+ }
+
+ memset(np, 0, sizeof(*np));
+
+ /*
+ * TODO: Support multiple directories.
+ *
+ * XXX: We currently only create a TMPFS_REG node as
+ * to keep things initially simple.
+ */
+ root_np = TAILQ_FIRST(&root);
+ np->dirvp = dirvp;
+ np->type = TMPFS_REG;
+ memcpy(np->rpath, pcp, strlen(pcp) + 1);
+ TAILQ_INSERT_TAIL(&root_np->dirents, np, link);
+
+ if ((error = tmpfs_ref(np)) != 0) {
+ return error;
+ }
+
+ *args->vpp = np->vp;
+ return 0;
+}
+
+/*
+ * TMPFS write callback for VFS
+ *
+ * Node buffers are orthogonally managed. That is, each
+ * node has their own respective data buffers. When
+ * writing to a node, we need to take into account of the
+ * length of the buffer. This value may need to expanded as
+ * well as more pages allocated if the amount of bytes to
+ * be written exceeds it.
+ */
+static int
+tmpfs_write(struct vnode *vp, struct sio_txn *sio)
+{
+ struct tmpfs_node *np;
+ uint8_t *buf;
+
+ if (sio->buf == NULL || sio->len == 0) {
+ return -EINVAL;
+ }
+
+ /* This should not happen but you never know */
+ if ((np = vp->data) == NULL) {
+ return -EIO;
+ }
+
+ /* Is this even a regular file? */
+ if (np->type != VREG) {
+ return -EISDIR;
+ }
+
+ spinlock_acquire(&np->lock);
+
+ /*
+ * If the residual byte count is zero, we need to
+ * allocate a new page to be used. However if this
+ * fails we'll throw back an -ENOMEM.
+ */
+ if (np->len == 0) {
+ np->data = dynalloc(TMPFS_BSIZE);
+ if (np->data == NULL) {
+ spinlock_release(&np->lock);
+ return -ENOMEM;
+ }
+ np->len += TMPFS_BSIZE;
+ }
+
+ /*
+ * If the length to be written exceeds the residual byte
+ * count. We will try to expand the buffer by the page
+ * size. However, if this fails, we will split the write
+ * into a suitable size that does not overflow what we
+ * have left.
+ */
+ if ((sio->offset + sio->len) > np->len) {
+ np->data = dynrealloc(np->data, (sio->offset + sio->len));
+ if (np->data == NULL) {
+ sio->len = np->len;
+ } else {
+ np->len = sio->offset + sio->len;
+ }
+ }
+
+ buf = np->data;
+ memcpy(&buf[sio->offset], sio->buf, sio->len);
+ spinlock_release(&np->lock);
+ kprintf("%d\n", sio->len);
+ return sio->len;
+}
+
+/*
+ * TMPFS read callback for VFS
+ */
+static int
+tmpfs_read(struct vnode *vp, struct sio_txn *sio)
+{
+ struct tmpfs_node *np;
+ uint8_t *buf;
+
+ if (sio->buf == NULL || sio->len == 0) {
+ return -EINVAL;
+ }
+
+ /* This should not happen but you never know */
+ if ((np = vp->data) == NULL) {
+ return -EIO;
+ }
+
+ /* Is this even a regular file? */
+ if (np->type != VREG) {
+ return -EISDIR;
+ }
+
+ spinlock_acquire(&np->lock);
+
+ if (sio->offset > np->len - 1) {
+ return -EINVAL;
+ }
+ if ((sio->offset + sio->len) > np->len) {
+ sio->len = np->len;
+ }
+
+ buf = np->data;
+ memcpy(sio->buf, &buf[sio->offset], sio->len);
+ spinlock_release(&np->lock);
+ return sio->len;
+}
+
+static int
+tmpfs_reclaim(struct vnode *vp)
+{
+ struct tmpfs_node *np;
+
+ if ((np = vp->data) == NULL) {
+ return 0;
+ }
+
+ np->vp = NULL;
+ vp->data = NULL;
+ return 0;
+}
+
+static int
+tmpfs_init(struct fs_info *fip)
+{
+ struct tmpfs_node *np;
+ struct vnode *vp;
+ struct mount *mp;
+ int error;
+
+ /* Grab ourselves a new vnode for /tmp */
+ if ((error = vfs_alloc_vnode(&vp, VDIR)) != 0) {
+ return error;
+ }
+
+ vp->vops = &g_tmpfs_vops;
+ mp = vfs_alloc_mount(vp, fip);
+ vfs_name_mount(mp, "tmp");
+ TAILQ_INSERT_TAIL(&g_mountlist, mp, mnt_list);
+
+ /* Pre-allocate the first entry */
+ if ((np = dynalloc(sizeof(*np))) == NULL) {
+ return -ENOMEM;
+ }
+
+ TAILQ_INIT(&root);
+ memset(np, 0, sizeof(*np));
+
+ memcpy(np->rpath, ROOT_RPATH, sizeof(ROOT_RPATH));
+ np->type = TMPFS_DIR;
+ TAILQ_INIT(&np->dirents);
+ TAILQ_INSERT_TAIL(&root, np, link);
+ return 0;
+}
+
+const struct vops g_tmpfs_vops = {
+ .lookup = tmpfs_lookup,
+ .getattr = NULL,
+ .read = tmpfs_read,
+ .write = tmpfs_write,
+ .reclaim = tmpfs_reclaim,
+ .create = tmpfs_create,
+};
+
+const struct vfsops g_tmpfs_vfsops = {
+ .init = tmpfs_init
+};