/* * Copyright (c) 2023-2024 Ian Marco Moffett and the Osmora Team. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Hyra nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include /* * Allocate a file descriptor. * * @fd_out: Pointer to allocated file descriptor output. * * This routine will create a new file descriptor * table entry. * * Returns 0 on success. */ int fd_alloc(struct filedesc **fd_out) { struct filedesc *fd; struct proc *td = this_td(); /* Find free fd table entry */ for (size_t i = 3; i < PROC_MAX_FILEDES; ++i) { if (td->fds[i] != NULL) { /* In use */ continue; } fd = dynalloc(sizeof(struct filedesc)); memset(fd, 0, sizeof(struct filedesc)); if (fd == NULL) { return -ENOMEM; } fd->refcnt = 1; fd->fdno = i; td->fds[i] = fd; if (fd_out != NULL) *fd_out = fd; return 0; } return -EMFILE; } /* * Fetch a file descriptor from a file descriptor * number. * * @fdno: File descriptor to fetch */ struct filedesc * fd_get(unsigned int fdno) { struct proc *td = this_td(); if (fdno > PROC_MAX_FILEDES) { return NULL; } return td->fds[fdno]; } /* * Close a file descriptor with a file * descriptor number. * * @fd: File descriptor number to close. */ int fd_close(unsigned int fd) { struct filedesc *filedes; struct proc *td; if ((filedes = fd_get(fd)) == NULL) { return -EBADF; } /* Return if other threads still hold a ref */ if (atomic_dec_int(&filedes->refcnt) > 0) { return 0; } td = this_td(); /* * Each file descriptor structure references a vnode, * we want to reclaim it or at the very least drop * one of its references. After we've cleaned up within * the file descriptor, we can clear it from the fd table * and free up the memory for it. */ vfs_release_vnode(filedes->vp); td->fds[fd] = NULL; dynfree(filedes); return 0; } /* * Read bytes from a file using a file * descriptor number. * * @fd: File descriptor number. * @buf: Buffer to read into. * @count: Number of bytes to read. */ int fd_read(unsigned int fd, void *buf, size_t count) { char *kbuf = NULL; struct filedesc *filedes; struct sio_txn sio; scret_t retval = 0; if (count > SSIZE_MAX) { retval = -EINVAL; goto done; } filedes = fd_get(fd); kbuf = dynalloc(count); if (kbuf == NULL) { retval = -ENOMEM; goto done; } if (filedes == NULL) { retval = -EBADF; goto done; } if (filedes->is_dir) { retval = -EISDIR; goto done; } sio.len = count; sio.buf = kbuf; sio.offset = filedes->offset; if ((count = vfs_vop_read(filedes->vp, &sio)) < 0) { retval = -EIO; goto done; } if (copyout(kbuf, buf, count) < 0) { retval = -EFAULT; goto done; } retval = count; done: if (kbuf != NULL) { dynfree(kbuf); } return retval; } /* * 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; struct filedesc *filedes; struct nameidata nd; nd.path = pathname; nd.flags = 0; if ((error = namei(&nd)) < 0) { return error; } if ((error = fd_alloc(&filedes)) != 0) { vfs_release_vnode(nd.vp); return error; } filedes->vp = nd.vp; return filedes->fdno; } /* * Duplicate a file descriptor. New file descriptor * points to the same vnode. */ int fd_dup(int fd) { int error; struct filedesc *new_desc, *tmp; tmp = fd_get(fd); if (tmp == NULL) return -EBADF; if ((error = fd_alloc(&new_desc)) != 0) return error; /* Ref that vnode before we point to it */ vfs_vref(tmp->vp); new_desc->vp = tmp->vp; return new_desc->fdno; }