1 /* src/vm/zip.cpp - ZIP file handling for bootstrap classloader
3 Copyright (C) 1996-2005, 2006, 2007, 2008
4 CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
6 This file is part of CACAO.
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2, or (at
11 your option) any later version.
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
35 #include "vm/descriptor.hpp" /* needed to prevent circular dependency */
36 #include "toolbox/hashtable.h"
38 #include "mm/memory.hpp"
40 #include "vm/global.h"
42 #include "vm/suck.hpp"
48 /* start size for classes hashtable *******************************************/
50 #define HASHTABLE_CLASSES_SIZE (1 << 10)
54 http://www.pkware.com/business_and_developers/developer/popups/appnote.txt
57 /* all signatures in the ZIP file have a length of 4 bytes ********************/
59 #define SIGNATURE_LENGTH 4
61 /* Central directory structure *************************************************
72 central file header signature 4 bytes (0x02014b50)
73 version made by 2 bytes
74 version needed to extract 2 bytes
75 general purpose bit flag 2 bytes
76 compression method 2 bytes
77 last mod file time 2 bytes
78 last mod file date 2 bytes
80 compressed size 4 bytes
81 uncompressed size 4 bytes
82 file name length 2 bytes
83 extra field length 2 bytes
84 file comment length 2 bytes
85 disk number start 2 bytes
86 internal file attributes 2 bytes
87 external file attributes 4 bytes
88 relative offset of local header 4 bytes
90 file name (variable size)
91 extra field (variable size)
92 file comment (variable size)
96 header signature 4 bytes (0x05054b50)
98 signature data (variable size)
100 *******************************************************************************/
102 #define CDSFH_HEADER_SIZE 46
104 #define CDSFH_SIGNATURE 0x02014b50
105 #define CDSFH_COMPRESSION_METHOD 10
106 #define CDSFH_COMPRESSED_SIZE 20
107 #define CDSFH_UNCOMPRESSED_SIZE 24
108 #define CDSFH_FILE_NAME_LENGTH 28
109 #define CDSFH_EXTRA_FIELD_LENGTH 30
110 #define CDSFH_FILE_COMMENT_LENGTH 32
111 #define CDSFH_RELATIVE_OFFSET 42
112 #define CDSFH_FILENAME 46
114 typedef struct cdsfh cdsfh;
117 u2 compressionmethod;
122 u2 filecommentlength;
127 /* End of central directory record *********************************************
129 end of central dir signature 4 bytes (0x06054b50)
130 number of this disk 2 bytes
131 number of the disk with the
132 start of the central directory 2 bytes
133 total number of entries in the
134 central directory on this disk 2 bytes
135 total number of entries in
136 the central directory 2 bytes
137 size of the central directory 4 bytes
138 offset of start of central
139 directory with respect to
140 the starting disk number 4 bytes
141 .ZIP file comment length 2 bytes
142 .ZIP file comment (variable size)
144 *******************************************************************************/
146 #define EOCDR_SIGNATURE 0x06054b50
147 #define EOCDR_ENTRIES 10
148 #define EOCDR_OFFSET 16
150 typedef struct eocdr eocdr;
157 #if defined(__cplusplus)
161 /* zip_open ********************************************************************
165 *******************************************************************************/
167 hashtable *zip_open(char *path)
170 hashtable_zipfile_entry *htzfe;
172 u1 lfh_signature[SIGNATURE_LENGTH];
179 const char *filename;
180 const char *classext;
182 u4 key; /* hashkey computed from utf-text */
183 u4 slot; /* slot in hashtable */
185 /* first of all, open the file */
187 if ((fd = open(path, O_RDONLY)) == -1)
190 /* check for signature in first local file header */
192 if (read(fd, lfh_signature, SIGNATURE_LENGTH) != SIGNATURE_LENGTH)
195 if (SUCK_LE_U4(lfh_signature) != LFH_SIGNATURE)
198 /* get the file length */
200 if ((len = lseek(fd, 0, SEEK_END)) == -1)
203 /* we better mmap the file */
205 filep = (u1*) mmap(0, len, PROT_READ, MAP_PRIVATE, fd, 0);
207 /* some older compilers, like DEC OSF cc, don't like comparisons
210 if ((ptrint) filep == (ptrint) MAP_FAILED)
213 /* find end of central directory record */
215 for (p = filep + len; p >= filep; p--)
216 if (SUCK_LE_U4(p) == EOCDR_SIGNATURE)
219 /* get number of entries in central directory */
221 eocdr.entries = SUCK_LE_U2(p + EOCDR_ENTRIES);
222 eocdr.offset = SUCK_LE_U4(p + EOCDR_OFFSET);
224 /* create hashtable for filenames */
228 hashtable_create(ht, HASHTABLE_CLASSES_SIZE);
230 /* add all file entries into the hashtable */
232 for (i = 0, p = filep + eocdr.offset; i < eocdr.entries; i++) {
233 /* check file header signature */
235 if (SUCK_LE_U4(p) != CDSFH_SIGNATURE)
238 /* we found an entry */
240 cdsfh.compressionmethod = SUCK_LE_U2(p + CDSFH_COMPRESSION_METHOD);
241 cdsfh.compressedsize = SUCK_LE_U4(p + CDSFH_COMPRESSED_SIZE);
242 cdsfh.uncompressedsize = SUCK_LE_U4(p + CDSFH_UNCOMPRESSED_SIZE);
243 cdsfh.filenamelength = SUCK_LE_U2(p + CDSFH_FILE_NAME_LENGTH);
244 cdsfh.extrafieldlength = SUCK_LE_U2(p + CDSFH_EXTRA_FIELD_LENGTH);
245 cdsfh.filecommentlength = SUCK_LE_U2(p + CDSFH_FILE_COMMENT_LENGTH);
246 cdsfh.relativeoffset = SUCK_LE_U4(p + CDSFH_RELATIVE_OFFSET);
248 /* create utf8 string of filename, strip .class from classes */
250 filename = (const char *) (p + CDSFH_FILENAME);
251 classext = filename + cdsfh.filenamelength - strlen(".class");
253 /* skip directory entries */
255 if (filename[cdsfh.filenamelength - 1] != '/') {
256 if (strncmp(classext, ".class", strlen(".class")) == 0)
257 u = utf_new(filename, cdsfh.filenamelength - strlen(".class"));
259 u = utf_new(filename, cdsfh.filenamelength);
261 /* insert class into hashtable */
263 htzfe = NEW(hashtable_zipfile_entry);
266 htzfe->compressionmethod = cdsfh.compressionmethod;
267 htzfe->compressedsize = cdsfh.compressedsize;
268 htzfe->uncompressedsize = cdsfh.uncompressedsize;
269 htzfe->data = filep + cdsfh.relativeoffset;
271 /* get hashtable slot */
273 key = utf_hashkey(u->text, u->blength);
274 slot = key & (ht->size - 1);
276 /* insert into external chain */
278 htzfe->hashlink = (hashtable_zipfile_entry*) ht->ptr[slot];
280 /* insert hashtable zipfile entry */
282 ht->ptr[slot] = htzfe;
286 /* move to next central directory structure file header */
290 cdsfh.filenamelength +
291 cdsfh.extrafieldlength +
292 cdsfh.filecommentlength;
295 /* return pointer to hashtable */
301 /* zip_find ********************************************************************
303 Search for the given filename in the classpath entries of a zip file.
305 NOTE: The '.class' extension is stripped when reading a zip file, so if
306 you want to find a .class file, you must search for its name _without_
307 the '.class' extension.
308 XXX I dont like that, it makes foo and foo.class ambiguous. -Edwin
311 lce..........the classpath entries for the zip file
312 u............the filename to look for
315 hashtable_zipfile_entry * of the entry if found, or
318 *******************************************************************************/
320 hashtable_zipfile_entry *zip_find(list_classpath_entry *lce, utf *u)
323 u4 key; /* hashkey computed from utf-text */
324 u4 slot; /* slot in hashtable */
325 hashtable_zipfile_entry *htzfe; /* hashtable element */
327 /* get classes hashtable from the classpath entry */
331 /* get the hashtable slot of the name searched */
333 key = utf_hashkey(u->text, u->blength);
334 slot = key & (ht->size - 1);
335 htzfe = (hashtable_zipfile_entry*) ht->ptr[slot];
337 /* search external hash chain for utf-symbol */
340 if (htzfe->filename == u)
343 /* next element in external chain */
345 htzfe = htzfe->hashlink;
348 /* file not found in this archive */
354 /* zip_get ********************************************************************
358 *******************************************************************************/
360 classbuffer *zip_get(list_classpath_entry *lce, classinfo *c)
362 hashtable_zipfile_entry *htzfe;
370 /* try to find the class in the current archive */
372 htzfe = zip_find(lce, c->name);
377 /* read stuff from local file header */
379 lfh.filenamelength = SUCK_LE_U2(htzfe->data + LFH_FILE_NAME_LENGTH);
380 lfh.extrafieldlength = SUCK_LE_U2(htzfe->data + LFH_EXTRA_FIELD_LENGTH);
382 indata = htzfe->data +
385 lfh.extrafieldlength;
387 /* allocate buffer for uncompressed data */
389 outdata = MNEW(u1, htzfe->uncompressedsize);
391 /* how is the file stored? */
393 switch (htzfe->compressionmethod) {
395 /* fill z_stream structure */
398 zs.avail_in = htzfe->compressedsize;
399 zs.next_out = outdata;
400 zs.avail_out = htzfe->uncompressedsize;
406 /* initialize this inflate run */
408 if (inflateInit2(&zs, -MAX_WBITS) != Z_OK)
409 vm_abort("zip_get: inflateInit2 failed: %s", strerror(errno));
411 /* decompress the file into buffer */
413 err = inflate(&zs, Z_SYNC_FLUSH);
415 if ((err != Z_STREAM_END) && (err != Z_OK))
416 vm_abort("zip_get: inflate failed: %s", strerror(errno));
418 /* finish this inflate run */
420 if (inflateEnd(&zs) != Z_OK)
421 vm_abort("zip_get: inflateEnd failed: %s", strerror(errno));
425 /* uncompressed file, just copy the data */
426 MCOPY(outdata, indata, u1, htzfe->compressedsize);
430 vm_abort("zip_get: unknown compression method %d",
431 htzfe->compressionmethod);
434 /* allocate classbuffer */
436 cb = NEW(classbuffer);
439 cb->size = htzfe->uncompressedsize;
442 cb->path = lce->path;
444 /* return the filled classbuffer structure */
449 #if defined(__cplusplus)
454 * These are local overrides for various environment variables in Emacs.
455 * Please do not remove this and leave it at the end of the file, where
456 * Emacs will automagically detect them.
457 * ---------------------------------------------------------------------
460 * indent-tabs-mode: t
464 * vim:noexpandtab:sw=4:ts=4: