imgact_gzip.c revision 3785
1139749Simp/* 253413Sroger * ---------------------------------------------------------------------------- 353413Sroger * "THE BEER-WARE LICENSE" (Revision 42): 450724Scg * <phk@login.dkuug.dk> wrote this file. As long as you retain this notice you 553413Sroger * can do whatever you want with this stuff. If we meet some day, and you think 6119853Scg * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 750724Scg * ---------------------------------------------------------------------------- 850724Scg * 950724Scg * $Id: imgact_gzip.c,v 1.9 1994/10/22 11:40:27 phk Exp $ 1050724Scg * 1150724Scg * This module handles execution of a.out files which have been run through 1250724Scg * "gzip". This saves diskspace, but wastes cpu-cycles and VM. 1350724Scg * 1450724Scg * TODO: 1550724Scg * text-segments should be made R/O after being filled 1650724Scg * is the vm-stuff safe ? 1750724Scg * should handle the entire header of gzip'ed stuff. 1850724Scg * inflate isn't quite reentrant yet... 1950724Scg * error-handling is a mess... 2050724Scg * so is the rest... 2150724Scg * tidy up unnecesary includes 2250724Scg */ 2350724Scg 2450724Scg#include <sys/param.h> 2550724Scg#include <sys/exec.h> 2650724Scg#include <sys/imgact.h> 2750724Scg#include <sys/imgact_aout.h> 2850724Scg#include <sys/kernel.h> 2950724Scg#include <sys/mman.h> 3050724Scg#include <sys/resourcevar.h> 3150724Scg#include <sys/sysent.h> 3250724Scg#include <sys/systm.h> 3350724Scg#include <sys/inflate.h> 3450724Scg 3550724Scg#include <vm/vm.h> 3650724Scg#include <vm/vm_kern.h> 3750724Scg 3850724Scgstruct imgact_gzip { 3950724Scg struct image_params *ip; 4050724Scg struct exec a_out; 4150724Scg int error; 4253413Sroger int where; 4353413Sroger u_char *inbuf; 4453413Sroger u_long offset; 4554831Scg u_long output; 4654831Scg u_long len; 4753413Sroger int idx; 4853413Sroger u_long virtual_offset, file_offset, file_end, bss_size; 4953413Sroger}; 5053413Sroger 5153465Scgstatic int NextByte __P((void *vp)); 5253465Scgstatic int do_aout_hdr __P((struct imgact_gzip *)); 5353465Scgstatic int Flush __P((void *vp, u_char *, u_long siz)); 5450724Scg 55119287Simpextern struct sysentvec aout_sysvec; 56119287Simp 5750724Scgint 5853413Srogerexec_gzip_imgact(iparams) 5953413Sroger struct image_params *iparams; 6070134Scg{ 6170134Scg int error, error2 = 0; 6282180Scg u_char *p = (u_char *) iparams->image_header; 6382180Scg struct imgact_gzip igz; 6450724Scg struct inflate infl; 6550724Scg 6650724Scg /* If these four are not OK, it isn't a gzip file */ 6750724Scg if (p[0] != 0x1f) 6853413Sroger return -1; /* 0 Simply magic */ 6956154Speter if (p[1] != 0x8b) 7076086Scg return -1; /* 1 Simply magic */ 71119548Sorion if (p[2] != 0x08) 7250724Scg return -1; /* 2 Compression method */ 7378033Scg if (p[9] != 0x03) 7476086Scg return -1; /* 9 OS compressed on */ 7576086Scg 7676086Scg /* 7776086Scg * If this one contains anything but a comment or a filename marker, 7876086Scg * we don't want to chew on it 7976086Scg */ 8076086Scg if (p[3] & ~(0x18)) 8176086Scg return ENOEXEC; /* 3 Flags */ 8276086Scg 8376086Scg /* These are of no use to us */ 8495678Scg /* 4-7 Timestamp */ 8576086Scg /* 8 Extra flags */ 86119548Sorion 87119548Sorion bzero(&igz, sizeof igz); 8884658Scg bzero(&infl, sizeof infl); 8959019Scg infl.gz_private = (void *) &igz; 90152419Sariff infl.gz_input = NextByte; 91152419Sariff infl.gz_output = Flush; 92152419Sariff 93152419Sariff igz.ip = iparams; 94152419Sariff igz.idx = 10; 95152419Sariff 96167648Sariff if (p[3] & 0x08) { /* skip a filename */ 97167648Sariff while (p[igz.idx++]) 98167648Sariff if (igz.idx >= PAGE_SIZE) 99167648Sariff return ENOEXEC; 100167648Sariff } 101152419Sariff if (p[3] & 0x10) { /* skip a comment */ 102152419Sariff while (p[igz.idx++]) 103152419Sariff if (igz.idx >= PAGE_SIZE) 10450724Scg return ENOEXEC; 10550724Scg } 10650724Scg igz.len = igz.ip->attr->va_size; 10755209Scg 10850724Scg error = inflate(&infl); 10974763Scg 11074763Scg if (igz.inbuf) { 111152419Sariff error2 = 112152419Sariff vm_deallocate(kernel_map, (vm_offset_t) igz.inbuf, 113164614Sariff PAGE_SIZE); 114164614Sariff } 115164614Sariff if (igz.error || error || error2) { 11655209Scg printf("Output=%lu ", igz.output); 11750724Scg printf("Inflate_error=%d igz.error=%d error2=%d where=%d\n", 118152419Sariff error, igz.error, error2, igz.where); 119152419Sariff } 120152419Sariff if (igz.error) 121152419Sariff return igz.error; 122152419Sariff if (error) 123152419Sariff return ENOEXEC; 124152419Sariff if (error2) 125152419Sariff return error2; 126152419Sariff return 0; 127152419Sariff} 128152419Sariff 129152419Sariffstatic int 130152419Sariffdo_aout_hdr(struct imgact_gzip * gz) 131152419Sariff{ 132152419Sariff int error; 133152419Sariff struct vmspace *vmspace = gz->ip->proc->p_vmspace; 134152419Sariff u_long vmaddr; 135152419Sariff 136152419Sariff /* 137152419Sariff * Set file/virtual offset based on a.out variant. We do two cases: 138152419Sariff * host byte order and network byte order (for NetBSD compatibility) 139152419Sariff */ 140152419Sariff switch ((int) (gz->a_out.a_magic & 0xffff)) { 141152419Sariff case ZMAGIC: 142152419Sariff gz->virtual_offset = 0; 143152419Sariff if (gz->a_out.a_text) { 144152419Sariff gz->file_offset = NBPG; 145152419Sariff } else { 146152419Sariff /* Bill's "screwball mode" */ 147152419Sariff gz->file_offset = 0; 148152419Sariff } 149152419Sariff break; 150152419Sariff case QMAGIC: 151152419Sariff gz->virtual_offset = NBPG; 152152419Sariff gz->file_offset = 0; 153152419Sariff break; 154152419Sariff default: 155152419Sariff /* NetBSD compatibility */ 156152419Sariff switch ((int) (ntohl(gz->a_out.a_magic) & 0xffff)) { 157152419Sariff case ZMAGIC: 158152419Sariff case QMAGIC: 159152419Sariff gz->virtual_offset = NBPG; 160152419Sariff gz->file_offset = 0; 161152419Sariff break; 162152419Sariff default: 163152419Sariff gz->where = __LINE__; 164152419Sariff return (-1); 165152419Sariff } 166152419Sariff } 167152419Sariff 168152419Sariff gz->bss_size = roundup(gz->a_out.a_bss, NBPG); 169152419Sariff 170152419Sariff /* 171152419Sariff * Check various fields in header for validity/bounds. 172152419Sariff */ 173152419Sariff if ( /* entry point must lay with text region */ 174152419Sariff gz->a_out.a_entry < gz->virtual_offset || 175152419Sariff gz->a_out.a_entry >= gz->virtual_offset + gz->a_out.a_text || 176152419Sariff 177152419Sariff /* text and data size must each be page rounded */ 178152419Sariff gz->a_out.a_text % NBPG || 179152419Sariff gz->a_out.a_data % NBPG) { 18055209Scg gz->where = __LINE__; 18150724Scg return (-1); 18250724Scg } 18350724Scg /* 18450724Scg * text/data/bss must not exceed limits 18565644Scg */ 18665644Scg if ( /* text can't exceed maximum text size */ 18765644Scg gz->a_out.a_text > MAXTSIZ || 18865644Scg 18959019Scg /* data + bss can't exceed maximum data size */ 19054831Scg gz->a_out.a_data + gz->bss_size > MAXDSIZ || 191164614Sariff 19284658Scg /* data + bss can't exceed rlimit */ 19350724Scg gz->a_out.a_data + gz->bss_size > 194150832Snetchild gz->ip->proc->p_rlimit[RLIMIT_DATA].rlim_cur) { 195150832Snetchild gz->where = __LINE__; 196152419Sariff return (ENOMEM); 197152419Sariff } 198148591Snetchild /* Find out how far we should go */ 199164614Sariff gz->file_end = gz->file_offset + gz->a_out.a_text + gz->a_out.a_data; 200164614Sariff 20155209Scg /* copy in arguments and/or environment from old process */ 20250724Scg error = exec_extract_strings(gz->ip); 203150832Snetchild if (error) { 204150832Snetchild gz->where = __LINE__; 205150832Snetchild return (error); 20654831Scg } 20753413Sroger /* 20854831Scg * Destroy old process VM and create a new one (with a new stack) 209150832Snetchild */ 210164614Sariff exec_new_vmspace(gz->ip); 211164614Sariff 212164614Sariff vmaddr = gz->virtual_offset; 213164614Sariff 214150832Snetchild error = vm_mmap(&vmspace->vm_map, /* map */ 21554831Scg &vmaddr,/* address */ 216164614Sariff gz->a_out.a_text, /* size */ 21750724Scg VM_PROT_READ | VM_PROT_EXECUTE | VM_PROT_WRITE, /* protection */ 218164614Sariff VM_PROT_READ | VM_PROT_EXECUTE | VM_PROT_WRITE, 21964881Scg MAP_ANON | MAP_FIXED, /* flags */ 22064881Scg 0, /* vnode */ 22164881Scg 0); /* offset */ 22264881Scg 22364881Scg if (error) { 22450724Scg gz->where = __LINE__; 225150832Snetchild return (error); 22650724Scg } 22750724Scg vmaddr = gz->virtual_offset + gz->a_out.a_text; 22850724Scg 22950724Scg /* 23050724Scg * Map data read/write (if text is 0, assume text is in data area 23150724Scg * [Bill's screwball mode]) 23250724Scg */ 23350724Scg 23450724Scg error = vm_mmap(&vmspace->vm_map, 235152419Sariff &vmaddr, 23650724Scg gz->a_out.a_data, 23750724Scg VM_PROT_READ | VM_PROT_WRITE | (gz->a_out.a_text ? 0 : VM_PROT_EXECUTE), 23850724Scg VM_PROT_ALL, MAP_ANON | MAP_FIXED, 23950724Scg 0, 24050724Scg 0); 24150724Scg 24250724Scg if (error) { 24350724Scg gz->where = __LINE__; 24454831Scg return (error); 24554831Scg } 24650724Scg /* 247164614Sariff * Allocate demand-zeroed area for uninitialized data "bss" = 'block 248148591Snetchild * started by symbol' - named after the IBM 7090 instruction of the 249148591Snetchild * same name. 250148591Snetchild */ 251148591Snetchild vmaddr = gz->virtual_offset + gz->a_out.a_text + gz->a_out.a_data; 252164614Sariff error = vm_allocate(&vmspace->vm_map, &vmaddr, gz->bss_size, FALSE); 253148591Snetchild if (error) { 254164614Sariff gz->where = __LINE__; 255148591Snetchild return (error); 256164614Sariff } 257148591Snetchild /* Fill in process VM information */ 258164614Sariff vmspace->vm_tsize = gz->a_out.a_text >> PAGE_SHIFT; 259148591Snetchild vmspace->vm_dsize = (gz->a_out.a_data + gz->bss_size) >> PAGE_SHIFT; 260148591Snetchild vmspace->vm_taddr = (caddr_t) gz->virtual_offset; 261148591Snetchild vmspace->vm_daddr = (caddr_t) gz->virtual_offset + gz->a_out.a_text; 262150832Snetchild 263164614Sariff /* Fill in image_params */ 264148591Snetchild gz->ip->interpreted = 0; 265148591Snetchild gz->ip->entry_addr = gz->a_out.a_entry; 266148591Snetchild 267148591Snetchild gz->ip->proc->p_sysent = &aout_sysvec; 268148591Snetchild 269148591Snetchild return 0; 270148591Snetchild} 271148591Snetchild 272148591Snetchildstatic int 273148591SnetchildNextByte(void *vp) 274148591Snetchild{ 275148591Snetchild int error; 276148591Snetchild struct imgact_gzip *igz = (struct imgact_gzip *) vp; 277148591Snetchild 278148591Snetchild if (igz->idx >= igz->len) { 27970134Scg igz->where = __LINE__; 28070134Scg return GZ_EOF; 28170134Scg } 28250724Scg if (igz->inbuf && igz->idx < (igz->offset + PAGE_SIZE)) { 28374763Scg return igz->inbuf[(igz->idx++) - igz->offset]; 28450724Scg } 285152419Sariff if (igz->inbuf) { 28650724Scg error = vm_deallocate(kernel_map, 287164614Sariff (vm_offset_t) igz->inbuf, PAGE_SIZE); 28850724Scg if (error) { 289152419Sariff igz->where = __LINE__; 29050724Scg igz->error = error; 291164614Sariff return GZ_EOF; 292164614Sariff } 293164614Sariff } 294164614Sariff igz->offset += PAGE_SIZE; 295152419Sariff 296152419Sariff error = vm_mmap(kernel_map, /* map */ 297152419Sariff (vm_offset_t *) & igz->inbuf, /* address */ 298152419Sariff PAGE_SIZE, /* size */ 299152419Sariff VM_PROT_READ, /* protection */ 300152419Sariff VM_PROT_READ, /* max protection */ 301152419Sariff 0, /* flags */ 302152419Sariff (caddr_t) igz->ip->vnodep, /* vnode */ 303152419Sariff igz->offset); /* offset */ 30450724Scg if (error) { 30550724Scg igz->where = __LINE__; 306164614Sariff igz->error = error; 307164614Sariff return GZ_EOF; 308164614Sariff } 309164614Sariff return igz->inbuf[(igz->idx++) - igz->offset]; 310152419Sariff} 311152419Sariff 31250724Scgstatic int 313164614SariffFlush(void *vp, u_char * ptr, u_long siz) 31450724Scg{ 31550724Scg struct imgact_gzip *gz = (struct imgact_gzip *) vp; 31650724Scg u_char *p = ptr, *q; 31774763Scg int i; 31850724Scg 319150832Snetchild /* First, find a a.out-header */ 320152419Sariff if (gz->output < sizeof gz->a_out) { 32150724Scg q = (u_char *) & gz->a_out; 322164614Sariff i = min(siz, sizeof gz->a_out - gz->output); 323164614Sariff bcopy(p, q + gz->output, i); 32450724Scg gz->output += i; 325164614Sariff p += i; 326164614Sariff siz -= i; 327164614Sariff if (gz->output == sizeof gz->a_out) { 328164614Sariff i = do_aout_hdr(gz); 329164614Sariff if (i == -1) { 330150832Snetchild if (!gz->where) 331150832Snetchild gz->where = __LINE__; 332152419Sariff gz->error = ENOEXEC; 333164614Sariff return ENOEXEC; 334152419Sariff } else if (i) { 335164614Sariff gz->where = __LINE__; 336152419Sariff gz->error = i; 33750724Scg return ENOEXEC; 338164614Sariff } 339150832Snetchild if (gz->file_offset < sizeof gz->a_out) { 340152419Sariff q = (u_char *) gz->virtual_offset + gz->output - gz->file_offset; 341164614Sariff bcopy(&gz->a_out, q, sizeof gz->a_out - gz->file_offset); 342164614Sariff } 34350724Scg } 344150832Snetchild } 345152419Sariff /* Skip over zero-padded first PAGE if needed */ 346152419Sariff if (gz->output < gz->file_offset && (gz->output + siz) > gz->file_offset) { 347150832Snetchild i = min(siz, gz->file_offset - gz->output); 348150832Snetchild gz->output += i; 349164614Sariff p += i; 35050724Scg siz -= i; 35150724Scg } 35250724Scg if (gz->output >= gz->file_offset && gz->output < gz->file_end) { 353164614Sariff i = min(siz, gz->file_end - gz->output); 35450724Scg q = (u_char *) gz->virtual_offset + gz->output - gz->file_offset; 355150832Snetchild bcopy(p, q, i); 35650724Scg gz->output += i; 35750724Scg p += i; 358150832Snetchild siz -= i; 35950724Scg } 36050724Scg gz->output += siz; 36150724Scg return 0; 36250724Scg} 36350724Scg 364150832Snetchild 365152419Sariff/* 366164614Sariff * Tell kern_execve.c about it, with a little help from the linker. 367152419Sariff * Since `const' objects end up in the text segment, TEXT_SET is the 368150832Snetchild * correct directive to use. 369150832Snetchild */ 370150832Snetchild 371150832Snetchildstatic const struct execsw gzip_execsw = {exec_gzip_imgact, "gzip"}; 372150832SnetchildTEXT_SET(execsw_set, gzip_execsw); 373150832Snetchild