whiterose

linux unikernel
Log | Files | Refs | README | LICENSE | git clone https://git.ne02ptzero.me/git/whiterose

commit d723fba160d47df298b70d4c2c830fdcea36a482
parent 62eb04442b7cfc5454f5a4c74561355d671f3f17
Author: Louis Solofrizzo <louis@ne02ptzero.me>
Date:   Tue, 23 Apr 2019 06:33:15 +0200

ukl: Add fcntl function in the library

This patches adds the supports for fcntl(2) in the ukl library. Should
be working the same as the musl. I've also modified open for a corner
case with FD_CLOEXEC.

There was also a bug in close(2), which have been fixed. I've modified
some error messages for stdin, stdout and stderr mocking.

Signed-off-by: Louis Solofrizzo <louis@ne02ptzero.me>

Diffstat:
Mfs/fcntl.c | 4++--
Minclude/linux/stddef.h | 6++++++
Minclude/ukl/fcntl.h | 1+
Mukl/Makefile | 2+-
Mukl/TO_PORT | 1-
Aukl/fcntl/fcntl.c | 113+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mukl/fcntl/open.c | 5++---
Mukl/unistd/close.c | 13++++++++++++-
Mukl/unistd/read.c | 4++--
9 files changed, 139 insertions(+), 10 deletions(-)

