imgact_gzip.c revision 48079
13332Sphk/* 23332Sphk * ---------------------------------------------------------------------------- 33332Sphk * "THE BEER-WARE LICENSE" (Revision 42): 43784Sphk * <phk@login.dkuug.dk> wrote this file. As long as you retain this notice you 53332Sphk * can do whatever you want with this stuff. If we meet some day, and you think 63332Sphk * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 73332Sphk * ---------------------------------------------------------------------------- 83332Sphk * 948079Shoek * $Id: imgact_gzip.c,v 1.37 1999/05/09 16:04:09 peter Exp $ 103332Sphk * 113332Sphk * This module handles execution of a.out files which have been run through 123784Sphk * "gzip". This saves diskspace, but wastes cpu-cycles and VM. 133332Sphk * 143332Sphk * TODO: 153332Sphk * text-segments should be made R/O after being filled 163332Sphk * is the vm-stuff safe ? 173332Sphk * should handle the entire header of gzip'ed stuff. 183332Sphk * inflate isn't quite reentrant yet... 193332Sphk * error-handling is a mess... 203332Sphk * so is the rest... 213417Scsgr * tidy up unnecesary includes 223332Sphk */ 233332Sphk 243784Sphk#include <sys/param.h> 253332Sphk#include <sys/exec.h> 263332Sphk#include <sys/imgact.h> 273332Sphk#include <sys/imgact_aout.h> 283332Sphk#include <sys/kernel.h> 293507Scsgr#include <sys/mman.h> 3015494Sbde#include <sys/proc.h> 313507Scsgr#include <sys/resourcevar.h> 323332Sphk#include <sys/sysent.h> 333507Scsgr#include <sys/systm.h> 3415494Sbde#include <sys/vnode.h> 353784Sphk#include <sys/inflate.h> 363332Sphk 373332Sphk#include <vm/vm.h> 3812662Sdg#include <vm/vm_param.h> 3912662Sdg#include <vm/vm_prot.h> 4022521Sdyson#include <sys/lock.h> 4112662Sdg#include <vm/pmap.h> 4212662Sdg#include <vm/vm_map.h> 433332Sphk#include <vm/vm_kern.h> 4412662Sdg#include <vm/vm_extern.h> 453332Sphk 463784Sphkstruct imgact_gzip { 473784Sphk struct image_params *ip; 483784Sphk struct exec a_out; 493784Sphk int error; 5048079Shoek int gotheader; 513784Sphk int where; 523784Sphk u_char *inbuf; 533784Sphk u_long offset; 543784Sphk u_long output; 553784Sphk u_long len; 563784Sphk int idx; 573784Sphk u_long virtual_offset, file_offset, file_end, bss_size; 583784Sphk}; 593784Sphk 6012568Sbdestatic int exec_gzip_imgact __P((struct image_params *imgp)); 613784Sphkstatic int NextByte __P((void *vp)); 623784Sphkstatic int do_aout_hdr __P((struct imgact_gzip *)); 633784Sphkstatic int Flush __P((void *vp, u_char *, u_long siz)); 643784Sphk 6512568Sbdestatic int 6612130Sdgexec_gzip_imgact(imgp) 6712130Sdg struct image_params *imgp; 683332Sphk{ 693784Sphk int error, error2 = 0; 7017974Sbde const u_char *p = (const u_char *) imgp->image_header; 713784Sphk struct imgact_gzip igz; 723784Sphk struct inflate infl; 7317386Sphk struct vmspace *vmspace; 743332Sphk 753348Sphk /* If these four are not OK, it isn't a gzip file */ 763784Sphk if (p[0] != 0x1f) 773784Sphk return -1; /* 0 Simply magic */ 783784Sphk if (p[1] != 0x8b) 793784Sphk return -1; /* 1 Simply magic */ 803784Sphk if (p[2] != 0x08) 813784Sphk return -1; /* 2 Compression method */ 823784Sphk if (p[9] != 0x03) 833784Sphk return -1; /* 9 OS compressed on */ 843332Sphk 853784Sphk /* 863784Sphk * If this one contains anything but a comment or a filename marker, 873784Sphk * we don't want to chew on it 883348Sphk */ 893784Sphk if (p[3] & ~(0x18)) 903784Sphk return ENOEXEC; /* 3 Flags */ 913332Sphk 923348Sphk /* These are of no use to us */ 933784Sphk /* 4-7 Timestamp */ 943784Sphk /* 8 Extra flags */ 953348Sphk 963784Sphk bzero(&igz, sizeof igz); 973784Sphk bzero(&infl, sizeof infl); 983784Sphk infl.gz_private = (void *) &igz; 993784Sphk infl.gz_input = NextByte; 1003784Sphk infl.gz_output = Flush; 1013332Sphk 10212130Sdg igz.ip = imgp; 1033784Sphk igz.idx = 10; 1043353Sphk 1053784Sphk if (p[3] & 0x08) { /* skip a filename */ 1063784Sphk while (p[igz.idx++]) 1073784Sphk if (igz.idx >= PAGE_SIZE) 1083784Sphk return ENOEXEC; 1093348Sphk } 1103784Sphk if (p[3] & 0x10) { /* skip a comment */ 1113784Sphk while (p[igz.idx++]) 1123784Sphk if (igz.idx >= PAGE_SIZE) 1133784Sphk return ENOEXEC; 1143348Sphk } 11517386Sphk igz.len = imgp->attr->va_size; 1163332Sphk 1173784Sphk error = inflate(&infl); 1183348Sphk 11948079Shoek /* 12048079Shoek * The unzipped file may not even have been long enough to contain 12148079Shoek * a header giving Flush() a chance to return error. Check for this. 12248079Shoek */ 12348079Shoek if ( !igz.gotheader ) 12448079Shoek return ENOEXEC; 12548079Shoek 12617386Sphk if ( !error ) { 12717386Sphk vmspace = imgp->proc->p_vmspace; 12817386Sphk error = vm_map_protect(&vmspace->vm_map, 12917386Sphk (vm_offset_t) vmspace->vm_taddr, 13017386Sphk (vm_offset_t) (vmspace->vm_taddr + 13117386Sphk (vmspace->vm_tsize << PAGE_SHIFT)) , 13217386Sphk VM_PROT_READ|VM_PROT_EXECUTE,0); 13317386Sphk } 13417386Sphk 1353784Sphk if (igz.inbuf) { 1363784Sphk error2 = 1376579Sdg vm_map_remove(kernel_map, (vm_offset_t) igz.inbuf, 1386579Sdg (vm_offset_t) igz.inbuf + PAGE_SIZE); 1393332Sphk } 1403784Sphk if (igz.error || error || error2) { 1413784Sphk printf("Output=%lu ", igz.output); 1423784Sphk printf("Inflate_error=%d igz.error=%d error2=%d where=%d\n", 1433784Sphk error, igz.error, error2, igz.where); 1443348Sphk } 1453784Sphk if (igz.error) 1463784Sphk return igz.error; 1473784Sphk if (error) 1483784Sphk return ENOEXEC; 1498876Srgrimes if (error2) 1503784Sphk return error2; 1513784Sphk return 0; 1523332Sphk} 1533332Sphk 1543784Sphkstatic int 1553784Sphkdo_aout_hdr(struct imgact_gzip * gz) 1563332Sphk{ 1573784Sphk int error; 15824848Sdyson struct vmspace *vmspace; 15914703Sbde vm_offset_t vmaddr; 1603332Sphk 1613784Sphk /* 1623784Sphk * Set file/virtual offset based on a.out variant. We do two cases: 1633784Sphk * host byte order and network byte order (for NetBSD compatibility) 1643784Sphk */ 1653784Sphk switch ((int) (gz->a_out.a_magic & 0xffff)) { 1663332Sphk case ZMAGIC: 1673784Sphk gz->virtual_offset = 0; 1683784Sphk if (gz->a_out.a_text) { 16915538Sphk gz->file_offset = PAGE_SIZE; 1703784Sphk } else { 1713784Sphk /* Bill's "screwball mode" */ 1723784Sphk gz->file_offset = 0; 1733784Sphk } 1743784Sphk break; 1753332Sphk case QMAGIC: 17615538Sphk gz->virtual_offset = PAGE_SIZE; 1773784Sphk gz->file_offset = 0; 1783784Sphk break; 1793332Sphk default: 1803784Sphk /* NetBSD compatibility */ 1813784Sphk switch ((int) (ntohl(gz->a_out.a_magic) & 0xffff)) { 1823784Sphk case ZMAGIC: 1833784Sphk case QMAGIC: 18415538Sphk gz->virtual_offset = PAGE_SIZE; 1853784Sphk gz->file_offset = 0; 1863784Sphk break; 1873784Sphk default: 1883784Sphk gz->where = __LINE__; 1893784Sphk return (-1); 1903784Sphk } 1913332Sphk } 1923332Sphk 19315538Sphk gz->bss_size = roundup(gz->a_out.a_bss, PAGE_SIZE); 1943332Sphk 1953784Sphk /* 1963784Sphk * Check various fields in header for validity/bounds. 1973784Sphk */ 1983784Sphk if ( /* entry point must lay with text region */ 1993784Sphk gz->a_out.a_entry < gz->virtual_offset || 2003784Sphk gz->a_out.a_entry >= gz->virtual_offset + gz->a_out.a_text || 2013332Sphk 2023332Sphk /* text and data size must each be page rounded */ 20315538Sphk gz->a_out.a_text & PAGE_MASK || gz->a_out.a_data & PAGE_MASK) { 2043784Sphk gz->where = __LINE__; 2053784Sphk return (-1); 2063784Sphk } 2073784Sphk /* 2083784Sphk * text/data/bss must not exceed limits 2093784Sphk */ 2103784Sphk if ( /* text can't exceed maximum text size */ 2113784Sphk gz->a_out.a_text > MAXTSIZ || 2123332Sphk 2133332Sphk /* data + bss can't exceed rlimit */ 2143784Sphk gz->a_out.a_data + gz->bss_size > 2153332Sphk gz->ip->proc->p_rlimit[RLIMIT_DATA].rlim_cur) { 2163784Sphk gz->where = __LINE__; 2173784Sphk return (ENOMEM); 2183784Sphk } 2193784Sphk /* Find out how far we should go */ 2203784Sphk gz->file_end = gz->file_offset + gz->a_out.a_text + gz->a_out.a_data; 2213332Sphk 2223784Sphk /* copy in arguments and/or environment from old process */ 2233784Sphk error = exec_extract_strings(gz->ip); 2243784Sphk if (error) { 2253784Sphk gz->where = __LINE__; 2263784Sphk return (error); 2273784Sphk } 2283784Sphk /* 2293784Sphk * Destroy old process VM and create a new one (with a new stack) 2303784Sphk */ 2313784Sphk exec_new_vmspace(gz->ip); 2323348Sphk 23324848Sdyson vmspace = gz->ip->proc->p_vmspace; 23424848Sdyson 2353784Sphk vmaddr = gz->virtual_offset; 2363332Sphk 2373784Sphk error = vm_mmap(&vmspace->vm_map, 2383784Sphk &vmaddr, 23917386Sphk gz->a_out.a_text + gz->a_out.a_data, 24017386Sphk VM_PROT_ALL, VM_PROT_ALL, MAP_ANON | MAP_FIXED, 2413784Sphk 0, 2423784Sphk 0); 2433332Sphk 2443784Sphk if (error) { 2453784Sphk gz->where = __LINE__; 2463784Sphk return (error); 2473784Sphk } 24817386Sphk 2496579Sdg if (gz->bss_size != 0) { 2506579Sdg /* 25114087Sphk * Allocate demand-zeroed area for uninitialized data. 25214087Sphk * "bss" = 'block started by symbol' - named after the 25314087Sphk * IBM 7090 instruction of the same name. 2546579Sdg */ 25514087Sphk vmaddr = gz->virtual_offset + gz->a_out.a_text + 25614087Sphk gz->a_out.a_data; 25717386Sphk error = vm_map_find(&vmspace->vm_map, 25817386Sphk NULL, 25917386Sphk 0, 26017386Sphk &vmaddr, 26117386Sphk gz->bss_size, 26217386Sphk FALSE, VM_PROT_ALL, VM_PROT_ALL, 0); 2636579Sdg if (error) { 2646579Sdg gz->where = __LINE__; 2656579Sdg return (error); 2666579Sdg } 2673784Sphk } 2683784Sphk /* Fill in process VM information */ 2693784Sphk vmspace->vm_tsize = gz->a_out.a_text >> PAGE_SHIFT; 2703784Sphk vmspace->vm_dsize = (gz->a_out.a_data + gz->bss_size) >> PAGE_SHIFT; 27137656Sbde vmspace->vm_taddr = (caddr_t) (uintptr_t) gz->virtual_offset; 27237656Sbde vmspace->vm_daddr = (caddr_t) (uintptr_t) 27337656Sbde (gz->virtual_offset + gz->a_out.a_text); 2743332Sphk 2753784Sphk /* Fill in image_params */ 2763784Sphk gz->ip->interpreted = 0; 2773784Sphk gz->ip->entry_addr = gz->a_out.a_entry; 2783332Sphk 2793784Sphk gz->ip->proc->p_sysent = &aout_sysvec; 2803332Sphk 2813784Sphk return 0; 2823784Sphk} 2833332Sphk 2843784Sphkstatic int 2853784SphkNextByte(void *vp) 2863784Sphk{ 2873784Sphk int error; 2883784Sphk struct imgact_gzip *igz = (struct imgact_gzip *) vp; 2893332Sphk 2903784Sphk if (igz->idx >= igz->len) { 2913784Sphk igz->where = __LINE__; 2923784Sphk return GZ_EOF; 2933784Sphk } 2943784Sphk if (igz->inbuf && igz->idx < (igz->offset + PAGE_SIZE)) { 2953784Sphk return igz->inbuf[(igz->idx++) - igz->offset]; 2963784Sphk } 2973784Sphk if (igz->inbuf) { 2986579Sdg error = vm_map_remove(kernel_map, (vm_offset_t) igz->inbuf, 2996579Sdg (vm_offset_t) igz->inbuf + PAGE_SIZE); 3003784Sphk if (error) { 3013784Sphk igz->where = __LINE__; 3023784Sphk igz->error = error; 3033784Sphk return GZ_EOF; 3043784Sphk } 3053784Sphk } 3066342Sphk igz->offset = igz->idx & ~PAGE_MASK; 3073332Sphk 3083784Sphk error = vm_mmap(kernel_map, /* map */ 3093784Sphk (vm_offset_t *) & igz->inbuf, /* address */ 3103784Sphk PAGE_SIZE, /* size */ 3113784Sphk VM_PROT_READ, /* protection */ 3123784Sphk VM_PROT_READ, /* max protection */ 3133784Sphk 0, /* flags */ 31412130Sdg (caddr_t) igz->ip->vp, /* vnode */ 3153784Sphk igz->offset); /* offset */ 3163784Sphk if (error) { 3173784Sphk igz->where = __LINE__; 3183784Sphk igz->error = error; 3193784Sphk return GZ_EOF; 3203784Sphk } 3213784Sphk return igz->inbuf[(igz->idx++) - igz->offset]; 3223784Sphk} 3233332Sphk 3243784Sphkstatic int 3253784SphkFlush(void *vp, u_char * ptr, u_long siz) 3263784Sphk{ 3273784Sphk struct imgact_gzip *gz = (struct imgact_gzip *) vp; 3283784Sphk u_char *p = ptr, *q; 3293784Sphk int i; 3303348Sphk 3313784Sphk /* First, find a a.out-header */ 3323784Sphk if (gz->output < sizeof gz->a_out) { 3333784Sphk q = (u_char *) & gz->a_out; 3343784Sphk i = min(siz, sizeof gz->a_out - gz->output); 3353784Sphk bcopy(p, q + gz->output, i); 3363784Sphk gz->output += i; 3373784Sphk p += i; 3383784Sphk siz -= i; 3393784Sphk if (gz->output == sizeof gz->a_out) { 34048079Shoek gz->gotheader = 1; 3413784Sphk i = do_aout_hdr(gz); 3423784Sphk if (i == -1) { 3433785Sphk if (!gz->where) 3443785Sphk gz->where = __LINE__; 3453784Sphk gz->error = ENOEXEC; 3463784Sphk return ENOEXEC; 3473784Sphk } else if (i) { 3483784Sphk gz->where = __LINE__; 3493784Sphk gz->error = i; 3503784Sphk return ENOEXEC; 3513784Sphk } 35231718Sjdp if (gz->file_offset == 0) { 35337656Sbde q = (u_char *) (uintptr_t) gz->virtual_offset; 35437015Sbde copyout(&gz->a_out, q, sizeof gz->a_out); 3553784Sphk } 3563784Sphk } 3573784Sphk } 3583784Sphk /* Skip over zero-padded first PAGE if needed */ 35937015Sbde if (gz->output < gz->file_offset && 36037015Sbde gz->output + siz > gz->file_offset) { 3613784Sphk i = min(siz, gz->file_offset - gz->output); 3623784Sphk gz->output += i; 3633784Sphk p += i; 3643784Sphk siz -= i; 3653784Sphk } 3663784Sphk if (gz->output >= gz->file_offset && gz->output < gz->file_end) { 3673784Sphk i = min(siz, gz->file_end - gz->output); 36837656Sbde q = (u_char *) (uintptr_t) 36937656Sbde (gz->virtual_offset + gz->output - gz->file_offset); 37037015Sbde copyout(p, q, i); 3713784Sphk gz->output += i; 3723784Sphk p += i; 3733784Sphk siz -= i; 3743784Sphk } 3753784Sphk gz->output += siz; 3763784Sphk return 0; 3773332Sphk} 3783332Sphk 3793784Sphk 3803332Sphk/* 3813332Sphk * Tell kern_execve.c about it, with a little help from the linker. 3823332Sphk */ 38343402Sdillonstatic struct execsw gzip_execsw = {exec_gzip_imgact, "gzip"}; 38440435SpeterEXEC_SET(execgzip, gzip_execsw); 385