aboutsummaryrefslogtreecommitdiff
path: root/kernel/fs/vfs_syscall.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/fs/vfs_syscall.c')
-rw-r--r--kernel/fs/vfs_syscall.c626
1 files changed, 595 insertions, 31 deletions
diff --git a/kernel/fs/vfs_syscall.c b/kernel/fs/vfs_syscall.c
index e05fe5f..ba4aa0c 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,61 @@ 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);
+ dbg(DBG_PRINT, "VFS: do_write: write failed\n");
+ return bytes_written;
+ }
+
+ file->f_pos += bytes_written;
+ fput(&file);
+ return bytes_written;
}
/*
@@ -63,8 +166,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
+ curproc->p_files[fd] = NULL;
+ fput(&file);
+
+ return 0;
}
/*
@@ -78,8 +199,32 @@ 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)
+ {
+ return ret;
+ }
+
+ // Copy the file
+ curproc->p_files[new_fd] = file;
+ return new_fd;
}
/*
@@ -94,8 +239,39 @@ 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)
+ {
+ do_close(nfd);
+ }
+
+ // Copy the file
+ curproc->p_files[nfd] = file;
+ return nfd;
}
/*
@@ -117,8 +293,19 @@ 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);
+ return ret;
}
/*
@@ -143,8 +330,47 @@ 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
+ ret = dir->vn_ops->mkdir(dir, name, len, &base);
+ // vput(&base);
+ vput(&dir);
+ return ret;
}
/*
@@ -165,8 +391,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;
+ }
+
+ // Remove the directory
+ vlock(dir);
+ ret = dir->vn_ops->rmdir(dir, name, len);
+ vunlock(dir);
+ vput(&base);
+ vput(&dir);
+
+ return ret;
}
/*
@@ -183,8 +476,63 @@ 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;
+ }
+
+ // Call the unlink operation
+ vlock(dir);
+ ret = dir->vn_ops->unlink(dir, name, len);
+ vunlock(dir);
+
+ vput(&base);
+ vput(&dir);
+
+ return ret;
}
/*
@@ -206,8 +554,58 @@ 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);
+ vput(&dir);
+ return -ENOTDIR;
+ }
+ // Check if name is too long
+ if (len > NAME_LEN)
+ {
+ vput(&old_vnode);
+ vput(&dir);
+ 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 +629,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 +642,60 @@ 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))
+ {
+ vput(&old_dir);
+ 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);
+ vput(&new_dir);
+ return -ENOTDIR;
+ }
+
+ // Check if the names are too long
+ if (old_len > NAME_LEN || new_len > NAME_LEN)
+ {
+ vput(&old_dir);
+ vput(&new_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 +712,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 +751,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 +805,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 +859,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__