aboutsummaryrefslogtreecommitdiff
path: root/sys/pc98/boot/kzipboot/unzip.c
blob: 93a2c8b8427988d1b442b6e2a54f6a9a6cd36e73 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/*
 * unzip.c -- decompress files in gzip or pkzip format.
 * Copyright (C) 1992-1993 Jean-loup Gailly
 *
 * Adapted for Linux booting by Hannu Savolainen 1993
 * Adapted for FreeBSD booting by Serge Vakulenko
 *
 * This is free software; you can redistribute it and/or modify it under the
 * terms of the GNU General Public License, see the file COPYING.
 *
 * The code in this file is derived from the file funzip.c written
 * and put in the public domain by Mark Adler.
 */

/*
 * This version can extract files in gzip or pkzip format.
 * For the latter, only the first entry is extracted, and it has to be
 * either deflated or stored.
 */

#include "gzip.h"

#include <sys/types.h>
#include <sys/inflate.h>

/* PKZIP header definitions */
#define LOCSIG  0x04034b50L     /* four-byte lead-in (lsb first) */
#define LOCFLG  6               /* offset of bit flag */
#define  CRPFLG 1               /*  bit for encrypted entry */
#define  EXTFLG 8               /*  bit for extended local header */
#define LOCHOW  8               /* offset of compression method */
#define LOCTIM  10              /* file mod time (for decryption) */
#define LOCCRC  14              /* offset of crc */
#define LOCSIZ  18              /* offset of compressed size */
#define LOCLEN  22              /* offset of uncompressed length */
#define LOCFIL  26              /* offset of file name field length */
#define LOCEXT  28              /* offset of extra field length */
#define LOCHDR  30              /* size of local header, including sig */
#define EXTHDR  16              /* size of extended local header, inc sig */

int pkzip;                      /* set for a pkzip file */
int extended;                   /* set if extended local header */

/* Macros for getting two-byte and four-byte header values */
#define SH(p) ((ushort)(uchar)((p)[0]) | ((ushort)(uchar)((p)[1]) << 8))
#define LG(p) ((ulong)(SH(p)) | ((ulong)(SH((p)+2)) << 16))

/*
 * Check zip file and advance inptr to the start of the compressed data.
 * Get ofname from the local header if necessary.
 */
void check_zipfile()
{
	uchar *h = inbuf + inptr;       /* first local header */

	/* Check validity of local header, and skip name and extra fields */
	inptr += LOCHDR + SH(h + LOCFIL) + SH(h + LOCEXT);

	if (inptr > insize || LG(h) != LOCSIG)
		error("input not a zip");

	method = h[LOCHOW];
	if (method != STORED && method != DEFLATED)
		error("first entry not deflated or stored--can't extract");

	/* If entry encrypted, decrypt and validate encryption header */
	if (h[LOCFLG] & CRPFLG)
		error("encrypted file");

	/* Save flags for unzip() */
	extended = (h[LOCFLG] & EXTFLG) != 0;
	pkzip = 1;
}

int
Flush (void *nu, u_char *buf, u_long cnt)
{
	outcnt = cnt;
	flush_window();
	return 0;
}

int
NextByte (void *nu)
{
	return ((int) get_byte ());
}

struct inflate infl; /* put it into the BSS */

/*
 * Unzip in to out.  This routine works on both gzip and pkzip files.
 *
 * IN assertions: the buffer inbuf contains already the beginning of
 * the compressed data, from offsets inptr to insize-1 included.
 * The magic header has already been checked. The output buffer is cleared.
 */

void unzip()
{
	ulong orig_crc = 0;     /* original crc */
	ulong orig_len = 0;     /* original uncompressed length */
	uchar buf[EXTHDR];      /* extended local header */
	int n, res;

	crc = 0xffffffffL;      /* initialize crc */

	if (pkzip && !extended) { /* crc and length at the end otherwise */
		orig_crc = LG(inbuf + LOCCRC);
		orig_len = LG(inbuf + LOCLEN);
	}

	if (method != DEFLATED)
		error("internal error, invalid method");
	infl.gz_input = NextByte;
	infl.gz_output = Flush;
	infl.gz_slide = window;
	res = inflate (&infl);
	if (res == 3)
		error("out of memory");
	else if (res != 0)
		error("invalid compressed format");

	/* Get the crc and original length */
	if (!pkzip) {
		/* crc32 (see algorithm.doc)
		 * uncompressed input size modulo 2^32
		 */
		for (n = 0; n < 8; n++)
			buf[n] = get_byte();    /* may cause an error if EOF */
		orig_crc = LG(buf);
		orig_len = LG(buf+4);

	} else if (extended) {          /* If extended header, check it */
		/* signature - 4bytes: 0x50 0x4b 0x07 0x08
		 * CRC-32 value
		 * compressed size 4-bytes
		 * uncompressed size 4-bytes
		 */
		for (n = 0; n < EXTHDR; n++)
			buf[n] = get_byte();    /* may cause an error if EOF */
		orig_crc = LG(buf+4);
		orig_len = LG(buf+12);
	}

	/* Validate decompression */
	if (orig_crc != (crc ^ 0xffffffffL))
		error("crc error");
	if (orig_len != output_ptr)
		error("length error");

	/* Check if there are more entries in a pkzip file */
	if (pkzip && inptr+4 < insize && LG(inbuf+inptr) == LOCSIG)
		error("zip file has more than one entry");
}