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