]> git.corax.cc Git - corax/commitdiff
sys/io: Implement handling of processes and file descriptors
authorMatthias Kruk <m@m10k.eu>
Mon, 10 Aug 2020 12:08:54 +0000 (21:08 +0900)
committerMatthias Kruk <m@m10k.eu>
Mon, 10 Aug 2020 12:08:54 +0000 (21:08 +0900)
sys/io/Makefile
sys/io/file.c [new file with mode: 0644]
sys/io/file.h [new file with mode: 0644]
sys/io/filedesc.c [new file with mode: 0644]
sys/io/filedesc.h [new file with mode: 0644]
sys/io/pipe.c [new file with mode: 0644]
sys/io/pipe.h [new file with mode: 0644]
sys/io/proc.c [new file with mode: 0644]
sys/io/proc.h [new file with mode: 0644]

index 9c599f710e7989d865b425ac558560f63ecb31f6..2b000672b621f6768e320d3c3d007fadcbe26d0d 100644 (file)
@@ -1,4 +1,4 @@
-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
 
diff --git a/sys/io/file.c b/sys/io/file.c
new file mode 100644 (file)
index 0000000..9151621
--- /dev/null
@@ -0,0 +1,115 @@
+#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);
+}
diff --git a/sys/io/file.h b/sys/io/file.h
new file mode 100644 (file)
index 0000000..2394435
--- /dev/null
@@ -0,0 +1,25 @@
+#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 */
diff --git a/sys/io/filedesc.c b/sys/io/filedesc.c
new file mode 100644 (file)
index 0000000..7897001
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * 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);
+}
diff --git a/sys/io/filedesc.h b/sys/io/filedesc.h
new file mode 100644 (file)
index 0000000..3f4d84b
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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 */
diff --git a/sys/io/pipe.c b/sys/io/pipe.c
new file mode 100644 (file)
index 0000000..73953e7
--- /dev/null
@@ -0,0 +1,121 @@
+#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;
+}
diff --git a/sys/io/pipe.h b/sys/io/pipe.h
new file mode 100644 (file)
index 0000000..951672a
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef PIPE_H
+#define PIPE_H
+
+int pipe_new(const int, struct file**);
+void cxio_pipe(pid_t, struct cxio_pipe*);
+
+#endif /* PIPE_H */
diff --git a/sys/io/proc.c b/sys/io/proc.c
new file mode 100644 (file)
index 0000000..0579490
--- /dev/null
@@ -0,0 +1,233 @@
+#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);
+}
diff --git a/sys/io/proc.h b/sys/io/proc.h
new file mode 100644 (file)
index 0000000..8588823
--- /dev/null
@@ -0,0 +1,16 @@
+#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 */