axf-os161 / kern / syscall / file_syscalls.c
file_syscalls.c
Raw
#include <types.h>
#include <device.h>
#include <kern/unistd.h>
#include <vfs.h>
#include <kern/fcntl.h>
#include <proc.h>
#include <limits.h>
#include <syscall.h>
#include <kern/errno.h>
#include <current.h>
#include <copyinout.h>
#include <uio.h>
#include <vnode.h>
#include <kern/seek.h>
#include <stat.h>
#include <kern/stat.h>

// #define FILENAME_MAX_LEN (128U)

struct lock *open_files_tbl_lock;

static openfile open_files_tbl[OPEN_MAX];

void file_syscalls_bootstrap(void)
{
    open_files_tbl_lock = lock_create("open_files_tbl_lock");
    if(open_files_tbl_lock == NULL)
    {
        panic("could not create file table lock!");
    }

    for(int i = 0; i < OPEN_MAX; i++)
    {
        open_files_tbl[i].in_use = false;
        open_files_tbl[i].offset = 0;
        open_files_tbl[i].vnodeptr = NULL;
        open_files_tbl[i].openstatus = FILE_CLOSED;
        open_files_tbl[i].refcount = 0;
    }
}

/*
 * Implementation of open system call
 * arguments:
 * user_filename_ptr: pointer to filename string in userspace
 * flags: flag to determinehow to open file
 * mode_t: optional argument providing file permissions
 * err: pointer to error return value
 */
int
sys_open(userptr_t user_filename_ptr, int flags, mode_t mode, int *err)
{
    *err = 0;

    // Check for valid user pointer
    if(user_filename_ptr == NULL)
    {
        *err = EFAULT;
        return -1;
    }
        
    char filename[__PATH_MAX];
    size_t filename_len;

    // Copy-in filename string from userspace
    const int copyinstr_result = copyinstr(user_filename_ptr, filename, (size_t) __PATH_MAX, &filename_len);
	if (copyinstr_result) {
        *err = copyinstr_result;
		return -1;
	}

    int filestatus = flags & O_ACCMODE;    

    // Find open entry in global fd table
    int open_files_tbl_idx = 0;
    lock_acquire(open_files_tbl_lock);
    while( (open_files_tbl[open_files_tbl_idx].in_use) && (open_files_tbl_idx < OPEN_MAX) )
    {
        open_files_tbl_idx++;
    }

    // If no available slots, return with error stating fd table full
    if((open_files_tbl_idx == OPEN_MAX - 1) && open_files_tbl[open_files_tbl_idx].in_use)
    {
        lock_release(open_files_tbl_lock);
        *err = ENFILE;
        return -1;
    }

    lock_acquire(curproc->fdtbl_lock);

    // find unused file descriptor in current process's table
    int fd = FIRST_NON_RESEVERED_FD;

    while(fd < OPEN_MAX)
    {

        if(curproc->fdtbl[fd] == NULL)
        {
            openfile *file =  &open_files_tbl[open_files_tbl_idx];

            file->in_use = true;
            file->offset = 0;
            file->vnodeptr = NULL;
            file->openstatus = filestatus;
            file->refcount = 1;
            curproc->fdtbl[fd] = &open_files_tbl[open_files_tbl_idx];

            break;
        }
        fd++;
    }

    // if never found an open table entry in process's fd table
    if(fd == OPEN_MAX)
    {
        lock_release(curproc->fdtbl_lock);
        lock_release(open_files_tbl_lock);
        *err = EMFILE;
        return -1;
    }
    
    int vfsreturn = vfs_open((char *)filename, flags, mode, &curproc->fdtbl[fd]->vnodeptr);
    
    if(vfsreturn)
    {
        curproc->fdtbl[fd]->in_use = false;
        curproc->fdtbl[fd]->offset = 0;
        curproc->fdtbl[fd]->vnodeptr = NULL;
        curproc->fdtbl[fd]->openstatus = FILE_CLOSED;
        curproc->fdtbl[fd]->refcount = 0;
        curproc->fdtbl[fd] = NULL;
        lock_release(curproc->fdtbl_lock);
        lock_release(open_files_tbl_lock);
        *err = EINVAL;
        return -1;
    }
    lock_release(curproc->fdtbl_lock);
    lock_release(open_files_tbl_lock);

    return fd;
}

void proc_close_fds(struct proc *proc)
{
	for(int fd = 0; fd < OPEN_MAX; fd++)
	{
		if(proc->fdtbl[fd] != NULL)
		{

            // check if file descriptor valid
            if (fd < 0 || fd >= OPEN_MAX || proc->fdtbl[fd] == NULL) {
                panic("trying to close invald fd\n");
            }

            lock_acquire(open_files_tbl_lock);
            lock_acquire(proc->fdtbl_lock);

            proc->fdtbl[fd]->refcount--;
            if (proc->fdtbl[fd]->refcount == 0) {
                vfs_close(proc->fdtbl[fd]->vnodeptr);
                proc->fdtbl[fd]->openstatus = FILE_CLOSED;
            }

            proc->fdtbl[fd]->in_use = false;
            proc->fdtbl[fd]->offset = 0;
            proc->fdtbl[fd] = NULL;
            lock_release(proc->fdtbl_lock);
            lock_release(open_files_tbl_lock);

		}
	}
}

