601e31d08c7564996f3d67cdc1510b6e571c8cd6
[cacao.git] / src / vm / zip.cpp
1 /* src/vm/zip.cpp - ZIP file handling for bootstrap classloader
2
3    Copyright (C) 1996-2005, 2006, 2007, 2008
4    CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
5
6    This file is part of CACAO.
7
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.
12
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.
17
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
21    02110-1301, USA.
22
23 */
24
25
26 #include "config.h"
27
28 #include <assert.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <zlib.h>
33 #include <sys/mman.h>
34
35 #include "vm/types.h"
36
37 #include "vm/descriptor.hpp" /* needed to prevent circular dependency */
38 #include "toolbox/hashtable.h"
39
40 #include "mm/memory.hpp"
41
42 #include "vm/global.h"
43 #include "vm/suck.hpp"
44 #include "vm/utf8.h"
45 #include "vm/vm.hpp"
46 #include "vm/zip.hpp"
47
48
49 /* start size for classes hashtable *******************************************/
50
51 #define HASHTABLE_CLASSES_SIZE    (1 << 10)
52
53
54 /* info taken from:
55    http://www.pkware.com/business_and_developers/developer/popups/appnote.txt
56 */
57
58 /* all signatures in the ZIP file have a length of 4 bytes ********************/
59
60 #define SIGNATURE_LENGTH    4
61
62 /* Central directory structure *************************************************
63
64    [file header 1]
65    .
66    .
67    . 
68    [file header n]
69    [digital signature] 
70    
71    File header:
72    
73      central file header signature   4 bytes  (0x02014b50)
74      version made by                 2 bytes
75      version needed to extract       2 bytes
76      general purpose bit flag        2 bytes
77      compression method              2 bytes
78      last mod file time              2 bytes
79      last mod file date              2 bytes
80      crc-32                          4 bytes
81      compressed size                 4 bytes
82      uncompressed size               4 bytes
83      file name length                2 bytes
84      extra field length              2 bytes
85      file comment length             2 bytes
86      disk number start               2 bytes
87      internal file attributes        2 bytes
88      external file attributes        4 bytes
89      relative offset of local header 4 bytes
90    
91      file name (variable size)
92      extra field (variable size)
93      file comment (variable size)
94
95    Digital signature:
96    
97      header signature                4 bytes  (0x05054b50)
98      size of data                    2 bytes
99      signature data (variable size)
100
101 *******************************************************************************/
102
103 #define CDSFH_HEADER_SIZE            46
104
105 #define CDSFH_SIGNATURE              0x02014b50
106 #define CDSFH_COMPRESSION_METHOD     10
107 #define CDSFH_COMPRESSED_SIZE        20
108 #define CDSFH_UNCOMPRESSED_SIZE      24
109 #define CDSFH_FILE_NAME_LENGTH       28
110 #define CDSFH_EXTRA_FIELD_LENGTH     30
111 #define CDSFH_FILE_COMMENT_LENGTH    32
112 #define CDSFH_RELATIVE_OFFSET        42
113 #define CDSFH_FILENAME               46
114
115 typedef struct cdsfh cdsfh;
116
117 struct cdsfh {
118         u2 compressionmethod;
119         u4 compressedsize;
120         u4 uncompressedsize;
121         u2 filenamelength;
122         u2 extrafieldlength;
123         u2 filecommentlength;
124         u4 relativeoffset;
125 };
126
127
128 /* End of central directory record *********************************************
129
130    end of central dir signature    4 bytes  (0x06054b50)
131    number of this disk             2 bytes
132    number of the disk with the
133    start of the central directory  2 bytes
134    total number of entries in the
135    central directory on this disk  2 bytes
136    total number of entries in
137    the central directory           2 bytes
138    size of the central directory   4 bytes
139    offset of start of central
140    directory with respect to
141    the starting disk number        4 bytes
142    .ZIP file comment length        2 bytes
143    .ZIP file comment       (variable size)
144
145 *******************************************************************************/
146
147 #define EOCDR_SIGNATURE              0x06054b50
148 #define EOCDR_ENTRIES                10
149 #define EOCDR_OFFSET                 16
150
151 typedef struct eocdr eocdr;
152
153 struct eocdr {
154         u2 entries;
155         u4 offset;
156 };
157
158 #if defined(__cplusplus)
159 extern "C" {
160 #endif
161
162 /* zip_open ********************************************************************
163
164    XXX
165
166 *******************************************************************************/
167
168 hashtable *zip_open(char *path)
169 {
170         hashtable               *ht;
171         hashtable_zipfile_entry *htzfe;
172         int                      fd;
173         u1                       lfh_signature[SIGNATURE_LENGTH];
174         off_t                    len;
175         u1                      *filep;
176         s4                       i;
177         u1                      *p;
178         eocdr                    eocdr;
179         cdsfh                    cdsfh;
180         const char              *filename;
181         const char              *classext;
182         utf                     *u;
183         u4                       key;       /* hashkey computed from utf-text     */
184         u4                       slot;      /* slot in hashtable                  */
185
186         /* first of all, open the file */
187
188         if ((fd = open(path, O_RDONLY)) == -1)
189                 return NULL;
190
191         /* check for signature in first local file header */
192
193         if (read(fd, lfh_signature, SIGNATURE_LENGTH) != SIGNATURE_LENGTH)
194                 return NULL;
195
196         if (SUCK_LE_U4(lfh_signature) != LFH_SIGNATURE)
197                 return NULL;
198
199         /* get the file length */
200
201         if ((len = lseek(fd, 0, SEEK_END)) == -1)
202                 return NULL;
203
204         /* we better mmap the file */
205
206         filep = (u1*) mmap(0, len, PROT_READ, MAP_PRIVATE, fd, 0);
207
208         /* some older compilers, like DEC OSF cc, don't like comparisons
209        on void* types */
210
211         if ((ptrint) filep == (ptrint) MAP_FAILED)
212                 return NULL;
213
214         /* find end of central directory record */
215
216         for (p = filep + len; p >= filep; p--)
217                 if (SUCK_LE_U4(p) == EOCDR_SIGNATURE)
218                         break;
219
220         /* get number of entries in central directory */
221
222         eocdr.entries = SUCK_LE_U2(p + EOCDR_ENTRIES);
223         eocdr.offset  = SUCK_LE_U4(p + EOCDR_OFFSET);
224
225         /* create hashtable for filenames */
226
227         ht = NEW(hashtable);
228
229         hashtable_create(ht, HASHTABLE_CLASSES_SIZE);
230
231         /* add all file entries into the hashtable */
232
233         for (i = 0, p = filep + eocdr.offset; i < eocdr.entries; i++) {
234                 /* check file header signature */
235
236                 if (SUCK_LE_U4(p) != CDSFH_SIGNATURE)
237                         return NULL;
238
239                 /* we found an entry */
240
241                 cdsfh.compressionmethod = SUCK_LE_U2(p + CDSFH_COMPRESSION_METHOD);
242                 cdsfh.compressedsize    = SUCK_LE_U4(p + CDSFH_COMPRESSED_SIZE);
243                 cdsfh.uncompressedsize  = SUCK_LE_U4(p + CDSFH_UNCOMPRESSED_SIZE);
244                 cdsfh.filenamelength    = SUCK_LE_U2(p + CDSFH_FILE_NAME_LENGTH);
245                 cdsfh.extrafieldlength  = SUCK_LE_U2(p + CDSFH_EXTRA_FIELD_LENGTH);
246                 cdsfh.filecommentlength = SUCK_LE_U2(p + CDSFH_FILE_COMMENT_LENGTH);
247                 cdsfh.relativeoffset    = SUCK_LE_U4(p + CDSFH_RELATIVE_OFFSET);
248
249                 /* create utf8 string of filename, strip .class from classes */
250
251                 filename = (const char *) (p + CDSFH_FILENAME);
252                 classext = filename + cdsfh.filenamelength - strlen(".class");
253
254                 /* skip directory entries */
255
256                 if (filename[cdsfh.filenamelength - 1] != '/') {
257                         if (strncmp(classext, ".class", strlen(".class")) == 0)
258                                 u = utf_new(filename, cdsfh.filenamelength - strlen(".class"));
259                         else
260                                 u = utf_new(filename, cdsfh.filenamelength);
261
262                         /* insert class into hashtable */
263
264                         htzfe = NEW(hashtable_zipfile_entry);
265
266                         htzfe->filename          = u;
267                         htzfe->compressionmethod = cdsfh.compressionmethod;
268                         htzfe->compressedsize    = cdsfh.compressedsize;
269                         htzfe->uncompressedsize  = cdsfh.uncompressedsize;
270                         htzfe->data              = filep + cdsfh.relativeoffset;
271
272                         /* get hashtable slot */
273
274                         key  = utf_hashkey(u->text, u->blength);
275                         slot = key & (ht->size - 1);
276
277                         /* insert into external chain */
278
279                         htzfe->hashlink = (hashtable_zipfile_entry*) ht->ptr[slot];
280
281                         /* insert hashtable zipfile entry */
282
283                         ht->ptr[slot] = htzfe;
284                         ht->entries++;
285                 }
286
287                 /* move to next central directory structure file header */
288
289                 p = p +
290                         CDSFH_HEADER_SIZE +
291                         cdsfh.filenamelength +
292                         cdsfh.extrafieldlength +
293                         cdsfh.filecommentlength;
294         }
295
296         /* return pointer to hashtable */
297
298         return ht;
299 }
300
301
302 /* zip_find ********************************************************************
303
304    Search for the given filename in the classpath entries of a zip file.
305
306    NOTE: The '.class' extension is stripped when reading a zip file, so if
307    you want to find a .class file, you must search for its name _without_
308    the '.class' extension. 
309    XXX I dont like that, it makes foo and foo.class ambiguous. -Edwin
310
311    IN:
312       lce..........the classpath entries for the zip file
313           u............the filename to look for
314
315    RETURN VALUE:
316       hashtable_zipfile_entry * of the entry if found, or
317           NULL if not found
318
319 *******************************************************************************/
320
321 hashtable_zipfile_entry *zip_find(list_classpath_entry *lce, utf *u)
322 {
323         hashtable               *ht;
324         u4                       key;       /* hashkey computed from utf-text     */
325         u4                       slot;      /* slot in hashtable                  */
326         hashtable_zipfile_entry *htzfe;     /* hashtable element                  */
327
328         /* get classes hashtable from the classpath entry */
329
330         ht = lce->htclasses;
331
332         /* get the hashtable slot of the name searched */
333
334         key   = utf_hashkey(u->text, u->blength);
335         slot  = key & (ht->size - 1);
336         htzfe = (hashtable_zipfile_entry*) ht->ptr[slot];
337
338         /* search external hash chain for utf-symbol */
339
340         while (htzfe) {
341                 if (htzfe->filename == u)
342                         return htzfe;
343
344                 /* next element in external chain */
345
346                 htzfe = htzfe->hashlink;
347         }
348
349         /* file not found in this archive */
350
351         return NULL;
352 }
353
354
355 /* zip_get ********************************************************************
356
357    XXX
358
359 *******************************************************************************/
360
361 classbuffer *zip_get(list_classpath_entry *lce, classinfo *c)
362 {
363         hashtable_zipfile_entry *htzfe;
364         lfh                      lfh;
365         u1                      *indata;
366         u1                      *outdata;
367         z_stream                 zs;
368         int                      err;
369         classbuffer             *cb;
370
371         /* try to find the class in the current archive */
372
373         htzfe = zip_find(lce, c->name);
374
375         if (htzfe == NULL)
376                 return NULL;
377
378         /* read stuff from local file header */
379
380         lfh.filenamelength   = SUCK_LE_U2(htzfe->data + LFH_FILE_NAME_LENGTH);
381         lfh.extrafieldlength = SUCK_LE_U2(htzfe->data + LFH_EXTRA_FIELD_LENGTH);
382
383         indata = htzfe->data +
384                 LFH_HEADER_SIZE +
385                 lfh.filenamelength +
386                 lfh.extrafieldlength;
387
388         /* allocate buffer for uncompressed data */
389
390         outdata = MNEW(u1, htzfe->uncompressedsize);
391
392         /* how is the file stored? */
393
394         switch (htzfe->compressionmethod) {
395         case Z_DEFLATED:
396                 /* fill z_stream structure */
397
398                 zs.next_in   = indata;
399                 zs.avail_in  = htzfe->compressedsize;
400                 zs.next_out  = outdata;
401                 zs.avail_out = htzfe->uncompressedsize;
402
403                 zs.zalloc = Z_NULL;
404                 zs.zfree  = Z_NULL;
405                 zs.opaque = Z_NULL;
406
407                 /* initialize this inflate run */
408
409                 if (inflateInit2(&zs, -MAX_WBITS) != Z_OK)
410                         vm_abort("zip_get: inflateInit2 failed: %s", strerror(errno));
411
412                 /* decompress the file into buffer */
413
414                 err = inflate(&zs, Z_SYNC_FLUSH);
415
416                 if ((err != Z_STREAM_END) && (err != Z_OK))
417                         vm_abort("zip_get: inflate failed: %s", strerror(errno));
418
419                 /* finish this inflate run */
420
421                 if (inflateEnd(&zs) != Z_OK)
422                         vm_abort("zip_get: inflateEnd failed: %s", strerror(errno));
423                 break;
424
425         case 0:
426                 /* uncompressed file, just copy the data */
427                 MCOPY(outdata, indata, u1, htzfe->compressedsize);
428                 break;
429
430         default:
431                 vm_abort("zip_get: unknown compression method %d",
432                                  htzfe->compressionmethod);
433         }
434         
435         /* allocate classbuffer */
436
437         cb = NEW(classbuffer);
438
439         cb->clazz = c;
440         cb->size  = htzfe->uncompressedsize;
441         cb->data  = outdata;
442         cb->pos   = outdata;
443         cb->path  = lce->path;
444
445         /* return the filled classbuffer structure */
446
447         return cb;
448 }
449
450 #if defined(__cplusplus)
451 }
452 #endif
453
454 /*
455  * These are local overrides for various environment variables in Emacs.
456  * Please do not remove this and leave it at the end of the file, where
457  * Emacs will automagically detect them.
458  * ---------------------------------------------------------------------
459  * Local variables:
460  * mode: c++
461  * indent-tabs-mode: t
462  * c-basic-offset: 4
463  * tab-width: 4
464  * End:
465  * vim:noexpandtab:sw=4:ts=4:
466  */