imgact_gzip.c revision 6579
1/*
2 * ----------------------------------------------------------------------------
3 * "THE BEER-WARE LICENSE" (Revision 42):
4 * <phk@login.dkuug.dk> wrote this file.  As long as you retain this notice you
5 * can do whatever you want with this stuff. If we meet some day, and you think
6 * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7 * ----------------------------------------------------------------------------
8 *
9 * $Id: imgact_gzip.c,v 1.11 1995/02/13 07:40:33 phk Exp $
10 *
11 * This module handles execution of a.out files which have been run through
12 * "gzip".  This saves diskspace, but wastes cpu-cycles and VM.
13 *
14 * TODO:
15 *	text-segments should be made R/O after being filled
16 *	is the vm-stuff safe ?
17 * 	should handle the entire header of gzip'ed stuff.
18 *	inflate isn't quite reentrant yet...
19 *	error-handling is a mess...
20 *	so is the rest...
21 *	tidy up unnecesary includes
22 */
23
24#include <sys/param.h>
25#include <sys/exec.h>
26#include <sys/imgact.h>
27#include <sys/imgact_aout.h>
28#include <sys/kernel.h>
29#include <sys/mman.h>
30#include <sys/resourcevar.h>
31#include <sys/sysent.h>
32#include <sys/systm.h>
33#include <sys/inflate.h>
34
35#include <vm/vm.h>
36#include <vm/vm_kern.h>
37
38struct imgact_gzip {
39	struct image_params *ip;
40	struct exec     a_out;
41	int             error;
42	int             where;
43	u_char         *inbuf;
44	u_long          offset;
45	u_long          output;
46	u_long          len;
47	int             idx;
48	u_long          virtual_offset, file_offset, file_end, bss_size;
49};
50
51static int NextByte __P((void *vp));
52static int do_aout_hdr __P((struct imgact_gzip *));
53static int Flush __P((void *vp, u_char *, u_long siz));
54
55extern struct sysentvec aout_sysvec;
56
57int
58exec_gzip_imgact(iparams)
59	struct image_params *iparams;
60{
61	int             error, error2 = 0;
62	u_char         *p = (u_char *) iparams->image_header;
63	struct imgact_gzip igz;
64	struct inflate  infl;
65
66	/* If these four are not OK, it isn't a gzip file */
67	if (p[0] != 0x1f)
68		return -1;	/* 0    Simply magic	 */
69	if (p[1] != 0x8b)
70		return -1;	/* 1    Simply magic	 */
71	if (p[2] != 0x08)
72		return -1;	/* 2    Compression method	 */
73	if (p[9] != 0x03)
74		return -1;	/* 9    OS compressed on	 */
75
76	/*
77	 * If this one contains anything but a comment or a filename marker,
78	 * we don't want to chew on it
79	 */
80	if (p[3] & ~(0x18))
81		return ENOEXEC;	/* 3    Flags		 */
82
83	/* These are of no use to us */
84	/* 4-7  Timestamp		 */
85	/* 8    Extra flags		 */
86
87	bzero(&igz, sizeof igz);
88	bzero(&infl, sizeof infl);
89	infl.gz_private = (void *) &igz;
90	infl.gz_input = NextByte;
91	infl.gz_output = Flush;
92
93	igz.ip = iparams;
94	igz.idx = 10;
95
96	if (p[3] & 0x08) {	/* skip a filename */
97		while (p[igz.idx++])
98			if (igz.idx >= PAGE_SIZE)
99				return ENOEXEC;
100	}
101	if (p[3] & 0x10) {	/* skip a comment */
102		while (p[igz.idx++])
103			if (igz.idx >= PAGE_SIZE)
104				return ENOEXEC;
105	}
106	igz.len = igz.ip->attr->va_size;
107
108	error = inflate(&infl);
109
110	if (igz.inbuf) {
111		error2 =
112			vm_map_remove(kernel_map, (vm_offset_t) igz.inbuf,
113			    (vm_offset_t) igz.inbuf + PAGE_SIZE);
114	}
115	if (igz.error || error || error2) {
116		printf("Output=%lu ", igz.output);
117		printf("Inflate_error=%d igz.error=%d error2=%d where=%d\n",
118		       error, igz.error, error2, igz.where);
119	}
120	if (igz.error)
121		return igz.error;
122	if (error)
123		return ENOEXEC;
124	if (error2)
125		return error2;
126	return 0;
127}
128
129static int
130do_aout_hdr(struct imgact_gzip * gz)
131{
132	int             error;
133	struct vmspace *vmspace = gz->ip->proc->p_vmspace;
134	u_long          vmaddr;
135
136	/*
137	 * Set file/virtual offset based on a.out variant. We do two cases:
138	 * host byte order and network byte order (for NetBSD compatibility)
139	 */
140	switch ((int) (gz->a_out.a_magic & 0xffff)) {
141	case ZMAGIC:
142		gz->virtual_offset = 0;
143		if (gz->a_out.a_text) {
144			gz->file_offset = NBPG;
145		} else {
146			/* Bill's "screwball mode" */
147			gz->file_offset = 0;
148		}
149		break;
150	case QMAGIC:
151		gz->virtual_offset = NBPG;
152		gz->file_offset = 0;
153		break;
154	default:
155		/* NetBSD compatibility */
156		switch ((int) (ntohl(gz->a_out.a_magic) & 0xffff)) {
157		case ZMAGIC:
158		case QMAGIC:
159			gz->virtual_offset = NBPG;
160			gz->file_offset = 0;
161			break;
162		default:
163			gz->where = __LINE__;
164			return (-1);
165		}
166	}
167
168	gz->bss_size = roundup(gz->a_out.a_bss, NBPG);
169
170	/*
171	 * Check various fields in header for validity/bounds.
172	 */
173	if (			/* entry point must lay with text region */
174	    gz->a_out.a_entry < gz->virtual_offset ||
175	    gz->a_out.a_entry >= gz->virtual_offset + gz->a_out.a_text ||
176
177	/* text and data size must each be page rounded */
178	    gz->a_out.a_text % NBPG ||
179	    gz->a_out.a_data % NBPG) {
180		gz->where = __LINE__;
181		return (-1);
182	}
183	/*
184	 * text/data/bss must not exceed limits
185	 */
186	if (			/* text can't exceed maximum text size */
187	    gz->a_out.a_text > MAXTSIZ ||
188
189	/* data + bss can't exceed maximum data size */
190	    gz->a_out.a_data + gz->bss_size > MAXDSIZ ||
191
192	/* data + bss can't exceed rlimit */
193	    gz->a_out.a_data + gz->bss_size >
194	    gz->ip->proc->p_rlimit[RLIMIT_DATA].rlim_cur) {
195		gz->where = __LINE__;
196		return (ENOMEM);
197	}
198	/* Find out how far we should go */
199	gz->file_end = gz->file_offset + gz->a_out.a_text + gz->a_out.a_data;
200
201	/* copy in arguments and/or environment from old process */
202	error = exec_extract_strings(gz->ip);
203	if (error) {
204		gz->where = __LINE__;
205		return (error);
206	}
207	/*
208	 * Destroy old process VM and create a new one (with a new stack)
209	 */
210	exec_new_vmspace(gz->ip);
211
212	vmaddr = gz->virtual_offset;
213
214	error = vm_mmap(&vmspace->vm_map,	/* map */
215			&vmaddr,/* address */
216			gz->a_out.a_text,	/* size */
217			VM_PROT_READ | VM_PROT_EXECUTE | VM_PROT_WRITE,	/* protection */
218			VM_PROT_READ | VM_PROT_EXECUTE | VM_PROT_WRITE,
219			MAP_ANON | MAP_FIXED,	/* flags */
220			0,	/* vnode */
221			0);	/* offset */
222
223	if (error) {
224		gz->where = __LINE__;
225		return (error);
226	}
227	vmaddr = gz->virtual_offset + gz->a_out.a_text;
228
229	/*
230	 * Map data read/write (if text is 0, assume text is in data area
231	 * [Bill's screwball mode])
232	 */
233
234	error = vm_mmap(&vmspace->vm_map,
235			&vmaddr,
236			gz->a_out.a_data,
237			VM_PROT_READ | VM_PROT_WRITE | (gz->a_out.a_text ? 0 : VM_PROT_EXECUTE),
238			VM_PROT_ALL, MAP_ANON | MAP_FIXED,
239			0,
240			0);
241
242	if (error) {
243		gz->where = __LINE__;
244		return (error);
245	}
246	if (gz->bss_size != 0) {
247		/*
248		 * Allocate demand-zeroed area for uninitialized data "bss" = 'block
249		 * started by symbol' - named after the IBM 7090 instruction of the
250		 * same name.
251		 */
252		vmaddr = gz->virtual_offset + gz->a_out.a_text + gz->a_out.a_data;
253		error = vm_map_find(&vmspace->vm_map, NULL, 0, &vmaddr, gz->bss_size, FALSE);
254		if (error) {
255			gz->where = __LINE__;
256			return (error);
257		}
258	}
259	/* Fill in process VM information */
260	vmspace->vm_tsize = gz->a_out.a_text >> PAGE_SHIFT;
261	vmspace->vm_dsize = (gz->a_out.a_data + gz->bss_size) >> PAGE_SHIFT;
262	vmspace->vm_taddr = (caddr_t) gz->virtual_offset;
263	vmspace->vm_daddr = (caddr_t) gz->virtual_offset + gz->a_out.a_text;
264
265	/* Fill in image_params */
266	gz->ip->interpreted = 0;
267	gz->ip->entry_addr = gz->a_out.a_entry;
268
269	gz->ip->proc->p_sysent = &aout_sysvec;
270
271	return 0;
272}
273
274static int
275NextByte(void *vp)
276{
277	int             error;
278	struct imgact_gzip *igz = (struct imgact_gzip *) vp;
279
280	if (igz->idx >= igz->len) {
281		igz->where = __LINE__;
282		return GZ_EOF;
283	}
284	if (igz->inbuf && igz->idx < (igz->offset + PAGE_SIZE)) {
285		return igz->inbuf[(igz->idx++) - igz->offset];
286	}
287	if (igz->inbuf) {
288		error = vm_map_remove(kernel_map, (vm_offset_t) igz->inbuf,
289			    (vm_offset_t) igz->inbuf + PAGE_SIZE);
290		if (error) {
291			igz->where = __LINE__;
292			igz->error = error;
293			return GZ_EOF;
294		}
295	}
296	igz->offset = igz->idx & ~PAGE_MASK;
297
298	error = vm_mmap(kernel_map,	/* map */
299			(vm_offset_t *) & igz->inbuf,	/* address */
300			PAGE_SIZE,	/* size */
301			VM_PROT_READ,	/* protection */
302			VM_PROT_READ,	/* max protection */
303			0,	/* flags */
304			(caddr_t) igz->ip->vnodep,	/* vnode */
305			igz->offset);	/* offset */
306	if (error) {
307		igz->where = __LINE__;
308		igz->error = error;
309		return GZ_EOF;
310	}
311	return igz->inbuf[(igz->idx++) - igz->offset];
312}
313
314static int
315Flush(void *vp, u_char * ptr, u_long siz)
316{
317	struct imgact_gzip *gz = (struct imgact_gzip *) vp;
318	u_char         *p = ptr, *q;
319	int             i;
320
321	/* First, find a a.out-header */
322	if (gz->output < sizeof gz->a_out) {
323		q = (u_char *) & gz->a_out;
324		i = min(siz, sizeof gz->a_out - gz->output);
325		bcopy(p, q + gz->output, i);
326		gz->output += i;
327		p += i;
328		siz -= i;
329		if (gz->output == sizeof gz->a_out) {
330			i = do_aout_hdr(gz);
331			if (i == -1) {
332				if (!gz->where)
333					gz->where = __LINE__;
334				gz->error = ENOEXEC;
335				return ENOEXEC;
336			} else if (i) {
337				gz->where = __LINE__;
338				gz->error = i;
339				return ENOEXEC;
340			}
341			if (gz->file_offset < sizeof gz->a_out) {
342				q = (u_char *) gz->virtual_offset + gz->output - gz->file_offset;
343				bcopy(&gz->a_out, q, sizeof gz->a_out - gz->file_offset);
344			}
345		}
346	}
347	/* Skip over zero-padded first PAGE if needed */
348	if (gz->output < gz->file_offset && (gz->output + siz) > gz->file_offset) {
349		i = min(siz, gz->file_offset - gz->output);
350		gz->output += i;
351		p += i;
352		siz -= i;
353	}
354	if (gz->output >= gz->file_offset && gz->output < gz->file_end) {
355		i = min(siz, gz->file_end - gz->output);
356		q = (u_char *) gz->virtual_offset + gz->output - gz->file_offset;
357		bcopy(p, q, i);
358		gz->output += i;
359		p += i;
360		siz -= i;
361	}
362	gz->output += siz;
363	return 0;
364}
365
366
367/*
368 * Tell kern_execve.c about it, with a little help from the linker.
369 * Since `const' objects end up in the text segment, TEXT_SET is the
370 * correct directive to use.
371 */
372
373static const struct execsw gzip_execsw = {exec_gzip_imgact, "gzip"};
374TEXT_SET(execsw_set, gzip_execsw);
375