1 /* src/vmcore/zip.c - ZIP file handling for bootstrap classloader
3 Copyright (C) 1996-2005, 2006, 2007 R. Grafl, A. Krall, C. Kruegel,
4 C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring,
5 E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich,
6 J. Wenninger, Institut f. Computersprachen - TU Wien
8 This file is part of CACAO.
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License as
12 published by the Free Software Foundation; either version 2, or (at
13 your option) any later version.
15 This program is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 $Id: zip.c 7548 2007-03-21 13:19:44Z twisti $
41 #include "toolbox/hashtable.h"
43 #include "mm/memory.h"
45 #include "vm/global.h"
48 #include "vmcore/suck.h"
49 #include "vmcore/utf8.h"
50 #include "vmcore/zip.h"
53 /* start size for classes hashtable *******************************************/
55 #define HASHTABLE_CLASSES_SIZE (1 << 10)
59 http://www.pkware.com/business_and_developers/developer/popups/appnote.txt
62 /* all signatures in the ZIP file have a length of 4 bytes ********************/
64 #define SIGNATURE_LENGTH 4
67 /* Local file header ***********************************************************
69 local file header signature 4 bytes (0x04034b50)
70 version needed to extract 2 bytes
71 general purpose bit flag 2 bytes
72 compression method 2 bytes
73 last mod file time 2 bytes
74 last mod file date 2 bytes
76 compressed size 4 bytes
77 uncompressed size 4 bytes
78 file name length 2 bytes
79 extra field length 2 bytes
81 file name (variable size)
82 extra field (variable size)
84 *******************************************************************************/
86 #define LFH_HEADER_SIZE 30
88 #define LFH_SIGNATURE 0x04034b50
89 #define LFH_FILE_NAME_LENGTH 26
90 #define LFH_EXTRA_FIELD_LENGTH 28
92 typedef struct lfh lfh;
103 /* Central directory structure *************************************************
114 central file header signature 4 bytes (0x02014b50)
115 version made by 2 bytes
116 version needed to extract 2 bytes
117 general purpose bit flag 2 bytes
118 compression method 2 bytes
119 last mod file time 2 bytes
120 last mod file date 2 bytes
122 compressed size 4 bytes
123 uncompressed size 4 bytes
124 file name length 2 bytes
125 extra field length 2 bytes
126 file comment length 2 bytes
127 disk number start 2 bytes
128 internal file attributes 2 bytes
129 external file attributes 4 bytes
130 relative offset of local header 4 bytes
132 file name (variable size)
133 extra field (variable size)
134 file comment (variable size)
138 header signature 4 bytes (0x05054b50)
140 signature data (variable size)
142 *******************************************************************************/
144 #define CDSFH_HEADER_SIZE 46
146 #define CDSFH_SIGNATURE 0x02014b50
147 #define CDSFH_COMPRESSION_METHOD 10
148 #define CDSFH_COMPRESSED_SIZE 20
149 #define CDSFH_UNCOMPRESSED_SIZE 24
150 #define CDSFH_FILE_NAME_LENGTH 28
151 #define CDSFH_EXTRA_FIELD_LENGTH 30
152 #define CDSFH_FILE_COMMENT_LENGTH 32
153 #define CDSFH_RELATIVE_OFFSET 42
154 #define CDSFH_FILENAME 46
156 typedef struct cdsfh cdsfh;
159 u2 compressionmethod;
164 u2 filecommentlength;
169 /* End of central directory record *********************************************
171 end of central dir signature 4 bytes (0x06054b50)
172 number of this disk 2 bytes
173 number of the disk with the
174 start of the central directory 2 bytes
175 total number of entries in the
176 central directory on this disk 2 bytes
177 total number of entries in
178 the central directory 2 bytes
179 size of the central directory 4 bytes
180 offset of start of central
181 directory with respect to
182 the starting disk number 4 bytes
183 .ZIP file comment length 2 bytes
184 .ZIP file comment (variable size)
186 *******************************************************************************/
188 #define EOCDR_SIGNATURE 0x06054b50
189 #define EOCDR_ENTRIES 10
190 #define EOCDR_OFFSET 16
192 typedef struct eocdr eocdr;
200 /* zip_open ********************************************************************
204 *******************************************************************************/
206 hashtable *zip_open(char *path)
209 hashtable_zipfile_entry *htzfe;
211 u1 lfh_signature[SIGNATURE_LENGTH];
218 const char *filename;
219 const char *classext;
221 u4 key; /* hashkey computed from utf-text */
222 u4 slot; /* slot in hashtable */
224 /* first of all, open the file */
226 if ((fd = open(path, O_RDONLY)) == -1)
229 /* check for signature in first local file header */
231 if (read(fd, lfh_signature, SIGNATURE_LENGTH) != SIGNATURE_LENGTH)
234 if (SUCK_LE_U4(lfh_signature) != LFH_SIGNATURE)
237 /* get the file length */
239 if ((len = lseek(fd, 0, SEEK_END)) == -1)
242 /* we better mmap the file */
244 filep = mmap(0, len, PROT_READ, MAP_PRIVATE, fd, 0);
246 /* some older compilers, like DEC OSF cc, don't like comparisons
249 if ((ptrint) filep == (ptrint) MAP_FAILED)
252 /* find end of central directory record */
254 for (p = filep + len; p >= filep; p--)
255 if (SUCK_LE_U4(p) == EOCDR_SIGNATURE)
258 /* get number of entries in central directory */
260 eocdr.entries = SUCK_LE_U2(p + EOCDR_ENTRIES);
261 eocdr.offset = SUCK_LE_U4(p + EOCDR_OFFSET);
263 /* create hashtable for filenames */
267 hashtable_create(ht, HASHTABLE_CLASSES_SIZE);
269 /* add all file entries into the hashtable */
271 for (i = 0, p = filep + eocdr.offset; i < eocdr.entries; i++) {
272 /* check file header signature */
274 if (SUCK_LE_U4(p) != CDSFH_SIGNATURE)
277 /* we found an entry */
279 cdsfh.compressionmethod = SUCK_LE_U2(p + CDSFH_COMPRESSION_METHOD);
280 cdsfh.compressedsize = SUCK_LE_U4(p + CDSFH_COMPRESSED_SIZE);
281 cdsfh.uncompressedsize = SUCK_LE_U4(p + CDSFH_UNCOMPRESSED_SIZE);
282 cdsfh.filenamelength = SUCK_LE_U2(p + CDSFH_FILE_NAME_LENGTH);
283 cdsfh.extrafieldlength = SUCK_LE_U2(p + CDSFH_EXTRA_FIELD_LENGTH);
284 cdsfh.filecommentlength = SUCK_LE_U2(p + CDSFH_FILE_COMMENT_LENGTH);
285 cdsfh.relativeoffset = SUCK_LE_U4(p + CDSFH_RELATIVE_OFFSET);
287 /* create utf8 string of filename, strip .class from classes */
289 filename = (const char *) (p + CDSFH_FILENAME);
290 classext = filename + cdsfh.filenamelength - strlen(".class");
292 /* skip directory entries */
294 if (filename[cdsfh.filenamelength - 1] != '/') {
295 if (strncmp(classext, ".class", strlen(".class")) == 0)
296 u = utf_new(filename, cdsfh.filenamelength - strlen(".class"));
298 u = utf_new(filename, cdsfh.filenamelength);
300 /* insert class into hashtable */
302 htzfe = NEW(hashtable_zipfile_entry);
305 htzfe->compressionmethod = cdsfh.compressionmethod;
306 htzfe->compressedsize = cdsfh.compressedsize;
307 htzfe->uncompressedsize = cdsfh.uncompressedsize;
308 htzfe->data = filep + cdsfh.relativeoffset;
310 /* get hashtable slot */
312 key = utf_hashkey(u->text, u->blength);
313 slot = key & (ht->size - 1);
315 /* insert into external chain */
317 htzfe->hashlink = ht->ptr[slot];
319 /* insert hashtable zipfile entry */
321 ht->ptr[slot] = htzfe;
325 /* move to next central directory structure file header */
329 cdsfh.filenamelength +
330 cdsfh.extrafieldlength +
331 cdsfh.filecommentlength;
334 /* return pointer to hashtable */
340 /* zip_find ********************************************************************
342 Search for the given filename in the classpath entries of a zip file.
344 NOTE: The '.class' extension is stripped when reading a zip file, so if
345 you want to find a .class file, you must search for its name _without_
346 the '.class' extension.
347 XXX I dont like that, it makes foo and foo.class ambiguous. -Edwin
350 lce..........the classpath entries for the zip file
351 u............the filename to look for
354 hashtable_zipfile_entry * of the entry if found, or
357 *******************************************************************************/
359 hashtable_zipfile_entry *zip_find(list_classpath_entry *lce, utf *u)
362 u4 key; /* hashkey computed from utf-text */
363 u4 slot; /* slot in hashtable */
364 hashtable_zipfile_entry *htzfe; /* hashtable element */
366 /* get classes hashtable from the classpath entry */
370 /* get the hashtable slot of the name searched */
372 key = utf_hashkey(u->text, u->blength);
373 slot = key & (ht->size - 1);
374 htzfe = ht->ptr[slot];
376 /* search external hash chain for utf-symbol */
379 if (htzfe->filename == u)
382 /* next element in external chain */
384 htzfe = htzfe->hashlink;
387 /* file not found in this archive */
393 /* zip_get ********************************************************************
397 *******************************************************************************/
399 classbuffer *zip_get(list_classpath_entry *lce, classinfo *c)
401 hashtable_zipfile_entry *htzfe;
409 /* try to find the class in the current archive */
411 htzfe = zip_find(lce, c->name);
416 /* read stuff from local file header */
418 lfh.filenamelength = SUCK_LE_U2(htzfe->data + LFH_FILE_NAME_LENGTH);
419 lfh.extrafieldlength = SUCK_LE_U2(htzfe->data + LFH_EXTRA_FIELD_LENGTH);
421 indata = htzfe->data +
424 lfh.extrafieldlength;
426 /* allocate buffer for uncompressed data */
428 outdata = MNEW(u1, htzfe->uncompressedsize);
430 /* how is the file stored? */
432 switch (htzfe->compressionmethod) {
434 /* fill z_stream structure */
437 zs.avail_in = htzfe->compressedsize;
438 zs.next_out = outdata;
439 zs.avail_out = htzfe->uncompressedsize;
445 /* initialize this inflate run */
447 if (inflateInit2(&zs, -MAX_WBITS) != Z_OK)
448 vm_abort("zip_get: inflateInit2 failed: %s", strerror(errno));
450 /* decompress the file into buffer */
452 err = inflate(&zs, Z_SYNC_FLUSH);
454 if ((err != Z_STREAM_END) && (err != Z_OK))
455 vm_abort("zip_get: inflate failed: %s", strerror(errno));
457 /* finish this inflate run */
459 if (inflateEnd(&zs) != Z_OK)
460 vm_abort("zip_get: inflateEnd failed: %s", strerror(errno));
464 /* uncompressed file, just copy the data */
465 MCOPY(outdata, indata, u1, htzfe->compressedsize);
469 vm_abort("zip_get: unknown compression method %d",
470 htzfe->compressionmethod);
473 /* allocate classbuffer */
475 cb = NEW(classbuffer);
478 cb->size = htzfe->uncompressedsize;
481 cb->path = lce->path;
483 /* return the filled classbuffer structure */
490 * These are local overrides for various environment variables in Emacs.
491 * Please do not remove this and leave it at the end of the file, where
492 * Emacs will automagically detect them.
493 * ---------------------------------------------------------------------
496 * indent-tabs-mode: t
500 * vim:noexpandtab:sw=4:ts=4: