Blame


1 5391aec5 2021-08-21 op /*
2 5391aec5 2021-08-21 op * This is free and unencumbered software released into the public domain.
3 5391aec5 2021-08-21 op */
4 5391aec5 2021-08-21 op
5 5391aec5 2021-08-21 op #include <sys/mman.h>
6 5391aec5 2021-08-21 op
7 5391aec5 2021-08-21 op #include <endian.h>
8 5391aec5 2021-08-21 op #include <err.h>
9 5391aec5 2021-08-21 op #include <fcntl.h>
10 5391aec5 2021-08-21 op #include <limits.h>
11 5391aec5 2021-08-21 op #include <stdint.h>
12 5391aec5 2021-08-21 op #include <stdio.h>
13 5391aec5 2021-08-21 op #include <string.h>
14 5391aec5 2021-08-21 op #include <unistd.h>
15 5391aec5 2021-08-21 op #include <zlib.h>
16 5391aec5 2021-08-21 op
17 5391aec5 2021-08-21 op #define COMPRESSION_NONE 0x00
18 5391aec5 2021-08-21 op #define COMPRESSION_DEFLATE 0x08
19 5391aec5 2021-08-21 op
20 5391aec5 2021-08-21 op #define MIN(a, b) ((a) < (b) ? (a) : (b))
21 5391aec5 2021-08-21 op
22 5391aec5 2021-08-21 op void *
23 5391aec5 2021-08-21 op find_central_directory(uint8_t *addr, size_t len)
24 5391aec5 2021-08-21 op {
25 5391aec5 2021-08-21 op uint32_t offset;
26 5391aec5 2021-08-21 op uint16_t clen;
27 5391aec5 2021-08-21 op uint8_t *p, *end;
28 5391aec5 2021-08-21 op
29 5391aec5 2021-08-21 op /*
30 5391aec5 2021-08-21 op * At -22 bytes from the end there is the end of the central
31 5391aec5 2021-08-21 op * directory assuming an empty comment. It's a sensible place
32 5391aec5 2021-08-21 op * from which start.
33 5391aec5 2021-08-21 op */
34 5391aec5 2021-08-21 op if (len < 22)
35 5391aec5 2021-08-21 op return NULL;
36 5391aec5 2021-08-21 op end = addr + len;
37 5391aec5 2021-08-21 op p = end - 22;
38 5391aec5 2021-08-21 op
39 5391aec5 2021-08-21 op again:
40 5391aec5 2021-08-21 op for (; p > addr; --p)
41 5391aec5 2021-08-21 op if (memcmp(p, "\x50\x4b\x05\x06", 4) == 0)
42 5391aec5 2021-08-21 op break;
43 5391aec5 2021-08-21 op
44 5391aec5 2021-08-21 op if (p == addr)
45 5391aec5 2021-08-21 op return NULL;
46 5391aec5 2021-08-21 op
47 5391aec5 2021-08-21 op /* read comment length */
48 5391aec5 2021-08-21 op memcpy(&clen, p + 20, sizeof(clen));
49 5391aec5 2021-08-21 op clen = le16toh(clen);
50 5391aec5 2021-08-21 op
51 5391aec5 2021-08-21 op /* false signature inside a comment? */
52 5391aec5 2021-08-21 op if (clen + 22 != end - p) {
53 5391aec5 2021-08-21 op p--;
54 5391aec5 2021-08-21 op goto again;
55 5391aec5 2021-08-21 op }
56 5391aec5 2021-08-21 op
57 5391aec5 2021-08-21 op /* read the offset for the central directory */
58 5391aec5 2021-08-21 op memcpy(&offset, p + 16, sizeof(offset));
59 5391aec5 2021-08-21 op offset = le32toh(offset);
60 5391aec5 2021-08-21 op
61 5391aec5 2021-08-21 op if (addr + offset > p)
62 5391aec5 2021-08-21 op return NULL;
63 5391aec5 2021-08-21 op
64 5391aec5 2021-08-21 op return addr + offset;
65 5391aec5 2021-08-21 op }
66 5391aec5 2021-08-21 op
67 5391aec5 2021-08-21 op void
68 5391aec5 2021-08-21 op unzip_none(uint8_t *data, size_t size, unsigned long ocrc)
69 5391aec5 2021-08-21 op {
70 5391aec5 2021-08-21 op unsigned long crc = 0;
71 5391aec5 2021-08-21 op
72 5391aec5 2021-08-21 op fwrite(data, 1, size, stdout);
73 5391aec5 2021-08-21 op
74 5391aec5 2021-08-21 op crc = crc32(0, data, size);
75 5391aec5 2021-08-21 op if (crc != ocrc)
76 5391aec5 2021-08-21 op errx(1, "CRC mismatch");
77 5391aec5 2021-08-21 op }
78 5391aec5 2021-08-21 op
79 5391aec5 2021-08-21 op void
80 5391aec5 2021-08-21 op unzip_deflate(uint8_t *data, size_t size, unsigned long ocrc)
81 5391aec5 2021-08-21 op {
82 5391aec5 2021-08-21 op z_stream stream;
83 5391aec5 2021-08-21 op size_t have;
84 5391aec5 2021-08-21 op unsigned long crc = 0;
85 5391aec5 2021-08-21 op char buf[BUFSIZ];
86 5391aec5 2021-08-21 op
87 5391aec5 2021-08-21 op stream.zalloc = Z_NULL;
88 5391aec5 2021-08-21 op stream.zfree = Z_NULL;
89 5391aec5 2021-08-21 op stream.opaque = Z_NULL;
90 5391aec5 2021-08-21 op stream.next_in = data;
91 5391aec5 2021-08-21 op stream.avail_in = size;
92 5391aec5 2021-08-21 op stream.next_out = Z_NULL;
93 5391aec5 2021-08-21 op stream.avail_out = 0;
94 5391aec5 2021-08-21 op if (inflateInit2(&stream, -15) != Z_OK)
95 5391aec5 2021-08-21 op err(1, "inflateInit failed");
96 5391aec5 2021-08-21 op
97 5391aec5 2021-08-21 op do {
98 5391aec5 2021-08-21 op stream.next_out = buf;
99 5391aec5 2021-08-21 op stream.avail_out = sizeof(buf);
100 5391aec5 2021-08-21 op
101 5391aec5 2021-08-21 op switch (inflate(&stream, Z_BLOCK)) {
102 5391aec5 2021-08-21 op case Z_STREAM_ERROR:
103 5391aec5 2021-08-21 op errx(1, "stream error");
104 5391aec5 2021-08-21 op case Z_NEED_DICT:
105 5391aec5 2021-08-21 op errx(1, "need dict");
106 5391aec5 2021-08-21 op case Z_DATA_ERROR:
107 5391aec5 2021-08-21 op errx(1, "data error: %s", stream.msg);
108 5391aec5 2021-08-21 op case Z_MEM_ERROR:
109 5391aec5 2021-08-21 op errx(1, "memory error");
110 5391aec5 2021-08-21 op }
111 5391aec5 2021-08-21 op
112 5391aec5 2021-08-21 op have = sizeof(buf) - stream.avail_out;
113 5391aec5 2021-08-21 op fwrite(buf, 1, have, stdout);
114 5391aec5 2021-08-21 op crc = crc32(crc, buf, have);
115 5391aec5 2021-08-21 op } while (stream.avail_out == 0);
116 5391aec5 2021-08-21 op
117 5391aec5 2021-08-21 op inflateEnd(&stream);
118 5391aec5 2021-08-21 op
119 5391aec5 2021-08-21 op if (crc != ocrc)
120 5391aec5 2021-08-21 op errx(1, "CRC mismatch");
121 5391aec5 2021-08-21 op }
122 5391aec5 2021-08-21 op
123 5391aec5 2021-08-21 op void
124 5391aec5 2021-08-21 op unzip(uint8_t *zip, size_t len, uint8_t *entry)
125 5391aec5 2021-08-21 op {
126 5391aec5 2021-08-21 op uint32_t size, osize, crc, off;
127 5391aec5 2021-08-21 op uint16_t flags, compression;
128 5391aec5 2021-08-21 op uint16_t flen, xlen;
129 5391aec5 2021-08-21 op uint8_t *data, *offset;
130 5391aec5 2021-08-21 op
131 5391aec5 2021-08-21 op /* read the offset of the file record */
132 5391aec5 2021-08-21 op memcpy(&off, entry + 42, sizeof(off));
133 5391aec5 2021-08-21 op offset = zip + le32toh(off);
134 5391aec5 2021-08-21 op
135 5391aec5 2021-08-21 op if (offset > zip + len - 4 ||
136 5391aec5 2021-08-21 op memcmp(offset, "\x50\x4b\x03\x04", 4) != 0)
137 5391aec5 2021-08-21 op errx(1, "invalid offset or file header signature");
138 5391aec5 2021-08-21 op
139 5391aec5 2021-08-21 op memcpy(&flags, offset + 6, sizeof(flags));
140 5391aec5 2021-08-21 op memcpy(&compression, offset + 8, sizeof(compression));
141 5391aec5 2021-08-21 op
142 5391aec5 2021-08-21 op flags = le16toh(flags);
143 5391aec5 2021-08-21 op compression = le16toh(compression);
144 5391aec5 2021-08-21 op
145 5391aec5 2021-08-21 op memcpy(&crc, entry + 16, sizeof(crc));
146 5391aec5 2021-08-21 op memcpy(&size, entry + 20, sizeof(size));
147 5391aec5 2021-08-21 op memcpy(&osize, entry + 24, sizeof(osize));
148 5391aec5 2021-08-21 op
149 5391aec5 2021-08-21 op crc = le32toh(crc);
150 5391aec5 2021-08-21 op size = le32toh(size);
151 5391aec5 2021-08-21 op osize = le32toh(osize);
152 5391aec5 2021-08-21 op
153 5391aec5 2021-08-21 op memcpy(&flen, offset + 26, sizeof(flen));
154 5391aec5 2021-08-21 op memcpy(&xlen, offset + 28, sizeof(xlen));
155 5391aec5 2021-08-21 op
156 5391aec5 2021-08-21 op flen = le16toh(flen);
157 5391aec5 2021-08-21 op xlen = le16toh(xlen);
158 5391aec5 2021-08-21 op
159 5391aec5 2021-08-21 op data = offset + 30 + flen + xlen;
160 5391aec5 2021-08-21 op if (data + size > zip + len)
161 5391aec5 2021-08-21 op errx(1, "corrupted zip, offset out of file");
162 5391aec5 2021-08-21 op
163 5391aec5 2021-08-21 op switch (compression) {
164 5391aec5 2021-08-21 op case COMPRESSION_NONE:
165 5391aec5 2021-08-21 op unzip_none(data, size, crc);
166 5391aec5 2021-08-21 op break;
167 5391aec5 2021-08-21 op case COMPRESSION_DEFLATE:
168 5391aec5 2021-08-21 op unzip_deflate(data, size, crc);
169 5391aec5 2021-08-21 op break;
170 5391aec5 2021-08-21 op default:
171 5391aec5 2021-08-21 op errx(1, "unknown compression method 0x%02x",
172 5391aec5 2021-08-21 op compression);
173 5391aec5 2021-08-21 op }
174 5391aec5 2021-08-21 op }
175 5391aec5 2021-08-21 op
176 5391aec5 2021-08-21 op void *
177 5391aec5 2021-08-21 op next(uint8_t *zip, size_t len, uint8_t *entry)
178 5391aec5 2021-08-21 op {
179 5391aec5 2021-08-21 op uint16_t flen, xlen, clen;
180 5391aec5 2021-08-21 op uint8_t *next, *end;
181 5391aec5 2021-08-21 op
182 5391aec5 2021-08-21 op memcpy(&flen, entry + 28, sizeof(flen));
183 5391aec5 2021-08-21 op memcpy(&xlen, entry + 28 + 2, sizeof(xlen));
184 5391aec5 2021-08-21 op memcpy(&clen, entry + 28 + 2 + 2, sizeof(xlen));
185 5391aec5 2021-08-21 op
186 5391aec5 2021-08-21 op flen = le16toh(flen);
187 5391aec5 2021-08-21 op xlen = le16toh(xlen);
188 5391aec5 2021-08-21 op clen = le16toh(clen);
189 5391aec5 2021-08-21 op
190 5391aec5 2021-08-21 op next = entry + 46 + flen + xlen + clen;
191 5391aec5 2021-08-21 op end = zip + len;
192 5391aec5 2021-08-21 op if (next >= end - 46 ||
193 5391aec5 2021-08-21 op memcmp(next, "\x50\x4b\x01\x02", 4) != 0)
194 5391aec5 2021-08-21 op return NULL;
195 5391aec5 2021-08-21 op return next;
196 5391aec5 2021-08-21 op }
197 5391aec5 2021-08-21 op
198 5391aec5 2021-08-21 op void
199 5391aec5 2021-08-21 op filename(uint8_t *zip, size_t len, uint8_t *entry, char *buf,
200 5391aec5 2021-08-21 op size_t size)
201 5391aec5 2021-08-21 op {
202 5391aec5 2021-08-21 op uint16_t flen;
203 5391aec5 2021-08-21 op size_t s;
204 5391aec5 2021-08-21 op
205 5391aec5 2021-08-21 op memcpy(&flen, entry + 28, sizeof(flen));
206 5391aec5 2021-08-21 op flen = le16toh(flen);
207 5391aec5 2021-08-21 op
208 5391aec5 2021-08-21 op s = MIN(size-1, flen);
209 5391aec5 2021-08-21 op memcpy(buf, entry + 46, s);
210 5391aec5 2021-08-21 op buf[s] = '\0';
211 5391aec5 2021-08-21 op }
212 5391aec5 2021-08-21 op
213 5391aec5 2021-08-21 op void
214 5391aec5 2021-08-21 op ls(uint8_t *zip, size_t len, uint8_t *cd)
215 5391aec5 2021-08-21 op {
216 5391aec5 2021-08-21 op char name[PATH_MAX];
217 5391aec5 2021-08-21 op
218 5391aec5 2021-08-21 op do {
219 5391aec5 2021-08-21 op filename(zip, len, cd, name, sizeof(name));
220 5391aec5 2021-08-21 op printf("%s\n", name);
221 5391aec5 2021-08-21 op } while ((cd = next(zip, len, cd)) != NULL);
222 5391aec5 2021-08-21 op }
223 5391aec5 2021-08-21 op
224 5391aec5 2021-08-21 op void *
225 5391aec5 2021-08-21 op find_file(uint8_t *zip, size_t len, uint8_t *cd, const char *target)
226 5391aec5 2021-08-21 op {
227 5391aec5 2021-08-21 op char name[PATH_MAX];
228 5391aec5 2021-08-21 op
229 5391aec5 2021-08-21 op do {
230 5391aec5 2021-08-21 op filename(zip, len, cd, name, sizeof(name));
231 5391aec5 2021-08-21 op if (!strcmp(name, target))
232 5391aec5 2021-08-21 op return cd;
233 5391aec5 2021-08-21 op } while ((cd = next(zip, len, cd)) != NULL);
234 5391aec5 2021-08-21 op
235 5391aec5 2021-08-21 op return NULL;
236 5391aec5 2021-08-21 op }
237 5391aec5 2021-08-21 op
238 5391aec5 2021-08-21 op int
239 5391aec5 2021-08-21 op extract_file(uint8_t *zip, size_t len, uint8_t *cd, const char *target)
240 5391aec5 2021-08-21 op {
241 5391aec5 2021-08-21 op if ((cd = find_file(zip, len, cd, target)) == NULL)
242 5391aec5 2021-08-21 op return -1;
243 5391aec5 2021-08-21 op
244 5391aec5 2021-08-21 op unzip(zip, len, cd);
245 5391aec5 2021-08-21 op return 0;
246 5391aec5 2021-08-21 op }
247 5391aec5 2021-08-21 op
248 5391aec5 2021-08-21 op void *
249 5391aec5 2021-08-21 op map_file(int fd, size_t *len)
250 5391aec5 2021-08-21 op {
251 5391aec5 2021-08-21 op off_t jump;
252 5391aec5 2021-08-21 op void *addr;
253 5391aec5 2021-08-21 op
254 5391aec5 2021-08-21 op if ((jump = lseek(fd, 0, SEEK_END)) == -1)
255 5391aec5 2021-08-21 op err(1, "lseek");
256 5391aec5 2021-08-21 op
257 5391aec5 2021-08-21 op if (lseek(fd, 0, SEEK_SET) == -1)
258 5391aec5 2021-08-21 op err(1, "lseek");
259 5391aec5 2021-08-21 op
260 5391aec5 2021-08-21 op if ((addr = mmap(NULL, jump, PROT_READ, MAP_PRIVATE, fd, 0))
261 5391aec5 2021-08-21 op == MAP_FAILED)
262 5391aec5 2021-08-21 op err(1, "mmap");
263 5391aec5 2021-08-21 op
264 5391aec5 2021-08-21 op *len = jump;
265 5391aec5 2021-08-21 op return addr;
266 5391aec5 2021-08-21 op }
267 5391aec5 2021-08-21 op
268 5391aec5 2021-08-21 op int
269 5391aec5 2021-08-21 op main(int argc, char **argv)
270 5391aec5 2021-08-21 op {
271 5391aec5 2021-08-21 op int i, fd;
272 5391aec5 2021-08-21 op void *zip, *cd;
273 5391aec5 2021-08-21 op size_t len;
274 5391aec5 2021-08-21 op
275 5391aec5 2021-08-21 op if (argc < 2) {
276 5391aec5 2021-08-21 op fprintf(stderr, "Usage: %s archive.zip [files...]",
277 5391aec5 2021-08-21 op *argv);
278 5391aec5 2021-08-21 op return 1;
279 5391aec5 2021-08-21 op }
280 5391aec5 2021-08-21 op
281 5391aec5 2021-08-21 op if ((fd = open(argv[1], O_RDONLY)) == -1)
282 5391aec5 2021-08-21 op err(1, "can't open %s", argv[1]);
283 5391aec5 2021-08-21 op
284 5391aec5 2021-08-21 op zip = map_file(fd, &len);
285 5391aec5 2021-08-21 op
286 5391aec5 2021-08-21 op #ifdef __OpenBSD__
287 5391aec5 2021-08-21 op if (pledge("stdio", NULL) == -1)
288 5391aec5 2021-08-21 op err(1, "pledge");
289 5391aec5 2021-08-21 op #endif
290 5391aec5 2021-08-21 op
291 5391aec5 2021-08-21 op if ((cd = find_central_directory(zip, len)) == NULL)
292 5391aec5 2021-08-21 op errx(1, "can't find the central directory");
293 5391aec5 2021-08-21 op
294 5391aec5 2021-08-21 op if (argc == 2)
295 5391aec5 2021-08-21 op ls(zip, len, cd);
296 5391aec5 2021-08-21 op else {
297 5391aec5 2021-08-21 op for (i = 2; i < argc; ++i)
298 5391aec5 2021-08-21 op extract_file(zip, len, cd, argv[i]);
299 5391aec5 2021-08-21 op }
300 5391aec5 2021-08-21 op
301 5391aec5 2021-08-21 op munmap(zip, len);
302 5391aec5 2021-08-21 op close(fd);
303 5391aec5 2021-08-21 op
304 5391aec5 2021-08-21 op return 0;
305 5391aec5 2021-08-21 op }