diff --git a/fs/fcntl.c b/fs/fcntl.c @@ -319,7 +319,7 @@ static long fcntl_rw_hint(struct file *file, unsigned int cmd, } } -static long do_fcntl(int fd, unsigned int cmd, unsigned long arg, +__static_ukl long do_fcntl(int fd, unsigned int cmd, unsigned long arg, struct file *filp) { void __user *argp = (void __user *)arg; @@ -432,7 +432,7 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg, return err; } -static int check_fcntl_cmd(unsigned cmd) +__static_ukl int check_fcntl_cmd(unsigned cmd) { switch (cmd) { case F_DUPFD: diff --git a/include/linux/stddef.h b/include/linux/stddef.h @@ -36,4 +36,10 @@ enum { #define offsetofend(TYPE, MEMBER) \ (offsetof(TYPE, MEMBER) + sizeof_field(TYPE, MEMBER)) +#ifndef CONFIG_UKL_LINUX +#define __static_ukl static +#else +#define __static_ukl +#endif /* CONFIG_UKL_LINUX */ + #endif diff --git a/include/ukl/fcntl.h b/include/ukl/fcntl.h @@ -5,5 +5,6 @@ #include <linux/syscalls.h> int open(const char *filename, int flags, ...); +int fcntl(int fd, int cmd, ...); #endif /* FCNTL_H */ diff --git a/ukl/Makefile b/ukl/Makefile @@ -1,2 +1,2 @@ ukl-real-obj-m := $(addprefix ../../../../../../../..$(UKL)/,$(ukl-obj-m)) -obj-y := unistd/write.o unistd/read.o unistd/close.o fcntl/open.o $(ukl-real-obj-m) +obj-y := unistd/write.o unistd/read.o unistd/close.o fcntl/open.o fcntl/fcntl.o $(ukl-real-obj-m) diff --git a/ukl/TO_PORT b/ukl/TO_PORT @@ -68,7 +68,6 @@ Syscalls to port: - msgsnd - msgrcv - msgctl -- fcntl - flock - fsync - fdatasync diff --git a/ukl/fcntl/fcntl.c b/ukl/fcntl/fcntl.c @@ -0,0 +1,113 @@ +#include <fcntl.h> +#include <unistd.h> +#include <linux/security.h> + +extern long do_fcntl(int fd, unsigned int cmd, unsigned long arg, struct file *filp); +extern int check_fcntl_cmd(unsigned cmd); + +static long sys_fcntl(int fd, unsigned int cmd, unsigned long arg) +{ + struct fd f = fdget_raw(fd); /* Get the file associated with the file descriptor */ + long err = -EBADF; + + /* Not a file, nothing to do! */ + if (!f.file) + return err; + + /* On PATH mode, check that the cmd issued is compliant */ + if (unlikely(f.file->f_mode) & FMODE_PATH) + { + if (!check_fcntl_cmd(cmd)) + { + /* cmd is not compliant, release the file lock and return the err */ + fdput(f); + return err; + } + } + + /* Check the rights on file */ + err = security_file_fcntl(f.file, cmd, arg); + + /* Do the syscall */ + if (!err) + err = do_fcntl(fd, cmd, arg, f.file); + + /* Release the lock and return the code */ + fdput(f); + return err; +} + +int fcntl(int fd, int cmd, ...) +{ + unsigned long arg; + va_list ap; + + if (fd >= 0 && fd <= 2) + { + printk(KERN_ERR "Trying to call fcntl() with %d file descriptor\n", fd); + return -EINVAL; + } + + /** + * Little trick used to mock stdin, stdout and stderr for UKL + * Don't make any sense in kernel space, since a fd could very well + * be 0. + */ + fd -= 3; + + /* Read optionnals arguments */ + va_start(ap, cmd); + arg = va_arg(ap, unsigned long); + va_end(ap); + + if (cmd == F_SETFL) + arg |= O_LARGEFILE; + + if (cmd == F_SETLKW) + return sys_fcntl(fd, cmd, arg); + + /* If the fd is a socket, get the process owner's pid */ + if (cmd == F_GETOWN) + { + struct f_owner_ex ex; + int ret = sys_fcntl(fd, F_GETOWN_EX, &ex); + + if (ret == -EINVAL) + return sys_fcntl(fd, cmd, arg); + + if (ret) + return ret; + return ex.type == F_OWNER_PGRP ? -ex.pid : ex.pid; + } + + /* Clone the fd, with FD_CLOEXEC flag */ + if (cmd == F_DUPFD_CLOEXEC) + { + int ret = sys_fcntl(fd, F_DUPFD_CLOEXEC, arg); /* Try to dup */ + + if (ret != -EINVAL) + { + /* Dup successful, set the flag and return the new fd */ + if (ret >= 0) + sys_fcntl(ret, F_SETFD, FD_CLOEXEC); + return ret; + } + + /* Dup has failed, cleanup and return error */ + ret = sys_fcntl(fd, F_DUPFD_CLOEXEC, 0); + if (ret != -EINVAL) + { + if (ret >= 0) + close(ret); + return -EINVAL; + } + + /* Last try, make the dup and set in two calls */ + ret = sys_fcntl(fd, F_DUPFD, arg); + if (ret >= 0) + sys_fcntl(ret, F_SETFD, FD_CLOEXEC); + return ret; + } + + return sys_fcntl(fd, cmd, arg); +} diff --git a/ukl/fcntl/open.c b/ukl/fcntl/open.c @@ -6,7 +6,6 @@ int open(const char *filename, int flags, ...) { umode_t mode = 0; - mm_segment_t old_fs; int ret = -1; /* Parse the flags */ @@ -22,8 +21,8 @@ int open(const char *filename, int flags, ...) /* Make the call */ ret = do_sys_open(AT_FDCWD, filename, flags, mode); - /*if (fd >= 0 && (flags & O_CLOEXEC))*/ - /*fcntl(fd, F_SETFD, FD_CLOEXEC);*/ + if (ret >= 0 && (flags & O_CLOEXEC)) + fcntl(ret + 3, F_SETFD, FD_CLOEXEC); /** * Little trick used to mock stdin, stdout and stderr for UKL diff --git a/ukl/unistd/close.c b/ukl/unistd/close.c @@ -3,5 +3,16 @@ int close(int fd) { - return ksys_close(fd); + if (fd >= 0 && fd <= 2) + { + printk(KERN_ERR "Trying to call close() with %d file descriptor\n", fd); + return -EINVAL; + } + + /** + * Little trick used to mock stdin, stdout and stderr for UKL + * Don't make any sense in kernel space, since a fd could very well + * be 0. + */ + return ksys_close(fd - 3); } diff --git a/ukl/unistd/read.c b/ukl/unistd/read.c @@ -6,8 +6,8 @@ ssize_t read(int fd, void *buf, size_t count) /* We don't support pipe, stdin, stdout or stderr reading */ if (fd >= 0 && fd <= 2) { - printk(KERN_ERR "Unsupported read to %d\n", fd); - return 0; + printk(KERN_ERR "Trying to call read() with %d file descriptor\n", fd); + return -EINVAL; } /**