January 9, 2022
The goal of this lab is to implement an interface for users to interact with persistent media or with other I/O devices, without having to distinguish between them.
File Interface: Provide an abstraction for the user that doesn’t depend on the type of file. This will allow user applications to interact with different types of files without large changes in the code. For example, the method for attaining bytes will be the same when reading input from a file and as when reading from stdin.
System Calls: The system call interface provides a barrier for the kernel to validate user program input. This way, we can keep the I/O device state consistent. No user program can directly affect the state of the kernel’s data structures. Furthermore, when we are in the kernel code, we then don’t have to go through the syscall interface, which cuts down superfluous error checking for trusted code.
include/kernel/fs.h
provides a struct file
that we can use to back each file descriptor. include/kernel/console.h
provides console file structs for stdin and stdout.
Each process will have an array of open files (Bounded by PROC_MAX_FILE
) in the process struct. The file descriptor will be the respective index into the file table. Ex: stdin is typically file descriptor 0, so the corresponding file struct will be the first element. A system call can use proc_current()
to get a pointer to the process control block (i.e., the struct proc
) for the currently-running process.
We need to parse arguments from the user and validate them (we never trust the user). There are a few useful functions provided by osv:
bool fetch_arg(void *arg, int n, sysarg_t *ret)
: Given args, fetch nth argument and store it at *ret
. Return true if fetched successfully, false if nth argument is unavailablestatic bool validate_str(char *s)
: Given a string s
, validates if the whole string is within a valid memory region of the process.static bool validate_ptr(void* ptr, size_t size)
: Given a buffer ptr
of size
, validate if the buffer is within a valid memory region of the process.Since all our system calls will be dealing with files, we think it will be useful to add a function that allocates a file descriptor, and another that validates a file descriptor:
static int alloc_fd(struct file *f)
: Will get a pointer to file, look through process’s open file table to find an available fd, and store the pointer there. Returns the chosen fd.static bool validate_fd(int fd)
: Will get the file descriptor, making sure it’s a valid file descriptor (in the open file table for the process).The main goals of the sys_*
functions is to do argument parsing and validation, and then calling the associated fs_*_file
functions.
sys_write
, sys_read
:
f_pos
of the respective file struct.include/lib/usyscall.h
)sys_open
:
include/lib/usyscall.h
)sys_close
:
include/lib/usyscall.h
)sys_readdir
:
include/lib/usyscall.h
)sys_dup
:
fs_reopen_file()
.include/lib/usyscall.h
)sys_fstat
:
struct file
and its struct inode
.include/lib/usyscall.h
)We will need to use several file system functions declared in include/kernel/fs.h
. These are:
fs_open_file
: Used to open a file.fs_reopen_file
: Used to up a file’s reference count.fs_read_file
: Performs a read operation on the given file.fs_write_file
: Performs a write operation on the given file.fs_close_file
: Closes an open file.fs_readdir
: Reads a directory.First, I will implement the per process open file table. Then I will retrieve and validate syscall inputs, and call the respective file functions. I will also update process initialization so that fd 0 and 1 point to stdin and stdout from console.h
.
When you author your own design documents on future labs, you will include a time estimate for the various tasks. You might practice your estimation skills by making estimates for this lab and keep track of your time to check them.