#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; }