From 265d12f6b29981ddca4e332f3c2ab7306003d847 Mon Sep 17 00:00:00 2001 From: Matheus Date: Mon, 26 Jun 2023 23:18:49 -0300 Subject: [PATCH] file: fd duping and related syscalls --- kernel-src/fs/file.c | 93 ++++++++++++++++++++++++++++------- kernel-src/sys/syscalls/dup.c | 39 +++++++++++++++ 2 files changed, 115 insertions(+), 17 deletions(-) create mode 100644 kernel-src/sys/syscalls/dup.c diff --git a/kernel-src/fs/file.c b/kernel-src/fs/file.c index d690ee6b..fcad21b8 100644 --- a/kernel-src/fs/file.c +++ b/kernel-src/fs/file.c @@ -44,9 +44,32 @@ file_t *fd_allocate() { return newfile(); } +static int getfree(int start) { + proc_t *proc = _cpu()->thread->proc; + int fd = -1; + int sfd; + for (sfd = start; sfd < proc->fdcount && proc->fd[sfd].file; ++sfd); + + if (sfd != proc->fdcount) + fd = sfd; + + return fd; +} + +static int growtable(int newcount) { + proc_t *proc = _cpu()->thread->proc; + __assert(newcount > proc->fdcount); + void *newtable = realloc(proc->fd, sizeof(fd_t) * newcount); + if (newtable == NULL) + return ENOMEM; + + proc->fd = newtable; + proc->fdcount = newcount; + return 0; +} + file_t *fd_get(int fd) { proc_t *proc = _cpu()->thread->proc; - bool intstatus = interrupt_set(false); spinlock_acquire(&proc->fdlock); file_t *file = fd < proc->fdcount ? proc->fd[fd].file : NULL; @@ -54,7 +77,6 @@ file_t *fd_get(int fd) { FILE_HOLD(file); spinlock_release(&proc->fdlock); - interrupt_set(intstatus); if (file) MUTEX_ACQUIRE(&file->mutex, false); @@ -76,25 +98,17 @@ int fd_new(int flags, file_t **rfile, int *rfd) { spinlock_acquire(&proc->fdlock); // find first free fd - int fd = -1; - int sfd; - for (sfd = proc->fdfirst; fd < proc->fdcount && proc->fd[fd].file; ++fd); - - if (sfd != proc->fdcount) - fd = sfd; + int fd = getfree(proc->fdfirst); // resize table if not found if (fd == -1) { - void *newtable = realloc(proc->fd, sizeof(fd_t) * (proc->fdcount + 1)); - if (newtable == NULL) { + fd = proc->fdcount; + int error = growtable(fd + 1); + if (error) { spinlock_release(&proc->fdlock); cleanfile(file); - return ENOMEM; + return error; } - - proc->fd = newtable; - fd = proc->fdcount; - ++proc->fdcount; } proc->fdfirst = fd + 1; @@ -112,7 +126,6 @@ int fd_new(int flags, file_t **rfile, int *rfd) { int fd_close(int fd) { proc_t *proc = _cpu()->thread->proc; - bool intstatus = interrupt_set(false); spinlock_acquire(&proc->fdlock); file_t *file = fd < proc->fdcount ? proc->fd[fd].file : NULL; @@ -122,7 +135,6 @@ int fd_close(int fd) { } spinlock_release(&proc->fdlock); - interrupt_set(intstatus); if (file) { FILE_RELEASE(file); @@ -159,3 +171,50 @@ int fd_clone(proc_t *targproc) { spinlock_release(&proc->fdlock); return error; } + +int fd_dup(int oldfd, int newfd, bool exact, int fdflags, int *retfd) { + proc_t *proc = _cpu()->thread->proc; + int err = 0; + spinlock_acquire(&proc->fdlock); + + file_t *file = oldfd < proc->fdcount ? proc->fd[oldfd].file : NULL; + if (file == NULL) { + err = EBADF; + goto cleanup; + } + + if (exact) { + if (newfd >= proc->fdcount) { + err = growtable(newfd + 1); + if (err) + goto cleanup; + } + + if (proc->fd[newfd].file) + FILE_RELEASE(proc->fd[newfd].file); + + proc->fd[newfd].file = file; + proc->fd[newfd].flags = fdflags; + *retfd = newfd; + } else { + int fd = getfree(newfd); + + if (fd == -1) { + size_t newsize = newfd < proc->fdcount ? proc->fdcount + 1 : newfd; + fd = newsize - 1; + err = growtable(newsize); + if (err) + goto cleanup; + } + + proc->fd[fd].file = file; + proc->fd[fd].flags = fdflags; + *retfd = fd; + } + + FILE_HOLD(file); + + cleanup: + spinlock_release(&proc->fdlock); + return err; +} diff --git a/kernel-src/sys/syscalls/dup.c b/kernel-src/sys/syscalls/dup.c new file mode 100644 index 00000000..36814ac0 --- /dev/null +++ b/kernel-src/sys/syscalls/dup.c @@ -0,0 +1,39 @@ +#include +#include +#include + +syscallret_t syscall_dup(context_t *, int oldfd) { + syscallret_t ret; + int r = -1; + ret.errno = fd_dup(oldfd, 0, false, 0, &r); + ret.ret = r; + return ret; +} + +syscallret_t syscall_dup2(context_t *, int oldfd, int newfd) { + syscallret_t ret; + if (oldfd == newfd) { + ret.errno = 0; + ret.ret = newfd; + } else { + int r = -1; + ret.errno = fd_dup(oldfd, newfd, true, 0, &r); + ret.ret = r; + } + + return ret; +} + +syscallret_t syscall_dup3(context_t *, int oldfd, int newfd, int flags) { + syscallret_t ret; + if (oldfd == newfd) { + ret.errno = EINVAL; + ret.ret = -1; + } else { + int r = -1; + ret.errno = fd_dup(oldfd, newfd, true, flags, &r); + ret.ret = r; + } + + return ret; +}