-OBJECTS = main.o #globals.o open.o close.o read.o write.o
+OBJECTS = main.o proc.o file.o filedesc.o pipe.o
OUTPUT = io
MODULE = io.ko
--- /dev/null
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <corax/spinlock.h>
+#include "file.h"
+
+struct file {
+ struct file_ops *f_ops;
+ void *f_priv;
+ int f_refcnt;
+ cx_spinlock_t f_lock;
+};
+
+void _file_lock(struct file *file)
+{
+ cx_spinlock_lock(&file->f_lock);
+ return;
+}
+
+void _file_unlock(struct file *file)
+{
+ cx_spinlock_unlock(&file->f_lock);
+ return;
+}
+
+int file_new(struct file **dst)
+{
+ struct file *file;
+ int ret_val;
+
+ ret_val = -ENOMEM;
+
+ file = malloc(sizeof(*file));
+
+ if(file) {
+ memset(file, 0, sizeof(*file));
+ file->f_refcnt = 1;
+
+ *dst = file;
+ ret_val = 0;
+ }
+
+ return(ret_val);
+}
+
+int file_free(struct file *file)
+{
+ int ret_val;
+ int refs;
+
+ ret_val = -EINVAL;
+
+ if(file) {
+ refs = file_unref(file);
+
+ if(refs < 1) {
+ free(file);
+ }
+
+ ret_val = 0;
+ }
+
+ return(ret_val);
+}
+
+int file_ref(struct file *file)
+{
+ int ret_val;
+
+ _file_lock(file);
+ ret_val = ++file->f_refcnt;
+ _file_unlock(file);
+
+ return(ret_val);
+}
+
+int file_unref(struct file *file)
+{
+ int ret_val;
+
+ _file_lock(file);
+ ret_val = --file->f_refcnt;
+ _file_unlock(file);
+
+ return(ret_val);
+}
+
+void file_set_ops(struct file *file, struct file_ops *fops)
+{
+ _file_lock(file);
+ file->f_ops = fops;
+ _file_unlock(file);
+
+ return;
+}
+
+void file_set_priv(struct file *file, void *priv)
+{
+ _file_lock(file);
+ file->f_priv = priv;
+ _file_unlock(file);
+
+ return;
+}
+
+void *file_get_priv(struct file *file)
+{
+ void *ret_val;
+
+ _file_lock(file);
+ ret_val = file->f_priv;
+ _file_unlock(file);
+
+ return(ret_val);
+}
--- /dev/null
+#ifndef FILE_H
+#define FILE_H
+
+#include <sys/types.h>
+
+struct file;
+
+struct file_ops {
+ ssize_t (*fo_read)(struct file*, void*, const size_t);
+ ssize_t (*fo_write)(struct file*, const void*, const size_t);
+};
+
+int file_new(struct file**);
+int file_free(struct file*);
+
+int file_ref(struct file*);
+int file_unref(struct file*);
+
+void file_set_ops(struct file*, struct file_ops*);
+
+void file_set_priv(struct file*, void*);
+void *file_get_priv(struct file*);
+
+
+#endif /* FILE_H */
--- /dev/null
+/*
+ * This file is part of the Corax operating system.
+ * Copyright (C) 2020 Matthias Kruk
+ *
+ * Corax is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Corax is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Corax. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <corax/spinlock.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "filedesc.h"
+
+struct file;
+
+struct filedesc {
+ cx_spinlock_t fd_lock;
+ struct file *fd_file;
+ unsigned int fd_flags;
+ int fd_refcnt;
+};
+
+/*
+ * _fd_lock() - Lock a file descriptor
+ *
+ * SYNOPSIS
+ * void _fd_lock(struct filedesc *fd);
+ *
+ * DESCRIPTION
+ * The _fd_lock() function locks the internal spinlock of the file descriptor pointed to by `fd'.
+ * Since waiting on a spinlock is a very expensive operation, the caller must make sure not to
+ * hold the spinlock for extensive periods of time. Ideally, the spinlock should only be held for
+ * the duration that accesses to the file descriptor's members are made. In particular, callers
+ * must not make syscalls while the spinlock is held.
+ *
+ * RETURN VALUE
+ * void
+ *
+ * ERRORS
+ * none
+ */
+static inline void _fd_lock(struct filedesc *fd)
+{
+ cx_spinlock_lock(&fd->fd_lock);
+ return;
+}
+
+/*
+ * _fd_unlock() - Unlock a file descriptor
+ *
+ * SYNOPSIS
+ * void _fd_unlock(struct filedesc *fd);
+ *
+ * DESCRIPTION
+ * The _fd_unlock() function unlocks the internal spinlock of the file descriptor pointed to by
+ * `fd'.
+ *
+ * RETURN VALUE
+ * void
+ *
+ * ERRORS
+ * none
+ */
+static inline void _fd_unlock(struct filedesc *fd)
+{
+ cx_spinlock_unlock(&fd->fd_lock);
+ return;
+}
+
+/*
+ * fd_new() - Allocate a new file descriptor
+ *
+ * SYNOPSIS
+ * int fd_new(struct filedesc **dst);
+ *
+ * DESCRIPTION
+ * The fd_new() function allocates memory for a new file descriptor and initializes it. Upon
+ * success, the pointer pointed to by `dst' will be updated to point to the newly allocated file
+ * descriptor. The file descriptor will be initialized with a reference count of 1.
+ *
+ * RETURN VALUE
+ * Upon success, zero is returned. Otherwise, a negative error number is returned and the value
+ * of `*dst' is undefined.
+ *
+ * ERRORS
+ * -ENOMEM There was not enough memory to allocate the new file descriptor
+ */
+int fd_new(struct filedesc **dst)
+{
+ int ret_val;
+ struct filedesc *fd;
+
+ ret_val = -ENOMEM;
+ fd = malloc(sizeof(*fd));
+
+ if(fd) {
+ memset(fd, 0, sizeof(*fd));
+ fd->fd_refcnt = 1;
+ ret_val = 0;
+ }
+
+ return(ret_val);
+}
+
+/*
+ * fd_free() - Free the memory occupied by a file descriptor
+ *
+ * SYNOPSIS
+ * int fd_free(struct filedesc *fd);
+ *
+ * DESCRIPTION
+ * The fd_free() function decrements the reference count of the file descriptor pointed to by
+ * `fd' and frees the memory occupied by it if there are no more references to it.
+ *
+ * RETURN VALUE
+ * This function returns zero upon success, or a negative error number if an error has occured.
+ * The caller must not use the file descriptor after a call to this function, even if its
+ * reference count suggests that it has not been freed yet.
+ *
+ * ERRORS
+ * -EINVAL The value of `fd' is invalid
+ */
+int fd_free(struct filedesc *fd)
+{
+ int ret_val;
+
+ ret_val = -EINVAL;
+
+ if(fd) {
+ ret_val = 0;
+
+ if(fd_unref(fd) < 1) {
+ free(fd);
+ }
+ }
+
+ return(ret_val);
+}
+
+/*
+ * fd_set_file() - Set the file that is referenced by a file descriptor
+ *
+ * SYNOPSIS
+ * int fd_set_file(struct filedesc *fd, struct file *file);
+ *
+ * DESCRIPTION
+ * The fd_set_file() function modifies the file descriptor pointed to by `fd' so that it refers
+ * to the file pointed to by `file'. And subsequent file operations on the file descriptor `fd'
+ * will be performed on `file'.
+ *
+ * RETURN VALUE
+ * This function returns zero upon success, or a negative error number if an error has occured.
+ *
+ * ERRORS
+ * -EINVAL An invalid argument was passed to this function
+ */
+int fd_set_file(struct filedesc *fd, struct file *file)
+{
+ int ret_val;
+
+ ret_val = -EINVAL;
+
+ if(fd && file) {
+ _fd_lock(fd);
+ fd->fd_file = file;
+ _fd_unlock(fd);
+
+ ret_val = 0;
+ }
+
+ return(ret_val);
+}
+
+/*
+ * fd_ref() - Increment the reference count of a file descriptor
+ *
+ * SYNOPSIS
+ * int fd_ref(struct filedesc *fd);
+ *
+ * DESCRIPTION
+ * The fd_ref() function increments the reference count of the file descriptor pointed to by `fd'
+ * and returns the resulting reference count.
+ *
+ * RETURN VALUE
+ * The number of references of the file descriptor after the incrementation
+ *
+ * ERRORS
+ * none
+ */
+int fd_ref(struct filedesc *fd)
+{
+ int ret_val;
+
+ _fd_lock(fd);
+ ret_val = ++fd->fd_refcnt;
+ _fd_unlock(fd);
+
+ return(ret_val);
+}
+
+/*
+ * fd_unref() - Decrement the reference count of a file descriptor
+ *
+ * SYNOPSIS
+ * int fd_unref(struct filedesc *fd);
+ *
+ * DESCRIPTION
+ * The fd_unref() function decrements the reference count of the file descriptor pointed to by
+ * `fd' and returns the resulting reference count.
+ *
+ * RETURN VALUE
+ * The number of references of the file descriptor after the decrementation
+ *
+ * ERRORS
+ * none
+ */
+int fd_unref(struct filedesc *fd)
+{
+ int ret_val;
+
+ _fd_lock(fd);
+ ret_val = --fd->fd_refcnt;
+ _fd_unlock(fd);
+
+ return(ret_val);
+}
--- /dev/null
+/*
+ * This file is part of the Corax operating system.
+ * Copyright (C) 2020 Matthias Kruk
+ *
+ * Corax is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Corax is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Corax. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef FILEDESC_H
+#define FILEDESC_H
+
+struct file;
+struct filedesc;
+
+int fd_new(struct filedesc**);
+int fd_free(struct filedesc*);
+
+int fd_set_file(struct filedesc*, struct file*);
+
+int fd_ref(struct filedesc*);
+int fd_unref(struct filedesc*);
+
+#endif /* FILEDESC_H */
--- /dev/null
+#include <corax/ipc/io.h>
+#include <corax/types.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "file.h"
+#include "proc.h"
+#include "filedesc.h"
+
+#define PIPE_BUFSIZE 4096
+
+static ssize_t _pipe_read(struct file*, void*, const size_t);
+static ssize_t _pipe_write(struct file*, const void*, const size_t);
+
+static struct file_ops _pipe_ops = {
+ .fo_read = _pipe_read,
+ .fo_write = _pipe_write
+};
+
+struct pipe {
+ char data[PIPE_BUFSIZE];
+ size_t p_offset;
+ size_t p_len;
+
+ struct proc *p_owner;
+};
+
+static ssize_t _pipe_read(struct file *file, void *dst, const size_t dst_size)
+{
+ return(-ENOSYS);
+}
+
+static ssize_t _pipe_write(struct file *file, const void *src, const size_t src_size)
+{
+ return(-ENOSYS);
+}
+
+int pipe_new(const int flags, struct file **dst)
+{
+ struct file *file;
+ struct pipe *pipe;
+ int ret_val;
+
+ ret_val = -ENOMEM;
+ pipe = malloc(sizeof(*pipe));
+
+ if(pipe) {
+ memset(pipe, 0, sizeof(*pipe));
+
+ ret_val = file_new(&file);
+
+ if(!ret_val) {
+ file_set_ops(file, &_pipe_ops);
+ file_set_priv(file, pipe);
+
+ *dst = file;
+ } else {
+ free(pipe);
+ }
+ }
+
+ return(ret_val);
+}
+
+void cxio_pipe(pid_t pid, struct cxio_pipe *req)
+{
+ struct proc *proc;
+ struct file *file;
+ struct filedesc *fildes[2];
+ int fd[2];
+
+ int err;
+ int i;
+
+ file = NULL;
+ memset(&fd, 0, sizeof(fd));
+ memset(&fildes, 0, sizeof(fildes));
+
+ err = proc_lookup(pid, &proc);
+
+ if(err < 0) {
+ err = proc_new(pid, &proc);
+
+ if(err < 0) {
+ goto cleanup;
+ }
+ }
+
+ err = pipe_new(req->flags, &file);
+
+ if(err < 0) {
+ goto cleanup;
+ }
+
+ for(i = 0; i < 2; i++) {
+ err = fd_new(&fildes[i]);
+
+ if(err < 0) {
+ goto cleanup;
+ }
+
+ fd_set_file(fildes[i], file);
+ fd[i] = proc_add_fd(proc, fildes[i]);
+ }
+
+cleanup:
+ if(err < 0) {
+ if(file) {
+ file_free(file);
+ }
+
+ for(i = 0; i < 2; i++) {
+ if(fildes[i]) {
+ fd_free(fildes[i]);
+ }
+ }
+ }
+
+ return;
+}
--- /dev/null
+#ifndef PIPE_H
+#define PIPE_H
+
+int pipe_new(const int, struct file**);
+void cxio_pipe(pid_t, struct cxio_pipe*);
+
+#endif /* PIPE_H */
--- /dev/null
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <corax/array.h>
+#include "proc.h"
+#include "filedesc.h"
+
+#define PROC_INITFDS 8
+
+struct filedesc;
+
+struct proc {
+ struct proc *p_next;
+
+ pid_t p_pid;
+ pid_t p_ppid;
+
+ struct cx_array *p_fds;
+ int p_nextfd;
+
+ struct {
+ int ps_openfds;
+ } p_stats;
+
+ struct {
+ int pl_maxfds;
+ } p_limits;
+};
+
+#define p_openfds p_stats.ps_openfds
+#define p_maxfds p_limits.pl_maxfds
+
+static struct proc *_procs;
+
+int proc_lookup(const pid_t pid, struct proc **dst)
+{
+ struct proc *cur;
+ int ret_val;
+
+ ret_val = -ENOENT;
+
+ for(cur = _procs; cur; cur = cur->p_next) {
+ if(cur->p_pid == pid) {
+ *dst = cur;
+ ret_val = 0;
+ break;
+ }
+ }
+
+ return(ret_val);
+}
+
+/*
+ * _proc_list_append() - Add a process to the end of the internal process list
+ *
+ * SYNOPSIS
+ * void _proc_list_append(struct proc *proc);
+ *
+ * DESCRIPTION
+ * The _proc_list_append() function appends the process pointed to by `proc' to the internal list
+ * of processes. This function always succeeds.
+ *
+ * This function is not thread-safe.
+ *
+ * RETURN VALUE
+ * void
+ *
+ * ERRORS
+ * none
+ */
+static inline void _proc_list_append(struct proc *proc)
+{
+ struct proc **cur;
+
+ for(cur = &_procs; *cur; cur = &(*cur)->p_next);
+ *cur = proc;
+
+ return;
+}
+
+/*
+ * _proc_list_drop() - Remove a process from the internal process list
+ *
+ * SYNOPSIS
+ * int _proc_list_drop(struct proc *proc);
+ *
+ * DESCRIPTION
+ * The _proc_list_drop() function removes the process pointed to by `proc' from the internal list
+ * of processes. This function only removes the pointer from the linked list; it does not free
+ * any memory allocations. The p_next pointer of the removed object will be set to NULL, to make
+ * sure it cannot be used to reference other parts of the list.
+ *
+ * This function is not thread-safe.
+ *
+ * RETURN VALUE
+ * This function returns zero upon success, or a negative error number if an error has occured.
+ *
+ * ERRORS
+ * -ENOENT The process was not found in the internal process list.
+ */
+static inline int _proc_list_drop(struct proc *proc)
+{
+ struct proc **cur;
+ int ret_val;
+
+ ret_val = -ENOENT;
+
+ for(cur = &_procs; *cur; cur = &(*cur)->p_next) {
+ if(*cur == proc) {
+ *cur = proc->p_next;
+ proc->p_next = NULL;
+ ret_val = 0;
+ break;
+ }
+ }
+
+ return(ret_val);
+}
+
+/*
+ * proc_new() - Allocate memory for a new process
+ *
+ * SYNOPSIS
+ * int proc_new(const pid_t pid, struct proc **dst);
+ *
+ * DESCRIPTION
+ * The proc_new() function will allocate memory for a new process object and initialize the pid
+ * of the newly allocated process to `pid'. On success, the newly allocated process will be added
+ * to the internal process list, and the pointer pointed to by `dst' will be set to point to the
+ * newly allocated process.
+ *
+ * RETURN VALUE
+ * This function returns zero on success, or a negative error number if an error has occured.
+ *
+ * ERRORS
+ * -ENOMEM There wasn't enough space on the heap to allocate the new process
+ */
+int proc_new(const pid_t pid, struct proc **dst)
+{
+ struct proc *new;
+ int ret_val;
+
+ ret_val = -ENOMEM;
+ new = malloc(sizeof(*new));
+
+ if(new) {
+ new->p_pid = pid;
+
+ memset(new, 0, sizeof(*new));
+
+ ret_val = cx_array_new(PROC_INITFDS, &new->p_fds);
+
+ if(!ret_val) {
+ *dst = new;
+ _proc_list_append(new);
+ } else {
+ free(new);
+ }
+ }
+
+ return(ret_val);
+}
+
+/*
+ * proc_free() - Free the memory occupied by a process
+ *
+ * SYNOPSIS
+ * void proc_free(struct proc *proc);
+ *
+ * DESCRIPTION
+ * The proc_free() function releases the memory of the process pointed to by `proc'. This
+ * function will also cause the process to be removed from the internal list of processes before
+ * its memory is freed.
+ * This function does not make sure that file descriptors referenced by the process are closed.
+ * It is the responsibility of the caller to ensure that file descriptors are closed in order to
+ * avoid memory leaks.
+ *
+ * RETURN VALUE
+ * void
+ *
+ * ERRORS
+ * none
+ */
+void proc_free(struct proc *proc)
+{
+ _proc_list_drop(proc);
+ free(proc);
+
+ return;
+}
+
+/*
+ * proc_add_fd() - Add a file descriptor to a process
+ *
+ * SYNOPSIS
+ * int proc_add_fd(struct proc *proc, struct filedesc *fd);
+ *
+ * DESCRIPTION
+ * The proc_add_fd() function adds the file descriptor pointed to by `fd' to the file descriptor
+ * list of the process pointed to by `proc'.
+ *
+ * RETURN VALUE
+ * This function returns a value greater than or equal to zero upon success. This value should be
+ * used to refer to the file descriptor inside of the process. In case of an error, a negative
+ * error number is returned.
+ *
+ * ERRORS
+ * -EMFILE The process has too many open file descriptors
+ */
+int proc_add_fd(struct proc *proc, struct filedesc *fd)
+{
+ int ret_val;
+
+ ret_val = -EINVAL;
+
+ if(proc && fd) {
+ ret_val = -EMFILE;
+
+ if(proc->p_openfds <= proc->p_maxfds) {
+ ret_val = cx_array_set(proc->p_fds, proc->p_nextfd, fd);
+
+ if(!ret_val) {
+ fd_ref(fd);
+
+ proc->p_nextfd++;
+ proc->p_openfds++;
+ }
+ }
+ }
+
+ return(ret_val);
+}
--- /dev/null
+#ifndef PROC_H
+#define PROC_H
+
+#include <corax/types.h>
+#include <sys/types.h>
+
+struct proc;
+struct filedesc;
+
+int proc_lookup(const pid_t, struct proc**);
+int proc_new(const pid_t, struct proc**);
+void proc_free(struct proc*);
+
+int proc_add_fd(struct proc*, struct filedesc*);
+
+#endif /* PROC_H */