From a17999858ddaada83071d953d920e3c2a2b390c8 Mon Sep 17 00:00:00 2001 From: sotech117 Date: Thu, 25 Apr 2024 07:45:04 +0000 Subject: s5 from ramfs --- Config.mk | 2 +- kernel/fs/s5fs/s5fs.c | 471 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 448 insertions(+), 25 deletions(-) diff --git a/Config.mk b/Config.mk index 70edadf..c17e704 100644 --- a/Config.mk +++ b/Config.mk @@ -12,7 +12,7 @@ # DRIVERS=1 VFS=1 - S5FS=0 + S5FS=1 VM=0 DYNAMIC=0 # When you finish S5FS, first enable "VM"; once this is working, then enable 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; } /** -- cgit v1.2.3-70-g09d2