aboutsummaryrefslogtreecommitdiff
path: root/kernel/fs/ramfs/ramfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/fs/ramfs/ramfs.c')
-rw-r--r--kernel/fs/ramfs/ramfs.c852
1 files changed, 852 insertions, 0 deletions
diff --git a/kernel/fs/ramfs/ramfs.c b/kernel/fs/ramfs/ramfs.c
new file mode 100644
index 0000000..72547c4
--- /dev/null
+++ b/kernel/fs/ramfs/ramfs.c
@@ -0,0 +1,852 @@
+/*
+ * This is a special filesystem designed to be a test filesystem before s5fs has
+ * been written. It is an in-memory filesystem that supports almost all of the
+ * vnode operations. It has the following restrictions:
+ *
+ * o File sizes are limited to a single page (4096 bytes) in order
+ * to keep the code simple.
+ *
+ * o There is no support for fill_pframe, etc.
+ *
+ * o There is a maximum directory size limit
+ *
+ * o There is a maximum number of files/directories limit
+ */
+
+#include "fs/ramfs/ramfs.h"
+#include "errno.h"
+#include "fs/dirent.h"
+#include "fs/stat.h"
+#include "fs/vfs.h"
+#include "fs/vnode.h"
+#include "globals.h"
+#include "kernel.h"
+#include "mm/kmalloc.h"
+#include "mm/slab.h"
+#include "util/debug.h"
+#include "util/string.h"
+
+/*
+ * Filesystem operations
+ */
+static void ramfs_read_vnode(fs_t *fs, vnode_t *vn);
+
+static void ramfs_delete_vnode(fs_t *fs, vnode_t *vn);
+
+static long ramfs_umount(fs_t *fs);
+
+static fs_ops_t ramfs_ops = {.read_vnode = ramfs_read_vnode,
+ .delete_vnode = ramfs_delete_vnode,
+ .umount = ramfs_umount};
+
+/*
+ * vnode operations
+ */
+static ssize_t ramfs_read(vnode_t *file, size_t offset, void *buf,
+ size_t count);
+
+static ssize_t ramfs_write(vnode_t *file, size_t offset, const void *buf,
+ size_t count);
+
+/* getpage */
+static ssize_t ramfs_create(vnode_t *dir, const char *name, size_t name_len,
+ vnode_t **result);
+
+static ssize_t ramfs_mknod(struct vnode *dir, const char *name, size_t name_len,
+ int mode, devid_t devid, struct vnode **out);
+
+static ssize_t ramfs_lookup(vnode_t *dir, const char *name, size_t namelen,
+ vnode_t **out);
+
+static long ramfs_link(vnode_t *dir, const char *name, size_t namelen,
+ vnode_t *child);
+
+static ssize_t ramfs_unlink(vnode_t *dir, const char *name, size_t name_len);
+
+static ssize_t ramfs_rename(vnode_t *olddir, const char *oldname,
+ size_t oldnamelen, vnode_t *newdir,
+ const char *newname, size_t newnamelen);
+
+static ssize_t ramfs_mkdir(vnode_t *dir, const char *name, size_t name_len,
+ struct vnode **out);
+
+static ssize_t ramfs_rmdir(vnode_t *dir, const char *name, size_t name_len);
+
+static ssize_t ramfs_readdir(vnode_t *dir, size_t offset, struct dirent *d);
+
+static ssize_t ramfs_stat(vnode_t *file, stat_t *buf);
+
+static void ramfs_truncate_file(vnode_t *file);
+
+static vnode_ops_t ramfs_dir_vops = {.read = NULL,
+ .write = NULL,
+ .mmap = NULL,
+ .mknod = ramfs_mknod,
+ .lookup = ramfs_lookup,
+ .link = ramfs_link,
+ .unlink = ramfs_unlink,
+ .rename = ramfs_rename,
+ .mkdir = ramfs_mkdir,
+ .rmdir = ramfs_rmdir,
+ .readdir = ramfs_readdir,
+ .stat = ramfs_stat,
+ .acquire = NULL,
+ .release = NULL,
+ .get_pframe = NULL,
+ .fill_pframe = NULL,
+ .flush_pframe = NULL,
+ .truncate_file = NULL};
+
+static vnode_ops_t ramfs_file_vops = {.read = ramfs_read,
+ .write = ramfs_write,
+ .mmap = NULL,
+ .mknod = NULL,
+ .lookup = NULL,
+ .link = NULL,
+ .unlink = NULL,
+ .mkdir = NULL,
+ .rmdir = NULL,
+ .stat = ramfs_stat,
+ .acquire = NULL,
+ .release = NULL,
+ .get_pframe = NULL,
+ .fill_pframe = NULL,
+ .flush_pframe = NULL,
+ .truncate_file = ramfs_truncate_file};
+
+/*
+ * The ramfs 'inode' structure
+ */
+typedef struct ramfs_inode
+{
+ size_t rf_size; /* Total file size */
+ ino_t rf_ino; /* Inode number */
+ char *rf_mem; /* Memory for this file (1 page) */
+ ssize_t rf_mode; /* Type of file */
+ ssize_t rf_linkcount; /* Number of links to this file */
+} ramfs_inode_t;
+
+#define RAMFS_TYPE_DATA 0
+#define RAMFS_TYPE_DIR 1
+#define RAMFS_TYPE_CHR 2
+#define RAMFS_TYPE_BLK 3
+
+#define VNODE_TO_RAMFSINODE(vn) ((ramfs_inode_t *)(vn)->vn_i)
+#define VNODE_TO_RAMFS(vn) ((ramfs_t *)(vn)->vn_fs->fs_i)
+#define VNODE_TO_DIRENT(vn) ((ramfs_dirent_t *)VNODE_TO_RAMFSINODE(vn)->rf_mem)
+
+/*
+ * ramfs filesystem structure
+ */
+#define RAMFS_MAX_FILES 64
+
+typedef struct ramfs
+{
+ ramfs_inode_t *rfs_inodes[RAMFS_MAX_FILES]; /* Array of all files */
+} ramfs_t;
+
+/*
+ * For directories, we simply store an array of (ino, name) pairs in the
+ * memory portion of the inode.
+ */
+typedef struct ramfs_dirent
+{
+ ssize_t rd_ino; /* Inode number of this entry */
+ char rd_name[NAME_LEN]; /* Name of this entry */
+} ramfs_dirent_t;
+
+#define RAMFS_MAX_DIRENT ((size_t)(PAGE_SIZE / sizeof(ramfs_dirent_t)))
+
+/* Helper functions */
+static ssize_t ramfs_alloc_inode(fs_t *fs, ssize_t type, devid_t devid)
+{
+ ramfs_t *rfs = (ramfs_t *)fs->fs_i;
+ KASSERT((RAMFS_TYPE_DATA == type) || (RAMFS_TYPE_DIR == type) ||
+ (RAMFS_TYPE_CHR == type) || (RAMFS_TYPE_BLK == type));
+ /* Find a free inode */
+ ssize_t i;
+ for (i = 0; i < RAMFS_MAX_FILES; i++)
+ {
+ if (NULL == rfs->rfs_inodes[i])
+ {
+ ramfs_inode_t *inode;
+ if (NULL == (inode = kmalloc(sizeof(ramfs_inode_t))))
+ {
+ return -ENOSPC;
+ }
+
+ if (RAMFS_TYPE_CHR == type || RAMFS_TYPE_BLK == type)
+ {
+ /* Don't need any space in memory, so put devid in here */
+ inode->rf_mem = (char *)(uint64_t)devid;
+ }
+ else
+ {
+ /* We allocate space for the file's contents immediately */
+ if (NULL == (inode->rf_mem = page_alloc()))
+ {
+ kfree(inode);
+ return -ENOSPC;
+ }
+ memset(inode->rf_mem, 0, PAGE_SIZE);
+ }
+ inode->rf_size = 0;
+ inode->rf_ino = i;
+ inode->rf_mode = type;
+ inode->rf_linkcount = 1;
+
+ /* Install in table and return */
+ rfs->rfs_inodes[i] = inode;
+ return i;
+ }
+ }
+ return -ENOSPC;
+}
+
+/*
+ * Function implementations
+ */
+
+long ramfs_mount(struct fs *fs)
+{
+ /* Allocate filesystem */
+ ramfs_t *rfs = kmalloc(sizeof(ramfs_t));
+ if (NULL == rfs)
+ {
+ return -ENOMEM;
+ }
+
+ memset(rfs->rfs_inodes, 0, sizeof(rfs->rfs_inodes));
+
+ fs->fs_i = rfs;
+ fs->fs_ops = &ramfs_ops;
+
+ /* Set up root inode */
+ ssize_t root_ino;
+ if (0 > (root_ino = ramfs_alloc_inode(fs, RAMFS_TYPE_DIR, 0)))
+ {
+ return root_ino;
+ }
+
+ slab_allocator_t *allocator =
+ slab_allocator_create("ramfs_node", sizeof(vnode_t));
+ fs->fs_vnode_allocator = allocator;
+ KASSERT(allocator);
+
+ KASSERT(0 == root_ino);
+ ramfs_inode_t *root = rfs->rfs_inodes[root_ino];
+
+ /* Set up '.' and '..' in the root directory */
+ ramfs_dirent_t *rootdent = (ramfs_dirent_t *)root->rf_mem;
+ rootdent->rd_ino = 0;
+ strcpy(rootdent->rd_name, ".");
+ rootdent++;
+ rootdent->rd_ino = 0;
+ strcpy(rootdent->rd_name, "..");
+
+ /* Increase root inode size accordingly */
+ root->rf_size = 2 * sizeof(ramfs_dirent_t);
+
+ /* Put the root in the inode table */
+ rfs->rfs_inodes[0] = root;
+
+ /* And vget the root vnode */
+ fs->fs_root = vget(fs, 0);
+
+ return 0;
+}
+
+static void ramfs_read_vnode(fs_t *fs, vnode_t *vn)
+{
+ ramfs_t *rfs = VNODE_TO_RAMFS(vn);
+ ramfs_inode_t *inode = rfs->rfs_inodes[vn->vn_vno];
+ KASSERT(inode && inode->rf_ino == vn->vn_vno);
+
+ inode->rf_linkcount++;
+
+ vn->vn_i = inode;
+ vn->vn_len = inode->rf_size;
+
+ switch (inode->rf_mode)
+ {
+ case RAMFS_TYPE_DATA:
+ vn->vn_mode = S_IFREG;
+ vn->vn_ops = &ramfs_file_vops;
+ break;
+ case RAMFS_TYPE_DIR:
+ vn->vn_mode = S_IFDIR;
+ vn->vn_ops = &ramfs_dir_vops;
+ break;
+ case RAMFS_TYPE_CHR:
+ vn->vn_mode = S_IFCHR;
+ vn->vn_ops = NULL;
+ vn->vn_devid = (devid_t)(uint64_t)(inode->rf_mem);
+ break;
+ case RAMFS_TYPE_BLK:
+ vn->vn_mode = S_IFBLK;
+ vn->vn_ops = NULL;
+ vn->vn_devid = (devid_t)(uint64_t)(inode->rf_mem);
+ break;
+ default:
+ panic("inode %ld has unknown/invalid type %ld!!\n",
+ (ssize_t)vn->vn_vno, (ssize_t)inode->rf_mode);
+ }
+}
+
+static void ramfs_delete_vnode(fs_t *fs, vnode_t *vn)
+{
+ ramfs_inode_t *inode = VNODE_TO_RAMFSINODE(vn);
+ ramfs_t *rfs = VNODE_TO_RAMFS(vn);
+
+ if (0 == --inode->rf_linkcount)
+ {
+ KASSERT(rfs->rfs_inodes[vn->vn_vno] == inode);
+
+ rfs->rfs_inodes[vn->vn_vno] = NULL;
+ if (inode->rf_mode == RAMFS_TYPE_DATA ||
+ inode->rf_mode == RAMFS_TYPE_DIR)
+ {
+ page_free(inode->rf_mem);
+ }
+ /* otherwise, inode->rf_mem is a devid */
+
+ kfree(inode);
+ }
+}
+
+static ssize_t ramfs_umount(fs_t *fs)
+{
+ /* We don't need to do any flushing or anything as everything is in memory.
+ * Just free all of our allocated memory */
+ ramfs_t *rfs = (ramfs_t *)fs->fs_i;
+
+ vput(&fs->fs_root);
+
+ /* Free all the inodes */
+ ssize_t i;
+ for (i = 0; i < RAMFS_MAX_FILES; i++)
+ {
+ if (NULL != rfs->rfs_inodes[i])
+ {
+ if (NULL != rfs->rfs_inodes[i]->rf_mem &&
+ (rfs->rfs_inodes[i]->rf_mode == RAMFS_TYPE_DATA ||
+ rfs->rfs_inodes[i]->rf_mode == RAMFS_TYPE_DIR))
+ {
+ page_free(rfs->rfs_inodes[i]->rf_mem);
+ }
+ kfree(rfs->rfs_inodes[i]);
+ }
+ }
+
+ return 0;
+}
+
+static ssize_t ramfs_create(vnode_t *dir, const char *name, size_t name_len,
+ vnode_t **result)
+{
+ vnode_t *vn;
+ size_t i;
+ ramfs_dirent_t *entry;
+
+ /* Look for space in the directory */
+ entry = VNODE_TO_DIRENT(dir);
+ for (i = 0; i < RAMFS_MAX_DIRENT; i++, entry++)
+ {
+ if (!entry->rd_name[0])
+ {
+ break;
+ }
+ }
+
+ if (i == RAMFS_MAX_DIRENT)
+ {
+ return -ENOSPC;
+ }
+
+ /* Allocate an inode */
+ ssize_t ino;
+ if (0 > (ino = ramfs_alloc_inode(dir->vn_fs, RAMFS_TYPE_DATA, 0)))
+ {
+ return ino;
+ }
+
+ /* Get a vnode, set entry in directory */
+ vn = vget(dir->vn_fs, (ino_t)ino);
+
+ entry->rd_ino = vn->vn_vno;
+ strncpy(entry->rd_name, name, MIN(name_len, NAME_LEN - 1));
+ entry->rd_name[MIN(name_len, NAME_LEN - 1)] = '\0';
+
+ VNODE_TO_RAMFSINODE(dir)->rf_size += sizeof(ramfs_dirent_t);
+
+ *result = vn;
+
+ return 0;
+}
+
+static ssize_t ramfs_mknod(struct vnode *dir, const char *name, size_t name_len,
+ int mode, devid_t devid, struct vnode **out)
+{
+ size_t i;
+ ramfs_dirent_t *entry;
+
+ /* Look for space in the directory */
+ entry = VNODE_TO_DIRENT(dir);
+ for (i = 0; i < RAMFS_MAX_DIRENT; i++, entry++)
+ {
+ if (!entry->rd_name[0])
+ {
+ break;
+ }
+ }
+
+ if (i == RAMFS_MAX_DIRENT)
+ {
+ return -ENOSPC;
+ }
+
+ ssize_t ino;
+ if (S_ISCHR(mode))
+ {
+ ino = ramfs_alloc_inode(dir->vn_fs, RAMFS_TYPE_CHR, devid);
+ }
+ else if (S_ISBLK(mode))
+ {
+ ino = ramfs_alloc_inode(dir->vn_fs, RAMFS_TYPE_BLK, devid);
+ }
+ else if (S_ISREG(mode))
+ {
+ ino = ramfs_alloc_inode(dir->vn_fs, RAMFS_TYPE_DATA, devid);
+ }
+ else
+ {
+ panic("Invalid mode!\n");
+ }
+
+ if (ino < 0)
+ {
+ return ino;
+ }
+
+ /* Set entry in directory */
+ entry->rd_ino = ino;
+ strncpy(entry->rd_name, name, MIN(name_len, NAME_LEN - 1));
+ entry->rd_name[MIN(name_len, NAME_LEN - 1)] = '\0';
+
+ VNODE_TO_RAMFSINODE(dir)->rf_size += sizeof(ramfs_dirent_t);
+
+ vnode_t *child = vget(dir->vn_fs, ino);
+
+ dbg(DBG_VFS, "creating ino(%ld), vno(%d) with path: %s\n", ino,
+ child->vn_vno, entry->rd_name);
+
+ KASSERT(child);
+ *out = child;
+ return 0;
+}
+
+static ssize_t ramfs_lookup(vnode_t *dir, const char *name, size_t namelen,
+ vnode_t **out)
+{
+ size_t i;
+ ramfs_inode_t *inode = VNODE_TO_RAMFSINODE(dir);
+ ramfs_dirent_t *entry = (ramfs_dirent_t *)inode->rf_mem;
+
+ for (i = 0; i < RAMFS_MAX_DIRENT; i++, entry++)
+ {
+ if (name_match(entry->rd_name, name, namelen))
+ {
+ if (dir->vn_vno != entry->rd_ino)
+ {
+ fs_t *fs = (dir)->vn_fs;
+ *out = vget(fs, entry->rd_ino);
+ }
+ else
+ {
+ vref(dir);
+ *out = dir;
+ }
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+
+static ssize_t ramfs_find_dirent(vnode_t *dir, const char *name,
+ size_t namelen)
+{
+ size_t i;
+ ramfs_inode_t *inode = VNODE_TO_RAMFSINODE(dir);
+ ramfs_dirent_t *entry = (ramfs_dirent_t *)inode->rf_mem;
+
+ for (i = 0; i < RAMFS_MAX_DIRENT; i++, entry++)
+ {
+ if (name_match(entry->rd_name, name, namelen))
+ {
+ return entry->rd_ino;
+ }
+ }
+
+ return -ENOENT;
+}
+
+static ssize_t ramfs_append_dirent(vnode_t *dir, const char *name,
+ size_t namelen, vnode_t *child)
+{
+ vnode_t *vn;
+ size_t i;
+ ramfs_dirent_t *entry;
+
+ KASSERT(child->vn_fs == dir->vn_fs);
+
+ /* Look for space in the directory */
+ entry = VNODE_TO_DIRENT(dir);
+ for (i = 0; i < RAMFS_MAX_DIRENT; i++, entry++)
+ {
+ if (name_match(entry->rd_name, name, namelen))
+ {
+ return -EEXIST;
+ }
+
+ if (!entry->rd_name[0])
+ {
+ break;
+ }
+ }
+
+ if (i == RAMFS_MAX_DIRENT)
+ {
+ return -ENOSPC;
+ }
+
+ /* Set entry in parent */
+ entry->rd_ino = child->vn_vno;
+ strncpy(entry->rd_name, name, MIN(namelen, NAME_LEN - 1));
+ entry->rd_name[MIN(namelen, NAME_LEN - 1)] = '\0';
+
+ VNODE_TO_RAMFSINODE(dir)->rf_size += sizeof(ramfs_dirent_t);
+
+ /* Increase linkcount */
+ VNODE_TO_RAMFSINODE(child)->rf_linkcount++;
+
+ return 0;
+}
+
+static ssize_t ramfs_delete_dirent(vnode_t *dir, const char *name,
+ size_t namelen, vnode_t *child)
+{
+ int found = 0;
+ size_t i;
+ ramfs_dirent_t *entry = VNODE_TO_DIRENT(dir);
+ for (i = 0; i < RAMFS_MAX_DIRENT; i++, entry++)
+ {
+ if (name_match(entry->rd_name, name, namelen))
+ {
+ found = 1;
+ entry->rd_name[0] = '\0';
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ return -EEXIST;
+ }
+
+ VNODE_TO_RAMFSINODE(dir)->rf_size -= sizeof(ramfs_dirent_t);
+ VNODE_TO_RAMFSINODE(child)->rf_linkcount--;
+
+ return 0;
+}
+
+static long ramfs_link(vnode_t *dir, const char *name, size_t namelen,
+ vnode_t *child)
+{
+ return ramfs_append_dirent(dir, name, namelen, child);
+}
+
+static ssize_t ramfs_unlink(vnode_t *dir, const char *name, size_t namelen)
+{
+ ssize_t ret;
+ size_t i;
+ ramfs_dirent_t *entry;
+
+ vnode_t *vn = dir;
+
+ long ino = ramfs_find_dirent(dir, name, namelen);
+ if (ino < 0)
+ {
+ return ino;
+ }
+
+ vnode_t *child = vget_locked(dir->vn_fs, (ino_t)ino);
+ KASSERT(!S_ISDIR(child->vn_mode) && "handled at VFS level");
+
+ ret = ramfs_delete_dirent(dir, name, namelen, child);
+ KASSERT(ret == 0);
+
+ vput_locked(&child);
+
+ return 0;
+}
+
+static ssize_t ramfs_rename(vnode_t *olddir, const char *oldname,
+ size_t oldnamelen, vnode_t *newdir,
+ const char *newname, size_t newnamelen)
+{
+ long ino = ramfs_find_dirent(olddir, oldname, oldnamelen);
+ if (ino < 0)
+ {
+ return ino;
+ }
+
+ vnode_t *oldvn = vget_locked(olddir->vn_fs, (ino_t)ino);
+ if (S_ISDIR(oldvn->vn_mode))
+ {
+ vput_locked(&oldvn);
+ return -EPERM;
+ }
+ if (S_ISDIR(oldvn->vn_mode))
+ {
+ vput_locked(&oldvn);
+ return -EISDIR;
+ }
+
+ /* Determine if an entry corresponding to `newname` already exists */
+ ino = ramfs_find_dirent(newdir, newname, newnamelen);
+ if (ino != -ENOENT)
+ {
+ if (ino < 0)
+ {
+ return ino;
+ }
+ return -EEXIST;
+ }
+
+ ssize_t ret = ramfs_append_dirent(newdir, newname, newnamelen, oldvn);
+ if (ret < 0)
+ {
+ vput_locked(&oldvn);
+ return ret;
+ }
+
+ ret = ramfs_delete_dirent(olddir, oldname, oldnamelen, oldvn);
+ vput_locked(&oldvn);
+
+ return ret;
+}
+
+static ssize_t ramfs_mkdir(vnode_t *dir, const char *name, size_t name_len,
+ struct vnode **out)
+{
+ vnode_t *vn;
+ size_t i;
+ ramfs_dirent_t *entry;
+
+ /* Look for space in the directory */
+ entry = VNODE_TO_DIRENT(dir);
+ for (i = 0; i < RAMFS_MAX_DIRENT; i++, entry++)
+ {
+ if (!entry->rd_name[0])
+ {
+ break;
+ }
+ }
+
+ if (i == RAMFS_MAX_DIRENT)
+ {
+ return -ENOSPC;
+ }
+
+ /* Allocate an inode */
+ ssize_t ino;
+ if (0 > (ino = ramfs_alloc_inode(dir->vn_fs, RAMFS_TYPE_DIR, 0)))
+ {
+ return ino;
+ }
+
+ /* Set entry in parent */
+ entry->rd_ino = ino;
+ strncpy(entry->rd_name, name, MIN(name_len, NAME_LEN - 1));
+ entry->rd_name[MIN(name_len, NAME_LEN - 1)] = '\0';
+
+ VNODE_TO_RAMFSINODE(dir)->rf_size += sizeof(ramfs_dirent_t);
+
+ /* Set up '.' and '..' in the directory */
+ entry = (ramfs_dirent_t *)VNODE_TO_RAMFS(dir)->rfs_inodes[ino]->rf_mem;
+ entry->rd_ino = ino;
+ strcpy(entry->rd_name, ".");
+ entry++;
+ entry->rd_ino = dir->vn_vno;
+ strcpy(entry->rd_name, "..");
+
+ /* Increase inode size accordingly */
+ VNODE_TO_RAMFS(dir)->rfs_inodes[ino]->rf_size = 2 * sizeof(ramfs_dirent_t);
+
+ /* This probably can't fail... (unless OOM :/) */
+ *out = vget(dir->vn_fs, ino);
+
+ return 0;
+}
+
+static ssize_t ramfs_rmdir(vnode_t *dir, const char *name, size_t name_len)
+{
+ ssize_t ret;
+ size_t i;
+ ramfs_dirent_t *entry;
+
+ KASSERT(!name_match(".", name, name_len) &&
+ !name_match("..", name, name_len));
+
+ long ino = ramfs_find_dirent(dir, name, name_len);
+ if (ino < 0)
+ {
+ return ino;
+ }
+
+ vnode_t *child = vget_locked(dir->vn_fs, (ino_t)ino);
+ if (!S_ISDIR(child->vn_mode))
+ {
+ vput_locked(&child);
+ return -ENOTDIR;
+ }
+
+ /* We have to make sure that this directory is empty */
+ entry = VNODE_TO_DIRENT(child);
+ for (i = 0; i < RAMFS_MAX_DIRENT; i++, entry++)
+ {
+ if (!strcmp(entry->rd_name, ".") || !strcmp(entry->rd_name, ".."))
+ {
+ continue;
+ }
+
+ if (entry->rd_name[0])
+ {
+ vput_locked(&child);
+ return -ENOTEMPTY;
+ }
+ }
+
+ /* Finally, remove the entry from the parent directory */
+ entry = VNODE_TO_DIRENT(dir);
+ for (i = 0; i < RAMFS_MAX_DIRENT; i++, entry++)
+ {
+ if (name_match(entry->rd_name, name, name_len))
+ {
+ entry->rd_name[0] = '\0';
+ break;
+ }
+ }
+ VNODE_TO_RAMFSINODE(dir)->rf_size -= sizeof(ramfs_dirent_t);
+
+ VNODE_TO_RAMFSINODE(child)->rf_linkcount--;
+ vput_locked(&child);
+
+ return 0;
+}
+
+static ssize_t ramfs_read(vnode_t *file, size_t offset, void *buf,
+ size_t count)
+{
+ ssize_t ret;
+ ramfs_inode_t *inode = VNODE_TO_RAMFSINODE(file);
+
+ KASSERT(!S_ISDIR(file->vn_mode));
+
+ if (offset > inode->rf_size)
+ {
+ ret = 0;
+ }
+ else if (offset + count > inode->rf_size)
+ {
+ ret = inode->rf_size - offset;
+ }
+ else
+ {
+ ret = count;
+ }
+
+ memcpy(buf, inode->rf_mem + offset, ret);
+ return ret;
+}
+
+static ssize_t ramfs_write(vnode_t *file, size_t offset, const void *buf,
+ size_t count)
+{
+ ssize_t ret;
+ ramfs_inode_t *inode = VNODE_TO_RAMFSINODE(file);
+
+ KASSERT(!S_ISDIR(file->vn_mode));
+
+ ret = MIN((size_t)count, (size_t)PAGE_SIZE - offset);
+ memcpy(inode->rf_mem + offset, buf, ret);
+
+ KASSERT(file->vn_len == inode->rf_size);
+ file->vn_len = MAX(file->vn_len, offset + ret);
+ inode->rf_size = file->vn_len;
+
+ return ret;
+}
+
+static ssize_t ramfs_readdir(vnode_t *dir, size_t offset, struct dirent *d)
+{
+ ssize_t ret = 0;
+ ramfs_dirent_t *dir_entry, *targ_entry;
+
+ KASSERT(S_ISDIR(dir->vn_mode));
+ KASSERT(0 == offset % sizeof(ramfs_dirent_t));
+
+ dir_entry = VNODE_TO_DIRENT(dir);
+ dir_entry = (ramfs_dirent_t *)(((char *)dir_entry) + offset);
+ targ_entry = dir_entry;
+
+ while ((offset < (size_t)(RAMFS_MAX_DIRENT * sizeof(ramfs_dirent_t))) &&
+ (!targ_entry->rd_name[0]))
+ {
+ ++targ_entry;
+ offset += sizeof(ramfs_dirent_t);
+ }
+
+ if (offset >= (size_t)(RAMFS_MAX_DIRENT * sizeof(ramfs_dirent_t)))
+ {
+ return 0;
+ }
+
+ ret = sizeof(ramfs_dirent_t) +
+ (targ_entry - dir_entry) * sizeof(ramfs_dirent_t);
+
+ d->d_ino = targ_entry->rd_ino;
+ d->d_off = 0; /* unused */
+ strncpy(d->d_name, targ_entry->rd_name, NAME_LEN - 1);
+ d->d_name[NAME_LEN - 1] = '\0';
+ return ret;
+}
+
+static ssize_t ramfs_stat(vnode_t *file, stat_t *buf)
+{
+ ramfs_inode_t *i = VNODE_TO_RAMFSINODE(file);
+ memset(buf, 0, sizeof(stat_t));
+ buf->st_mode = file->vn_mode;
+ buf->st_ino = (ssize_t)file->vn_vno;
+ buf->st_dev = 0;
+ if (file->vn_mode == S_IFCHR || file->vn_mode == S_IFBLK)
+ {
+ buf->st_rdev = (ssize_t)i->rf_mem;
+ }
+ buf->st_nlink = i->rf_linkcount - 1;
+ buf->st_size = (ssize_t)i->rf_size;
+ buf->st_blksize = (ssize_t)PAGE_SIZE;
+ buf->st_blocks = 1;
+
+ return 0;
+}
+
+static void ramfs_truncate_file(vnode_t *file)
+{
+ KASSERT(S_ISREG(file->vn_mode) && "This routine should only be called for regular files");
+ ramfs_inode_t *i = VNODE_TO_RAMFSINODE(file);
+ i->rf_size = 0;
+ file->vn_len = 0;
+ memset(i->rf_mem, 0, PAGE_SIZE);
+} \ No newline at end of file