diff options
Diffstat (limited to 'kernel/fs')
-rw-r--r-- | kernel/fs/namev.c | 168 | ||||
-rw-r--r-- | kernel/fs/open.c | 99 | ||||
-rw-r--r-- | kernel/fs/s5fs/s5fs.c | 471 | ||||
-rw-r--r-- | kernel/fs/vfs_syscall.c | 628 | ||||
-rw-r--r-- | kernel/fs/vnode_specials.c | 32 |
5 files changed, 1332 insertions, 66 deletions
diff --git a/kernel/fs/namev.c b/kernel/fs/namev.c index e8b01e8..dd44450 100644 --- a/kernel/fs/namev.c +++ b/kernel/fs/namev.c @@ -76,8 +76,27 @@ long namev_is_descendant(vnode_t *a, vnode_t *b) long namev_lookup(vnode_t *dir, const char *name, size_t namelen, vnode_t **res_vnode) { - NOT_YET_IMPLEMENTED("VFS: namev_lookup"); - return 0; + // // NOT_YET_IMPLEMENTED("VFS: namev_lookup"); + + // KASSERT(NULL != dir); + // KASSERT(NULL != name); + // KASSERT(NULL != res_vnode); + + // if (namelen == 0) + // { + // return -EINVAL; + // } + + if ( + dir->vn_ops == NULL + || dir->vn_ops->lookup == NULL + || !S_ISDIR(dir->vn_mode) + ) + { + return -ENOTDIR; + } + + return dir->vn_ops->lookup(dir, name, namelen, res_vnode); } /* @@ -187,10 +206,89 @@ static const char *namev_tokenize(const char **search, size_t *len) long namev_dir(vnode_t *base, const char *path, vnode_t **res_vnode, const char **name, size_t *namelen) { - NOT_YET_IMPLEMENTED("VFS: namev_dir"); + // NOT_YET_IMPLEMENTED("VFS: namev_dir"); + + // Check if the pathname is nullish + if (path == NULL || *path == '\0') + { + return -EINVAL; + } + + // Get a vnode based on the pathname + vnode_t *dir = NULL; + if (*path == '/') + { + dir = vfs_root_fs.fs_root; + } + else if (base == NULL) + { + dir = curproc->p_cwd; + } + else + { + dir = base; + } + // Refcount it + vref(dir); + + // Tokenize the path + const char *token = NULL; + size_t len = 0; + + while ((token = namev_tokenize(&path, &len)) && len > 0) + { + if (path == NULL || *path == '\0') + { + break; + } + + // Check if the whole path is a sequence of '/' then a null + int index = 0; + while (path[index] == '/') + { + index++; + } + if (path[index] == '\0') + { + break; + } + + // Check if the name is too long + if (len > NAME_LEN) + { + vput(&dir); + return -ENAMETOOLONG; + } + + // Lookup the token + vnode_t *next = NULL; + vlock(dir); + long err = namev_lookup(dir, token, len, &next); + vunlock(dir); + if (err < 0) + { + vput(&dir); + return err; + } + + // Reduce refcount and move to the next token + vput(&dir); + dir = next; + } + + // Check if it's a directory + if (!S_ISDIR(dir->vn_mode)) + { + vput(&dir); + return -ENOTDIR; + } + + // Assign the ret vars + *res_vnode = dir; + *name = token; + *namelen = len; return 0; } - /* * Open the file specified by `base` and `path`, or create it, if necessary. * Return the file's vnode via `res_vnode`, which should be returned unlocked @@ -217,7 +315,67 @@ long namev_dir(vnode_t *base, const char *path, vnode_t **res_vnode, long namev_open(vnode_t *base, const char *path, int oflags, int mode, devid_t devid, struct vnode **res_vnode) { - NOT_YET_IMPLEMENTED("VFS: namev_open"); + // NOT_YET_IMPLEMENTED("VFS: namev_open"); + + // KASSERT(NULL != base); + KASSERT(NULL != path); + KASSERT(NULL != res_vnode); + + const char *name = NULL; + vnode_t *dir = NULL; + size_t namelen = 0; + + long err = namev_dir(base, path, &dir, &name, &namelen); + if (err < 0) + { + return err; + } + if (namelen > NAME_LEN) + { + vput(&dir); + return -ENAMETOOLONG; + } + + vnode_t *res = NULL; + vlock(dir); + err = namev_lookup(dir, name, namelen, &res); + vunlock(dir); + + if (err < 0) + { + if (!(oflags & O_CREAT)) + { + vput(&dir); + return err; + } + else + { + vlock(dir); + err = dir->vn_ops->mknod(dir, name, namelen, mode, devid, &res); + vunlock(dir); + + vput(&dir); + *res_vnode = res; + return err; + } + } + + if (path[strlen(path) - 1] == '/' && !S_ISDIR(res->vn_mode)) + { + vput(&dir); + vput(&res); + return -ENOTDIR; + } + + if (S_ISDIR(res->vn_mode) && (oflags & O_CREAT)) + { + vput(&dir); + vput(&res); + return -EISDIR; // TODO: check if this error + } + + vput(&dir); + *res_vnode = res; return 0; } diff --git a/kernel/fs/open.c b/kernel/fs/open.c index 8c9fee6..8f0d893 100644 --- a/kernel/fs/open.c +++ b/kernel/fs/open.c @@ -62,6 +62,101 @@ long get_empty_fd(int *fd) */ long do_open(const char *filename, int oflags) { - NOT_YET_IMPLEMENTED("VFS: do_open"); - return -1; + // NOT_YET_IMPLEMENTED("VFS: do_open"); + + // Check if oflags is valid + if ((oflags & O_WRONLY) && (oflags & O_RDWR)) + { + return -EINVAL; + } + + // Get an empty file descriptor + int fd = -1; + long ret = get_empty_fd(&fd); + if (ret < 0) + { + return ret; + } + + // Check if the file is a directory + if (filename[strlen(filename) - 1] == '/' && (oflags & O_WRONLY)) + { + return -EISDIR; + } + + // Open the file + vnode_t *res_vnode = NULL; + vnode_t *base = curproc->p_cwd; + ret = namev_open(base, filename, oflags, S_IFREG, 0, &res_vnode); + if (ret < 0) + { + if (res_vnode != NULL) + { + vput(&res_vnode); + } + return ret; + } + + // Check if the vnode is a directory + if (S_ISDIR(res_vnode->vn_mode) && ((oflags & O_WRONLY) || (oflags & O_RDWR))) + { + vput(&res_vnode); + return -EISDIR; + } + + // Check if the vnode is a blockdev or chardev + if (S_ISCHR(res_vnode->vn_mode) && res_vnode->vn_dev.chardev == NULL) + { + vput(&res_vnode); + return -ENXIO; + } + if (S_ISBLK(res_vnode->vn_mode) && res_vnode->vn_dev.blockdev == NULL) + { + vput(&res_vnode); + return -ENXIO; + } + + // Convert oflags to file access flags + int fmode = 0; + if (oflags & O_RDONLY) + { + fmode |= FMODE_READ; + } + + if (oflags & O_WRONLY) + { + fmode |= FMODE_WRITE; + } + else + { + fmode |= FMODE_READ; + } + + if (oflags & O_RDWR) + { + fmode |= FMODE_READ | FMODE_WRITE; + } + + if (oflags & O_APPEND) + { + fmode |= FMODE_APPEND; + } + // Truncate the file if O_TRUNC is specified + if (oflags & O_TRUNC && S_ISREG(res_vnode->vn_mode)) + { + vlock(res_vnode); + res_vnode->vn_ops->truncate_file(res_vnode); + vunlock(res_vnode); + } + + // Create the file descriptor + file_t *file = fcreate(fd, res_vnode, fmode); + if (file == NULL) + { + vput(&res_vnode); + return -ENOMEM; + } + + vput(&res_vnode); + return fd; } diff --git a/kernel/fs/s5fs/s5fs.c b/kernel/fs/s5fs/s5fs.c index d1df3a5..2bcec7d 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 } /* @@ -285,8 +365,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. */ @@ -294,8 +374,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); } /* @@ -334,8 +414,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. @@ -356,8 +496,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. @@ -369,8 +538,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. @@ -389,8 +565,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. @@ -441,8 +638,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. @@ -473,8 +760,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. @@ -495,8 +840,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. @@ -518,8 +905,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. @@ -542,8 +944,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; } /** diff --git a/kernel/fs/vfs_syscall.c b/kernel/fs/vfs_syscall.c index e05fe5f..205020b 100644 --- a/kernel/fs/vfs_syscall.c +++ b/kernel/fs/vfs_syscall.c @@ -26,8 +26,58 @@ */ ssize_t do_read(int fd, void *buf, size_t len) { - NOT_YET_IMPLEMENTED("VFS: do_read"); - return -1; + // NOT_YET_IMPLEMENTED("VFS: do_read"); + + // Check if the file descriptor is valid + if (fd < 0 || fd >= NFILES) + { + return -EBADF; + } + + // fget the file + file_t *file = fget(fd); + if (file == NULL) + { + return -EBADF; + } + + // Check if the file is open for reading + if (!(file->f_mode & FMODE_READ)) + { + fput(&file); + return -EBADF; + } + + // Check if the file is a directory + if (S_ISDIR(file->f_vnode->vn_mode)) + { + fput(&file); + return -EISDIR; + } + + // Check is read is valid + if (file->f_vnode->vn_ops->read == NULL) + { + fput(&file); + return -EISDIR; + } + + // Read the file + vlock(file->f_vnode); + ssize_t bytes_read = file->f_vnode->vn_ops->read(file->f_vnode, file->f_pos, buf, len); + vunlock(file->f_vnode); + + // Check if the read was successful + if (bytes_read < 0) + { + fput(&file); + return bytes_read; + } + + // Update the file position + file->f_pos += bytes_read; + fput(&file); + return bytes_read; } /* @@ -46,8 +96,60 @@ ssize_t do_read(int fd, void *buf, size_t len) */ ssize_t do_write(int fd, const void *buf, size_t len) { - NOT_YET_IMPLEMENTED("VFS: do_write"); - return -1; + // NOT_YET_IMPLEMENTED("VFS: do_write"); + + // Check if the file descriptor is valid + if (fd < 0 || fd >= NFILES) + { + return -EBADF; + } + + // dbg(DBG_PRINT, "VFS: do_write: fd = %d, len = %ld\n", fd, len); + + // fget the file + file_t *file = fget(fd); + if (file == NULL) + { + return -EBADF; + } + + // Check if the file is open for writing + if (!(file->f_mode & FMODE_WRITE)) + { + fput(&file); + return -EBADF; + } + + // Check is write is valid + if (file->f_vnode->vn_ops->write == NULL) + { + fput(&file); + return -EISDIR; + } + + // Move the file position to the end of the file if the file is open in append mode + if (file->f_mode & FMODE_APPEND) + { + vlock(file->f_vnode); + file->f_pos = file->f_vnode->vn_len; + vunlock(file->f_vnode); + } + + // Write the file + vlock(file->f_vnode); + ssize_t bytes_written = file->f_vnode->vn_ops->write(file->f_vnode, file->f_pos, buf, len); + vunlock(file->f_vnode); + + // Check if the write was successful + if (bytes_written < 0) + { + fput(&file); + return bytes_written; + } + + file->f_pos += bytes_written; + fput(&file); + return bytes_written; } /* @@ -63,8 +165,26 @@ ssize_t do_write(int fd, const void *buf, size_t len) */ long do_close(int fd) { - NOT_YET_IMPLEMENTED("VFS: do_close"); - return -1; + // NOT_YET_IMPLEMENTED("VFS: do_close"); + + // Check if the file descriptor is valid + if (fd < 0 || fd >= NFILES) + { + return -EBADF; + } + + // fget the file + file_t *file = fget(fd); + if (file == NULL) + { + return -EBADF; + } + + // Close the file + fput(&file); + fput(&curproc->p_files[fd]); + + return 0; } /* @@ -78,8 +198,33 @@ long do_close(int fd) */ long do_dup(int fd) { - NOT_YET_IMPLEMENTED("VFS: do_dup"); - return -1; + // NOT_YET_IMPLEMENTED("VFS: do_dup"); + + // Check if the file descriptor is valid + if (fd < 0 || fd >= NFILES) + { + return -EBADF; + } + + // fget the file + file_t *file = fget(fd); + if (file == NULL) + { + return -EBADF; + } + + // Get an empty file descriptor + int new_fd = -1; + long ret = get_empty_fd(&new_fd); + if (ret < 0) + { + fput(&file); + return ret; + } + + // Copy the file + curproc->p_files[new_fd] = file; + return new_fd; } /* @@ -94,8 +239,40 @@ long do_dup(int fd) */ long do_dup2(int ofd, int nfd) { - NOT_YET_IMPLEMENTED("VFS: do_dup2"); - return -1; + // NOT_YET_IMPLEMENTED("VFS: do_dup2"); + + // Check if the file descriptors are valid + if (ofd < 0 || ofd >= NFILES || nfd < 0 || nfd >= NFILES) + { + return -EBADF; + } + // Check if the file descriptors are the same + if (!curproc->p_files[ofd]) + { + return -EBADF; + } + if (ofd == nfd) + { + return nfd; + } + + // fget the file + file_t *file = fget(ofd); + if (file == NULL) + { + return -EBADF; + } + // figet the new file + file_t *new_file = fget(nfd); + if (new_file != NULL) + { + fput(&new_file); + do_close(nfd); + } + + // Copy the file + curproc->p_files[nfd] = file; + return nfd; } /* @@ -117,8 +294,20 @@ long do_dup2(int ofd, int nfd) */ long do_mknod(const char *path, int mode, devid_t devid) { - NOT_YET_IMPLEMENTED("VFS: do_mknod"); - return -1; + // NOT_YET_IMPLEMENTED("VFS: do_mknod"); + + // Check if the mode is valid + if (mode != S_IFCHR && mode != S_IFBLK && mode != S_IFREG) + { + return -EINVAL; + } + + // get vnode + vnode_t *vnode = NULL; + vnode_t *base = curproc->p_cwd; + long ret = namev_open(base, path, O_CREAT, mode, devid, &vnode); + vput(&vnode); + return ret; } /* @@ -143,8 +332,53 @@ long do_mknod(const char *path, int mode, devid_t devid) */ long do_mkdir(const char *path) { - NOT_YET_IMPLEMENTED("VFS: do_mkdir"); - return -1; + // NOT_YET_IMPLEMENTED("VFS: do_mkdir"); + + // Call namev_dir() to find the parent of the directory to be created + size_t len = 0; + const char *name = NULL; + vnode_t *dir = NULL; + long ret = namev_dir(curproc->p_cwd, path, &dir, &name, &len); + dbg(DBG_TEST, "VFS: do_mkdir: namev_dir returned %ld\n", ret); + + if (ret < 0) + { + return ret; + } + if (!S_ISDIR(dir->vn_mode)) + { + vput(&dir); + return -ENOTDIR; + } + if (len > NAME_LEN) + { + vput(&dir); + return -ENAMETOOLONG; + } + + vnode_t *base = NULL; + vlock(dir); + ret = namev_lookup(dir, name, len, &base); + vunlock(dir); + // Check if the directory already exists + if (ret >= 0) + { + vput(&base); + vput(&dir); + return -EEXIST; + } + + // Create the directory + long err = dir->vn_ops->mkdir(dir, name, len, &base); + if (err < 0) + { + vput(&dir); + return err; + } + + vput(&base); + vput(&dir); + return 0; } /* @@ -165,8 +399,75 @@ long do_mkdir(const char *path) */ long do_rmdir(const char *path) { - NOT_YET_IMPLEMENTED("VFS: do_rmdir"); - return -1; + // NOT_YET_IMPLEMENTED("VFS: do_rmdir"); + + // Call namev_dir() to find the parent of the directory to be removed + size_t len = 0; + const char *name = NULL; + vnode_t *dir = NULL; + long ret = namev_dir(curproc->p_cwd, path, &dir, &name, &len); + + // Check if the directory is valid + if (ret < 0) + { + return ret; + } + + // Check if the directory is a directory + if (!S_ISDIR(dir->vn_mode)) + { + vput(&dir); + return -ENOTDIR; + } + + // Check if name is too long + if (len > NAME_LEN) + { + vput(&dir); + return -ENAMETOOLONG; + } + + // Check if the directory is empty + if (len == 1 && name[0] == '.') + { + vput(&dir); + return -EINVAL; + } + if (len == 2 && name[0] == '.' && name[1] == '.') + { + vput(&dir); + return -ENOTEMPTY; + } + + // Remove the directory + vnode_t *base = NULL; + vlock(dir); + ret = namev_lookup(dir, name, len, &base); + vunlock(dir); + + // Check if the directory exists + if (ret < 0) + { + vput(&dir); + return ret; + } + + // Check if the directory is a directory + if (!S_ISDIR(base->vn_mode)) + { + vput(&base); + vput(&dir); + return -ENOTDIR; + } + + vput(&base); + // Remove the directory + vlock(dir); + ret = dir->vn_ops->rmdir(dir, name, len); + vunlock(dir); + vput(&dir); + + return ret; } /* @@ -183,8 +484,62 @@ long do_rmdir(const char *path) */ long do_unlink(const char *path) { - NOT_YET_IMPLEMENTED("VFS: do_unlink"); - return -1; + // NOT_YET_IMPLEMENTED("VFS: do_unlink"); + + // Call namev_dir() to find the parent of the file to be unlinked + size_t len = 0; + const char *name = NULL; + vnode_t *dir = NULL; + long ret = namev_dir(curproc->p_cwd, path, &dir, &name, &len); + + // Check if the directory is valid + if (ret < 0) + { + return ret; + } + + // Check if the directory is a directory + if (!S_ISDIR(dir->vn_mode)) + { + vput(&dir); + return -ENOTDIR; + } + + // Check if name is too long + if (len > NAME_LEN) + { + vput(&dir); + return -ENAMETOOLONG; + } + + // Remove the link + vnode_t *base = NULL; + vlock(dir); + ret = namev_lookup(dir, name, len, &base); + vunlock(dir); + + // Check if the file exists + if (ret < 0) + { + vput(&dir); + return ret; + } + // Check if the file is a directory + if (S_ISDIR(base->vn_mode)) + { + vput(&base); + vput(&dir); + return -EPERM; + } + + vput(&base); + // Call the unlink operation + vlock(dir); + ret = dir->vn_ops->unlink(dir, name, len); + vunlock(dir); + vput(&dir); + + return ret; } /* @@ -206,8 +561,56 @@ long do_unlink(const char *path) */ long do_link(const char *oldpath, const char *newpath) { - NOT_YET_IMPLEMENTED("VFS: do_link"); - return -1; + // NOT_YET_IMPLEMENTED("VFS: do_link"); + + // Resolve the oldpath + vnode_t *old_vnode = NULL; + + long ret = namev_resolve(NULL, oldpath, &old_vnode); + // Check if the oldpath is valid + if (ret < 0) + { + return ret; + } + // Check if the oldpath is a directory + if (S_ISDIR(old_vnode->vn_mode)) + { + vput(&old_vnode); + return -EPERM; + } + + // Get the directory of the newpath + size_t len = 0; + const char *name = NULL; + vnode_t *dir = NULL; + ret = namev_dir(curproc->p_cwd, newpath, &dir, &name, &len); + // Check if the directory is valid + if (ret < 0) + { + vput(&old_vnode); + return ret; + } + // Check if the directory is a directory + if (!S_ISDIR(dir->vn_mode)) + { + vput(&old_vnode); + return -ENOTDIR; + } + // Check if name is too long + if (len > NAME_LEN) + { + vput(&old_vnode); + return -ENAMETOOLONG; + } + + // Lock the vnodes and call link + vlock_in_order(old_vnode, dir); + ret = dir->vn_ops->link(old_vnode, dir, name, len); + vunlock_in_order(old_vnode, dir); + + vput(&old_vnode); + vput(&dir); + return ret; } /* Rename a file or directory. @@ -231,7 +634,7 @@ long do_link(const char *oldpath, const char *newpath) * projects this is harder and you will get no extra credit (but you * will get our admiration). Please make sure the normal version works first. * Steps: - * 1. namev_dir oldpath --> olddir vnode + * 1. oldpath --> olddir vnode * 2. namev_dir newpath --> newdir vnode * 3. Lock the global filesystem `vnode_rename_mutex` * 4. Lock the olddir and newdir in ancestor-first order (see `vlock_in_order`) @@ -244,8 +647,57 @@ long do_link(const char *oldpath, const char *newpath) */ long do_rename(const char *oldpath, const char *newpath) { - NOT_YET_IMPLEMENTED("VFS: do_rename"); - return -1; + // NOT_YET_IMPLEMENTED("VFS: do_rename"); + + // Get the old directory + size_t old_len = 0; + const char *old_name = NULL; + vnode_t *old_dir = NULL; + long ret = namev_dir(curproc->p_cwd, oldpath, &old_dir, &old_name, &old_len); + // Check if the old directory is valid + if (ret < 0) + { + return ret; + } + // Check if the old directory is a directory + if (!S_ISDIR(old_dir->vn_mode)) + { + return -ENOTDIR; + } + + // Get the new directory + size_t new_len = 0; + const char *new_name = NULL; + vnode_t *new_dir = NULL; + ret = namev_dir(curproc->p_cwd, newpath, &new_dir, &new_name, &new_len); + // Check if the new directory is valid + if (ret < 0) + { + vput(&old_dir); + return ret; + } + // Check if the new directory is a directory + if (!S_ISDIR(new_dir->vn_mode)) + { + vput(&old_dir); + return -ENOTDIR; + } + + // Check if the names are too long + if (old_len > NAME_LEN || new_len > NAME_LEN) + { + vput(&old_dir); + return -ENAMETOOLONG; + } + + // Lock the vnodes and call rename + vlock_in_order(old_dir, new_dir); + ret = old_dir->vn_ops->rename(old_dir, old_name, old_len, new_dir, new_name, new_len); + vunlock_in_order(old_dir, new_dir); + + vput(&old_dir); + vput(&new_dir); + return ret; } /* Set the current working directory to the directory represented by path. @@ -262,8 +714,27 @@ long do_rename(const char *oldpath, const char *newpath) */ long do_chdir(const char *path) { - NOT_YET_IMPLEMENTED("VFS: do_chdir"); - return -1; + // NOT_YET_IMPLEMENTED("VFS: do_chdir"); + + // Resolve the path + vnode_t *vnode = NULL; + long ret = namev_resolve(NULL, path, &vnode); + // Check if the path is valid + if (ret < 0) + { + return ret; + } + // Check if the path is a directory + if (!S_ISDIR(vnode->vn_mode)) + { + vput(&vnode); + return -ENOTDIR; + } + + // Set the current working directory + vput(&curproc->p_cwd); + curproc->p_cwd = vnode; + return 0; } /* @@ -282,8 +753,43 @@ long do_chdir(const char *path) */ ssize_t do_getdent(int fd, struct dirent *dirp) { - NOT_YET_IMPLEMENTED("VFS: do_getdent"); - return -1; + // NOT_YET_IMPLEMENTED("VFS: do_getdent"); + + // Check if the file descriptor is valid + if (fd < 0 || fd >= NFILES) + { + return -EBADF; + } + + // fget the file + file_t *file = fget(fd); + if (file == NULL) + { + return -EBADF; + } + + // Check if the file is a directory + if (!S_ISDIR(file->f_vnode->vn_mode)) + { + fput(&file); + return -ENOTDIR; + } + + // Read the directory entry + vlock(file->f_vnode); + ssize_t bytes_read = file->f_vnode->vn_ops->readdir(file->f_vnode, file->f_pos, dirp); + vunlock(file->f_vnode); + // Check if the read was successful + if (bytes_read <= 0) + { + fput(&file); + return bytes_read; + } + + // Update the file position + file->f_pos += bytes_read; + fput(&file); + return sizeof(dirent_t); } /* @@ -301,8 +807,51 @@ ssize_t do_getdent(int fd, struct dirent *dirp) */ off_t do_lseek(int fd, off_t offset, int whence) { - NOT_YET_IMPLEMENTED("VFS: do_lseek"); - return -1; + // NOT_YET_IMPLEMENTED("VFS: do_lseek"); + + // Check if the file descriptor is valid + if (fd < 0 || fd >= NFILES) + { + return -EBADF; + } + + // fget the file + file_t *file = fget(fd); + if (file == NULL) + { + return -EBADF; + } + + // Check if the whence is valid + off_t new_pos = 0; + switch (whence) + { + case SEEK_SET: + new_pos = offset; + break; + case SEEK_CUR: + new_pos = file->f_pos + offset; + break; + case SEEK_END: + new_pos = file->f_vnode->vn_len + offset; + break; + default: + fput(&file); + return -EINVAL; + } + + // Check if the new position is negative + if (new_pos < 0) + { + fput(&file); + return -EINVAL; + } + + // Update the file position + file->f_pos = new_pos; + off_t pos = file->f_pos; + fput(&file); + return pos; } /* Use buf to return the status of the file represented by path. @@ -312,8 +861,25 @@ off_t do_lseek(int fd, off_t offset, int whence) */ long do_stat(const char *path, stat_t *buf) { - NOT_YET_IMPLEMENTED("VFS: do_stat"); - return -1; + // NOT_YET_IMPLEMENTED("VFS: do_stat"); + + // Resolve the path + vnode_t *vnode = NULL; + long ret = namev_resolve(NULL, path, &vnode); + + // Check if the path is valid + if (ret < 0) + { + return ret; + } + + // Get the status of the file + vlock(vnode); + ret = vnode->vn_ops->stat(vnode, buf); + vunlock(vnode); + vput(&vnode); + + return ret; } #ifdef __MOUNTING__ diff --git a/kernel/fs/vnode_specials.c b/kernel/fs/vnode_specials.c index cd21549..fb6df0b 100644 --- a/kernel/fs/vnode_specials.c +++ b/kernel/fs/vnode_specials.c @@ -110,8 +110,20 @@ static long special_file_stat(vnode_t *file, stat_t *ss) static ssize_t chardev_file_read(vnode_t *file, size_t pos, void *buf, size_t count) { - NOT_YET_IMPLEMENTED("VFS: chardev_file_read"); - return 0; + // NOT_YET_IMPLEMENTED("VFS: chardev_file_read"); + + // check if the vnode represetns a chardev + if (file->vn_dev.chardev == NULL) + { + return -ENXIO; + } + + // Unlock the file and call read + vunlock(file); + ssize_t ret = file->vn_dev.chardev->cd_ops->read(file->vn_dev.chardev, pos, buf, count); + vlock(file); + + return ret; } /* @@ -125,8 +137,20 @@ static ssize_t chardev_file_read(vnode_t *file, size_t pos, void *buf, static long chardev_file_write(vnode_t *file, size_t pos, const void *buf, size_t count) { - NOT_YET_IMPLEMENTED("VFS: chardev_file_write"); - return 0; + // NOT_YET_IMPLEMENTED("VFS: chardev_file_write"); + + // check if the vnode represents a chardev + if (file->vn_dev.chardev == NULL) + { + return -ENXIO; + } + + // Unlock the file and call write + vunlock(file); + long ret = file->vn_dev.chardev->cd_ops->write(file->vn_dev.chardev, pos, buf, count); + vlock(file); + + return ret; } /* |