aboutsummaryrefslogtreecommitdiff
path: root/kernel/fs/s5fs
diff options
context:
space:
mode:
authornthnluu <nate1299@me.com>2024-01-28 21:20:27 -0500
committernthnluu <nate1299@me.com>2024-01-28 21:20:27 -0500
commitc63f340d90800895f007de64b7d2d14624263331 (patch)
tree2c0849fa597dd6da831c8707b6f2603403778d7b /kernel/fs/s5fs
Created student weenix repository
Diffstat (limited to 'kernel/fs/s5fs')
-rw-r--r--kernel/fs/s5fs/s5fs.c860
-rw-r--r--kernel/fs/s5fs/s5fs_subr.c590
2 files changed, 1450 insertions, 0 deletions
diff --git a/kernel/fs/s5fs/s5fs.c b/kernel/fs/s5fs/s5fs.c
new file mode 100644
index 0000000..3790c1a
--- /dev/null
+++ b/kernel/fs/s5fs/s5fs.c
@@ -0,0 +1,860 @@
+#include "errno.h"
+#include "globals.h"
+#include "kernel.h"
+#include <mm/slab.h>
+
+#include "util/debug.h"
+#include "util/printf.h"
+#include "util/string.h"
+
+#include "proc/kmutex.h"
+
+#include "fs/dirent.h"
+#include "fs/file.h"
+#include "fs/s5fs/s5fs.h"
+#include "fs/s5fs/s5fs_subr.h"
+#include "fs/stat.h"
+
+#include "mm/kmalloc.h"
+
+static long s5_check_super(s5_super_t *super);
+
+static long s5fs_check_refcounts(fs_t *fs);
+
+static void s5fs_read_vnode(fs_t *fs, vnode_t *vn);
+
+static void s5fs_delete_vnode(fs_t *fs, vnode_t *vn);
+
+static long s5fs_umount(fs_t *fs);
+
+static void s5fs_sync(fs_t *fs);
+
+static ssize_t s5fs_read(vnode_t *vnode, size_t pos, void *buf, size_t len);
+
+static ssize_t s5fs_write(vnode_t *vnode, size_t pos, const void *buf,
+ size_t len);
+
+static long s5fs_mmap(vnode_t *file, mobj_t **ret);
+
+static long s5fs_mknod(struct vnode *dir, const char *name, size_t namelen,
+ int mode, devid_t devid, struct vnode **out);
+
+static long s5fs_lookup(vnode_t *dir, const char *name, size_t namelen,
+ vnode_t **out);
+
+static long s5fs_link(vnode_t *dir, const char *name, size_t namelen,
+ vnode_t *child);
+
+static long s5fs_unlink(vnode_t *vdir, const char *name, size_t namelen);
+
+static long s5fs_rename(vnode_t *olddir, const char *oldname, size_t oldnamelen,
+ vnode_t *newdir, const char *newname,
+ size_t newnamelen);
+
+static long s5fs_mkdir(vnode_t *dir, const char *name, size_t namelen,
+ struct vnode **out);
+
+static long s5fs_rmdir(vnode_t *parent, const char *name, size_t namelen);
+
+static long s5fs_readdir(vnode_t *vnode, size_t pos, struct dirent *d);
+
+static long s5fs_stat(vnode_t *vnode, stat_t *ss);
+
+static void s5fs_truncate_file(vnode_t *vnode);
+
+static long s5fs_release(vnode_t *vnode, file_t *file);
+
+static long s5fs_get_pframe(vnode_t *vnode, size_t pagenum, long forwrite,
+ pframe_t **pfp);
+
+static long s5fs_fill_pframe(vnode_t *vnode, pframe_t *pf);
+
+static long s5fs_flush_pframe(vnode_t *vnode, pframe_t *pf);
+
+fs_ops_t s5fs_fsops = {.read_vnode = s5fs_read_vnode,
+ .delete_vnode = s5fs_delete_vnode,
+ .umount = s5fs_umount,
+ .sync = s5fs_sync};
+
+static vnode_ops_t s5fs_dir_vops = {.read = NULL,
+ .write = NULL,
+ .mmap = NULL,
+ .mknod = s5fs_mknod,
+ .lookup = s5fs_lookup,
+ .link = s5fs_link,
+ .unlink = s5fs_unlink,
+ .rename = s5fs_rename,
+ .mkdir = s5fs_mkdir,
+ .rmdir = s5fs_rmdir,
+ .readdir = s5fs_readdir,
+ .stat = s5fs_stat,
+ .acquire = NULL,
+ .release = NULL,
+ .get_pframe = s5fs_get_pframe,
+ .fill_pframe = s5fs_fill_pframe,
+ .flush_pframe = s5fs_flush_pframe,
+ .truncate_file = NULL};
+
+static vnode_ops_t s5fs_file_vops = {.read = s5fs_read,
+ .write = s5fs_write,
+ .mmap = s5fs_mmap,
+ .mknod = NULL,
+ .lookup = NULL,
+ .link = NULL,
+ .unlink = NULL,
+ .mkdir = NULL,
+ .rmdir = NULL,
+ .readdir = NULL,
+ .stat = s5fs_stat,
+ .acquire = NULL,
+ .release = NULL,
+ .get_pframe = s5fs_get_pframe,
+ .fill_pframe = s5fs_fill_pframe,
+ .flush_pframe = s5fs_flush_pframe,
+ .truncate_file = s5fs_truncate_file};
+
+
+static mobj_ops_t s5fs_mobj_ops = {.get_pframe = NULL,
+ .fill_pframe = blockdev_fill_pframe,
+ .flush_pframe = blockdev_flush_pframe,
+ .destructor = NULL};
+
+/*
+ * Initialize the passed-in fs_t. The only members of fs_t that are initialized
+ * before the call to s5fs_mount are fs_dev and fs_type ("s5fs"). You must
+ * initialize everything else: fs_vnode_allocator, fs_i, fs_ops, fs_root.
+ *
+ * Initialize the block device for the s5fs_t that is created, and copy
+ * the super block from disk into memory.
+ */
+long s5fs_mount(fs_t *fs)
+{
+ int num;
+
+ KASSERT(fs);
+
+ if (sscanf(fs->fs_dev, "disk%d", &num) != 1)
+ {
+ return -EINVAL;
+ }
+
+ blockdev_t *dev = blockdev_lookup(MKDEVID(DISK_MAJOR, num));
+ if (!dev)
+ return -EINVAL;
+
+ slab_allocator_t *allocator =
+ slab_allocator_create("s5_node", sizeof(s5_node_t));
+ fs->fs_vnode_allocator = allocator;
+
+ s5fs_t *s5fs = (s5fs_t *)kmalloc(sizeof(s5fs_t));
+
+ if (!s5fs)
+ {
+ slab_allocator_destroy(fs->fs_vnode_allocator);
+ fs->fs_vnode_allocator = NULL;
+ return -ENOMEM;
+ }
+
+ mobj_init(&s5fs->s5f_mobj, MOBJ_FS, &s5fs_mobj_ops);
+ s5fs->s5f_bdev = dev;
+
+#ifndef OLD
+ pframe_t *pf;
+ s5_get_meta_disk_block(s5fs, S5_SUPER_BLOCK, 0, &pf);
+ memcpy(&s5fs->s5f_super, pf->pf_addr, sizeof(s5_super_t));
+ s5_release_disk_block(&pf);
+#endif
+
+ if (s5_check_super(&s5fs->s5f_super))
+ {
+ kfree(s5fs);
+ slab_allocator_destroy(fs->fs_vnode_allocator);
+ fs->fs_vnode_allocator = NULL;
+ return -EINVAL;
+ }
+
+ kmutex_init(&s5fs->s5f_mutex);
+
+ s5fs->s5f_fs = fs;
+
+ fs->fs_i = s5fs;
+ fs->fs_ops = &s5fs_fsops;
+ fs->fs_root = vget(fs, s5fs->s5f_super.s5s_root_inode);
+ // vunlock(fs->fs_root);
+
+ return 0;
+}
+
+/* Initialize a vnode and inode by reading its corresponding inode info from
+ * disk.
+ *
+ * Hints:
+ * - To read the inode from disk, you will need to use the following:
+ * - VNODE_TO_S5NODE to obtain the s5_node_t with the inode corresponding
+ * to the provided vnode
+ * - FS_TO_S5FS to obtain the s5fs object
+ * - S5_INODE_BLOCK(vn->v_vno) to determine the block number of the block that
+ * contains the inode info
+ * - s5_get_disk_block and s5_release_disk_block to handle the disk block
+ * - S5_INODE_OFFSET to find the desired inode within the disk block
+ * containing it (returns the offset that the inode is stored within the block)
+ * - You should initialize the s5_node_t's inode field by reading directly from
+ * the inode on disk by using the page frame returned from s5_get_disk_block. Also
+ * make sure to initialize the dirtied_inode field.
+ * - Using the inode info, you need to initialize the following vnode fields:
+ * vn_len, vn_mode, and vn_ops using the fields found in the s5_inode struct.
+ * - See stat.h for vn_mode values.
+ * - For character and block devices:
+ * 1) Initialize vn_devid by reading the inode's s5_indirect_block field.
+ * 2) Set vn_ops to NULL.
+ */
+static void s5fs_read_vnode(fs_t *fs, vnode_t *vn)
+{
+ NOT_YET_IMPLEMENTED("S5FS: ***none***");
+}
+
+/* Clean up the inode corresponding to the given vnode.
+ *
+ * Hints:
+ * - This function is called in the following way:
+ * mobj_put -> vnode_destructor -> s5fs_delete_vnode.
+ * - Cases to consider:
+ * 1) The inode is no longer in use (linkcount == 0), so free it using
+ * s5_free_inode.
+ * 2) The inode is dirty, so write it back to disk.
+ * 3) The inode is unchanged, so do nothing.
+ */
+static void s5fs_delete_vnode(fs_t *fs, vnode_t *vn)
+{
+ NOT_YET_IMPLEMENTED("S5FS: ***none***");
+}
+
+/*
+ * See umount in vfs.h
+ *
+ * Check reference counts and the super block.
+ * Put the fs_root.
+ * Write the super block out to disk.
+ * Flush the underlying memory object.
+ */
+static long s5fs_umount(fs_t *fs)
+{
+ s5fs_t *s5fs = FS_TO_S5FS(fs);
+ blockdev_t *bd = s5fs->s5f_bdev;
+
+ if (s5fs_check_refcounts(fs))
+ {
+ panic(
+ "s5fs_umount: WARNING: linkcount corruption "
+ "discovered in fs on block device with major %d "
+ "and minor %d!!\n",
+ MAJOR(bd->bd_id), MINOR(bd->bd_id));
+ }
+ if (s5_check_super(&s5fs->s5f_super))
+ {
+ panic(
+ "s5fs_umount: WARNING: corrupted superblock "
+ "discovered on fs on block device with major %d "
+ "and minor %d!!\n",
+ MAJOR(bd->bd_id), MINOR(bd->bd_id));
+ }
+
+ vput(&fs->fs_root);
+
+ s5fs_sync(fs);
+ kfree(s5fs);
+ return 0;
+}
+
+static void s5fs_sync(fs_t *fs)
+{
+#ifdef FIXME
+ s5fs_t *s5fs = FS_TO_S5FS(fs);
+ #ifdef OLD
+ mobj_t *mobj = S5FS_TO_VMOBJ(s5fs);
+ #endif
+ mobj_t *mobj = 0; // XXX FIX ME
+
+ mobj_lock(mobj);
+
+ pframe_t *pf;
+ mobj_get_pframe(mobj, S5_SUPER_BLOCK, 1, &pf);
+ memcpy(pf->pf_addr, &s5fs->s5f_super, sizeof(s5_super_t));
+ pframe_release(&pf);
+
+ mobj_flush(S5FS_TO_VMOBJ(s5fs));
+ mobj_unlock(S5FS_TO_VMOBJ(s5fs));
+#endif
+}
+
+/* Wrapper around s5_read_file. */
+static ssize_t s5fs_read(vnode_t *vnode, size_t pos, void *buf, size_t len)
+{
+ KASSERT(!S_ISDIR(vnode->vn_mode) && "should be handled at the VFS level");
+ NOT_YET_IMPLEMENTED("S5FS: ***none***");
+ return -1;
+}
+
+/* Wrapper around s5_write_file. */
+static ssize_t s5fs_write(vnode_t *vnode, size_t pos, const void *buf,
+ size_t len)
+{
+ KASSERT(!S_ISDIR(vnode->vn_mode) && "should be handled at the VFS level");
+ NOT_YET_IMPLEMENTED("S5FS: ***none***");
+ return -1;
+}
+
+/*
+ * Any error handling should have been done before this function was called.
+ * Simply add a reference to the underlying mobj and return it through ret.
+ */
+static long s5fs_mmap(vnode_t *file, mobj_t **ret)
+{
+ NOT_YET_IMPLEMENTED("VM: ***none***");
+ return 0;
+}
+
+/* Allocate and initialize an inode and its corresponding vnode.
+ *
+ * dir - The directory in which to make the new inode
+ * name - The name of the new inode
+ * namelen - Name length
+ * mode - vn_mode of the new inode, see S_IF{} macros in stat.h
+ * devid - devid of the new inode for special devices
+ * out - Upon success, out must point to the newly created vnode
+ * Upon failure, out must be unchanged
+ *
+ * Return 0 on success, or:
+ * - ENOTSUP: mode is not S_IFCHR, S_BLK, or S_ISREG
+ * - Propagate errors from s5_alloc_inode and s5_link
+ *
+ * Hints:
+ * - Use mode to determine the S5_TYPE_{} for the inode.
+ * - Use s5_alloc_inode is allocate a new inode.
+ * - Use vget to obtain the vnode corresponding to the newly created inode.
+ * - Use s5_link to link the newly created inode/vnode to the parent directory.
+ * - You will need to clean up the vnode using vput in the case that
+ * the link operation fails.
+ */
+static long s5fs_mknod(struct vnode *dir, const char *name, size_t namelen,
+ int mode, devid_t devid, struct vnode **out)
+{
+ KASSERT(S_ISDIR(dir->vn_mode) && "should be handled at the VFS level");
+ NOT_YET_IMPLEMENTED("S5FS: ***none***");
+ return -1;
+}
+
+/* Search for a given entry within a directory.
+ *
+ * dir - The directory in which to search
+ * name - The name to search for
+ * namelen - Name length
+ * ret - Upon success, ret must point to the found vnode
+ *
+ * Return 0 on success, or:
+ * - Propagate errors from s5_find_dirent
+ *
+ * Hints:
+ * - Use s5_find_dirent, vget, and vref.
+ * - vref can be used in the case where the vnode you're looking for happens
+ * to be dir itself.
+ */
+long s5fs_lookup(vnode_t *dir, const char *name, size_t namelen,
+ vnode_t **ret)
+{
+ NOT_YET_IMPLEMENTED("S5FS: ***none***");
+ return -1;
+}
+
+/* Wrapper around s5_link.
+ *
+ * Return whatever s5_link returns, or:
+ * - EISDIR: child is a directory
+ */
+static long s5fs_link(vnode_t *dir, const char *name, size_t namelen,
+ vnode_t *child)
+{
+ KASSERT(S_ISDIR(dir->vn_mode) && "should be handled at the VFS level");
+ NOT_YET_IMPLEMENTED("S5FS: ***none***");
+ return -1;
+}
+
+/* Remove the directory entry in dir corresponding to name and namelen.
+ *
+ * Return 0 on success, or:
+ * - Propagate errors from s5_find_dirent
+ *
+ * Hints:
+ * - Use s5_find_dirent and s5_remove_dirent.
+ * - You will probably want to use vget_locked and vput_locked to protect the
+ * found vnode. Make sure your implementation of s5_remove_dirent knows what
+ * to expect.
+ */
+static long s5fs_unlink(vnode_t *dir, const char *name, size_t namelen)
+{
+ KASSERT(S_ISDIR(dir->vn_mode) && "should be handled at the VFS level");
+ KASSERT(!name_match(".", name, namelen));
+ KASSERT(!name_match("..", name, namelen));
+ NOT_YET_IMPLEMENTED("S5FS: ***none***");
+ return -1;
+}
+
+/* Change the name or location of a file.
+ *
+ * olddir - The directory in which the file currently resides
+ * oldname - The old name of the file
+ * oldnamelen - Length of the old name
+ * newdir - The directory in which to place the file
+ * newname - The new name of the file
+ * newnamelen - Length of the new name
+ *
+ * Return 0 on success, or:
+ * - ENAMETOOLONG: newname is >= NAME_LEN
+ * - ENOTDIR: newdir is not a directory
+ * - EISDIR: newname is a directory
+ * - Propagate errors from s5_find_dirent and s5_link
+ *
+ * Steps:
+ * 1) Use s5_find_dirent and vget_locked to obtain the vnode corresponding to old name.
+ * 2) If newdir already contains an entry for newname:
+ * a) Compare node numbers and do nothing if old name and new name refer to the same inode
+ * b) Check if new-name is a directory
+ * c) Remove the previously existing entry for new name using s5_remove_dirent
+ * d) Link the new direct using s5_link
+ * 3) If there is no entry for newname, use s5_link to add a link to the old node at new name
+ * 4) Use s5_remove_dirent to remove old name’s entry in olddir
+ *
+ *
+ * Hints:
+ * - olddir and newdir should be locked on entry and not unlocked during the
+ * duration of this function. Any other vnodes locked should be unlocked and
+ * put before return.
+ * - Be careful with locking! Because you are making changes to the vnodes,
+ * you should always be using vget_locked and vput_locked. Be sure to clean
+ * up properly in error/special cases.
+ * - You DO NOT need to support renaming of directories in Weenix. If you were to support this
+ * in the s5fs layer (which is not extra credit), you can use the following routine:
+ * 1) Use s5_find_dirent and vget_locked to obtain the vnode corresponding to old name.
+ * 2) If newer already contains an entry for newname:
+ * a) Compare node numbers and do nothing if old name and new name refer to the same inode
+ * b) Check if new-name is a directory
+ * c) Remove the previously existing entry for new name using s5_remove_dirent
+ * d) Link the new direct using s5_link
+ * 3) If there is no entry for newname, use s5_link to add a link to the old node at new name
+ * 4) Use s5_remove_dirent to remove old name’s entry in olddir
+ */
+static long s5fs_rename(vnode_t *olddir, const char *oldname, size_t oldnamelen,
+ vnode_t *newdir, const char *newname,
+ size_t newnamelen)
+{
+ NOT_YET_IMPLEMENTED("S5FS: ***none***");
+ return -1;
+}
+
+/* Create a directory.
+ *
+ * dir - The directory in which to create the new directory
+ * name - The name of the new directory
+ * namelen - Name length of the new directory
+ * out - On success, must point to the new directory, unlocked
+ * On failure, must be unchanged
+ *
+ * Return 0 on success, or:
+ * - Propagate errors from s5_alloc_inode and s5_link
+ *
+ * Steps:
+ * 1) Allocate an inode.
+ * 2) Get the child directory vnode.
+ * 3) Create the "." entry.
+ * 4) Create the ".." entry.
+ * 5) Create the name/namelen entry in the parent (that corresponds
+ * to the new directory)
+ *
+ * Hints:
+ * - If you run into any errors, you must undo previous steps.
+ * - You may assume/assert that undo operations do not fail.
+ * - It may help to assert that linkcounts are correct.
+ */
+static long s5fs_mkdir(vnode_t *dir, const char *name, size_t namelen,
+ struct vnode **out)
+{
+ KASSERT(S_ISDIR((dir)->vn_mode) && "should be handled at the VFS level");
+ NOT_YET_IMPLEMENTED("S5FS: ***none***");
+ return -1;
+}
+
+/* Remove a directory.
+ *
+ * Return 0 on success, or:
+ * - ENOTDIR: The specified entry is not a directory
+ * - ENOTEMPTY: The directory to be removed has entries besides "." and ".."
+ * - Propagate errors from s5_find_dirent
+ *
+ * Hints:
+ * - If you are confident you are managing directory entries properly, you can
+ * check for ENOTEMPTY by simply checking the length of the directory to be
+ * removed. An empty directory has two entries: "." and "..".
+ * - Remove the three entries created in s5fs_mkdir.
+ */
+static long s5fs_rmdir(vnode_t *parent, const char *name, size_t namelen)
+{
+ KASSERT(!name_match(".", name, namelen));
+ KASSERT(!name_match("..", name, namelen));
+ KASSERT(S_ISDIR(parent->vn_mode) && "should be handled at the VFS level");
+ NOT_YET_IMPLEMENTED("S5FS: ***none***");
+ return -1;
+}
+
+/* Read a directory entry.
+ *
+ * vnode - The directory from which to read an entry
+ * pos - The position within the directory to start reading from
+ * d - Caller-allocated dirent that must be properly initialized on
+ * successful return
+ *
+ * Return bytes read on success, or:
+ * - Propagate errors from s5_read_file
+ *
+ * Hints:
+ * - Use s5_read_file to read an s5_dirent_t. To do so, you can create a local
+ * s5_dirent_t variable and use that as the buffer to pass into s5_read_file.
+ * - Be careful that you read into an s5_dirent_t and populate the provided
+ * dirent_t properly.
+ */
+static long s5fs_readdir(vnode_t *vnode, size_t pos, struct dirent *d)
+{
+ KASSERT(S_ISDIR(vnode->vn_mode) && "should be handled at the VFS level");
+ NOT_YET_IMPLEMENTED("S5FS: ***none***");
+ return -1;
+}
+
+/* Get file status.
+ *
+ * vnode - The vnode of the file in question
+ * ss - Caller-allocated stat_t struct that must be initialized on success
+ *
+ * This function should not fail.
+ *
+ * Hint:
+ * - Initialize st_blocks using s5_inode_blocks.
+ * - Initialize st_mode using the corresponding vnode modes in stat.h.
+ * - Initialize st_rdev with the devid of special devices.
+ * - Initialize st_ino with the inode number.
+ * - Initialize st_nlink with the linkcount.
+ * - Initialize st_blksize with S5_BLOCK_SIZE.
+ * - Initialize st_size with the size of the file.
+ * - Initialize st_dev with the bd_id of the s5fs block device.
+ * - Set all other fields to 0.
+ */
+static long s5fs_stat(vnode_t *vnode, stat_t *ss)
+{
+ NOT_YET_IMPLEMENTED("S5FS: ***none***");
+ return -1;
+}
+
+/**
+ * Truncate the vnode and inode length to be 0.
+ *
+ * file - the vnode, whose size should be truncated
+ *
+ * This routine should only be called from do_open via
+ * vn_ops in the case that a regular file is opened with the
+ * O_TRUNC flag specified.
+ */
+static void s5fs_truncate_file(vnode_t *file)
+{
+ KASSERT(S_ISREG(file->vn_mode) && "This routine should only be called for regular files");
+ file->vn_len = 0;
+ s5_node_t* s5_node = VNODE_TO_S5NODE(file);
+ s5_inode_t* s5_inode = &s5_node->inode;
+ // setting the size of the inode to be 0 as well
+ s5_inode->s5_un.s5_size = 0;
+ s5_node->dirtied_inode = 1;
+
+ // Call subroutine to free the blocks that were used
+ vlock(file);
+ s5_remove_blocks(s5_node);
+ vunlock(file);
+}
+
+#ifdef OLD
+/*
+ * Wrapper around mobj_get_pframe. Remember to lock the memory object around
+ * the call to mobj_get_pframe. Assert that the get_pframe does not fail.
+ */
+inline void s5_get_disk_block(s5fs_t *s5fs, blocknum_t blocknum, long forwrite,
+ pframe_t **pfp)
+{
+ mobj_lock(S5FS_TO_VMOBJ(s5fs));
+ long ret = mobj_get_pframe(S5FS_TO_VMOBJ(s5fs), blocknum, forwrite, pfp);
+ mobj_unlock(S5FS_TO_VMOBJ(s5fs));
+ KASSERT(!ret && *pfp);
+}
+#endif
+
+/*
+ * Wrapper around device's read_block function; first looks up block in file-system cache.
+ * If not there, allocates and fills a page frame.
+ * Used for meta blocks, thus location is passed in.
+ */
+inline void s5_get_meta_disk_block(s5fs_t *s5fs, uint64_t blocknum, long forwrite,
+ pframe_t **pfp)
+{
+ mobj_lock(&s5fs->s5f_mobj);
+ mobj_find_pframe(&s5fs->s5f_mobj, blocknum, pfp);
+ if (*pfp)
+ {
+ // block is cached
+ mobj_unlock(&s5fs->s5f_mobj);
+ return;
+ }
+ mobj_create_pframe(&s5fs->s5f_mobj, blocknum, blocknum, pfp);
+ pframe_t *pf = *pfp;
+ pf->pf_addr = page_alloc();
+ KASSERT(pf->pf_addr);
+
+ blockdev_t *bd = s5fs->s5f_bdev;
+ long ret = bd->bd_ops->read_block(bd, pf->pf_addr, (blocknum_t)pf->pf_loc, 1);
+ pf->pf_dirty |= forwrite; // needed?
+ KASSERT (!ret);
+ mobj_unlock(&s5fs->s5f_mobj);
+ KASSERT(!ret && *pfp);
+}
+
+/*
+ * Wrapper around device's read_block function; allocates and fills a page frame.
+ * Assumes cache has already been searched.
+ * Used for file blocks, thus file block number is supplied.
+ */
+static inline void s5_get_file_disk_block(vnode_t *vnode, uint64_t blocknum, uint64_t loc, long forwrite,
+ pframe_t **pfp)
+{
+ //mobj_lock(&vnode->vn_mobj);
+ mobj_create_pframe(&vnode->vn_mobj, blocknum, loc, pfp);
+ //mobj_unlock(&vnode->vn_mobj);
+ pframe_t *pf = *pfp;
+ pf->pf_addr = page_alloc();
+ KASSERT(pf->pf_addr);
+ blockdev_t *bd = VNODE_TO_S5FS(vnode)->s5f_bdev;
+ long ret = bd->bd_ops->read_block(bd, pf->pf_addr, pf->pf_loc, 1);
+ pf->pf_dirty |= forwrite; // needed?
+ KASSERT (!ret);
+}
+
+/* Wrapper around pframe_release.
+ *
+ * Note: All pframe_release does is unlock the pframe. Why aren't we actually
+ * writing anything back yet? Because the pframe remains associated with
+ * whatever mobj we provided when we originally called mobj_get_pframe. If
+ * anyone tries to access the pframe later, Weenix will just give them the
+ * cached page frame from the mobj. If the pframe is ever freed (most likely on
+ * shutdown), then it will be written back to disk: mobj_flush_pframe ->
+ * blockdev_flush_pframe.
+ */
+inline void s5_release_disk_block(pframe_t **pfp) { pframe_release(pfp); }
+
+/*
+ * This is where the abstraction of vnode file block/page --> disk block is
+ * finally implemented. Check that the requested page lies within vnode->vn_len.
+ *
+ * Of course, you will want to use s5_file_block_to_disk_block. Pay attention
+ * to what the forwrite argument to s5fs_get_pframe means for the alloc argument
+ * in s5_file_block_to_disk_block.
+ *
+ * If the disk block for the corresponding file block is sparse, you should use
+ * mobj_default_get_pframe on the vnode's own memory object. This will trickle
+ * down to s5fs_fill_pframe if the pframe is not already resident.
+ *
+ * Otherwise, if the disk block is NOT sparse, you will want to simply use
+ * s5_get_disk_block. NOTE: in this case, you also need to make sure you free
+ * the pframe that resides in the vnode itself for the requested pagenum. To
+ * do so, you will want to use mobj_find_pframe and mobj_free_pframe.
+ *
+ * Given the above design, we s5fs itself does not need to implement
+ * flush_pframe. Any pframe that will be written to (forwrite = 1) should always
+ * have a disk block backing it on successful return. Thus, the page frame will
+ * reside in the block device of the filesystem, where the flush_pframe is
+ * already implemented. We do, however, need to implement fill_pframe for sparse
+ * blocks.
+ */
+static long s5fs_get_pframe(vnode_t *vnode, uint64_t pagenum, long forwrite,
+ pframe_t **pfp)
+{
+#ifdef OLD
+ if (vnode->vn_len <= pagenum * PAGE_SIZE)
+ return -EINVAL;
+ long loc =
+ s5_file_block_to_disk_block(VNODE_TO_S5NODE(vnode), pagenum, forwrite);
+ if (loc < 0)
+ return loc;
+ if (loc)
+ {
+ mobj_find_pframe(&vnode->vn_mobj, pagenum, pfp);
+ if (*pfp)
+ {
+ mobj_free_pframe(&vnode->vn_mobj, pfp);
+ }
+ s5_get_disk_block(VNODE_TO_S5FS(vnode), (blocknum_t)loc, forwrite, pfp);
+ return 0;
+ }
+ else
+ {
+ KASSERT(!forwrite);
+ return mobj_default_get_pframe(&vnode->vn_mobj, pagenum, forwrite, pfp);
+ }
+#endif
+
+ if (vnode->vn_len <= pagenum * PAGE_SIZE)
+ return -EINVAL;
+ mobj_find_pframe(&vnode->vn_mobj, pagenum, pfp);
+ if (*pfp)
+ {
+ // block is cached
+ return 0;
+ }
+ int new;
+ long loc = s5_file_block_to_disk_block(VNODE_TO_S5NODE(vnode), pagenum, forwrite, &new);
+ if (loc < 0)
+ return loc;
+ if (loc) {
+ // block is mapped
+ if (new) {
+ // block didn't previously exist, thus its current contents are meaningless
+ *pfp = s5_cache_and_clear_block(&vnode->vn_mobj, pagenum, loc);
+ } else {
+ // block must be read from disk
+ s5_get_file_disk_block(vnode, pagenum, loc, forwrite, pfp);
+ }
+ return 0;
+ }
+ else
+ {
+ // block is in a sparse region of the file
+ KASSERT(!forwrite);
+ return mobj_default_get_pframe(&vnode->vn_mobj, pagenum, forwrite, pfp);
+ }
+}
+
+/*
+ * According the documentation for s5fs_get_pframe, this only gets called when
+ * the file block for a given page number is sparse. In other words, pf
+ * corresponds to a sparse block.
+ */
+static long s5fs_fill_pframe(vnode_t *vnode, pframe_t *pf)
+{
+ memset(pf->pf_addr, 0, PAGE_SIZE);
+ return 0;
+}
+
+/*
+ * Verify the superblock. 0 on success; -1 on failure.
+ */
+static long s5_check_super(s5_super_t *super)
+{
+ if (!(super->s5s_magic == S5_MAGIC &&
+ (super->s5s_free_inode < super->s5s_num_inodes ||
+ super->s5s_free_inode == (uint32_t)-1) &&
+ super->s5s_root_inode < super->s5s_num_inodes))
+ {
+ return -1;
+ }
+ if (super->s5s_version != S5_CURRENT_VERSION)
+ {
+ dbg(DBG_PRINT,
+ "Filesystem is version %d; "
+ "only version %d is supported.\n",
+ super->s5s_version, S5_CURRENT_VERSION);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Calculate refcounts on the filesystem.
+ */
+static void calculate_refcounts(int *counts, vnode_t *vnode)
+{
+ long ret;
+
+ size_t pos = 0;
+ dirent_t dirent;
+ vnode_t *child;
+
+ while ((ret = s5fs_readdir(vnode, pos, &dirent)) > 0)
+ {
+ counts[dirent.d_ino]++;
+ dbg(DBG_S5FS, "incrementing count of inode %d to %d\n", dirent.d_ino,
+ counts[dirent.d_ino]);
+ if (counts[dirent.d_ino] == 1)
+ {
+ child = vget_locked(vnode->vn_fs, dirent.d_ino);
+ if (S_ISDIR(child->vn_mode))
+ {
+ calculate_refcounts(counts, child);
+ }
+ vput_locked(&child);
+ }
+ pos += ret;
+ }
+
+ KASSERT(!ret);
+}
+
+/*
+ * Verify refcounts on the filesystem. 0 on success; -1 on failure.
+ */
+long s5fs_check_refcounts(fs_t *fs)
+{
+ s5fs_t *s5fs = (s5fs_t *)fs->fs_i;
+ int *refcounts;
+ long ret = 0;
+
+ refcounts = kmalloc(s5fs->s5f_super.s5s_num_inodes * sizeof(int));
+ KASSERT(refcounts);
+ memset(refcounts, 0, s5fs->s5f_super.s5s_num_inodes * sizeof(int));
+
+ vlock(fs->fs_root);
+ refcounts[fs->fs_root->vn_vno]++;
+ calculate_refcounts(refcounts, fs->fs_root);
+ refcounts[fs->fs_root->vn_vno]--;
+
+ vunlock(fs->fs_root);
+
+ dbg(DBG_PRINT,
+ "Checking refcounts of s5fs filesystem on block "
+ "device with major %d, minor %d\n",
+ MAJOR(s5fs->s5f_bdev->bd_id), MINOR(s5fs->s5f_bdev->bd_id));
+
+ for (uint32_t i = 0; i < s5fs->s5f_super.s5s_num_inodes; i++)
+ {
+ if (!refcounts[i])
+ {
+ continue;
+ }
+
+ vnode_t *vn = vget(fs, i);
+ KASSERT(vn);
+ s5_node_t *sn = VNODE_TO_S5NODE(vn);
+
+ if (refcounts[i] != sn->inode.s5_linkcount)
+ {
+ dbg(DBG_PRINT, " Inode %d, expecting %d, found %d\n", i,
+ refcounts[i], sn->inode.s5_linkcount);
+ ret = -1;
+ }
+ vput(&vn);
+ }
+
+ dbg(DBG_PRINT,
+ "Refcount check of s5fs filesystem on block "
+ "device with major %d, minor %d completed %s.\n",
+ MAJOR(s5fs->s5f_bdev->bd_id), MINOR(s5fs->s5f_bdev->bd_id),
+ (ret ? "UNSUCCESSFULLY" : "successfully"));
+
+ kfree(refcounts);
+ return ret;
+}
+
+static long s5fs_flush_pframe(vnode_t *vnode, pframe_t *pf) {
+ return blockdev_flush_pframe(&((s5fs_t *)vnode->vn_fs->fs_i)->s5f_mobj, pf);
+} \ No newline at end of file
diff --git a/kernel/fs/s5fs/s5fs_subr.c b/kernel/fs/s5fs/s5fs_subr.c
new file mode 100644
index 0000000..c972d7c
--- /dev/null
+++ b/kernel/fs/s5fs/s5fs_subr.c
@@ -0,0 +1,590 @@
+#include "fs/s5fs/s5fs_subr.h"
+#include "drivers/blockdev.h"
+#include "errno.h"
+#include "fs/s5fs/s5fs.h"
+#include "fs/stat.h"
+#include "fs/vfs.h"
+#include "fs/vnode.h"
+#include "kernel.h"
+#include "mm/pframe.h"
+#include "proc/kmutex.h"
+#include "util/debug.h"
+#include "util/string.h"
+#include <fs/s5fs/s5fs.h>
+
+static void s5_free_block(s5fs_t *s5fs, blocknum_t block);
+
+static long s5_alloc_block(s5fs_t *s5fs);
+
+static inline void s5_lock_super(s5fs_t *s5fs)
+{
+ kmutex_lock(&s5fs->s5f_mutex);
+}
+
+static inline void s5_unlock_super(s5fs_t *s5fs)
+{
+ kmutex_unlock(&s5fs->s5f_mutex);
+}
+
+/* Helper function to obtain inode info from disk given an inode number.
+ *
+ * s5fs - The file system (it will usually be obvious what to pass for this
+ * parameter)
+ * ino - Inode number to fetch
+ * forwrite - Set if you intend to write any fields in the s5_inode_t, clear
+ * if you only intend to read
+ * pfp - Return parameter for a page frame that will contain the disk
+ * block of the desired inode
+ * inodep - Return parameter for the s5_inode_t corresponding to the desired
+ * inode
+ */
+static inline void s5_get_inode(s5fs_t *s5fs, ino_t ino, long forwrite,
+ pframe_t **pfp, s5_inode_t **inodep)
+{
+ s5_get_meta_disk_block(s5fs, S5_INODE_BLOCK(ino), forwrite, pfp);
+ *inodep = (s5_inode_t *)(*pfp)->pf_addr + S5_INODE_OFFSET(ino);
+ KASSERT((*inodep)->s5_number == ino);
+}
+
+/* Release an inode by releasing the page frame of the disk block containing the
+ * inode. See comments above s5_release_disk_block to see why we don't write
+ * anything back yet.
+ *
+ * pfp - The page frame containing the inode
+ * inodep - The inode to be released
+ *
+ * On return, pfp and inodep both point to NULL.
+ */
+static inline void s5_release_inode(pframe_t **pfp, s5_inode_t **inodep)
+{
+ KASSERT((s5_inode_t *)(*pfp)->pf_addr +
+ S5_INODE_OFFSET((*inodep)->s5_number) ==
+ *inodep);
+ *inodep = NULL;
+ s5_release_disk_block(pfp);
+}
+
+/* Helper function to obtain a specific block of a file.
+ *
+ * sn - The s5_node representing the file in question
+ * blocknum - The offset of the desired block relative to the beginning of the
+ * file, i.e. index 8000 is block 1 of the file, even though it may
+ * not be block 1 of the disk
+ * forwrite - Set if you intend to write to the block, clear if you only intend
+ * to read
+ * pfp - Return parameter for a page frame containing the block data
+ */
+static inline long s5_get_file_block(s5_node_t *sn, size_t blocknum,
+ long forwrite, pframe_t **pfp)
+{
+ return sn->vnode.vn_mobj.mo_ops.get_pframe(&sn->vnode.vn_mobj, blocknum,
+ forwrite, pfp);
+}
+
+/* Release the page frame associated with a file block. See comments above
+ * s5_release_disk_block to see why we don't write anything back yet.
+ *
+ * On return, pfp points to NULL.
+ */
+static inline void s5_release_file_block(pframe_t **pfp)
+{
+ pframe_release(pfp);
+}
+
+#ifdef OLD
+/* Given a file and a file block number, return the disk block number of the
+ * desired file block.
+ *
+ * sn - The s5_node representing the file
+ * file_blocknum - The offset of the desired block relative to the beginning of
+ * the file
+ * alloc - If set, allocate the block / indirect block as necessary
+ * If clear, don't allocate sparse blocks
+ *
+ * Return a disk block number on success, or:
+ * - 0: The block is sparse, and alloc is clear, OR
+ * The indirect block would contain the block, but the indirect block is
+ * sparse, and alloc is clear
+ * - EINVAL: The specified block number is greater than or equal to
+ * S5_MAX_FILE_BLOCKS
+ * - Propagate errors from s5_alloc_block.
+ *
+ * Hints:
+ * - Use the file inode's s5_direct_blocks and s5_indirect_block to perform the
+ * translation.
+ * - Use s5_alloc_block to allocate blocks.
+ * - Be sure to mark the inode as dirty when appropriate, i.e. when you are
+ * making changes to the actual s5_inode_t struct. Hint: Does allocating a
+ * direct block dirty the inode? What about allocating the indirect block?
+ * Finally, what about allocating a block pointed to by the indirect block?
+ * - Cases to consider:
+ * 1) file_blocknum < S_NDIRECT_BLOCKS
+ * 2) Indirect block is not allocated but alloc is set. Be careful not to
+ * leak a block in an error case!
+ * 3) Indirect block is allocated. The desired block may be sparse, and you
+ * may have to allocate it.
+ * 4) The indirect block has not been allocated and alloc is clear.
+ */
+long s5_file_block_to_disk_block(s5_node_t *sn, size_t file_blocknum,
+ int alloc)
+{
+ NOT_YET_IMPLEMENTED("S5FS: ***none***");
+ return -1;
+}
+#endif
+
+
+long s5_file_block_to_disk_block(s5_node_t *sn, size_t file_blocknum,
+ int alloc, int *newp)
+{
+ NOT_YET_IMPLEMENTED("S5FS: ***none***");
+ return -1;
+}
+
+pframe_t *s5_cache_and_clear_block(mobj_t *mo, long block, long loc) {
+ pframe_t *pf;
+ mobj_create_pframe(mo, block, loc, &pf);
+ pf->pf_addr = page_alloc();
+ memset(pf->pf_addr, 0, PAGE_SIZE);
+ pf->pf_dirty = 1; // XXX do this later
+ return pf;
+}
+
+/* Read from a file.
+ *
+ * sn - The s5_node representing the file to read from
+ * pos - The position to start reading from
+ * buf - The buffer to read into
+ * len - The number of bytes to read
+ *
+ * Return the number of bytes read, or:
+ * - Propagate errors from s5_get_file_block (do not return a partial
+ * read). As in, if s5_get_file_block returns an error,
+ * the call to s5_read_file should fail.
+ *
+ * Hints:
+ * - Do not directly call s5_file_block_to_disk_block. To obtain pframes with
+ * the desired blocks, use s5_get_file_block and s5_release_file_block.
+ * - Be sure to handle all edge cases regarding pos and len relative to the
+ * length of the actual file. (If pos is greater than or equal to the length
+ * of the file, then s5_read_file should return 0).
+ */
+ssize_t s5_read_file(s5_node_t *sn, size_t pos, char *buf, size_t len)
+{
+ NOT_YET_IMPLEMENTED("S5FS: ***none***");
+ return -1;
+}
+
+/* Write to a file.
+ *
+ * sn - The s5_node representing the file to write to
+ * pos - The position to start writing to
+ * buf - The buffer to write from
+ * len - The number of bytes to write
+ *
+ * Return the number of bytes written, or:
+ * - EFBIG: pos was beyond S5_MAX_FILE_SIZE
+ * - Propagate errors from s5_get_file_block (that is, do not return a partial
+ * write)
+ *
+ * Hints:
+ * - You should return -EFBIG only if the provided pos was invalid. Otherwise,
+ * it is okay to make a partial write up to the maximum file size.
+ * - Use s5_get_file_block and s5_release_file_block to obtain pframes with
+ * the desired blocks.
+ * - Because s5_get_file_block calls s5fs_get_pframe, which checks the length
+ * of the vnode, you may have to update the vnode's length before you call
+ * s5_get_file_block. In this case, you should also update the inode's
+ * s5_size and mark the inode dirty.
+ * - If, midway through writing, you run into an error with s5_get_file_block,
+ * it is okay to merely undo your most recent changes while leaving behind
+ * writes you've already made to other blocks, before returning the error.
+ * That is, it is okay to make a partial write that the caller does not know
+ * about, as long as the file's length is consistent with what you've
+ * actually written so far.
+ * - You should maintain the vn_len of the vnode and the s5_un.s5_size field of the
+ * inode to be the same.
+ */
+ssize_t s5_write_file(s5_node_t *sn, size_t pos, const char *buf, size_t len)
+{
+ NOT_YET_IMPLEMENTED("S5FS: ***none***");
+ return -1;
+}
+
+#ifdef OLD
+/* Allocate one block from the filesystem.
+ *
+ * Return the block number of the newly allocated block, or:
+ * - ENOSPC: There are no more free blocks
+ *
+ * Hints:
+ * - Protect access to the super block using s5_lock_super and s5_unlock super.
+ * - Recall that the free block list is a linked list of blocks containing disk
+ * block numbers of free blocks. Each node contains S5_NBLKS_PER_FNODE block
+ * numbers, where the last entry is a pointer to the next node in the linked
+ * list, or -1 if there are no more free blocks remaining. The super block's
+ * s5s_free_blocks is the first node of this linked list.
+ * - The super block's s5s_nfree member is the number of blocks that are free
+ * within s5s_free_blocks. You could use it as an index into the
+ * s5s_free_blocks array. Be sure to update the field appropriately.
+ * - When s5s_free_blocks runs out (i.e. s5s_nfree == 0), refill it by
+ * collapsing the next node of the free list into the super block. Exactly
+ * when you do this is up to you.
+ * - You should initialize the block's contents to 0. Specifically,
+ * when you use s5_alloc_block to allocate an indirect block,
+ * as your implementation of s5_file_block_to_disk_block probably expects
+ * sparse blocks to be represented by a 0.
+ * - You may find it helpful to take a look at the implementation of
+ * s5_free_block below.
+ * - You may assume/assert that any pframe calls succeed.
+ */
+static long s5_alloc_block(s5fs_t *s5fs)
+{
+ NOT_YET_IMPLEMENTED("S5FS: ***none***");
+ return -1;
+}
+#endif
+
+static long s5_alloc_block(s5fs_t *s5fs)
+{
+ NOT_YET_IMPLEMENTED("S5FS: ***none***");
+ return -1;
+}
+
+/*
+ * The exact opposite of s5_alloc_block: add blockno to the free list of the
+ * filesystem. This should never fail. You may assert that any pframe calls
+ * succeed.
+ *
+ * Don't forget to protect access to the super block, update s5s_nfree, and
+ * expand the linked list correctly if the super block can no longer hold any
+ * more free blocks in its s5s_free_blocks array according to s5s_nfree.
+ */
+static void s5_free_block(s5fs_t *s5fs, blocknum_t blockno)
+{
+ s5_lock_super(s5fs);
+ s5_super_t *s = &s5fs->s5f_super;
+ dbg(DBG_S5FS, "freeing disk block %d\n", blockno);
+ KASSERT(blockno);
+ KASSERT(s->s5s_nfree < S5_NBLKS_PER_FNODE);
+
+ if (s->s5s_nfree == S5_NBLKS_PER_FNODE - 1)
+ {
+ // FIX THIS! Don't need to read prior contents
+ pframe_t *pf;
+ s5_get_meta_disk_block(s5fs, blockno, 1, &pf);
+ memcpy(pf->pf_addr, s->s5s_free_blocks, sizeof(s->s5s_free_blocks));
+ s5_release_disk_block(&pf);
+
+ s->s5s_nfree = 0;
+ s->s5s_free_blocks[S5_NBLKS_PER_FNODE - 1] = blockno;
+ }
+ else
+ {
+ s->s5s_free_blocks[s->s5s_nfree++] = blockno;
+ }
+ s5_unlock_super(s5fs);
+}
+
+/*
+ * Allocate one inode from the filesystem. You will need to use the super block
+ * s5s_free_inode member. You must initialize the on-disk contents of the
+ * allocated inode according to the arguments type and devid.
+ *
+ * Recall that the free inode list is a linked list. Each free inode contains a
+ * link to the next free inode. The super block s5s_free_inode must always point
+ * to the next free inode, or contain -1 to indicate no more inodes are
+ * available.
+ *
+ * Don't forget to protect access to the super block and update s5s_free_inode.
+ *
+ * You should use s5_get_inode and s5_release_inode.
+ *
+ * On success, return the newly allocated inode number.
+ * On failure, return -ENOSPC.
+ */
+long s5_alloc_inode(s5fs_t *s5fs, uint16_t type, devid_t devid)
+{
+ KASSERT((S5_TYPE_DATA == type) || (S5_TYPE_DIR == type) ||
+ (S5_TYPE_CHR == type) || (S5_TYPE_BLK == type));
+
+ s5_lock_super(s5fs);
+ uint32_t new_ino = s5fs->s5f_super.s5s_free_inode;
+ if (new_ino == (uint32_t)-1)
+ {
+ s5_unlock_super(s5fs);
+ return -ENOSPC;
+ }
+
+ pframe_t *pf;
+ s5_inode_t *inode;
+ s5_get_inode(s5fs, new_ino, 1, &pf, &inode);
+
+ s5fs->s5f_super.s5s_free_inode = inode->s5_un.s5_next_free;
+ KASSERT(inode->s5_un.s5_next_free != inode->s5_number);
+
+ inode->s5_un.s5_size = 0;
+ inode->s5_type = type;
+ inode->s5_linkcount = 0;
+ memset(inode->s5_direct_blocks, 0, sizeof(inode->s5_direct_blocks));
+ inode->s5_indirect_block =
+ (S5_TYPE_CHR == type || S5_TYPE_BLK == type) ? devid : 0;
+
+ s5_release_inode(&pf, &inode);
+ s5_unlock_super(s5fs);
+
+ dbg(DBG_S5FS, "allocated inode %d\n", new_ino);
+ return new_ino;
+}
+
+/*
+ * Free the inode by:
+ * 1) adding the inode to the free inode linked list (opposite of
+ * s5_alloc_inode), and 2) freeing all blocks being used by the inode.
+ *
+ * The suggested order of operations to avoid deadlock, is:
+ * 1) lock the super block
+ * 2) get the inode to be freed
+ * 3) update the free inode linked list
+ * 4) copy the blocks to be freed from the inode onto the stack
+ * 5) release the inode
+ * 6) unlock the super block
+ * 7) free all direct blocks
+ * 8) get the indirect block
+ * 9) copy the indirect block array onto the stack
+ * 10) release the indirect block
+ * 11) free the indirect blocks
+ * 12) free the indirect block itself
+ */
+void s5_free_inode(s5fs_t *s5fs, ino_t ino)
+{
+ pframe_t *pf;
+ s5_inode_t *inode;
+ s5_lock_super(s5fs);
+ s5_get_inode(s5fs, ino, 1, &pf, &inode);
+
+ uint32_t direct_blocks_to_free[S5_NDIRECT_BLOCKS];
+ uint32_t indirect_block_to_free;
+ if (inode->s5_type == S5_TYPE_DATA || inode->s5_type == S5_TYPE_DIR)
+ {
+ indirect_block_to_free = inode->s5_indirect_block;
+ memcpy(direct_blocks_to_free, inode->s5_direct_blocks,
+ sizeof(direct_blocks_to_free));
+ }
+ else
+ {
+ KASSERT(inode->s5_type == S5_TYPE_BLK || inode->s5_type == S5_TYPE_CHR);
+ indirect_block_to_free = 0;
+ memset(direct_blocks_to_free, 0, sizeof(direct_blocks_to_free));
+ }
+
+ inode->s5_un.s5_next_free = s5fs->s5f_super.s5s_free_inode;
+ inode->s5_type = S5_TYPE_FREE;
+ s5fs->s5f_super.s5s_free_inode = inode->s5_number;
+
+ s5_release_inode(&pf, &inode);
+ s5_unlock_super(s5fs);
+
+ for (unsigned i = 0; i < S5_NDIRECT_BLOCKS; i++)
+ {
+ if (direct_blocks_to_free[i])
+ {
+ s5_free_block(s5fs, direct_blocks_to_free[i]);
+ }
+ }
+ if (indirect_block_to_free)
+ {
+ uint32_t indirect_blocks_to_free[S5_NIDIRECT_BLOCKS];
+
+ s5_get_meta_disk_block(s5fs, indirect_block_to_free, 0, &pf);
+ KASSERT(S5_BLOCK_SIZE == PAGE_SIZE);
+ memcpy(indirect_blocks_to_free, pf->pf_addr, S5_BLOCK_SIZE);
+ s5_release_disk_block(&pf);
+
+ for (unsigned i = 0; i < S5_NIDIRECT_BLOCKS; i++)
+ {
+ if (indirect_blocks_to_free[i])
+ {
+ s5_free_block(s5fs, indirect_blocks_to_free[i]);
+ }
+ }
+ s5_free_block(s5fs, indirect_block_to_free);
+ }
+ dbg(DBG_S5FS, "freed inode %d\n", ino);
+}
+
+/* Return the inode number corresponding to the directory entry specified by
+ * name and namelen within a given directory.
+ *
+ * sn - The directory to search in
+ * name - The name to search for
+ * namelen - Length of name
+ * filepos - If non-NULL, use filepos to return the starting position of the
+ * directory entry
+ *
+ * Return the desired inode number, or:
+ * - ENOENT: Could not find a directory entry with the specified name
+ *
+ * Hints:
+ * - Use s5_read_file in increments of sizeof(s5_dirent_t) to read successive
+ * directory entries and compare them against name and namelen.
+ * - To avoid reading beyond the end of the directory, check if the return
+ * value of s5_read_file is 0
+ * - You could optimize this function by using s5_get_file_block (rather than
+ * s5_read_file) to ensure you do not read beyond the length of the file,
+ * but doing so is optional.
+ */
+long s5_find_dirent(s5_node_t *sn, const char *name, size_t namelen,
+ size_t *filepos)
+{
+ KASSERT(S_ISDIR(sn->vnode.vn_mode) && "should be handled at the VFS level");
+ KASSERT(S5_BLOCK_SIZE == PAGE_SIZE && "be wary, thee");
+ NOT_YET_IMPLEMENTED("S5FS: ***none***");
+ return -1;
+}
+
+/* Remove the directory entry specified by name and namelen from the directory
+ * sn.
+ *
+ * child - The found directory entry must correspond to the caller-provided
+ * child
+ *
+ * No return value. This function should never fail. You should assert that
+ * anything which could be incorrect is correct, and any function calls which
+ * could fail succeed.
+ *
+ * Hints:
+ * - Assert that the directory exists.
+ * - Assert that the found directory entry corresponds to child.
+ * - Ensure that the remaining directory entries in the file are contiguous. To
+ * do this, you should:
+ * - Overwrite the removed entry with the last directory entry.
+ * - Truncate the length of the directory by sizeof(s5_dirent_t).
+ * - Make sure you are only using s5_dirent_t, and not dirent_t structs.
+ * - Decrement the child's linkcount, because you have removed the directory's
+ * link to the child.
+ * - Mark the inodes as dirtied.
+ * - Use s5_find_dirent to find the position of the entry being removed.
+ */
+void s5_remove_dirent(s5_node_t *sn, const char *name, size_t namelen,
+ s5_node_t *child)
+{
+ vnode_t *dir = &sn->vnode;
+ s5_inode_t *inode = &sn->inode;
+ NOT_YET_IMPLEMENTED("S5FS: ***none***");
+}
+
+/* Replace a directory entry.
+ *
+ * sn - The directory to search within
+ * name - The name of the old directory entry
+ * namelen - Length of the old directory entry name
+ * old - The s5_node corresponding to the old directory entry
+ * new - The s5_node corresponding to the new directory entry
+ *
+ * No return value. Similar to s5_remove_dirent, this function should never
+ * fail. You should assert that everything behaves correctly.
+ *
+ * Hints:
+ * - Assert that the directory exists, that the directory entry exists, and
+ * that it corresponds to the old s5_node.
+ * - When forming the new directory entry, use the same name and namelen from
+ * before, but use the inode number from the new s5_node.
+ * - Update linkcounts and dirty inodes appropriately.
+ *
+ * s5_replace_dirent is NOT necessary to implement. It's only useful if
+ * you're planning on implementing the renaming of directories (which you shouldn't
+ * attempt until after the rest of S5FS is done).
+ */
+void s5_replace_dirent(s5_node_t *sn, const char *name, size_t namelen,
+ s5_node_t *old, s5_node_t *new)
+{
+ vnode_t *dir = &sn->vnode;
+ s5_inode_t *inode = &sn->inode;
+ NOT_YET_IMPLEMENTED("S5FS: ***none***");
+}
+
+/* Create a directory entry.
+ *
+ * dir - The directory within which to create a new entry
+ * name - The name of the new entry
+ * namelen - Length of the new entry name
+ * child - The s5_node holding the inode which the new entry should represent
+ *
+ * Return 0 on success, or:
+ * - EEXIST: The directory entry already exists
+ * - Propagate errors from s5_write_file
+ *
+ * Hints:
+ * - Update linkcounts and mark inodes dirty appropriately.
+ * - You may wish to assert at the end of s5_link that the directory entry
+ * exists and that its inode is, as expected, the inode of child.
+ */
+long s5_link(s5_node_t *dir, const char *name, size_t namelen,
+ s5_node_t *child)
+{
+ KASSERT(kmutex_owns_mutex(&dir->vnode.vn_mobj.mo_mutex));
+
+ NOT_YET_IMPLEMENTED("S5FS: ***none***");
+ return -1;
+}
+
+/* Return the number of file blocks allocated for sn. This means any
+ * file blocks that are not sparse, direct or indirect. If the indirect
+ * block itself is allocated, that must also count. This function should not
+ * fail.
+ *
+ * Hint:
+ * - You may wish to assert that the special character / block files do not
+ * have any blocks allocated to them. Remember, the s5_indirect_block for
+ * these special files is actually the device id.
+ */
+long s5_inode_blocks(s5_node_t *sn)
+{
+ NOT_YET_IMPLEMENTED("S5FS: ***none***");
+ return -1;
+}
+
+/**
+ * Given a s5_node_t, frees the associated direct blocks and
+ * the indirect blocks if they exist.
+ *
+ * Should only be called from the truncate_file routine.
+ */
+void s5_remove_blocks(s5_node_t *sn)
+{
+ // Free the blocks used by the node
+ // First, free the the direct blocks
+ s5fs_t* s5fs = VNODE_TO_S5FS(&sn->vnode);
+ s5_inode_t* s5_inode = &sn->inode;
+ for (unsigned i = 0; i < S5_NDIRECT_BLOCKS; i++)
+ {
+ if (s5_inode->s5_direct_blocks[i])
+ {
+ s5_free_block(s5fs, s5_inode->s5_direct_blocks[i]);
+ }
+ }
+
+ memset(s5_inode->s5_direct_blocks, 0, sizeof(s5_inode->s5_direct_blocks));
+
+ // Get the indirect blocks and free them, if they exist
+ if (s5_inode->s5_indirect_block)
+ {
+ pframe_t *pf;
+ s5_get_meta_disk_block(s5fs, s5_inode->s5_indirect_block, 0, &pf);
+ uint32_t *blocknum_ptr = pf->pf_addr;
+
+ for (unsigned i = 0; i < S5_NIDIRECT_BLOCKS; i++)
+ {
+ if (blocknum_ptr[i])
+ {
+ s5_free_block(s5fs, blocknum_ptr[i]);
+ }
+ }
+
+ s5_release_disk_block(&pf);
+ // Free the indirect block itself
+ s5_free_block(s5fs, s5_inode->s5_indirect_block);
+ s5_inode->s5_indirect_block = 0;
+ }
+}