aboutsummaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/fs/s5fs/s5fs.c471
1 files changed, 447 insertions, 24 deletions
diff --git a/kernel/fs/s5fs/s5fs.c b/kernel/fs/s5fs/s5fs.c
index fe2a16c..fd0c779 100644
--- a/kernel/fs/s5fs/s5fs.c
+++ b/kernel/fs/s5fs/s5fs.c
@@ -210,7 +210,56 @@ long s5fs_mount(fs_t *fs)
*/
static void s5fs_read_vnode(fs_t *fs, vnode_t *vn)
{
- NOT_YET_IMPLEMENTED("S5FS: s5fs_read_vnode");
+ // NOT_YET_IMPLEMENTED("S5FS: s5fs_read_vnode");
+
+ // Get the s5_node from the vnode
+ s5_node_t *s5_node = VNODE_TO_S5NODE(vn);
+
+ // Get the inode
+ s5_inode_t *s5_inode = &s5_node->inode;
+ KASSERT(s5_inode);
+ // Update linkcount
+ s5_inode->s5_linkcount++;
+
+ // Get the inode from the disk
+ pframe_t *pf;
+ s5_get_file_disk_block(vn, S5_INODE_BLOCK(vn->vn_vno), S5_INODE_OFFSET(vn->vn_vno), 0, &pf);
+ memcpy(s5_inode, pf->pf_addr, sizeof(s5_inode_t));
+
+ // Release the disk block
+ s5_release_disk_block(&pf);
+
+ // Initialize the dirtied_inode field
+ s5_node->dirtied_inode = 0;
+
+ // Initialize the vnode fields
+ // lan and inode trivial
+ vn->vn_len = s5_inode->s5_un.s5_size;
+ vn->vn_i = s5_inode;
+ // Initialize the vn_ops and mode field based on the type
+ switch (s5_inode->s5_type)
+ {
+ case S5_TYPE_DIR: // directory
+ vn->vn_ops = &s5fs_dir_vops;
+ vn->vn_mode = S_IFDIR;
+ break;
+ case S5_TYPE_CHR: // character device
+ vn->vn_ops = NULL;
+ vn->vn_mode = S_IFCHR;
+ vn->vn_devid = s5_inode->s5_indirect_block;
+ break;
+ case S5_TYPE_BLK: // block device
+ vn->vn_ops = NULL;
+ vn->vn_mode = S_IFBLK;
+ vn->vn_devid = s5_inode->s5_indirect_block;
+ break;
+ case S5_TYPE_DATA: // regular file
+ vn->vn_ops = &s5fs_file_vops;
+ vn->vn_mode = S_IFREG;
+ break;
+ default: // unknown type
+ panic("Unknown inode type %d\n", s5_inode->s5_type);
+ }
}
/* Clean up the inode corresponding to the given vnode.
@@ -226,7 +275,38 @@ static void s5fs_read_vnode(fs_t *fs, vnode_t *vn)
*/
static void s5fs_delete_vnode(fs_t *fs, vnode_t *vn)
{
- NOT_YET_IMPLEMENTED("S5FS: s5fs_delete_vnode");
+ // NOT_YET_IMPLEMENTED("S5FS: s5fs_delete_vnode");
+
+ // Get the s5_node from the vnode
+ s5_node_t *s5_node = VNODE_TO_S5NODE(vn);
+
+ // Get the inode
+ s5_inode_t *s5_inode = &s5_node->inode;
+ KASSERT(s5_inode);
+
+ // Update linkcount
+ s5_inode->s5_linkcount--;
+
+ // Check if the inode is no longer in use
+ if (s5_inode->s5_linkcount == 0)
+ {
+ // Free the inode and return
+ s5_free_inode(FS_TO_S5FS(fs), s5_inode->s5_number);
+ return;
+ }
+
+ // Check if the inode is dirty
+ if (s5_node->dirtied_inode)
+ {
+ // Write the inode back to disk and return
+ pframe_t *pf;
+ s5_get_file_disk_block(vn, S5_INODE_BLOCK(vn->vn_vno), S5_INODE_OFFSET(vn->vn_vno), 1, &pf);
+ memcpy(pf->pf_addr, s5_inode, sizeof(s5_inode_t));
+ s5_release_disk_block(&pf);
+ return;
+ }
+
+ // Do nothing if the inode is unchanged
}
/*
@@ -291,8 +371,8 @@ static void s5fs_sync(fs_t *fs)
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: s5fs_read");
- return -1;
+ // NOT_YET_IMPLEMENTED("S5FS: s5fs_read");
+ return s5_read_file(VNODE_TO_S5NODE(vnode), pos, buf, len);
}
/* Wrapper around s5_write_file. */
@@ -300,8 +380,8 @@ 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: s5fs_write");
- return -1;
+ // NOT_YET_IMPLEMENTED("S5FS: s5fs_write");
+ return s5_write_file(VNODE_TO_S5NODE(vnode), pos, buf, len);
}
/*
@@ -340,8 +420,68 @@ 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: s5fs_mknod");
- return -1;
+ // NOT_YET_IMPLEMENTED("S5FS: s5fs_mknod");
+
+ // Check if the mode is not supported
+ if (mode != S_IFCHR && mode != S_IFBLK && mode != S_IFREG)
+ {
+ return -ENOTSUP;
+ }
+
+ // Allocate a new inode, get its properties first
+ s5fs_t *s5fs = VNODE_TO_S5FS(dir);
+ uint16_t type;
+ if(S_ISCHR(mode))
+ {
+ type = S5_TYPE_CHR;
+ }
+ else if(S_ISBLK(mode))
+ {
+ type = S5_TYPE_BLK;
+ }
+ else if(S_ISREG(mode))
+ {
+ type = S5_TYPE_DATA;
+ }
+ else
+ {
+ panic("Invalid s5 mode!\n");
+ }
+
+ // Allocate the inode
+ s5_inode_t *s5_inode;
+ long inode_num = s5_alloc_inode(s5fs, type, &s5_inode);
+ // Check if the inode allocation failed
+ if (inode_num < 0)
+ {
+ return inode_num;
+ }
+
+ // Create the new vnode
+ vnode_t *new_vnode = vget(dir->vn_fs, inode_num);
+ // Check if the vnode creation failed
+ if (!new_vnode)
+ {
+ s5_free_inode(s5fs, inode_num);
+ return -ENOMEM;
+ }
+
+ // Set the devid for the new inode
+ s5_inode->s5_indirect_block = devid;
+
+ // Link the new inode/vnode to the parent directory
+ long link = s5_link(dir, name, namelen, new_vnode);
+ // Check if the link operation failed
+ if (link < 0)
+ {
+ vput(new_vnode);
+ s5_free_inode(s5fs, inode_num);
+ return link;
+ }
+
+ // Set the out pointer to the new vnode
+ *out = new_vnode;
+ return 0;
}
/* Search for a given entry within a directory.
@@ -362,8 +502,37 @@ static long s5fs_mknod(struct vnode *dir, const char *name, size_t namelen,
long s5fs_lookup(vnode_t *dir, const char *name, size_t namelen,
vnode_t **ret)
{
- NOT_YET_IMPLEMENTED("S5FS: s5fs_lookup");
- return -1;
+ // NOT_YET_IMPLEMENTED("S5FS: s5fs_lookup");
+
+ // Find the directory entry
+ size_t filepos;
+ long ino = s5_find_dirent(VNODE_TO_S5NODE(dir), name, namelen, &filepos);
+ // Check if the directory entry was not found
+ if (ino < 0)
+ {
+ return ino;
+ }
+
+ // Check if the found vnode is the directory itself
+ if (ino == dir->vn_vno)
+ {
+ vref(dir);
+ *ret = dir;
+ return 0;
+ }
+
+
+ // If not, get the found vnode
+ vnode_t *found_vnode = vget(dir->vn_fs, ino);
+ // Check if the vnode creation failed
+ if (!found_vnode)
+ {
+ return -ENOMEM;
+ }
+ // Increment the reference count
+ // vref(found_vnode);
+ *ret = found_vnode;
+ return 0;
}
/* Wrapper around s5_link.
@@ -375,8 +544,15 @@ 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: s5fs_link");
- return -1;
+ //NOT_YET_IMPLEMENTED("S5FS: s5fs_link");
+
+ // Check if the child is a directory
+ if (S_ISDIR(child->vn_mode))
+ {
+ return -EISDIR;
+ }
+
+ return s5_link(VNODE_TO_S5NODE(dir), name, namelen, VNODE_TO_S5NODE(child));
}
/* Remove the directory entry in dir corresponding to name and namelen.
@@ -395,8 +571,29 @@ 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: s5fs_unlink");
- return -1;
+ // NOT_YET_IMPLEMENTED("S5FS: s5fs_unlink");
+
+ // Find the directory entry
+ s5_node_t *s5_node = VNODE_TO_S5NODE(dir);
+ size_t filepos;
+ long ino = s5_find_dirent(s5_node, name, namelen, &filepos);
+ // Check if the directory entry was not found
+ if (ino < 0)
+ {
+ return ino;
+ }
+
+ // Get the found vnode
+ vnode_t *child = vget_locked(dir->vn_fs, ino);
+ KASSERT(!S_ISDIR(child->vn_mode) && "should be handled at the VFS level");
+
+ // Remove the directory entry
+ s5_remove_dirent(s5_node, name, namelen, filepos);
+ // Check to see if this failed (TODO: ask in hours)
+
+ // Release the vnode
+ vput_locked(&child);
+ return 0;
}
/* Change the name or location of a file.
@@ -447,8 +644,98 @@ 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: s5fs_rename");
- return -1;
+ // NOT_YET_IMPLEMENTED("S5FS: s5fs_rename");
+
+ // Check if the new name is too long
+ if (newnamelen >= NAME_LEN)
+ {
+ return -ENAMETOOLONG;
+ }
+
+ // Find the old directory entry
+ s5_node_t *s5_node = VNODE_TO_S5NODE(olddir);
+ size_t filepos;
+ long ino = s5_find_dirent(s5_node, oldname, oldnamelen, &filepos);
+ // Check if the directory entry was not found
+ if (ino < 0)
+ {
+ return ino;
+ }
+
+ // Get the found vnode
+ vnode_t *child = vget_locked(olddir->vn_fs, ino);
+ KASSERT(!S_ISDIR(child->vn_mode) && "should be handled at the VFS level");
+
+ // Check if the new directory is not a directory
+ if (!S_ISDIR(newdir->vn_mode))
+ {
+ vput_locked(&child);
+ return -ENOTDIR;
+ }
+
+ // Find the new directory entry
+ s5_node_t *new_s5_node = VNODE_TO_S5NODE(newdir);
+ size_t new_filepos;
+ long new_ino = s5_find_dirent(new_s5_node, newname, newnamelen, &new_filepos);
+ // Check if the directory entry is new
+ if (new_ino == -ENOENT)
+ {
+ // Link the new directory
+ long link = s5_link(new_s5_node, newname, newnamelen, VNODE_TO_S5NODE(child));
+ // Check if the link operation failed
+ if (link < 0)
+ {
+ vput_locked(&child);
+ return link;
+ }
+
+ // Remove the old directory entry
+ s5_remove_dirent(s5_node, oldname, oldnamelen, filepos);
+ // Check if this failed (TODO: ask in hours)
+
+ return link;
+ }
+
+ // Else, the new directory entry was found and we need to replace it
+ // Get the new found vnode
+ vnode_t *new_child = vget_locked(newdir->vn_fs, new_ino);
+ KASSERT(!S_ISDIR(new_child->vn_mode) && "should be handled at the VFS level");
+
+ // Check if the old and new vnodes are the same
+ if (child->vn_vno == new_child->vn_vno)
+ {
+ vput_locked(&child);
+ vput_locked(&new_child);
+ return 0;
+ }
+
+ // Check if the new vnode is a directory
+ if (S_ISDIR(new_child->vn_mode))
+ {
+ vput_locked(&child);
+ vput_locked(&new_child);
+ return -EISDIR;
+ }
+
+ // Remove the new directory entry
+ s5_remove_dirent(new_s5_node, newname, newnamelen, new_filepos);
+ // Check if this failed (TODO: ask in hours)
+ // Link the new directory
+ long link = s5_link(new_s5_node, newname, newnamelen, VNODE_TO_S5NODE(child));
+ // Check if the link operation failed
+ if (link < 0)
+ {
+ vput_locked(&child);
+ vput_locked(&new_child);
+ return link;
+ }
+
+ // Remove the old directory entry
+ s5_remove_dirent(s5_node, oldname, oldnamelen, filepos);
+
+ vput_locked(&child);
+ vput_locked(&new_child);
+ return link;
}
/* Create a directory.
@@ -479,8 +766,66 @@ 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: s5fs_mkdir");
- return -1;
+ // NOT_YET_IMPLEMENTED("S5FS: s5fs_mkdir");
+
+ // Allocate a new inode, get its properties first
+ s5fs_t *s5fs = VNODE_TO_S5FS(dir);
+ uint16_t type = S5_TYPE_DIR;
+
+ // Allocate the inode
+ s5_inode_t *s5_inode;
+ long inode_num = s5_alloc_inode(s5fs, type, &s5_inode);
+ // Check if the inode allocation failed
+ if (inode_num < 0)
+ {
+ return inode_num;
+ }
+
+ // Create the new vnode
+ vnode_t *new_vnode = vget(dir->vn_fs, inode_num);
+ // Check if the vnode creation failed
+ if (!new_vnode)
+ {
+ s5_free_inode(s5fs, inode_num);
+ return -ENOMEM;
+ }
+
+ // Create the "." entry
+ long link = s5_link(VNODE_TO_S5NODE(new_vnode), ".", 1, VNODE_TO_S5NODE(new_vnode));
+ // Check if the link operation failed
+ if (link < 0)
+ {
+ vput(new_vnode);
+ s5_free_inode(s5fs, inode_num);
+ return link;
+ }
+
+ // Create the ".." entry
+ long link2 = s5_link(VNODE_TO_S5NODE(new_vnode), "..", 2, VNODE_TO_S5NODE(dir));
+ // Check if the link operation failed
+ if (link2 < 0)
+ {
+ s5_remove_dirent(VNODE_TO_S5NODE(new_vnode), ".", 1, 0);
+ vput(new_vnode);
+ s5_free_inode(s5fs, inode_num);
+ return link2;
+ }
+
+ // Link the new directory to the parent
+ long link3 = s5_link(VNODE_TO_S5NODE(dir), name, namelen, VNODE_TO_S5NODE(new_vnode));
+ // Check if the link operation failed
+ if (link3 < 0)
+ {
+ s5_remove_dirent(VNODE_TO_S5NODE(new_vnode), ".", 1, 0);
+ s5_remove_dirent(VNODE_TO_S5NODE(new_vnode), "..", 2, 0);
+ vput(new_vnode);
+ s5_free_inode(s5fs, inode_num);
+ return link3;
+ }
+
+ // Set the out pointer to the new vnode
+ *out = new_vnode;
+ return 0;
}
/* Remove a directory.
@@ -501,8 +846,50 @@ 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: s5fs_rmdir");
- return -1;
+ // NOT_YET_IMPLEMENTED("S5FS: s5fs_rmdir");
+
+ // Find the directory entry
+ s5_node_t *s5_node = VNODE_TO_S5NODE(parent);
+ size_t filepos;
+ long ino = s5_find_dirent(s5_node, name, namelen, &filepos);
+ // Check if the directory entry was not found
+ if (ino < 0)
+ {
+ return ino;
+ }
+
+ // Get the found vnode
+ vnode_t *child = vget_locked(parent->vn_fs, ino);
+ KASSERT(S_ISDIR(child->vn_mode) && "should be handled at the VFS level");
+
+ // Check if this is a directory
+ if (!S_ISDIR(child->vn_mode))
+ {
+ vput_locked(&child);
+ return -ENOTDIR;
+ }
+
+ // Check if the directory is not empty
+ if (child->vn_len > 2 * sizeof(s5_dirent_t))
+ {
+ vput_locked(&child);
+ return -ENOTEMPTY;
+ }
+
+ // Remove the "." entry
+ s5_remove_dirent(VNODE_TO_S5NODE(child), ".", 1, 0);
+ // Check if this failed (TODO: ask in hours)
+
+ // Remove the ".." entry
+ s5_remove_dirent(VNODE_TO_S5NODE(child), "..", 2, 0);
+
+ // Remove the directory entry
+ s5_remove_dirent(s5_node, name, namelen, filepos);
+ // Check if this failed (TODO: ask in hours)
+
+ // Release the vnode
+ vput_locked(&child);
+ return 0;
}
/* Read a directory entry.
@@ -524,8 +911,23 @@ 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)
{
KASSERT(S_ISDIR(vnode->vn_mode) && "should be handled at the VFS level");
- NOT_YET_IMPLEMENTED("S5FS: s5fs_readdir");
- return -1;
+ // NOT_YET_IMPLEMENTED("S5FS: s5fs_readdir");
+
+ // Read the directory entry
+ s5_dirent_t s5_dirent;
+ long bytes_read = s5_read_file(VNODE_TO_S5NODE(vnode), pos, &s5_dirent, sizeof(s5_dirent_t));
+ // Check if the read operation failed
+ if (bytes_read < 0)
+ {
+ return bytes_read;
+ }
+
+ // Initialize the dirent
+ d->d_ino = s5_dirent.s5d_inode;
+ d->d_off = pos + sizeof(s5_dirent_t);
+ memcpy(d->d_name, s5_dirent.s5d_name, 28);
+
+ return bytes_read;
}
/* Get file status.
@@ -548,8 +950,29 @@ static long s5fs_readdir(vnode_t *vnode, size_t pos, struct dirent *d)
*/
static long s5fs_stat(vnode_t *vnode, stat_t *ss)
{
- NOT_YET_IMPLEMENTED("S5FS: s5fs_stat");
- return -1;
+ // NOT_YET_IMPLEMENTED("S5FS: s5fs_stat");
+
+ // Get the inode
+ s5_inode_t *s5_inode = &VNODE_TO_S5NODE(vnode)->inode;
+
+ // Initialize the stat struct
+ ss->st_blocks = s5_inode_blocks(s5_inode);
+ ss->st_mode = vnode->vn_mode;
+ ss->st_rdev = vnode->vn_devid;
+ ss->st_ino = s5_inode->s5_number;
+ ss->st_nlink = s5_inode->s5_linkcount;
+ ss->st_blksize = S5_BLOCK_SIZE;
+ ss->st_size = s5_inode->s5_un.s5_size;
+ ss->st_dev = VNODE_TO_S5FS(vnode)->s5f_bdev->bd_id;
+
+ // Set all other fields to 0
+ ss->st_uid = 0;
+ ss->st_gid = 0;
+ ss->st_atime = 0;
+ ss->st_mtime = 0;
+ ss->st_ctime = 0;
+
+ return 0;
}
/**