diff options
Diffstat (limited to 'exec/hashexec.c')
-rw-r--r-- | exec/hashexec.c | 205 |
1 files changed, 128 insertions, 77 deletions
diff --git a/exec/hashexec.c b/exec/hashexec.c index 748f8e1b..2aa3844b 100644 --- a/exec/hashexec.c +++ b/exec/hashexec.c @@ -1,5 +1,5 @@ /* GNU Hurd standard exec server, #! script execution support. - Copyright (C) 1995, 1996 Free Software Foundation, Inc. + Copyright (C) 1995,96,97,98,99,2000,02 Free Software Foundation, Inc. Written by Roland McGrath. This file is part of the GNU Hurd. @@ -18,11 +18,11 @@ You should have received a copy of the GNU General Public License along with the GNU Hurd; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - #include "priv.h" #include <hurd/sigpreempt.h> #include <unistd.h> #include <envz.h> +#include <sys/param.h> /* This is called to check E for a #! interpreter specification. E has already been prepared (successfully) and checked (unsuccessfully). If @@ -43,13 +43,10 @@ check_hashbang (struct execdata *e, mach_port_t *deallocnames, u_int ndeallocnames, mach_port_t *destroynames, u_int ndestroynames) { - char *ibuf = NULL; - size_t ibufsiz = 0; char *p; char *interp, *arg; /* Interpreter file name, and first argument */ - size_t interp_len, len; + size_t interp_len, arg_len; file_t interp_file; /* Port open on the interpreter file. */ - FILE *f = &e->stream; char *new_argv; size_t new_argvlen; mach_port_t *new_dtable = NULL; @@ -57,12 +54,18 @@ check_hashbang (struct execdata *e, file_t user_fd (int fd) { - if (fd < 0 || fd >= dtablesize || dtable[fd] == MACH_PORT_NULL) + if (fd >= 0 && fd < dtablesize) { - errno = EBADF; - return MACH_PORT_NULL; + const file_t dport = dtable[fd]; + if (dport != MACH_PORT_NULL) + { + mach_port_mod_refs (mach_task_self (), dport, + MACH_PORT_RIGHT_SEND, +1); + return dport; + } } - return dtable[fd]; + errno = EBADF; + return MACH_PORT_NULL; } file_t user_crdir, user_cwdir; error_t user_port (int which, error_t (*operate) (mach_port_t)) @@ -71,6 +74,9 @@ check_hashbang (struct execdata *e, { error_t err; mach_port_t ref; + + /* MAKE_SEND is safe here because we destroy REF ourselves. */ + error_t uauth (auth_t auth) { return auth_user_authenticate (auth, @@ -121,50 +127,81 @@ check_hashbang (struct execdata *e, name, flags, 0, result); } - rewind (f); + const char *page; + char interp_buf[vm_page_size - 2 + 1]; - /* Check for our ``magic number''--"#!". */ + e->error = 0; + page = map (e, 0, 2); - errno = 0; - if (getc (f) != '#' || getc (f) != '!') + if (!page) { - /* No `#!' here. If there was a read error (not including EOF), - return that error indication. Otherwise return ENOEXEC to - say it's not a file we know how to execute. */ - e->error = ferror (f) ? errno : ENOEXEC; + if (!e->error) + e->error = ENOEXEC; return; } - /* Read the rest of the first line of the file. */ - - interp_len = getline (&ibuf, &ibufsiz, f); - if (ferror (f)) + /* Check for our ``magic number''--"#!". */ + if (page[0] != '#' || page[1] != '!') { - e->error = errno ?: EIO; + /* These are not the droids we're looking for. */ + e->error = ENOEXEC; return; } - if (ibuf[interp_len - 1] == '\n') - ibuf[--interp_len] = '\0'; - /* Find the name of the interpreter. */ - p = ibuf + strspn (ibuf, " \t"); - interp = strsep (&p, " \t"); + /* Read the rest of the first line of the file. + We in fact impose an arbitrary limit of about a page on this. */ - if (p) - /* Skip remaining blanks, and the rest of the line is the argument. */ + p = memccpy (interp_buf, page + 2, '\n', + MIN (map_fsize (e) - 2, sizeof interp_buf)); + if (p == NULL) { - p += strspn (p, " \t"); - arg = p; - len = interp_len - (arg - ibuf); + /* The first line went on for more than sizeof INTERP_BUF! */ + interp_len = sizeof interp_buf; + interp_buf[interp_len - 1] = '\0'; } else - /* There is no argument. */ - len = 0; + { + interp_len = p - interp_buf; /* Includes null terminator. */ + *--p = '\0'; /* Kill the newline. */ + } + + /* We are now done reading the script file. */ + finish (e, 0); + + + /* Find the name of the interpreter. */ + interp = interp_buf + strspn (interp_buf, " \t"); + p = strpbrk (interp, " \t"); + + if (p) + { + /* Terminate the interpreter name. */ + *p++ = '\0'; - if (len == 0) - arg = NULL; + /* Skip remaining blanks, and the rest of the line is the argument. */ + + arg = p + strspn (p, " \t"); + arg_len = interp_len - 1 - (arg - interp_buf); /* without null here */ + interp_len = p - interp; /* This one includes the null. */ + + if (arg_len == 0) + arg = NULL; + else + { + /* Trim trailing blanks after the argument. */ + size_t i = arg_len - 1; + while (arg[i] == ' ' || arg[i] == '\t') + arg[i--] = '\0'; + arg_len = i + 2; /* Include the terminating null. */ + } + } else - ++len; /* Include the terminating null. */ + { + /* There is no argument. */ + arg = NULL; + arg_len = 0; + interp_len -= interp - interp_buf; /* Account for blanks skipped. */ + } user_crdir = user_cwdir = MACH_PORT_NULL; @@ -179,7 +216,7 @@ check_hashbang (struct execdata *e, jmp_buf args_faulted; void fault_handler (int signo) { longjmp (args_faulted, 1); } - error_t setup_args (struct hurd_signal_preempter *preempter) + error_t setup_args (struct hurd_signal_preemptor *preemptor) { size_t namelen; char * volatile file_name = NULL; @@ -197,14 +234,13 @@ check_hashbang (struct execdata *e, char *name; int free_name = 0; /* True if we should free NAME. */ file_t name_file; - mach_port_t fileid; - dev_t filedev; + mach_port_t fileid, filefsid; ino_t fileno; /* Search $PATH for NAME, opening a port NAME_FILE on it. This is encapsulated in a function so we can catch faults reading the user's environment. */ - error_t search_path (struct hurd_signal_preempter *preempter) + error_t search_path (struct hurd_signal_preemptor *preemptor) { error_t err; char *path = envz_get (envp, envplen, "PATH"), *pfxed_name; @@ -228,9 +264,10 @@ check_hashbang (struct execdata *e, return err; } - error = io_identity (file, &fileid, &filedev, &fileno); + error = io_identity (file, &fileid, &filefsid, &fileno); if (error) goto out; + mach_port_deallocate (mach_task_self (), filefsid); if (memchr (argv, '\0', argvlen) == NULL) { @@ -251,11 +288,15 @@ check_hashbang (struct execdata *e, if (!error && name_file != MACH_PORT_NULL) { - mach_port_t id; - dev_t dev; + mach_port_t id, fsid; ino_t ino; - error = io_identity (name_file, &id, &dev, &ino); - __mach_port_deallocate (__mach_task_self (), id); + error = io_identity (name_file, &id, &fsid, &ino); + mach_port_deallocate (mach_task_self (), name_file); + if (!error) + { + mach_port_deallocate (mach_task_self (), fsid); + mach_port_deallocate (mach_task_self (), id); + } if (!error && id == fileid) { file_name = name; @@ -263,10 +304,9 @@ check_hashbang (struct execdata *e, } else if (free_name) free (name); - __mach_port_deallocate (__mach_task_self (), name_file); } - __mach_port_deallocate (__mach_task_self (), fileid); + mach_port_deallocate (mach_task_self (), fileid); } if (file_name == NULL) @@ -298,33 +338,50 @@ check_hashbang (struct execdata *e, /* Prepare the arguments to pass to the interpreter from the original arguments and the name of the script file. The args will look - like `ARGV[0] {ARG} FILE_NAME ARGV[1..n]' (ARG might have been + like `INTERP {ARG} FILE_NAME ARGV[1..n]' (ARG might have been omitted). */ namelen = strlen (file_name) + 1; - new_argvlen = argvlen + len + namelen; - e->error = vm_allocate (mach_task_self (), - (vm_address_t *) &new_argv, - new_argvlen, 1); - if (e->error) - return e->error; + new_argvlen + = (argvlen - strlen (argv) - 1) /* existing args - old argv[0] */ + + interp_len + arg_len + namelen; /* New args */ + + new_argv = mmap (0, new_argvlen, PROT_READ|PROT_WRITE, + MAP_ANON, 0, 0); + if (new_argv == (caddr_t) -1) + { + e->error = errno; + return e->error; + } + else + e->error = 0; if (! setjmp (args_faulted)) { char *other_args; - other_args = memccpy (new_argv, argv, '\0', argvlen); - p = &new_argv[other_args ? other_args - new_argv : argvlen]; + + p = new_argv; + + /* INTERP */ + memcpy (p, interp, interp_len); + p += interp_len; + + /* Maybe ARG */ if (arg) { - memcpy (p, arg, len); - p += len; + memcpy (p, arg, arg_len); + p += arg_len; } + + /* FILE_NAME */ memcpy (p, file_name, namelen); p += namelen; - if (other_args) - memcpy (p, other_args - new_argv + argv, - argvlen - (other_args - new_argv)); + + /* Maybe remaining args */ + other_args = argv + strlen (argv) + 1; + if (other_args - argv < argvlen) + memcpy (p, other_args, argvlen - (other_args - argv)); } else { @@ -332,7 +389,7 @@ check_hashbang (struct execdata *e, char *n = stpncpy (new_argv, "**fault in exec server reading argv[0]**", argvlen); - memcpy (memcpy (n, arg, len) + len, file_name, namelen); + memcpy (memcpy (n, arg, arg_len) + arg_len, file_name, namelen); } if (free_file_name) @@ -347,10 +404,6 @@ check_hashbang (struct execdata *e, &setup_args, &fault_handler); } - /* We are now done reading the script file. */ - finish (e, 0); - free (ibuf); - rwlock_reader_unlock (&std_lock); if (user_crdir != MACH_PORT_NULL) @@ -384,21 +437,19 @@ check_hashbang (struct execdata *e, task_resume (oldtask); /* Our caller suspended it. */ mach_port_deallocate (mach_task_self (), oldtask); if (! argv_copy) - vm_deallocate (mach_task_self (), (vm_address_t) argv, argvlen); + munmap (argv, argvlen); if (! envp_copy) - vm_deallocate (mach_task_self (), (vm_address_t) envp, envplen); + munmap (envp, envplen); for (i = 0; i < dtablesize; ++i) - mach_port_deallocate (mach_task_self (), dtable[i]); + if (MACH_PORT_VALID (dtable[i])) + mach_port_deallocate (mach_task_self (), dtable[i]); if (! dtable_copy) - vm_deallocate (mach_task_self (), (vm_address_t) dtable, - dtablesize * sizeof *dtable); + munmap (dtable, dtablesize * sizeof *dtable); for (i = 0; i < nports; ++i) mach_port_deallocate (mach_task_self (), portarray[i]); if (! portarray_copy) - vm_deallocate (mach_task_self (), (vm_address_t) portarray, - nports * sizeof *portarray); + munmap (portarray, nports * sizeof *portarray); if (! intarray_copy) - vm_deallocate (mach_task_self (), (vm_address_t) intarray, - nints * sizeof *intarray); + munmap (intarray, nints * sizeof *intarray); } } |