diff options
Diffstat (limited to 'sys/kern/kern_descrip.c')
-rw-r--r-- | sys/kern/kern_descrip.c | 167 |
1 files changed, 151 insertions, 16 deletions
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index da10f89..0fb026f 100644 --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023-2024 Ian Marco Moffett and the Osmora Team. + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,6 +31,7 @@ #include <sys/errno.h> #include <sys/proc.h> #include <sys/limits.h> +#include <sys/fcntl.h> #include <sys/namei.h> #include <sys/filedesc.h> #include <sys/systm.h> @@ -135,17 +136,20 @@ fd_close(unsigned int fd) } /* - * Read bytes from a file using a file + * Read/write bytes to/from a file using a file * descriptor number. * * @fd: File descriptor number. - * @buf: Buffer to read into. + * @buf: Buffer with data to read/write * @count: Number of bytes to read. + * @write: Set to 1 for writes */ -int -fd_read(unsigned int fd, void *buf, size_t count) +static int +fd_rw(unsigned int fd, void *buf, size_t count, uint8_t write) { char *kbuf = NULL; + ssize_t n; + uint32_t seal; struct filedesc *filedes; struct sio_txn sio; scret_t retval = 0; @@ -156,8 +160,17 @@ fd_read(unsigned int fd, void *buf, size_t count) } filedes = fd_get(fd); - kbuf = dynalloc(count); + seal = filedes->flags; + + /* Check the seal */ + if (write && !ISSET(seal, O_ALLOW_WR)) { + return -EPERM; + } + if (!write && ISSET(seal, O_WRONLY)) { + return -EPERM; + } + kbuf = dynalloc(count); if (kbuf == NULL) { retval = -ENOMEM; goto done; @@ -173,46 +186,111 @@ fd_read(unsigned int fd, void *buf, size_t count) goto done; } + /* Check if this violates the file seal */ + if (!ISSET(filedes->flags, O_ALLOW_WR) && write) { + return -EPERM; + } else if (ISSET(O_RDONLY, filedes->flags) && write) { + return -EPERM; + } + sio.len = count; sio.buf = kbuf; sio.offset = filedes->offset; - if ((count = vfs_vop_read(filedes->vp, &sio)) < 0) { - retval = -EIO; - goto done; - } + spinlock_acquire(&filedes->lock); + if (write) { + /* Copy in user buffer */ + if (copyin(buf, kbuf, count) < 0) { + retval = -EFAULT; + goto done; + } - if (copyout(kbuf, buf, count) < 0) { - retval = -EFAULT; - goto done; + /* Call VFS write hook */ + if ((n = vfs_vop_write(filedes->vp, &sio)) < 0) { + retval = n; + goto done; + } + } else { + if ((n = vfs_vop_read(filedes->vp, &sio)) < 0) { + retval = n; + goto done; + } + + /* End of file? */ + if (n == 0) { + retval = 0; + goto done; + } + + if (copyout(kbuf, buf, count) < 0) { + retval = -EFAULT; + goto done; + } } + /* Increment the offset per read */ + filedes->offset += n; retval = count; done: if (kbuf != NULL) { dynfree(kbuf); } + spinlock_release(&filedes->lock); return retval; } +static int +fd_do_create(const char *path, struct nameidata *ndp) +{ + struct vop_create_args cargs; + struct vnode *dirvp = ndp->vp; + const struct vops *vops = dirvp->vops; + int error; + + if (vops->create == NULL) { + return -EINVAL; + } + + cargs.path = path; + cargs.ppath = ndp->path; + cargs.dirvp = dirvp; + cargs.vpp = &ndp->vp; + if ((error = vops->create(&cargs)) < 0) { + return error; + } + + return 0; +} + +int +fd_read(unsigned int fd, void *buf, size_t count) +{ + return fd_rw(fd, buf, count, 0); +} + +int +fd_write(unsigned int fd, void *buf, size_t count) +{ + return fd_rw(fd, buf, count, 1); +} + /* * Open a file and get a file descriptor * number. * * @pathname: Path of file to open. * @flags: Flags to use. - * - * TODO: Use of flags. */ int fd_open(const char *pathname, int flags) { int error; + const struct vops *vops; struct filedesc *filedes; struct nameidata nd; nd.path = pathname; - nd.flags = 0; + nd.flags = ISSET(flags, O_CREAT) ? NAMEI_WANTPARENT : 0; if ((error = namei(&nd)) < 0) { return error; @@ -223,7 +301,16 @@ fd_open(const char *pathname, int flags) return error; } + vops = nd.vp->vops; + if (ISSET(flags, O_CREAT) && vops->create != NULL) { + error = fd_do_create(pathname, &nd); + } + if (error < 0) { + return error; + } + filedes->vp = nd.vp; + filedes->flags = flags; return filedes->fdno; } @@ -249,3 +336,51 @@ fd_dup(int fd) new_desc->vp = tmp->vp; return new_desc->fdno; } + +off_t +fd_seek(int fildes, off_t offset, int whence) +{ + struct filedesc *tmp; + struct vattr attr; + struct vop_getattr_args getattr_args; + + tmp = fd_get(fildes); + if (tmp == NULL) { + return -EBADF; + } + + getattr_args.vp = tmp->vp; + getattr_args.res = &attr; + if ((vfs_vop_getattr(tmp->vp, &getattr_args)) < 0) { + return -EPIPE; + } + + switch (whence) { + case SEEK_SET: + tmp->offset = offset; + break; + case SEEK_CUR: + tmp->offset += offset; + break; + case SEEK_END: + tmp->offset = attr.size + offset; + break; + default: + return -EINVAL; + } + + return tmp->offset; +} + +/* + * Update file offset + * + * arg0: `filedes' + * arg1: `offset' + * arg2: `whence' + */ +scret_t +sys_lseek(struct syscall_args *scargs) +{ + return fd_seek(scargs->arg0, scargs->arg1, scargs->arg2); +} |