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