diff options
Diffstat (limited to 'sys/fs')
-rw-r--r-- | sys/fs/ctlfs.c | 389 | ||||
-rw-r--r-- | sys/fs/devfs.c | 26 | ||||
-rw-r--r-- | sys/fs/initramfs.c | 113 | ||||
-rw-r--r-- | sys/fs/tmpfs.c | 390 |
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 +}; |