From 812ef277f7e381186c0a69f31da7af4316106f6a Mon Sep 17 00:00:00 2001 From: Matthias Kruk Date: Mon, 10 Aug 2020 21:08:54 +0900 Subject: [PATCH] sys/io: Implement handling of processes and file descriptors --- sys/io/Makefile | 2 +- sys/io/file.c | 115 ++++++++++++++++++++++ sys/io/file.h | 25 +++++ sys/io/filedesc.c | 237 ++++++++++++++++++++++++++++++++++++++++++++++ sys/io/filedesc.h | 33 +++++++ sys/io/pipe.c | 121 +++++++++++++++++++++++ sys/io/pipe.h | 7 ++ sys/io/proc.c | 233 +++++++++++++++++++++++++++++++++++++++++++++ sys/io/proc.h | 16 ++++ 9 files changed, 788 insertions(+), 1 deletion(-) create mode 100644 sys/io/file.c create mode 100644 sys/io/file.h create mode 100644 sys/io/filedesc.c create mode 100644 sys/io/filedesc.h create mode 100644 sys/io/pipe.c create mode 100644 sys/io/pipe.h create mode 100644 sys/io/proc.c create mode 100644 sys/io/proc.h diff --git a/sys/io/Makefile b/sys/io/Makefile index 9c599f7..2b00067 100644 --- a/sys/io/Makefile +++ b/sys/io/Makefile @@ -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 index 0000000..9151621 --- /dev/null +++ b/sys/io/file.c @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#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 index 0000000..2394435 --- /dev/null +++ b/sys/io/file.h @@ -0,0 +1,25 @@ +#ifndef FILE_H +#define FILE_H + +#include + +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 index 0000000..7897001 --- /dev/null +++ b/sys/io/filedesc.c @@ -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 . + */ + +#include +#include +#include +#include +#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 index 0000000..3f4d84b --- /dev/null +++ b/sys/io/filedesc.h @@ -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 . + */ + +#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 index 0000000..73953e7 --- /dev/null +++ b/sys/io/pipe.c @@ -0,0 +1,121 @@ +#include +#include +#include +#include +#include +#include +#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 index 0000000..951672a --- /dev/null +++ b/sys/io/pipe.h @@ -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 index 0000000..0579490 --- /dev/null +++ b/sys/io/proc.c @@ -0,0 +1,233 @@ +#include +#include +#include +#include +#include +#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 index 0000000..8588823 --- /dev/null +++ b/sys/io/proc.h @@ -0,0 +1,16 @@ +#ifndef PROC_H +#define PROC_H + +#include +#include + +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 */ -- 2.47.3