2 * Copyright (C) 2003, 2004, 2005, 2006, 2007
3 * Robert Lougher <rob@lougher.org.uk>.
5 * This file is part of JamVM.
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2,
10 * or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
32 #include <sys/types.h>
36 /* Definitions needed for hashtable */
37 #define HASHTABSZE 1<<8
38 #define HASH(ptr) utf8Hash(ptr)
39 #define COMPARE(ptr1, ptr2, hash1, hash2) (hash1 == hash2) && utf8Comp(ptr1, ptr2)
40 #define PREPARE(ptr) ptr
41 #define SCAVENGE(ptr) FALSE
42 #define FOUND(ptr) ptr
44 /* zlib window size */
45 #define MAX_WINDOW_BITS 15
47 /* Macros for reading non-aligned little-endian values from zip file */
48 #define READ_LE_INT(p) ((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24))
49 #define READ_LE_SHORT(p) ((p)[0]|((p)[1]<<8))
51 /* Offsets and lengths of fields within the zip file format */
55 /* End of central directory record */
56 #define END_CEN_SIG 0x06054b50
57 #define END_CEN_LEN 22
58 #define END_CEN_ENTRIES_OFFSET 8
59 #define END_CEN_DIR_START_OFFSET 16
61 /* Central directory file header */
62 #define CEN_FILE_HEADER_SIG 0x02014b50
63 #define CEN_FILE_HEADER_LEN 46
64 #define CEN_FILE_COMPMETH_OFFSET 10
65 #define CEN_FILE_COMPLEN_OFFSET 20
66 #define CEN_FILE_UNCOMPLEN_OFFSET 24
67 #define CEN_FILE_PATHLEN_OFFSET 28
68 #define CEN_FILE_EXTRALEN_OFFSET 30
69 #define CEN_FILE_COMMENTLEN_OFFSET 32
70 #define CEN_FILE_LOCALHDR_OFFSET 42
72 /* Local file header */
73 #define LOC_FILE_HEADER_SIG 0x04034b50
74 #define LOC_FILE_HEADER_LEN 30
75 #define LOC_FILE_EXTRA_OFFSET 28
77 /* Supported compression methods */
79 #define COMP_DEFLATED 8
81 ZipFile
*processArchive(char *path
) {
82 unsigned char magic
[SIG_LEN
];
83 unsigned char *data
, *pntr
;
87 HashTable
*hash_table
;
90 if((fd
= open(path
, O_RDONLY
)) == -1)
93 /* First 4 bytes must be the signature for the first local file header */
94 if(read(fd
, &magic
[0], SIG_LEN
) != SIG_LEN
95 || READ_LE_INT(magic
) != LOC_FILE_HEADER_SIG
)
99 len
= lseek(fd
, 0, SEEK_END
);
101 /* Mmap the file into memory */
102 if((data
= (unsigned char*)mmap(0, len
, PROT_READ
| PROT_WRITE
, MAP_FILE
|
103 MAP_PRIVATE
, fd
, 0)) == MAP_FAILED
)
106 /* Locate the end of central directory record by searching backwards for
107 the record signature. */
109 if(len
< END_CEN_LEN
)
112 for(pntr
= data
+ len
- END_CEN_LEN
; pntr
>= data
; )
113 if(*pntr
== (END_CEN_SIG
& 0xff))
114 if(READ_LE_INT(pntr
) == END_CEN_SIG
)
121 /* Check that we found it */
125 /* Get the number of entries in the central directory */
126 entries
= READ_LE_SHORT(pntr
+ END_CEN_ENTRIES_OFFSET
);
128 /* Create and initialise hash table to hold the directory.
129 Do not create lock -- we're single threaded (bootstrapping)
130 and once entries are added, table is read-only */
132 hash_table
= (HashTable
*)sysMalloc(sizeof(HashTable
));
133 initHashTable((*hash_table
), HASHTABSZE
, FALSE
);
135 /* Get the offset from the start of the file of the first directory entry */
136 pntr
= data
+ READ_LE_INT(pntr
+ END_CEN_DIR_START_OFFSET
);
138 /* Scan the directory list and add the entries to the hash table */
140 /* We terminate each pathname "in place" to save memory -- this may
141 over-write the signature for the next entry. We therefore
142 speculatively load the next sig *before* we terminate the pathname */
144 if((pntr
+ SIG_LEN
) > (data
+ len
))
147 /* Speculatively read first sig */
148 next_file_sig
= READ_LE_INT(pntr
);
151 char *found
, *pathname
, *term
;
152 int path_len
, comment_len
, extra_len
;
154 if((pntr
+ CEN_FILE_HEADER_LEN
) > (data
+ len
))
157 /* Check directory entry signature is present */
158 if(next_file_sig
!= CEN_FILE_HEADER_SIG
)
161 /* Get the length of the pathname */
162 path_len
= READ_LE_SHORT(pntr
+ CEN_FILE_PATHLEN_OFFSET
);
164 /* Not interested in these fields but need length to skip */
165 extra_len
= READ_LE_SHORT(pntr
+ CEN_FILE_EXTRALEN_OFFSET
);
166 comment_len
= READ_LE_SHORT(pntr
+ CEN_FILE_COMMENTLEN_OFFSET
);
168 /* The pathname starts after the fixed part of the dir entry */
169 pathname
= (char*)(pntr
+= CEN_FILE_HEADER_LEN
);
170 term
= (char*)(pntr
+= path_len
);
172 /* Skip rest of variable fields -- should then be pointing to next
173 sig. If no extra or comment fields, pntr == term */
174 pntr
+= extra_len
+ comment_len
;
176 /* Even if this is the last entry, a well-formed zip file will *always*
177 have at least 4 extra bytes */
178 if((pntr
+ SIG_LEN
) > (data
+ len
))
181 /* Speculatively read next sig */
182 next_file_sig
= READ_LE_INT(pntr
);
184 /* Terminate the pathname */
187 /* Add if absent, no scavenge, not locked */
188 findHashEntry((*hash_table
), pathname
, found
, TRUE
, FALSE
, FALSE
);
191 zip
= (ZipFile
*) sysMalloc(sizeof(ZipFile
));
195 zip
->dir_hash
= hash_table
;
207 char *findArchiveDirEntry(char *pathname
, ZipFile
*zip
) {
210 /* Do not add if absent, no scavenge, not locked */
211 findHashEntry((*zip
->dir_hash
), pathname
, found
, FALSE
, FALSE
, FALSE
);
216 char *findArchiveEntry(char *pathname
, ZipFile
*zip
, int *uncomp_len
) {
217 int offset
, path_len
, comp_len
, extra_len
, comp_method
;
218 unsigned char *decomp_buff
, *comp_data
;
219 unsigned char *dir_entry
;
221 if((dir_entry
= (unsigned char *)findArchiveDirEntry(pathname
, zip
)) == NULL
)
224 /* Found the file -- the pathname points directly into the
225 directory entry. Read the values relative to it */
227 /* Offset of the file entry relative to the start of the file */
228 offset
= READ_LE_INT(dir_entry
+ (CEN_FILE_LOCALHDR_OFFSET
-
229 CEN_FILE_HEADER_LEN
));
231 if((offset
+ LOC_FILE_HEADER_LEN
) > zip
->length
)
234 /* Get the variable length part of the local file header */
235 extra_len
= READ_LE_SHORT(zip
->data
+ offset
+ LOC_FILE_EXTRA_OFFSET
);
237 /* Get the path_len again -- faster than doing a strlen */
238 path_len
= READ_LE_SHORT(dir_entry
+ (CEN_FILE_PATHLEN_OFFSET
- CEN_FILE_HEADER_LEN
));
240 /* The file's length when uncompressed -- this is passed out */
241 *uncomp_len
= READ_LE_INT(dir_entry
+ (CEN_FILE_UNCOMPLEN_OFFSET
- CEN_FILE_HEADER_LEN
));
243 /* The compressed file's length, i.e. the data size in the file */
244 comp_len
= READ_LE_INT(dir_entry
+ (CEN_FILE_COMPLEN_OFFSET
- CEN_FILE_HEADER_LEN
));
246 /* How the file is compressed */
247 comp_method
= READ_LE_SHORT(dir_entry
+ (CEN_FILE_COMPMETH_OFFSET
- CEN_FILE_HEADER_LEN
));
249 /* Calculate the data start */
250 offset
+= LOC_FILE_HEADER_LEN
+ path_len
+ extra_len
;
252 /* Make sure we're not reading outside the file */
253 if((offset
+ comp_len
) > zip
->length
)
256 comp_data
= zip
->data
+ offset
;
257 decomp_buff
= sysMalloc(*uncomp_len
);
259 switch(comp_method
) {
261 /* Data isn't compressed, so just return it "as is" */
262 memcpy(decomp_buff
, comp_data
, comp_len
);
263 return (char*)decomp_buff
;
265 case COMP_DEFLATED
: {
269 stream
.next_in
= comp_data
;
270 stream
.avail_in
= comp_len
;
271 stream
.next_out
= decomp_buff
;
272 stream
.avail_out
= *uncomp_len
;
274 stream
.zalloc
= Z_NULL
;
275 stream
.zfree
= Z_NULL
;
277 /* Use a negative windowBits value to stop the inflator looking
279 if(inflateInit2(&stream
, -MAX_WINDOW_BITS
) != Z_OK
)
282 err
= inflate(&stream
, Z_SYNC_FLUSH
);
285 if(err
== Z_STREAM_END
|| (err
== Z_OK
&& stream
.avail_in
== 0))
286 return (char*)decomp_buff
;
295 sysFree(decomp_buff
);
299 ZipFile
*processArchive(char *path
) {
303 char *findArchiveDirEntry(char *pathname
, ZipFile
*zip
) {
307 char *findArchiveEntry(char *pathname
, ZipFile
*zip
, int *entry_len
) {