/*
 * Implementation of close system call
 * arguments:
 * fd: file descriptor to close
 * err: pointer to error return value
 */
int
sys_close(int fd, int *err)
{
    *err = 0;

    // check if file descriptor valid
    if (fd < 0 || fd >= OPEN_MAX || curproc->fdtbl[fd] == NULL) {
        *err = EBADF;
        return -1;
    }

    lock_acquire(open_files_tbl_lock);
    lock_acquire(curproc->fdtbl_lock);

    curproc->fdtbl[fd]->refcount--;
    if (curproc->fdtbl[fd]->refcount == 0) {
		vfs_close(curproc->fdtbl[fd]->vnodeptr);
		curproc->fdtbl[fd]->openstatus = FILE_CLOSED;
        curproc->fdtbl[fd]->in_use = false;
        curproc->fdtbl[fd]->offset = 0;
        curproc->fdtbl[fd] = NULL;

    }
    
    lock_release(curproc->fdtbl_lock);
    lock_release(open_files_tbl_lock);

    return 0;
}

/*
 * Implementation of read system call
 * arguments:
 * fd: file descriptor to read from
 * buf: pointer to location to store read bytes into
 * buflen: number of bytes to read from file
 * err: pointer to error return value
 * return: number of bytes read (or -1 if error)
 */
ssize_t
sys_read(int fd, userptr_t buf, size_t buflen, int *err)
{
    *err = 0;

    // check if file descriptor valid
    if (fd < 0 || fd >= OPEN_MAX || curproc->fdtbl[fd] == NULL) {
        *err = EBADF;
        return -1;
    }

    openfile *currfile = curproc->fdtbl[fd];

    lock_acquire(curproc->fdtbl_lock);

    // check if file opened with read access
    if (currfile->openstatus != O_RDONLY && currfile->openstatus != O_RDWR) {
        *err = EBADF;
        lock_release(curproc->fdtbl_lock);
        return -1;
    }

    struct uio _uio;
    struct iovec iov;

    uio_kinit(&iov, &_uio, (void *)buf, buflen, currfile->offset, UIO_READ);

    _uio.uio_segflg = UIO_USERSPACE;
    _uio.uio_space = proc_getas();

    ssize_t canReadLength = _uio.uio_resid;

    *err = VOP_READ(currfile->vnodeptr, &_uio);
    if (*err)
    {
        lock_release(curproc->fdtbl_lock);
        return -1;
    }

    // Update file offest 
    canReadLength = buflen - _uio.uio_resid;
    currfile->offset = _uio.uio_offset;

    lock_release(curproc->fdtbl_lock);

    return canReadLength;
}

/*
 * Implementation of write system call
 * arguments:
 * fd: file descriptor to write to
 * buf: pointer to location to store read bytes into
 * buflen: number of bytes to read from file
 * err: pointer to error return value 
 * return: number of bytes written (or -1 if error)
 */
ssize_t
sys_write(int fd, const_userptr_t buf, size_t nbytes, int *err)
{
    *err = 0;

    // Check if valid file descriptor
    if (fd < 0 || fd >= OPEN_MAX || curproc->fdtbl[fd] == NULL) {
        *err = EBADF;
        return -1;
    }

    lock_acquire(curproc->fdtbl_lock);

    openfile *currfile = curproc->fdtbl[fd];

    // Check if file opened with write access
    if (currfile->openstatus == O_RDONLY) {
        *err = EBADF;
        lock_release(curproc->fdtbl_lock);
        return -1;
    }

    struct uio _uio;
    struct iovec iov;


    uio_kinit(&iov, &_uio, (void *)buf, nbytes, currfile->offset, UIO_WRITE);

    _uio.uio_segflg = UIO_USERISPACE;
    _uio.uio_space = proc_getas();

    // Write to vnode
    *err = VOP_WRITE(currfile->vnodeptr, &_uio);
    if (*err)
    {
        lock_release(curproc->fdtbl_lock);
        return -1;
    }

    // Update file offest 
    currfile->offset =  _uio.uio_offset;
    ssize_t byteswritten = nbytes - _uio.uio_resid;

    lock_release(curproc->fdtbl_lock);

    return byteswritten;
    
}

/*
 * Implementation of lseek system call
 * arguments:
 * fd: file descriptor to seek
 * pos: value to modify file position by based on flag provided in whence
 * whence: flag to indicate seek mode
 * err: pointer to error return value 
 * return: new file position (or -1 if error)
 */
