diff options
Diffstat (limited to 'sys/kern/kern_descrip.c')
-rw-r--r-- | sys/kern/kern_descrip.c | 147 |
1 files changed, 132 insertions, 15 deletions
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index d122e89..83845f6 100644 --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -41,6 +41,7 @@ /* * Allocate a file descriptor. * + * @td: Process to allocate from (null for CURRENT) * @fd_out: Pointer to allocated file descriptor output. * * This routine will create a new file descriptor @@ -49,10 +50,13 @@ * Returns 0 on success. */ int -fd_alloc(struct filedesc **fd_out) +fd_alloc(struct proc *td, struct filedesc **fd_out) { struct filedesc *fd; - struct proc *td = this_td(); + + if (td == NULL) { + td = this_td(); + } /* Find free fd table entry */ for (size_t i = 3; i < PROC_MAX_FILEDES; ++i) { @@ -85,12 +89,15 @@ fd_alloc(struct filedesc **fd_out) * Fetch a file descriptor from a file descriptor * number. * + * @td: Process to get fd from (NULL for current) * @fdno: File descriptor to fetch */ struct filedesc * -fd_get(unsigned int fdno) +fd_get(struct proc *td, unsigned int fdno) { - struct proc *td = this_td(); + if (td == NULL) { + td = this_td(); + } if (fdno > PROC_MAX_FILEDES) { return NULL; @@ -111,7 +118,7 @@ fd_close(unsigned int fd) struct filedesc *filedes; struct proc *td; - if ((filedes = fd_get(fd)) == NULL) { + if ((filedes = fd_get(NULL, fd)) == NULL) { return -EBADF; } @@ -149,18 +156,32 @@ 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; + if (fd > PROC_MAX_FILEDES) { + return -EBADF; + } + if (count > SSIZE_MAX) { retval = -EINVAL; goto done; } - filedes = fd_get(fd); - kbuf = dynalloc(count); + filedes = fd_get(NULL, fd); + 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; @@ -187,6 +208,7 @@ fd_rw(unsigned int fd, void *buf, size_t count, uint8_t write) sio.buf = kbuf; sio.offset = filedes->offset; + spinlock_acquire(&filedes->lock); if (write) { /* Copy in user buffer */ if (copyin(buf, kbuf, count) < 0) { @@ -205,19 +227,52 @@ fd_rw(unsigned int fd, void *buf, size_t count, uint8_t write) goto done; } + /* End of file? */ + if (n == 0) { + retval = 0; + goto done; + } + if (copyout(kbuf, buf, count) < 0) { retval = -EFAULT; goto done; } } - retval = count; + + /* Increment the offset per read */ + filedes->offset += n; + retval = n; 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) { @@ -236,28 +291,35 @@ fd_write(unsigned int fd, void *buf, size_t count) * * @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; } - if ((error = fd_alloc(&filedes)) != 0) { + if ((error = fd_alloc(NULL, &filedes)) != 0) { vfs_release_vnode(nd.vp); 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; @@ -266,18 +328,25 @@ fd_open(const char *pathname, int flags) /* * Duplicate a file descriptor. New file descriptor * points to the same vnode. + * + * @td: Process of fd to dup (NULL for current) + * @fd: File descriptor to dup */ int -fd_dup(int fd) +fd_dup(struct proc *td, int fd) { int error; struct filedesc *new_desc, *tmp; - tmp = fd_get(fd); + if (td == NULL) { + td = this_td(); + } + + tmp = fd_get(td, fd); if (tmp == NULL) return -EBADF; - if ((error = fd_alloc(&new_desc)) != 0) + if ((error = fd_alloc(td, &new_desc)) != 0) return error; /* Ref that vnode before we point to it */ @@ -285,3 +354,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(NULL, fildes); + if (tmp == NULL) { + return -EBADF; + } + + getattr_args.vp = tmp->vp; + getattr_args.res = &attr; + if ((vfs_vop_getattr(&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); +} |