Initial import, 0.1 + pk 0.2.4-B1
[unix-history] / sys / kern / kern_execve
/*
* Copyright (c) 1992 William Jolitz. All rights reserved.
* Written by William Jolitz 1/92
*
* Redistribution and use in source and binary forms are freely permitted
* provided that the above copyright notice and attribution and date of work
* and this paragraph are duplicated in all such forms.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* This procedure implements a minimal program execution facility for
* 386BSD. It interfaces to the BSD kernel as the execve system call.
* Significant limitations and lack of compatiblity with POSIX are
* present with this version, to make its basic operation more clear.
*
*/
#include "param.h"
#include "systm.h"
#include "proc.h"
#include "mount.h"
#include "namei.h"
#include "vnode.h"
#include "file.h"
#include "exec.h"
#include "stat.h"
#include "wait.h"
#include "signalvar.h"
#include "mman.h"
#include "vm/vm.h"
#include "vm/vm_param.h"
#include "vm/vm_map.h"
#include "vm/vm_kern.h"
#include "machine/reg.h"
static char rcsid[] = "$Header: /usr/bill/working/sys/kern/RCS/kern_execve.c,v 1.3 92/01/21 21:29:13 william Exp $";
/*
* Bill's first-cut execve() system call. Puts hair on your chest.
*/
/* ARGSUSED */
execve(p, uap, retval)
struct proc *p;
register struct args {
char *fname;
char **argp;
char **envp;
} *uap;
int *retval;
{
register struct nameidata *ndp;
int rv, amt;
struct nameidata nd;
struct exec hdr;
char **kargbuf, **kargbufp, *kstringbuf, *kstringbufp;
char **org, **vectp, *ep;
u_int needsenv, limitonargs, stringlen;
int addr, size;
int argc;
char *cp;
struct stat statb;
struct vmspace *vs;
int tsize, dsize, bsize, cnt, foff;
/*
* Step 1. Lookup filename to see if we have something to execute.
*/
ndp = &nd;
ndp->ni_nameiop = LOOKUP | LOCKLEAF | FOLLOW;
ndp->ni_segflg = UIO_USERSPACE;
ndp->ni_dirp = uap->fname;
/* is it there? */
if (rv = namei(ndp, p))
return (rv);
/* is it a regular file? */
if (ndp->ni_vp->v_type != VREG) {
vput(ndp->ni_vp);
return(ENOEXEC);
}
/* is it executable? */
rv = VOP_ACCESS(ndp->ni_vp, VEXEC, p->p_ucred, p);
if (rv)
goto exec_fail;
rv = vn_stat(ndp->ni_vp, &statb, p);
if (rv)
goto exec_fail;
/*
* Step 2. Does the file contain a format we can
* understand and execute
*/
rv = vn_rdwr(UIO_READ, ndp->ni_vp, (caddr_t)&hdr, sizeof(hdr),
0, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &amt, p);
/* big enough to hold a header? */
if (rv)
goto exec_fail;
/* that we recognize? */
rv = ENOEXEC;
if (hdr.a_magic != ZMAGIC)
goto exec_fail;
/* sanity check "ain't not such thing as a sanity clause" -groucho */
if (/*hdr.a_text == 0 || */ hdr.a_text > MAXTSIZ
|| hdr.a_text % NBPG || hdr.a_text > statb.st_size)
goto exec_fail;
if (hdr.a_data == 0 || hdr.a_data > DFLDSIZ
|| hdr.a_data > statb.st_size
|| hdr.a_data + hdr.a_text > statb.st_size)
goto exec_fail;
if (hdr.a_bss > MAXDSIZ)
goto exec_fail;
if (hdr.a_text + hdr.a_data + hdr.a_bss > MAXTSIZ + MAXDSIZ)
goto exec_fail;
/*
* Step 3. File and header are valid. Now, dig out the strings
* out of the old process image.
*/
/* assumption: most execve's have less than 256 arguments, with a
* total of string storage space not exceeding 2K. It is more
* frequent that when this fails, string space falls short first
* (e.g. as when a large termcap environment variable is present).
* It is infrequent when more than 256 arguments are used that take
* up less than 2K of space (e.g. args average more than 8 chars).
*
* What we give up in this implementation is a dense encoding of
* the data structure in the receiving program's address space.
* This means that there is plenty of wasted space (up to 6KB)
* as the price we pay for a fast, single pass algorithm.
*
* Our alternative would be to accumulate strings and pointers
* in the first pass, then, knowing the sizes and number of the
* strings, pack them neatly and tightly togeither in the second
* pass. This means two copies of the strings, and string copying
* is much of the cost of exec.
*/
/* allocate string buffer and arg buffer */
org = kargbuf = (char **) kmem_alloc_wait(exec_map,
(NCARGS + PAGE_SIZE)/PAGE_SIZE);
kstringbuf = kstringbufp = ((char *)kargbuf) + NBPG/2;
kargbuf += NBPG/(4*sizeof(int));
kargbufp = kargbuf;
/* first, do args */
needsenv = 1;
vectp = uap->argp;
do_env_as_well:
cnt = 0;
/* for each envp, copy in string */
limitonargs = NCARGS;
if(vectp == 0) goto dont_bother;
do {
/* did we outgrow initial argbuf, if so, die */
if (kargbufp == (char **)kstringbuf)
goto exec_fail;
/* get an string pointer */
ep = (char *)fuword(vectp++);
if (ep == (char *)-1) {
rv = EFAULT;
goto exec_fail;
}
/* if not null pointer, copy in string */
if (ep) {
if (rv = copyinstr(ep, kstringbufp, limitonargs,
&stringlen)) goto exec_fail;
/* assume that strings usually all fit in last page */
*kargbufp = (char *)(kstringbufp - kstringbuf
+ USRSTACK - NBPG + NBPG/2);
kargbufp++;
cnt++;
kstringbufp += stringlen;
limitonargs -= stringlen + sizeof(long);
} else {
*kargbufp++ = 0;
limitonargs -= sizeof(long);
break;
}
} while (limitonargs > 0);
dont_bother:
if (limitonargs <= 0) {
rv = E2BIG;
goto exec_fail;
}
if (needsenv) {
argc = cnt;
vectp = uap->envp;
needsenv = 0;
goto do_env_as_well;
}
kargbuf[-1] = (char *)argc;
/*
* Step 4. Build the new processes image.
*/
/* At this point, we are committed -- destroy old executable */
vs = p->p_vmspace;
addr = 0;
size = USRSTACK - addr;
/* blow away all address space */
rv = vm_deallocate(&vs->vm_map, addr, size, FALSE);
if (rv)
goto exec_abort;
/* build a new address space */
addr = 0;
if (hdr.a_text == 0) {
/* screwball mode */
foff = tsize = 0;
hdr.a_data += hdr.a_text;
} else {
tsize = roundup(hdr.a_text, NBPG);
foff = NBPG;
}
dsize = roundup(hdr.a_data, NBPG);
bsize = roundup(hdr.a_bss + dsize, NBPG);
bsize -= dsize;
/* map text & data*/
rv = vm_mmap(&vs->vm_map, &addr, tsize+dsize, VM_PROT_ALL,
MAP_FILE|MAP_COPY|MAP_FIXED, (caddr_t)ndp->ni_vp, foff);
if (rv)
goto exec_abort;
/* r/w data, ro text */
if (tsize) {
addr = 0;
rv = vm_protect(&vs->vm_map, addr, tsize, FALSE, VM_PROT_READ|VM_PROT_EXECUTE);
if (rv)
goto exec_abort;
}
/* create anonymous memory region for bss */
addr = dsize + tsize;
rv = vm_allocate(&vs->vm_map, &addr, bsize, FALSE);
if (rv)
goto exec_abort;
/* create anonymous memory region for stack */
addr = USRSTACK - MAXSSIZ;
rv = vm_allocate(&vs->vm_map, &addr, MAXSSIZ, FALSE);
if (rv)
goto exec_abort;
/*
* Step 5. Prepare process for execution.
*/
/* touchup process information */
vs->vm_tsize = tsize/NBPG; /* text size (pages) XXX */
vs->vm_dsize = (dsize+bsize)/NBPG; /* data size (pages) XXX */
vs->vm_ssize = MAXSSIZ/NBPG; /* stack size (pages) */
vs->vm_taddr = 0; /* user virtual address of text XXX */
vs->vm_daddr = (caddr_t)tsize; /* user virtual address of data XXX */
/* user VA at max stack growth */
vs->vm_maxsaddr = (caddr_t)(USRSTACK - MAXSSIZ);
/* everything fits in a single page, no fixups, no more work */
/* (groan) due to bug in vm_map_copy, can't remap. copy for now. */
rv = copyout((caddr_t)org, (caddr_t)USRSTACK - NBPG, NBPG);
if(rv)
goto exec_abort;
/* close files on exec, fixup signals */
fdcloseexec(p);
execsigs(p);
p->p_regs[SP] = USRSTACK - NBPG + NBPG/4 - 4;
vs->vm_ssize = 1; /* stack size (pages) */
setregs(p, hdr.a_entry);
kmem_free_wakeup(exec_map, org, (NCARGS + PAGE_SIZE)/PAGE_SIZE);
vput(ndp->ni_vp);
return (0);
exec_fail:
vput(ndp->ni_vp);
return(rv);
exec_abort:
/* untested and probably bogus */
kmem_free_wakeup(exec_map, org, (NCARGS + PAGE_SIZE)/PAGE_SIZE);
vput(ndp->ni_vp);
exit(p, W_EXITCODE(0, SIGABRT));
return(0);
}