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