imgact_gzip.c revision 6579
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 * 96579Sdg * $Id: imgact_gzip.c,v 1.11 1995/02/13 07:40:33 phk 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> 303507Scsgr#include <sys/resourcevar.h> 313332Sphk#include <sys/sysent.h> 323507Scsgr#include <sys/systm.h> 333784Sphk#include <sys/inflate.h> 343332Sphk 353332Sphk#include <vm/vm.h> 363332Sphk#include <vm/vm_kern.h> 373332Sphk 383784Sphkstruct imgact_gzip { 393784Sphk struct image_params *ip; 403784Sphk struct exec a_out; 413784Sphk int error; 423784Sphk int where; 433784Sphk u_char *inbuf; 443784Sphk u_long offset; 453784Sphk u_long output; 463784Sphk u_long len; 473784Sphk int idx; 483784Sphk u_long virtual_offset, file_offset, file_end, bss_size; 493784Sphk}; 503784Sphk 513784Sphkstatic int NextByte __P((void *vp)); 523784Sphkstatic int do_aout_hdr __P((struct imgact_gzip *)); 533784Sphkstatic int Flush __P((void *vp, u_char *, u_long siz)); 543784Sphk 553332Sphkextern struct sysentvec aout_sysvec; 563332Sphk 573332Sphkint 583332Sphkexec_gzip_imgact(iparams) 593332Sphk struct image_params *iparams; 603332Sphk{ 613784Sphk int error, error2 = 0; 623784Sphk u_char *p = (u_char *) iparams->image_header; 633784Sphk struct imgact_gzip igz; 643784Sphk struct inflate infl; 653332Sphk 663348Sphk /* If these four are not OK, it isn't a gzip file */ 673784Sphk if (p[0] != 0x1f) 683784Sphk return -1; /* 0 Simply magic */ 693784Sphk if (p[1] != 0x8b) 703784Sphk return -1; /* 1 Simply magic */ 713784Sphk if (p[2] != 0x08) 723784Sphk return -1; /* 2 Compression method */ 733784Sphk if (p[9] != 0x03) 743784Sphk return -1; /* 9 OS compressed on */ 753332Sphk 763784Sphk /* 773784Sphk * If this one contains anything but a comment or a filename marker, 783784Sphk * we don't want to chew on it 793348Sphk */ 803784Sphk if (p[3] & ~(0x18)) 813784Sphk return ENOEXEC; /* 3 Flags */ 823332Sphk 833348Sphk /* These are of no use to us */ 843784Sphk /* 4-7 Timestamp */ 853784Sphk /* 8 Extra flags */ 863348Sphk 873784Sphk bzero(&igz, sizeof igz); 883784Sphk bzero(&infl, sizeof infl); 893784Sphk infl.gz_private = (void *) &igz; 903784Sphk infl.gz_input = NextByte; 913784Sphk infl.gz_output = Flush; 923332Sphk 933784Sphk igz.ip = iparams; 943784Sphk igz.idx = 10; 953353Sphk 963784Sphk if (p[3] & 0x08) { /* skip a filename */ 973784Sphk while (p[igz.idx++]) 983784Sphk if (igz.idx >= PAGE_SIZE) 993784Sphk return ENOEXEC; 1003348Sphk } 1013784Sphk if (p[3] & 0x10) { /* skip a comment */ 1023784Sphk while (p[igz.idx++]) 1033784Sphk if (igz.idx >= PAGE_SIZE) 1043784Sphk return ENOEXEC; 1053348Sphk } 1063784Sphk igz.len = igz.ip->attr->va_size; 1073332Sphk 1083784Sphk error = inflate(&infl); 1093348Sphk 1103784Sphk if (igz.inbuf) { 1113784Sphk error2 = 1126579Sdg vm_map_remove(kernel_map, (vm_offset_t) igz.inbuf, 1136579Sdg (vm_offset_t) igz.inbuf + PAGE_SIZE); 1143332Sphk } 1153784Sphk if (igz.error || error || error2) { 1163784Sphk printf("Output=%lu ", igz.output); 1173784Sphk printf("Inflate_error=%d igz.error=%d error2=%d where=%d\n", 1183784Sphk error, igz.error, error2, igz.where); 1193348Sphk } 1203784Sphk if (igz.error) 1213784Sphk return igz.error; 1223784Sphk if (error) 1233784Sphk return ENOEXEC; 1243784Sphk if (error2) 1253784Sphk return error2; 1263784Sphk return 0; 1273332Sphk} 1283332Sphk 1293784Sphkstatic int 1303784Sphkdo_aout_hdr(struct imgact_gzip * gz) 1313332Sphk{ 1323784Sphk int error; 1333784Sphk struct vmspace *vmspace = gz->ip->proc->p_vmspace; 1343784Sphk u_long vmaddr; 1353332Sphk 1363784Sphk /* 1373784Sphk * Set file/virtual offset based on a.out variant. We do two cases: 1383784Sphk * host byte order and network byte order (for NetBSD compatibility) 1393784Sphk */ 1403784Sphk switch ((int) (gz->a_out.a_magic & 0xffff)) { 1413332Sphk case ZMAGIC: 1423784Sphk gz->virtual_offset = 0; 1433784Sphk if (gz->a_out.a_text) { 1443784Sphk gz->file_offset = NBPG; 1453784Sphk } else { 1463784Sphk /* Bill's "screwball mode" */ 1473784Sphk gz->file_offset = 0; 1483784Sphk } 1493784Sphk break; 1503332Sphk case QMAGIC: 1513784Sphk gz->virtual_offset = NBPG; 1523784Sphk gz->file_offset = 0; 1533784Sphk break; 1543332Sphk default: 1553784Sphk /* NetBSD compatibility */ 1563784Sphk switch ((int) (ntohl(gz->a_out.a_magic) & 0xffff)) { 1573784Sphk case ZMAGIC: 1583784Sphk case QMAGIC: 1593784Sphk gz->virtual_offset = NBPG; 1603784Sphk gz->file_offset = 0; 1613784Sphk break; 1623784Sphk default: 1633784Sphk gz->where = __LINE__; 1643784Sphk return (-1); 1653784Sphk } 1663332Sphk } 1673332Sphk 1683784Sphk gz->bss_size = roundup(gz->a_out.a_bss, NBPG); 1693332Sphk 1703784Sphk /* 1713784Sphk * Check various fields in header for validity/bounds. 1723784Sphk */ 1733784Sphk if ( /* entry point must lay with text region */ 1743784Sphk gz->a_out.a_entry < gz->virtual_offset || 1753784Sphk gz->a_out.a_entry >= gz->virtual_offset + gz->a_out.a_text || 1763332Sphk 1773332Sphk /* text and data size must each be page rounded */ 1783784Sphk gz->a_out.a_text % NBPG || 1793784Sphk gz->a_out.a_data % NBPG) { 1803784Sphk gz->where = __LINE__; 1813784Sphk return (-1); 1823784Sphk } 1833784Sphk /* 1843784Sphk * text/data/bss must not exceed limits 1853784Sphk */ 1863784Sphk if ( /* text can't exceed maximum text size */ 1873784Sphk gz->a_out.a_text > MAXTSIZ || 1883332Sphk 1893332Sphk /* data + bss can't exceed maximum data size */ 1903784Sphk gz->a_out.a_data + gz->bss_size > MAXDSIZ || 1913332Sphk 1923332Sphk /* data + bss can't exceed rlimit */ 1933784Sphk gz->a_out.a_data + gz->bss_size > 1943332Sphk gz->ip->proc->p_rlimit[RLIMIT_DATA].rlim_cur) { 1953784Sphk gz->where = __LINE__; 1963784Sphk return (ENOMEM); 1973784Sphk } 1983784Sphk /* Find out how far we should go */ 1993784Sphk gz->file_end = gz->file_offset + gz->a_out.a_text + gz->a_out.a_data; 2003332Sphk 2013784Sphk /* copy in arguments and/or environment from old process */ 2023784Sphk error = exec_extract_strings(gz->ip); 2033784Sphk if (error) { 2043784Sphk gz->where = __LINE__; 2053784Sphk return (error); 2063784Sphk } 2073784Sphk /* 2083784Sphk * Destroy old process VM and create a new one (with a new stack) 2093784Sphk */ 2103784Sphk exec_new_vmspace(gz->ip); 2113348Sphk 2123784Sphk vmaddr = gz->virtual_offset; 2133332Sphk 2143784Sphk error = vm_mmap(&vmspace->vm_map, /* map */ 2153784Sphk &vmaddr,/* address */ 2163784Sphk gz->a_out.a_text, /* size */ 2173784Sphk VM_PROT_READ | VM_PROT_EXECUTE | VM_PROT_WRITE, /* protection */ 2183784Sphk VM_PROT_READ | VM_PROT_EXECUTE | VM_PROT_WRITE, 2193784Sphk MAP_ANON | MAP_FIXED, /* flags */ 2203784Sphk 0, /* vnode */ 2213784Sphk 0); /* offset */ 2223332Sphk 2233784Sphk if (error) { 2243784Sphk gz->where = __LINE__; 2253784Sphk return (error); 2263784Sphk } 2273784Sphk vmaddr = gz->virtual_offset + gz->a_out.a_text; 2283332Sphk 2293784Sphk /* 2303784Sphk * Map data read/write (if text is 0, assume text is in data area 2313784Sphk * [Bill's screwball mode]) 2323784Sphk */ 2333332Sphk 2343784Sphk error = vm_mmap(&vmspace->vm_map, 2353784Sphk &vmaddr, 2363784Sphk gz->a_out.a_data, 2373784Sphk VM_PROT_READ | VM_PROT_WRITE | (gz->a_out.a_text ? 0 : VM_PROT_EXECUTE), 2383784Sphk VM_PROT_ALL, MAP_ANON | MAP_FIXED, 2393784Sphk 0, 2403784Sphk 0); 2413332Sphk 2423784Sphk if (error) { 2433784Sphk gz->where = __LINE__; 2443784Sphk return (error); 2453784Sphk } 2466579Sdg if (gz->bss_size != 0) { 2476579Sdg /* 2486579Sdg * Allocate demand-zeroed area for uninitialized data "bss" = 'block 2496579Sdg * started by symbol' - named after the IBM 7090 instruction of the 2506579Sdg * same name. 2516579Sdg */ 2526579Sdg vmaddr = gz->virtual_offset + gz->a_out.a_text + gz->a_out.a_data; 2536579Sdg error = vm_map_find(&vmspace->vm_map, NULL, 0, &vmaddr, gz->bss_size, FALSE); 2546579Sdg if (error) { 2556579Sdg gz->where = __LINE__; 2566579Sdg return (error); 2576579Sdg } 2583784Sphk } 2593784Sphk /* Fill in process VM information */ 2603784Sphk vmspace->vm_tsize = gz->a_out.a_text >> PAGE_SHIFT; 2613784Sphk vmspace->vm_dsize = (gz->a_out.a_data + gz->bss_size) >> PAGE_SHIFT; 2623784Sphk vmspace->vm_taddr = (caddr_t) gz->virtual_offset; 2633784Sphk vmspace->vm_daddr = (caddr_t) gz->virtual_offset + gz->a_out.a_text; 2643332Sphk 2653784Sphk /* Fill in image_params */ 2663784Sphk gz->ip->interpreted = 0; 2673784Sphk gz->ip->entry_addr = gz->a_out.a_entry; 2683332Sphk 2693784Sphk gz->ip->proc->p_sysent = &aout_sysvec; 2703332Sphk 2713784Sphk return 0; 2723784Sphk} 2733332Sphk 2743784Sphkstatic int 2753784SphkNextByte(void *vp) 2763784Sphk{ 2773784Sphk int error; 2783784Sphk struct imgact_gzip *igz = (struct imgact_gzip *) vp; 2793332Sphk 2803784Sphk if (igz->idx >= igz->len) { 2813784Sphk igz->where = __LINE__; 2823784Sphk return GZ_EOF; 2833784Sphk } 2843784Sphk if (igz->inbuf && igz->idx < (igz->offset + PAGE_SIZE)) { 2853784Sphk return igz->inbuf[(igz->idx++) - igz->offset]; 2863784Sphk } 2873784Sphk if (igz->inbuf) { 2886579Sdg error = vm_map_remove(kernel_map, (vm_offset_t) igz->inbuf, 2896579Sdg (vm_offset_t) igz->inbuf + PAGE_SIZE); 2903784Sphk if (error) { 2913784Sphk igz->where = __LINE__; 2923784Sphk igz->error = error; 2933784Sphk return GZ_EOF; 2943784Sphk } 2953784Sphk } 2966342Sphk igz->offset = igz->idx & ~PAGE_MASK; 2973332Sphk 2983784Sphk error = vm_mmap(kernel_map, /* map */ 2993784Sphk (vm_offset_t *) & igz->inbuf, /* address */ 3003784Sphk PAGE_SIZE, /* size */ 3013784Sphk VM_PROT_READ, /* protection */ 3023784Sphk VM_PROT_READ, /* max protection */ 3033784Sphk 0, /* flags */ 3043784Sphk (caddr_t) igz->ip->vnodep, /* vnode */ 3053784Sphk igz->offset); /* offset */ 3063784Sphk if (error) { 3073784Sphk igz->where = __LINE__; 3083784Sphk igz->error = error; 3093784Sphk return GZ_EOF; 3103784Sphk } 3113784Sphk return igz->inbuf[(igz->idx++) - igz->offset]; 3123784Sphk} 3133332Sphk 3143784Sphkstatic int 3153784SphkFlush(void *vp, u_char * ptr, u_long siz) 3163784Sphk{ 3173784Sphk struct imgact_gzip *gz = (struct imgact_gzip *) vp; 3183784Sphk u_char *p = ptr, *q; 3193784Sphk int i; 3203348Sphk 3213784Sphk /* First, find a a.out-header */ 3223784Sphk if (gz->output < sizeof gz->a_out) { 3233784Sphk q = (u_char *) & gz->a_out; 3243784Sphk i = min(siz, sizeof gz->a_out - gz->output); 3253784Sphk bcopy(p, q + gz->output, i); 3263784Sphk gz->output += i; 3273784Sphk p += i; 3283784Sphk siz -= i; 3293784Sphk if (gz->output == sizeof gz->a_out) { 3303784Sphk i = do_aout_hdr(gz); 3313784Sphk if (i == -1) { 3323785Sphk if (!gz->where) 3333785Sphk gz->where = __LINE__; 3343784Sphk gz->error = ENOEXEC; 3353784Sphk return ENOEXEC; 3363784Sphk } else if (i) { 3373784Sphk gz->where = __LINE__; 3383784Sphk gz->error = i; 3393784Sphk return ENOEXEC; 3403784Sphk } 3413784Sphk if (gz->file_offset < sizeof gz->a_out) { 3423784Sphk q = (u_char *) gz->virtual_offset + gz->output - gz->file_offset; 3433784Sphk bcopy(&gz->a_out, q, sizeof gz->a_out - gz->file_offset); 3443784Sphk } 3453784Sphk } 3463784Sphk } 3473784Sphk /* Skip over zero-padded first PAGE if needed */ 3483784Sphk if (gz->output < gz->file_offset && (gz->output + siz) > gz->file_offset) { 3493784Sphk i = min(siz, gz->file_offset - gz->output); 3503784Sphk gz->output += i; 3513784Sphk p += i; 3523784Sphk siz -= i; 3533784Sphk } 3543784Sphk if (gz->output >= gz->file_offset && gz->output < gz->file_end) { 3553784Sphk i = min(siz, gz->file_end - gz->output); 3563784Sphk q = (u_char *) gz->virtual_offset + gz->output - gz->file_offset; 3573784Sphk bcopy(p, q, i); 3583784Sphk gz->output += i; 3593784Sphk p += i; 3603784Sphk siz -= i; 3613784Sphk } 3623784Sphk gz->output += siz; 3633784Sphk return 0; 3643332Sphk} 3653332Sphk 3663784Sphk 3673332Sphk/* 3683332Sphk * Tell kern_execve.c about it, with a little help from the linker. 3693332Sphk * Since `const' objects end up in the text segment, TEXT_SET is the 3703332Sphk * correct directive to use. 3713332Sphk */ 3723784Sphk 3733784Sphkstatic const struct execsw gzip_execsw = {exec_gzip_imgact, "gzip"}; 3743332SphkTEXT_SET(execsw_set, gzip_execsw); 375