off_t
sys_lseek(int fd, off_t pos, int whence, int *err)
{
    *err = 0;

    // Check if file descriptor valid
    if (fd < 0 || fd >= OPEN_MAX || curproc->fdtbl[fd] == NULL) {
        *err = EBADF;
        return -1;
    }

    openfile *currfile = curproc->fdtbl[fd];

    // Check if vnode is seek-able
    if (!VOP_ISSEEKABLE(currfile->vnodeptr)) 
    {
        *err = ESPIPE;
        return -1;
    }
    
    off_t offset;
    struct stat currfile_stat;

    // Update offset depending on provided flags
    lock_acquire(curproc->fdtbl_lock);
    switch(whence)
    {   
        // Set offset to value stored in pos
        case SEEK_SET:
            offset = pos;
            if(offset < 0)
            {
                *err = EINVAL;
                goto SEEK_ERR;
            }
            currfile->offset = offset;
            break;

        // Increment offset by value stored in pos
        case SEEK_CUR:
            offset = currfile->offset + pos;
            if(offset < 0)
            {
                *err = EINVAL;
                goto SEEK_ERR;
            }
            currfile->offset = offset;
            break;

        // Set offset to EOF + pos
        case SEEK_END:
            *err = VOP_STAT(currfile->vnodeptr, &currfile_stat);
            if(*err)
            {
                goto SEEK_ERR;
            }

            offset = pos + currfile_stat.st_size;
            
            if(offset < 0)
            {
                *err = EINVAL;
                goto SEEK_ERR;
            }
            currfile->offset = offset;
            break;

        default:
        *err = EINVAL;
        goto SEEK_ERR;

    }

    lock_release(curproc->fdtbl_lock);
    return offset;
SEEK_ERR:

    lock_release(curproc->fdtbl_lock);
    return -1;
}

/*
 * Implementation of dup2 system call
 * arguments:
 * oldfd: file descriptor to clone
 * newfd: file descriptor to clone into
 * err: pointer to error return value 
 * return: value of new file descriptor (or -1 if error)
 */
int
sys_dup2(int oldfd, int newfd, int *err)
{
    *err = 0;

    // Check for valid file descriptors
    if (oldfd < 0 || oldfd >= OPEN_MAX || newfd < 0 || newfd >= OPEN_MAX) {
        *err = EBADF;
        return -1;
    }

    if (curproc->fdtbl[oldfd] == NULL) {
        *err = EBADF;
        return -1;
    }

    if (oldfd == newfd) {
        return newfd;
    }


    // if file associated with new newfd already exists close file
    if (curproc->fdtbl[newfd]) {
        const int close_file_result = sys_close(newfd, err);
        if (close_file_result) {
            return close_file_result;
	    }
    }

    lock_acquire(curproc->fdtbl_lock);
    
    openfile *currfile = curproc->fdtbl[oldfd];

    curproc->fdtbl[newfd] = currfile;
    curproc->fdtbl[newfd]->refcount++;
    lock_release(curproc->fdtbl_lock);

    return newfd;
}

/*
 * Implementation of __getcwd system call
 * arguments:
 * buf: pointer to string to store cwd in
 * buflen: length of data stored
 * err: pointer to error return value 
 * return: length of data returned (or -1 if error)
 */
int
sys___getcwd(userptr_t buf, size_t buflen, int *err)
{
    *err = 0;
    struct uio _uio;
    struct iovec iov;

    // check for valid buffer
    if (buflen == 0) {
        *err = EFAULT;
        return -1;
    }

    if (buf == NULL) {
        *err = EFAULT;
        return -1;
    }

    uio_kinit(&iov, &_uio, buf, buflen, 0, UIO_READ);

    _uio.uio_space = proc_getas();
    _uio.uio_segflg = UIO_USERSPACE;
    int cwd_result = vfs_getcwd(&_uio);
    if(cwd_result){
        *err = EFAULT;
        return -1;
    }

    return buflen - _uio.uio_resid;
}

/*
 * Implementation of chdir system call
 * arguments:
 * pathname: pointer to pathname string
 * err: pointer to error return value 
 * return: 0 on success (or -1 if error)
 */
int
sys_chdir(const_userptr_t pathname, int *err)
{
    *err = 0;

    // check that provided pathname is valid
    if (pathname == NULL)
    {
        *err = EFAULT;
        return -1;
    }

    char *path;
    path = kmalloc(__PATH_MAX);
    size_t *path_len = kmalloc(sizeof(int));

    // copy in string pointed to by pathname pointer
    *err = copyinstr(pathname, path, PATH_MAX, path_len);
    if (*err)
    {
        kfree(path);
        kfree(path_len);
        return -1;
    }

    kfree(path_len);

    *err = vfs_chdir(path);
    if (*err)
    {
        kfree(path);
        return -1;
    }
    
    kfree(path);
    return 0;
}