diff options
Diffstat (limited to 'arch/um/os-Linux')
-rw-r--r-- | arch/um/os-Linux/Makefile | 10 | ||||
-rw-r--r-- | arch/um/os-Linux/drivers/ethertap_user.c | 1 | ||||
-rw-r--r-- | arch/um/os-Linux/execvp.c | 149 | ||||
-rw-r--r-- | arch/um/os-Linux/helper.c | 68 | ||||
-rw-r--r-- | arch/um/os-Linux/irq.c | 1 | ||||
-rw-r--r-- | arch/um/os-Linux/main.c | 1 | ||||
-rw-r--r-- | arch/um/os-Linux/process.c | 1 | ||||
-rw-r--r-- | arch/um/os-Linux/sigio.c | 1 | ||||
-rw-r--r-- | arch/um/os-Linux/signal.c | 31 | ||||
-rw-r--r-- | arch/um/os-Linux/skas/process.c | 2 | ||||
-rw-r--r-- | arch/um/os-Linux/sys-i386/tls.c | 3 | ||||
-rw-r--r-- | arch/um/os-Linux/time.c | 3 | ||||
-rw-r--r-- | arch/um/os-Linux/tls.c | 1 |
13 files changed, 231 insertions, 41 deletions
diff --git a/arch/um/os-Linux/Makefile b/arch/um/os-Linux/Makefile index b4183929b32..2f8c7946401 100644 --- a/arch/um/os-Linux/Makefile +++ b/arch/um/os-Linux/Makefile @@ -3,8 +3,8 @@ # Licensed under the GPL # -obj-y = aio.o elf_aux.o file.o helper.o irq.o main.o mem.o process.o sigio.o \ - signal.o start_up.o time.o trap.o tty.o uaccess.o umid.o tls.o \ +obj-y = aio.o elf_aux.o execvp.o file.o helper.o irq.o main.o mem.o process.o \ + sigio.o signal.o start_up.o time.o trap.o tty.o uaccess.o umid.o tls.o \ user_syms.o util.o drivers/ sys-$(SUBARCH)/ obj-$(CONFIG_MODE_SKAS) += skas/ @@ -15,9 +15,9 @@ user-objs-$(CONFIG_MODE_TT) += tt.o obj-$(CONFIG_TTY_LOG) += tty_log.o user-objs-$(CONFIG_TTY_LOG) += tty_log.o -USER_OBJS := $(user-objs-y) aio.o elf_aux.o file.o helper.o irq.o main.o mem.o \ - process.o sigio.o signal.o start_up.o time.o trap.o tty.o tls.o \ - uaccess.o umid.o util.o +USER_OBJS := $(user-objs-y) aio.o elf_aux.o execvp.o file.o helper.o irq.o \ + main.o mem.o process.o sigio.o signal.o start_up.o time.o trap.o tty.o \ + tls.o uaccess.o umid.o util.o CFLAGS_user_syms.o += -DSUBARCH_$(SUBARCH) diff --git a/arch/um/os-Linux/drivers/ethertap_user.c b/arch/um/os-Linux/drivers/ethertap_user.c index f559bdf746e..863981ba146 100644 --- a/arch/um/os-Linux/drivers/ethertap_user.c +++ b/arch/um/os-Linux/drivers/ethertap_user.c @@ -20,6 +20,7 @@ #include "net_user.h" #include "etap.h" #include "os.h" +#include "um_malloc.h" #define MAX_PACKET ETH_MAX_PACKET diff --git a/arch/um/os-Linux/execvp.c b/arch/um/os-Linux/execvp.c new file mode 100644 index 00000000000..66e583a4031 --- /dev/null +++ b/arch/um/os-Linux/execvp.c @@ -0,0 +1,149 @@ +/* Copyright (C) 2006 by Paolo Giarrusso - modified from glibc' execvp.c. + Original copyright notice follows: + + Copyright (C) 1991,92,1995-99,2002,2004 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ +#include <unistd.h> + +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <limits.h> + +#ifndef TEST +#include "um_malloc.h" +#else +#include <stdio.h> +#define um_kmalloc malloc +#endif +#include "os.h" + +/* Execute FILE, searching in the `PATH' environment variable if it contains + no slashes, with arguments ARGV and environment from `environ'. */ +int execvp_noalloc(char *buf, const char *file, char *const argv[]) +{ + if (*file == '\0') { + return -ENOENT; + } + + if (strchr (file, '/') != NULL) { + /* Don't search when it contains a slash. */ + execv(file, argv); + } else { + int got_eacces; + size_t len, pathlen; + char *name, *p; + char *path = getenv("PATH"); + if (path == NULL) + path = ":/bin:/usr/bin"; + + len = strlen(file) + 1; + pathlen = strlen(path); + /* Copy the file name at the top. */ + name = memcpy(buf + pathlen + 1, file, len); + /* And add the slash. */ + *--name = '/'; + + got_eacces = 0; + p = path; + do { + char *startp; + + path = p; + //Let's avoid this GNU extension. + //p = strchrnul (path, ':'); + p = strchr(path, ':'); + if (!p) + p = strchr(path, '\0'); + + if (p == path) + /* Two adjacent colons, or a colon at the beginning or the end + of `PATH' means to search the current directory. */ + startp = name + 1; + else + startp = memcpy(name - (p - path), path, p - path); + + /* Try to execute this name. If it works, execv will not return. */ + execv(startp, argv); + + /* + if (errno == ENOEXEC) { + } + */ + + switch (errno) { + case EACCES: + /* Record the we got a `Permission denied' error. If we end + up finding no executable we can use, we want to diagnose + that we did find one but were denied access. */ + got_eacces = 1; + case ENOENT: + case ESTALE: + case ENOTDIR: + /* Those errors indicate the file is missing or not executable + by us, in which case we want to just try the next path + directory. */ + case ENODEV: + case ETIMEDOUT: + /* Some strange filesystems like AFS return even + stranger error numbers. They cannot reasonably mean + anything else so ignore those, too. */ + case ENOEXEC: + /* We won't go searching for the shell + * if it is not executable - the Linux + * kernel already handles this enough, + * for us. */ + break; + + default: + /* Some other error means we found an executable file, but + something went wrong executing it; return the error to our + caller. */ + return -errno; + } + } while (*p++ != '\0'); + + /* We tried every element and none of them worked. */ + if (got_eacces) + /* At least one failure was due to permissions, so report that + error. */ + return -EACCES; + } + + /* Return the error from the last attempt (probably ENOENT). */ + return -errno; +} +#ifdef TEST +int main(int argc, char**argv) +{ + char buf[PATH_MAX]; + int ret; + argc--; + if (!argc) { + fprintf(stderr, "Not enough arguments\n"); + return 1; + } + argv++; + if (ret = execvp_noalloc(buf, argv[0], argv)) { + errno = -ret; + perror("execvp_noalloc"); + } + return 0; +} +#endif diff --git a/arch/um/os-Linux/helper.c b/arch/um/os-Linux/helper.c index cd15b9df5b5..c7ad6306e22 100644 --- a/arch/um/os-Linux/helper.c +++ b/arch/um/os-Linux/helper.c @@ -8,18 +8,21 @@ #include <unistd.h> #include <errno.h> #include <sched.h> +#include <limits.h> #include <sys/signal.h> #include <sys/wait.h> #include "user.h" #include "kern_util.h" #include "user_util.h" #include "os.h" +#include "um_malloc.h" struct helper_data { void (*pre_exec)(void*); void *pre_data; char **argv; int fd; + char *buf; }; /* Debugging aid, changed only from gdb */ @@ -35,22 +38,22 @@ static int helper_child(void *arg) char **argv = data->argv; int errval; - if(helper_pause){ + if (helper_pause){ signal(SIGHUP, helper_hup); pause(); } - if(data->pre_exec != NULL) + if (data->pre_exec != NULL) (*data->pre_exec)(data->pre_data); - execvp(argv[0], argv); - errval = -errno; - printk("helper_child - execve of '%s' failed - errno = %d\n", argv[0], errno); + errval = execvp_noalloc(data->buf, argv[0], argv); + printk("helper_child - execvp of '%s' failed - errno = %d\n", argv[0], -errval); os_write_file(data->fd, &errval, sizeof(errval)); kill(os_getpid(), SIGKILL); - return(0); + return 0; } /* Returns either the pid of the child process we run or -E* on failure. - * XXX The alloc_stack here breaks if this is called in the tracing thread */ + * XXX The alloc_stack here breaks if this is called in the tracing thread, so + * we need to receive a preallocated stack (a local buffer is ok). */ int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv, unsigned long *stack_out) { @@ -58,20 +61,21 @@ int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv, unsigned long stack, sp; int pid, fds[2], ret, n; - if((stack_out != NULL) && (*stack_out != 0)) + if ((stack_out != NULL) && (*stack_out != 0)) stack = *stack_out; - else stack = alloc_stack(0, __cant_sleep()); - if(stack == 0) + else + stack = alloc_stack(0, __cant_sleep()); + if (stack == 0) return -ENOMEM; ret = os_pipe(fds, 1, 0); - if(ret < 0){ + if (ret < 0) { printk("run_helper : pipe failed, ret = %d\n", -ret); goto out_free; } ret = os_set_exec_close(fds[1], 1); - if(ret < 0){ + if (ret < 0) { printk("run_helper : setting FD_CLOEXEC failed, ret = %d\n", -ret); goto out_close; @@ -82,11 +86,13 @@ int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv, data.pre_data = pre_data; data.argv = argv; data.fd = fds[1]; + data.buf = __cant_sleep() ? um_kmalloc_atomic(PATH_MAX) : + um_kmalloc(PATH_MAX); pid = clone(helper_child, (void *) sp, CLONE_VM | SIGCHLD, &data); - if(pid < 0){ + if (pid < 0) { ret = -errno; printk("run_helper : clone failed, errno = %d\n", errno); - goto out_close; + goto out_free2; } close(fds[1]); @@ -95,10 +101,10 @@ int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv, /* Read the errno value from the child, if the exec failed, or get 0 if * the exec succeeded because the pipe fd was set as close-on-exec. */ n = os_read_file(fds[0], &ret, sizeof(ret)); - if(n == 0) + if (n == 0) { ret = pid; - else { - if(n < 0){ + } else { + if (n < 0) { printk("run_helper : read on pipe failed, ret = %d\n", -n); ret = n; @@ -107,15 +113,16 @@ int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv, CATCH_EINTR(waitpid(pid, NULL, 0)); } +out_free2: + kfree(data.buf); out_close: if (fds[1] != -1) close(fds[1]); close(fds[0]); out_free: - if(stack_out == NULL) + if ((stack_out == NULL) || (*stack_out == 0)) free_stack(stack, 0); - else *stack_out = stack; - return(ret); + return ret; } int run_helper_thread(int (*proc)(void *), void *arg, unsigned int flags, @@ -125,31 +132,32 @@ int run_helper_thread(int (*proc)(void *), void *arg, unsigned int flags, int pid, status, err; stack = alloc_stack(stack_order, __cant_sleep()); - if(stack == 0) return(-ENOMEM); + if (stack == 0) + return -ENOMEM; sp = stack + (page_size() << stack_order) - sizeof(void *); pid = clone(proc, (void *) sp, flags | SIGCHLD, arg); - if(pid < 0){ + if (pid < 0) { err = -errno; printk("run_helper_thread : clone failed, errno = %d\n", errno); return err; } - if(stack_out == NULL){ + if (stack_out == NULL) { CATCH_EINTR(pid = waitpid(pid, &status, 0)); - if(pid < 0){ + if (pid < 0) { err = -errno; printk("run_helper_thread - wait failed, errno = %d\n", errno); pid = err; } - if(!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) + if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) printk("run_helper_thread - thread returned status " "0x%x\n", status); free_stack(stack, stack_order); - } - else *stack_out = stack; - return(pid); + } else + *stack_out = stack; + return pid; } int helper_wait(int pid) @@ -157,9 +165,9 @@ int helper_wait(int pid) int ret; CATCH_EINTR(ret = waitpid(pid, NULL, WNOHANG)); - if(ret < 0){ + if (ret < 0) { ret = -errno; printk("helper_wait : waitpid failed, errno = %d\n", errno); } - return(ret); + return ret; } diff --git a/arch/um/os-Linux/irq.c b/arch/um/os-Linux/irq.c index a97206df5b5..d46b818c131 100644 --- a/arch/um/os-Linux/irq.c +++ b/arch/um/os-Linux/irq.c @@ -18,6 +18,7 @@ #include "sigio.h" #include "irq_user.h" #include "os.h" +#include "um_malloc.h" static struct pollfd *pollfds = NULL; static int pollfds_num = 0; diff --git a/arch/um/os-Linux/main.c b/arch/um/os-Linux/main.c index d1c5670787d..685feaab65d 100644 --- a/arch/um/os-Linux/main.c +++ b/arch/um/os-Linux/main.c @@ -23,6 +23,7 @@ #include "choose-mode.h" #include "uml-config.h" #include "os.h" +#include "um_malloc.h" /* Set in set_stklim, which is called from main and __wrap_malloc. * __wrap_malloc only calls it if main hasn't started. diff --git a/arch/um/os-Linux/process.c b/arch/um/os-Linux/process.c index 51f0893640a..c692a192957 100644 --- a/arch/um/os-Linux/process.c +++ b/arch/um/os-Linux/process.c @@ -7,7 +7,6 @@ #include <stdio.h> #include <errno.h> #include <signal.h> -#include <linux/unistd.h> #include <sys/mman.h> #include <sys/wait.h> #include <sys/mman.h> diff --git a/arch/um/os-Linux/sigio.c b/arch/um/os-Linux/sigio.c index f6457765b17..925a65240cf 100644 --- a/arch/um/os-Linux/sigio.c +++ b/arch/um/os-Linux/sigio.c @@ -19,6 +19,7 @@ #include "user_util.h" #include "sigio.h" #include "os.h" +#include "um_malloc.h" /* Protected by sigio_lock(), also used by sigio_cleanup, which is an * exitcall. diff --git a/arch/um/os-Linux/signal.c b/arch/um/os-Linux/signal.c index 6b81739279d..b897e8592d7 100644 --- a/arch/um/os-Linux/signal.c +++ b/arch/um/os-Linux/signal.c @@ -15,6 +15,7 @@ #include "user.h" #include "signal_kern.h" #include "sysdep/sigcontext.h" +#include "sysdep/barrier.h" #include "sigcontext.h" #include "mode.h" #include "os.h" @@ -34,8 +35,12 @@ #define SIGALRM_BIT 2 #define SIGALRM_MASK (1 << SIGALRM_BIT) -static int signals_enabled = 1; -static int pending = 0; +/* These are used by both the signal handlers and + * block/unblock_signals. I don't want modifications cached in a + * register - they must go straight to memory. + */ +static volatile int signals_enabled = 1; +static volatile int pending = 0; void sig_handler(int sig, struct sigcontext *sc) { @@ -152,6 +157,12 @@ int change_sig(int signal, int on) void block_signals(void) { signals_enabled = 0; + /* This must return with signals disabled, so this barrier + * ensures that writes are flushed out before the return. + * This might matter if gcc figures out how to inline this and + * decides to shuffle this code into the caller. + */ + mb(); } void unblock_signals(void) @@ -171,9 +182,23 @@ void unblock_signals(void) */ signals_enabled = 1; + /* Setting signals_enabled and reading pending must + * happen in this order. + */ + mb(); + save_pending = pending; - if(save_pending == 0) + if(save_pending == 0){ + /* This must return with signals enabled, so + * this barrier ensures that writes are + * flushed out before the return. This might + * matter if gcc figures out how to inline + * this (unlikely, given its size) and decides + * to shuffle this code into the caller. + */ + mb(); return; + } pending = 0; diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/process.c index cb9ab54146c..9b34fe65949 100644 --- a/arch/um/os-Linux/skas/process.c +++ b/arch/um/os-Linux/skas/process.c @@ -14,7 +14,7 @@ #include <sys/mman.h> #include <sys/user.h> #include <sys/time.h> -#include <asm/unistd.h> +#include <sys/syscall.h> #include <asm/types.h> #include "user.h" #include "sysdep/ptrace.h" diff --git a/arch/um/os-Linux/sys-i386/tls.c b/arch/um/os-Linux/sys-i386/tls.c index 6e945ab4584..256532034c6 100644 --- a/arch/um/os-Linux/sys-i386/tls.c +++ b/arch/um/os-Linux/sys-i386/tls.c @@ -1,6 +1,9 @@ #include <errno.h> #include <linux/unistd.h> + #include <sys/syscall.h> +#include <unistd.h> + #include "sysdep/tls.h" #include "user_util.h" diff --git a/arch/um/os-Linux/time.c b/arch/um/os-Linux/time.c index 38be096e750..2115b8beb54 100644 --- a/arch/um/os-Linux/time.c +++ b/arch/um/os-Linux/time.c @@ -16,6 +16,7 @@ #include "process.h" #include "kern_constants.h" #include "os.h" +#include "uml-config.h" int set_interval(int is_virtual) { @@ -30,7 +31,7 @@ int set_interval(int is_virtual) return 0; } -#ifdef CONFIG_MODE_TT +#ifdef UML_CONFIG_MODE_TT void enable_timer(void) { set_interval(1); diff --git a/arch/um/os-Linux/tls.c b/arch/um/os-Linux/tls.c index a2de2580b8a..16215b99080 100644 --- a/arch/um/os-Linux/tls.c +++ b/arch/um/os-Linux/tls.c @@ -1,4 +1,5 @@ #include <errno.h> +#include <unistd.h> #include <sys/ptrace.h> #include <sys/syscall.h> #include <asm/ldt.h> |