author | sherman |
Mon, 29 Jun 2009 19:57:58 -0700 | |
changeset 3078 | c491f0d2a8aa |
parent 2592 | ef26f663a2ba |
child 5148 | 2bf5469864ea |
permissions | -rw-r--r-- |
2 | 1 |
/* |
2163 | 2 |
* Copyright 1995-2009 Sun Microsystems, Inc. All Rights Reserved. |
2 | 3 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 |
* |
|
5 |
* This code is free software; you can redistribute it and/or modify it |
|
6 |
* under the terms of the GNU General Public License version 2 only, as |
|
7 |
* published by the Free Software Foundation. Sun designates this |
|
8 |
* particular file as subject to the "Classpath" exception as provided |
|
9 |
* by Sun in the LICENSE file that accompanied this code. |
|
10 |
* |
|
11 |
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 |
* version 2 for more details (a copy is included in the LICENSE file that |
|
15 |
* accompanied this code). |
|
16 |
* |
|
17 |
* You should have received a copy of the GNU General Public License version |
|
18 |
* 2 along with this work; if not, write to the Free Software Foundation, |
|
19 |
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 |
* |
|
21 |
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
|
22 |
* CA 95054 USA or visit www.sun.com if you need additional information or |
|
23 |
* have any questions. |
|
24 |
*/ |
|
25 |
||
26 |
/* |
|
27 |
* Support for reading ZIP/JAR files. |
|
28 |
*/ |
|
29 |
||
30 |
#include <stdio.h> |
|
31 |
#include <stdlib.h> |
|
32 |
#include <stddef.h> |
|
33 |
#include <string.h> |
|
34 |
#include <fcntl.h> |
|
35 |
#include <limits.h> |
|
36 |
#include <time.h> |
|
37 |
#include <ctype.h> |
|
38 |
#include <assert.h> |
|
39 |
||
40 |
#include "jni.h" |
|
41 |
#include "jni_util.h" |
|
42 |
#include "jlong.h" |
|
43 |
#include "jvm.h" |
|
44 |
#include "io_util.h" |
|
45 |
#include "io_util_md.h" |
|
46 |
#include "zip_util.h" |
|
47 |
#include "zlib.h" |
|
48 |
||
49 |
/* USE_MMAP means mmap the CEN & ENDHDR part of the zip file. */ |
|
50 |
#ifdef USE_MMAP |
|
51 |
#include <sys/mman.h> |
|
52 |
#endif |
|
53 |
||
54 |
#define MAXREFS 0xFFFF /* max number of open zip file references */ |
|
55 |
||
56 |
#define MCREATE() JVM_RawMonitorCreate() |
|
57 |
#define MLOCK(lock) JVM_RawMonitorEnter(lock) |
|
58 |
#define MUNLOCK(lock) JVM_RawMonitorExit(lock) |
|
59 |
#define MDESTROY(lock) JVM_RawMonitorDestroy(lock) |
|
60 |
||
61 |
#define CENSIZE(cen) (CENHDR + CENNAM(cen) + CENEXT(cen) + CENCOM(cen)) |
|
62 |
||
63 |
static jzfile *zfiles = 0; /* currently open zip files */ |
|
64 |
static void *zfiles_lock = 0; |
|
65 |
||
66 |
static void freeCEN(jzfile *); |
|
67 |
||
68 |
#ifndef PATH_MAX |
|
69 |
#define PATH_MAX 1024 |
|
70 |
#endif |
|
71 |
||
72 |
static jint INITIAL_META_COUNT = 2; /* initial number of entries in meta name array */ |
|
73 |
||
74 |
/* |
|
75 |
* The ZFILE_* functions exist to provide some platform-independence with |
|
76 |
* respect to file access needs. |
|
77 |
*/ |
|
78 |
||
79 |
/* |
|
80 |
* Opens the named file for reading, returning a ZFILE. |
|
81 |
* |
|
82 |
* Compare this with winFileHandleOpen in windows/native/java/io/io_util_md.c. |
|
83 |
* This function does not take JNIEnv* and uses CreateFile (instead of |
|
84 |
* CreateFileW). The expectation is that this function will be called only |
|
85 |
* from ZIP_Open_Generic, which in turn is used by the JVM, where we do not |
|
86 |
* need to concern ourselves with wide chars. |
|
87 |
*/ |
|
88 |
static ZFILE |
|
89 |
ZFILE_Open(const char *fname, int flags) { |
|
90 |
#ifdef WIN32 |
|
91 |
const DWORD access = |
|
92 |
(flags & O_RDWR) ? (GENERIC_WRITE | GENERIC_READ) : |
|
93 |
(flags & O_WRONLY) ? GENERIC_WRITE : |
|
94 |
GENERIC_READ; |
|
95 |
const DWORD sharing = |
|
96 |
FILE_SHARE_READ | FILE_SHARE_WRITE; |
|
97 |
const DWORD disposition = |
|
98 |
/* Note: O_TRUNC overrides O_CREAT */ |
|
99 |
(flags & O_TRUNC) ? CREATE_ALWAYS : |
|
100 |
(flags & O_CREAT) ? OPEN_ALWAYS : |
|
101 |
OPEN_EXISTING; |
|
102 |
const DWORD maybeWriteThrough = |
|
103 |
(flags & (O_SYNC | O_DSYNC)) ? |
|
104 |
FILE_FLAG_WRITE_THROUGH : |
|
105 |
FILE_ATTRIBUTE_NORMAL; |
|
106 |
const DWORD maybeDeleteOnClose = |
|
107 |
(flags & O_TEMPORARY) ? |
|
108 |
FILE_FLAG_DELETE_ON_CLOSE : |
|
109 |
FILE_ATTRIBUTE_NORMAL; |
|
110 |
const DWORD flagsAndAttributes = maybeWriteThrough | maybeDeleteOnClose; |
|
111 |
||
112 |
return (jlong) CreateFile( |
|
113 |
fname, /* Wide char path name */ |
|
114 |
access, /* Read and/or write permission */ |
|
115 |
sharing, /* File sharing flags */ |
|
116 |
NULL, /* Security attributes */ |
|
117 |
disposition, /* creation disposition */ |
|
118 |
flagsAndAttributes, /* flags and attributes */ |
|
119 |
NULL); |
|
120 |
#else |
|
121 |
return JVM_Open(fname, flags, 0); |
|
122 |
#endif |
|
123 |
} |
|
124 |
||
125 |
/* |
|
126 |
* The io_util_md.h files do not provide IO_CLOSE, hence we use platform |
|
127 |
* specifics. |
|
128 |
*/ |
|
129 |
static void |
|
130 |
ZFILE_Close(ZFILE zfd) { |
|
131 |
#ifdef WIN32 |
|
132 |
CloseHandle((HANDLE) zfd); |
|
133 |
#else |
|
134 |
JVM_Close(zfd); |
|
135 |
#endif |
|
136 |
} |
|
137 |
||
138 |
static int |
|
139 |
ZFILE_read(ZFILE zfd, char *buf, jint nbytes) { |
|
140 |
#ifdef WIN32 |
|
141 |
return (int) IO_Read(zfd, buf, nbytes); |
|
142 |
#else |
|
143 |
/* |
|
144 |
* Calling JVM_Read will return JVM_IO_INTR when Thread.interrupt is called |
|
145 |
* only on Solaris. Continue reading jar file in this case is the best |
|
146 |
* thing to do since zip file reading is relatively fast and it is very onerous |
|
147 |
* for a interrupted thread to deal with this kind of hidden I/O. However, handling |
|
148 |
* JVM_IO_INTR is tricky and could cause undesired side effect. So we decided |
|
149 |
* to simply call "read" on Solaris/Linux. See details in bug 6304463. |
|
150 |
*/ |
|
151 |
return read(zfd, buf, nbytes); |
|
152 |
#endif |
|
153 |
} |
|
154 |
||
155 |
/* |
|
156 |
* Initialize zip file support. Return 0 if successful otherwise -1 |
|
157 |
* if could not be initialized. |
|
158 |
*/ |
|
159 |
static jint |
|
160 |
InitializeZip() |
|
161 |
{ |
|
162 |
static jboolean inited = JNI_FALSE; |
|
163 |
||
164 |
// Initialize errno to 0. It may be set later (e.g. during memory |
|
165 |
// allocation) but we can disregard previous values. |
|
166 |
errno = 0; |
|
167 |
||
168 |
if (inited) |
|
169 |
return 0; |
|
170 |
zfiles_lock = MCREATE(); |
|
171 |
if (zfiles_lock == 0) { |
|
172 |
return -1; |
|
173 |
} |
|
174 |
inited = JNI_TRUE; |
|
175 |
||
176 |
return 0; |
|
177 |
} |
|
178 |
||
179 |
/* |
|
180 |
* Reads len bytes of data into buf. |
|
181 |
* Returns 0 if all bytes could be read, otherwise returns -1. |
|
182 |
*/ |
|
183 |
static int |
|
184 |
readFully(ZFILE zfd, void *buf, jlong len) { |
|
185 |
char *bp = (char *) buf; |
|
186 |
||
187 |
while (len > 0) { |
|
188 |
jlong limit = ((((jlong) 1) << 31) - 1); |
|
189 |
jint count = (len < limit) ? |
|
190 |
(jint) len : |
|
191 |
(jint) limit; |
|
192 |
jint n = ZFILE_read(zfd, bp, count); |
|
193 |
if (n > 0) { |
|
194 |
bp += n; |
|
195 |
len -= n; |
|
196 |
} else if (n == JVM_IO_ERR && errno == EINTR) { |
|
197 |
/* Retry after EINTR (interrupted by signal). |
|
198 |
We depend on the fact that JVM_IO_ERR == -1. */ |
|
199 |
continue; |
|
200 |
} else { /* EOF or IO error */ |
|
201 |
return -1; |
|
202 |
} |
|
203 |
} |
|
204 |
return 0; |
|
205 |
} |
|
206 |
||
207 |
/* |
|
208 |
* Reads len bytes of data from the specified offset into buf. |
|
209 |
* Returns 0 if all bytes could be read, otherwise returns -1. |
|
210 |
*/ |
|
211 |
static int |
|
212 |
readFullyAt(ZFILE zfd, void *buf, jlong len, jlong offset) |
|
213 |
{ |
|
2074
346a841b2dcc
6599383: Unable to open zip files more than 2GB in size
kevinw
parents:
1572
diff
changeset
|
214 |
if (IO_Lseek(zfd, offset, SEEK_SET) == -1) { |
2 | 215 |
return -1; /* lseek failure. */ |
216 |
} |
|
217 |
||
218 |
return readFully(zfd, buf, len); |
|
219 |
} |
|
220 |
||
221 |
/* |
|
222 |
* Allocates a new zip file object for the specified file name. |
|
223 |
* Returns the zip file object or NULL if not enough memory. |
|
224 |
*/ |
|
225 |
static jzfile * |
|
226 |
allocZip(const char *name) |
|
227 |
{ |
|
228 |
jzfile *zip; |
|
229 |
if (((zip = calloc(1, sizeof(jzfile))) != NULL) && |
|
230 |
((zip->name = strdup(name)) != NULL) && |
|
231 |
((zip->lock = MCREATE()) != NULL)) { |
|
232 |
zip->zfd = -1; |
|
233 |
return zip; |
|
234 |
} |
|
235 |
||
236 |
if (zip != NULL) { |
|
237 |
free(zip->name); |
|
238 |
free(zip); |
|
239 |
} |
|
240 |
return NULL; |
|
241 |
} |
|
242 |
||
243 |
/* |
|
244 |
* Frees all native resources owned by the specified zip file object. |
|
245 |
*/ |
|
246 |
static void |
|
247 |
freeZip(jzfile *zip) |
|
248 |
{ |
|
249 |
/* First free any cached jzentry */ |
|
250 |
ZIP_FreeEntry(zip,0); |
|
251 |
if (zip->lock != NULL) MDESTROY(zip->lock); |
|
252 |
free(zip->name); |
|
253 |
freeCEN(zip); |
|
254 |
#ifdef USE_MMAP |
|
255 |
if (zip->maddr != NULL) munmap((char *)zip->maddr, zip->mlen); |
|
256 |
#else |
|
257 |
free(zip->cencache.data); |
|
258 |
#endif |
|
3078 | 259 |
if (zip->comment != NULL) |
260 |
free(zip->comment); |
|
2 | 261 |
if (zip->zfd != -1) ZFILE_Close(zip->zfd); |
262 |
free(zip); |
|
263 |
} |
|
264 |
||
265 |
/* The END header is followed by a variable length comment of size < 64k. */ |
|
266 |
static const jlong END_MAXLEN = 0xFFFF + ENDHDR; |
|
267 |
||
268 |
#define READBLOCKSZ 128 |
|
269 |
||
3078 | 270 |
static jboolean verifyEND(jzfile *zip, jlong endpos, char *endbuf) { |
271 |
/* ENDSIG matched, however the size of file comment in it does not |
|
272 |
match the real size. One "common" cause for this problem is some |
|
273 |
"extra" bytes are padded at the end of the zipfile. |
|
274 |
Let's do some extra verification, we don't care about the performance |
|
275 |
in this situation. |
|
276 |
*/ |
|
277 |
jlong cenpos = endpos - ENDSIZ(endbuf); |
|
278 |
jlong locpos = cenpos - ENDOFF(endbuf); |
|
279 |
char buf[4]; |
|
280 |
return (cenpos >= 0 && |
|
281 |
locpos >= 0 && |
|
282 |
readFullyAt(zip->zfd, buf, sizeof(buf), cenpos) != -1 && |
|
283 |
GETSIG(buf) == CENSIG && |
|
284 |
readFullyAt(zip->zfd, buf, sizeof(buf), locpos) != -1 && |
|
285 |
GETSIG(buf) == LOCSIG); |
|
286 |
} |
|
287 |
||
2 | 288 |
/* |
289 |
* Searches for end of central directory (END) header. The contents of |
|
290 |
* the END header will be read and placed in endbuf. Returns the file |
|
1572
9fed7b18f517
6763122: ZipFile ctor does not throw exception when file is not a zip file
alanb
parents:
1228
diff
changeset
|
291 |
* position of the END header, otherwise returns -1 if the END header |
9fed7b18f517
6763122: ZipFile ctor does not throw exception when file is not a zip file
alanb
parents:
1228
diff
changeset
|
292 |
* was not found or an error occurred. |
2 | 293 |
*/ |
294 |
static jlong |
|
295 |
findEND(jzfile *zip, void *endbuf) |
|
296 |
{ |
|
297 |
char buf[READBLOCKSZ]; |
|
298 |
jlong pos; |
|
299 |
const jlong len = zip->len; |
|
300 |
const ZFILE zfd = zip->zfd; |
|
301 |
const jlong minHDR = len - END_MAXLEN > 0 ? len - END_MAXLEN : 0; |
|
302 |
const jlong minPos = minHDR - (sizeof(buf)-ENDHDR); |
|
3078 | 303 |
jint clen; |
2 | 304 |
|
305 |
for (pos = len - sizeof(buf); pos >= minPos; pos -= (sizeof(buf)-ENDHDR)) { |
|
306 |
||
307 |
int i; |
|
308 |
jlong off = 0; |
|
309 |
if (pos < 0) { |
|
310 |
/* Pretend there are some NUL bytes before start of file */ |
|
311 |
off = -pos; |
|
312 |
memset(buf, '\0', off); |
|
313 |
} |
|
314 |
||
315 |
if (readFullyAt(zfd, buf + off, sizeof(buf) - off, |
|
316 |
pos + off) == -1) { |
|
317 |
return -1; /* System error */ |
|
318 |
} |
|
319 |
||
320 |
/* Now scan the block backwards for END header signature */ |
|
321 |
for (i = sizeof(buf) - ENDHDR; i >= 0; i--) { |
|
322 |
if (buf[i+0] == 'P' && |
|
323 |
buf[i+1] == 'K' && |
|
324 |
buf[i+2] == '\005' && |
|
325 |
buf[i+3] == '\006' && |
|
3078 | 326 |
((pos + i + ENDHDR + ENDCOM(buf + i) == len) |
327 |
|| verifyEND(zip, pos + i, buf + i))) { |
|
328 |
/* Found END header */ |
|
329 |
memcpy(endbuf, buf + i, ENDHDR); |
|
330 |
||
331 |
clen = ENDCOM(endbuf); |
|
332 |
if (clen != 0) { |
|
333 |
zip->comment = malloc(clen + 1); |
|
334 |
if (zip->comment == NULL) { |
|
335 |
return -1; |
|
336 |
} |
|
337 |
if (readFullyAt(zfd, zip->comment, clen, pos + i + ENDHDR) |
|
338 |
== -1) { |
|
339 |
free(zip->comment); |
|
340 |
zip->comment = NULL; |
|
341 |
return -1; |
|
342 |
} |
|
343 |
zip->comment[clen] = '\0'; |
|
344 |
zip->clen = clen; |
|
345 |
} |
|
346 |
return pos + i; |
|
2 | 347 |
} |
348 |
} |
|
349 |
} |
|
3078 | 350 |
|
1572
9fed7b18f517
6763122: ZipFile ctor does not throw exception when file is not a zip file
alanb
parents:
1228
diff
changeset
|
351 |
return -1; /* END header not found */ |
2 | 352 |
} |
353 |
||
354 |
/* |
|
2438
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
355 |
* Searches for the ZIP64 end of central directory (END) header. The |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
356 |
* contents of the ZIP64 END header will be read and placed in end64buf. |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
357 |
* Returns the file position of the ZIP64 END header, otherwise returns |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
358 |
* -1 if the END header was not found or an error occurred. |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
359 |
* |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
360 |
* The ZIP format specifies the "position" of each related record as |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
361 |
* ... |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
362 |
* [central directory] |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
363 |
* [zip64 end of central directory record] |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
364 |
* [zip64 end of central directory locator] |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
365 |
* [end of central directory record] |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
366 |
* |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
367 |
* The offset of zip64 end locator can be calculated from endpos as |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
368 |
* "endpos - ZIP64_LOCHDR". |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
369 |
* The "offset" of zip64 end record is stored in zip64 end locator. |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
370 |
*/ |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
371 |
static jlong |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
372 |
findEND64(jzfile *zip, void *end64buf, jlong endpos) |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
373 |
{ |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
374 |
char loc64[ZIP64_LOCHDR]; |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
375 |
jlong end64pos; |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
376 |
if (readFullyAt(zip->zfd, loc64, ZIP64_LOCHDR, endpos - ZIP64_LOCHDR) == -1) { |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
377 |
return -1; // end64 locator not found |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
378 |
} |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
379 |
end64pos = ZIP64_LOCOFF(loc64); |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
380 |
if (readFullyAt(zip->zfd, end64buf, ZIP64_ENDHDR, end64pos) == -1) { |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
381 |
return -1; // end64 record not found |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
382 |
} |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
383 |
return end64pos; |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
384 |
} |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
385 |
|
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
386 |
/* |
2 | 387 |
* Returns a hash code value for a C-style NUL-terminated string. |
388 |
*/ |
|
389 |
static unsigned int |
|
390 |
hash(const char *s) |
|
391 |
{ |
|
392 |
int h = 0; |
|
393 |
while (*s != '\0') |
|
394 |
h = 31*h + *s++; |
|
395 |
return h; |
|
396 |
} |
|
397 |
||
398 |
/* |
|
399 |
* Returns a hash code value for a string of a specified length. |
|
400 |
*/ |
|
401 |
static unsigned int |
|
402 |
hashN(const char *s, int length) |
|
403 |
{ |
|
404 |
int h = 0; |
|
405 |
while (length-- > 0) |
|
406 |
h = 31*h + *s++; |
|
407 |
return h; |
|
408 |
} |
|
409 |
||
410 |
static unsigned int |
|
411 |
hash_append(unsigned int hash, char c) |
|
412 |
{ |
|
413 |
return ((int)hash)*31 + c; |
|
414 |
} |
|
415 |
||
416 |
/* |
|
417 |
* Returns true if the specified entry's name begins with the string |
|
418 |
* "META-INF/" irrespective of case. |
|
419 |
*/ |
|
420 |
static int |
|
421 |
isMetaName(const char *name, int length) |
|
422 |
{ |
|
423 |
const char *s; |
|
424 |
if (length < sizeof("META-INF/") - 1) |
|
425 |
return 0; |
|
426 |
for (s = "META-INF/"; *s != '\0'; s++) { |
|
427 |
char c = *name++; |
|
428 |
// Avoid toupper; it's locale-dependent |
|
429 |
if (c >= 'a' && c <= 'z') c += 'A' - 'a'; |
|
430 |
if (*s != c) |
|
431 |
return 0; |
|
432 |
} |
|
433 |
return 1; |
|
434 |
} |
|
435 |
||
436 |
/* |
|
437 |
* Increases the capacity of zip->metanames. |
|
438 |
* Returns non-zero in case of allocation error. |
|
439 |
*/ |
|
440 |
static int |
|
441 |
growMetaNames(jzfile *zip) |
|
442 |
{ |
|
443 |
jint i; |
|
444 |
/* double the meta names array */ |
|
445 |
const jint new_metacount = zip->metacount << 1; |
|
446 |
zip->metanames = |
|
447 |
realloc(zip->metanames, new_metacount * sizeof(zip->metanames[0])); |
|
448 |
if (zip->metanames == NULL) return -1; |
|
449 |
for (i = zip->metacount; i < new_metacount; i++) |
|
450 |
zip->metanames[i] = NULL; |
|
451 |
zip->metacurrent = zip->metacount; |
|
452 |
zip->metacount = new_metacount; |
|
453 |
return 0; |
|
454 |
} |
|
455 |
||
456 |
/* |
|
457 |
* Adds name to zip->metanames. |
|
458 |
* Returns non-zero in case of allocation error. |
|
459 |
*/ |
|
460 |
static int |
|
461 |
addMetaName(jzfile *zip, const char *name, int length) |
|
462 |
{ |
|
463 |
jint i; |
|
464 |
if (zip->metanames == NULL) { |
|
465 |
zip->metacount = INITIAL_META_COUNT; |
|
466 |
zip->metanames = calloc(zip->metacount, sizeof(zip->metanames[0])); |
|
467 |
if (zip->metanames == NULL) return -1; |
|
468 |
zip->metacurrent = 0; |
|
469 |
} |
|
470 |
||
471 |
i = zip->metacurrent; |
|
472 |
||
473 |
/* current meta name array isn't full yet. */ |
|
474 |
if (i < zip->metacount) { |
|
475 |
zip->metanames[i] = (char *) malloc(length+1); |
|
476 |
if (zip->metanames[i] == NULL) return -1; |
|
477 |
memcpy(zip->metanames[i], name, length); |
|
478 |
zip->metanames[i][length] = '\0'; |
|
479 |
zip->metacurrent++; |
|
480 |
return 0; |
|
481 |
} |
|
482 |
||
483 |
/* No free entries in zip->metanames? */ |
|
484 |
if (growMetaNames(zip) != 0) return -1; |
|
485 |
return addMetaName(zip, name, length); |
|
486 |
} |
|
487 |
||
488 |
static void |
|
489 |
freeMetaNames(jzfile *zip) |
|
490 |
{ |
|
491 |
if (zip->metanames) { |
|
492 |
jint i; |
|
493 |
for (i = 0; i < zip->metacount; i++) |
|
494 |
free(zip->metanames[i]); |
|
495 |
free(zip->metanames); |
|
496 |
zip->metanames = NULL; |
|
497 |
} |
|
498 |
} |
|
499 |
||
500 |
/* Free Zip data allocated by readCEN() */ |
|
501 |
static void |
|
502 |
freeCEN(jzfile *zip) |
|
503 |
{ |
|
504 |
free(zip->entries); zip->entries = NULL; |
|
505 |
free(zip->table); zip->table = NULL; |
|
506 |
freeMetaNames(zip); |
|
507 |
} |
|
508 |
||
509 |
/* |
|
510 |
* Counts the number of CEN headers in a central directory extending |
|
511 |
* from BEG to END. Might return a bogus answer if the zip file is |
|
512 |
* corrupt, but will not crash. |
|
513 |
*/ |
|
514 |
static jint |
|
515 |
countCENHeaders(unsigned char *beg, unsigned char *end) |
|
516 |
{ |
|
517 |
jint count = 0; |
|
518 |
ptrdiff_t i; |
|
519 |
for (i = 0; i + CENHDR < end - beg; i += CENSIZE(beg + i)) |
|
520 |
count++; |
|
521 |
return count; |
|
522 |
} |
|
523 |
||
524 |
#define ZIP_FORMAT_ERROR(message) \ |
|
525 |
if (1) { zip->msg = message; goto Catch; } else ((void)0) |
|
526 |
||
527 |
/* |
|
528 |
* Reads zip file central directory. Returns the file position of first |
|
1572
9fed7b18f517
6763122: ZipFile ctor does not throw exception when file is not a zip file
alanb
parents:
1228
diff
changeset
|
529 |
* CEN header, otherwise returns -1 if an error occured. If zip->msg != NULL |
9fed7b18f517
6763122: ZipFile ctor does not throw exception when file is not a zip file
alanb
parents:
1228
diff
changeset
|
530 |
* then the error was a zip format error and zip->msg has the error text. |
2 | 531 |
* Always pass in -1 for knownTotal; it's used for a recursive call. |
532 |
*/ |
|
533 |
static jlong |
|
534 |
readCEN(jzfile *zip, jint knownTotal) |
|
535 |
{ |
|
536 |
/* Following are unsigned 32-bit */ |
|
2438
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
537 |
jlong endpos, end64pos, cenpos, cenlen, cenoff; |
2 | 538 |
/* Following are unsigned 16-bit */ |
539 |
jint total, tablelen, i, j; |
|
540 |
unsigned char *cenbuf = NULL; |
|
541 |
unsigned char *cenend; |
|
542 |
unsigned char *cp; |
|
543 |
#ifdef USE_MMAP |
|
544 |
static jlong pagesize; |
|
2074
346a841b2dcc
6599383: Unable to open zip files more than 2GB in size
kevinw
parents:
1572
diff
changeset
|
545 |
jlong offset; |
2 | 546 |
#endif |
547 |
unsigned char endbuf[ENDHDR]; |
|
2438
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
548 |
jint endhdrlen = ENDHDR; |
2 | 549 |
jzcell *entries; |
550 |
jint *table; |
|
551 |
||
552 |
/* Clear previous zip error */ |
|
553 |
zip->msg = NULL; |
|
554 |
/* Get position of END header */ |
|
555 |
if ((endpos = findEND(zip, endbuf)) == -1) |
|
1572
9fed7b18f517
6763122: ZipFile ctor does not throw exception when file is not a zip file
alanb
parents:
1228
diff
changeset
|
556 |
return -1; /* no END header or system error */ |
2 | 557 |
|
1572
9fed7b18f517
6763122: ZipFile ctor does not throw exception when file is not a zip file
alanb
parents:
1228
diff
changeset
|
558 |
if (endpos == 0) return 0; /* only END header present */ |
2 | 559 |
|
560 |
freeCEN(zip); |
|
561 |
/* Get position and length of central directory */ |
|
562 |
cenlen = ENDSIZ(endbuf); |
|
2438
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
563 |
cenoff = ENDOFF(endbuf); |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
564 |
total = ENDTOT(endbuf); |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
565 |
if (cenlen == ZIP64_MAGICVAL || cenoff == ZIP64_MAGICVAL || |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
566 |
total == ZIP64_MAGICCOUNT) { |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
567 |
unsigned char end64buf[ZIP64_ENDHDR]; |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
568 |
if ((end64pos = findEND64(zip, end64buf, endpos)) != -1) { |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
569 |
cenlen = ZIP64_ENDSIZ(end64buf); |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
570 |
cenoff = ZIP64_ENDOFF(end64buf); |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
571 |
total = (jint)ZIP64_ENDTOT(end64buf); |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
572 |
endpos = end64pos; |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
573 |
endhdrlen = ZIP64_ENDHDR; |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
574 |
} |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
575 |
} |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
576 |
|
2 | 577 |
if (cenlen > endpos) |
578 |
ZIP_FORMAT_ERROR("invalid END header (bad central directory size)"); |
|
579 |
cenpos = endpos - cenlen; |
|
580 |
||
581 |
/* Get position of first local file (LOC) header, taking into |
|
582 |
* account that there may be a stub prefixed to the zip file. */ |
|
2438
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
583 |
zip->locpos = cenpos - cenoff; |
2 | 584 |
if (zip->locpos < 0) |
585 |
ZIP_FORMAT_ERROR("invalid END header (bad central directory offset)"); |
|
586 |
||
587 |
#ifdef USE_MMAP |
|
588 |
/* On Solaris & Linux prior to JDK 6, we used to mmap the whole jar file to |
|
589 |
* read the jar file contents. However, this greatly increased the perceived |
|
590 |
* footprint numbers because the mmap'ed pages were adding into the totals shown |
|
591 |
* by 'ps' and 'top'. We switched to mmaping only the central directory of jar |
|
592 |
* file while calling 'read' to read the rest of jar file. Here are a list of |
|
593 |
* reasons apart from above of why we are doing so: |
|
594 |
* 1. Greatly reduces mmap overhead after startup complete; |
|
595 |
* 2. Avoids dual path code maintainance; |
|
596 |
* 3. Greatly reduces risk of address space (not virtual memory) exhaustion. |
|
597 |
*/ |
|
598 |
if (pagesize == 0) { |
|
599 |
pagesize = (jlong)sysconf(_SC_PAGESIZE); |
|
600 |
if (pagesize == 0) goto Catch; |
|
601 |
} |
|
602 |
if (cenpos > pagesize) { |
|
603 |
offset = cenpos & ~(pagesize - 1); |
|
604 |
} else { |
|
605 |
offset = 0; |
|
606 |
} |
|
607 |
/* When we are not calling recursively, knownTotal is -1. */ |
|
608 |
if (knownTotal == -1) { |
|
609 |
void* mappedAddr; |
|
610 |
/* Mmap the CEN and END part only. We have to figure |
|
611 |
out the page size in order to make offset to be multiples of |
|
612 |
page size. |
|
613 |
*/ |
|
2438
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
614 |
zip->mlen = cenpos - offset + cenlen + endhdrlen; |
2 | 615 |
zip->offset = offset; |
2074
346a841b2dcc
6599383: Unable to open zip files more than 2GB in size
kevinw
parents:
1572
diff
changeset
|
616 |
mappedAddr = mmap64(0, zip->mlen, PROT_READ, MAP_SHARED, zip->zfd, (off64_t) offset); |
2 | 617 |
zip->maddr = (mappedAddr == (void*) MAP_FAILED) ? NULL : |
618 |
(unsigned char*)mappedAddr; |
|
619 |
||
620 |
if (zip->maddr == NULL) { |
|
621 |
jio_fprintf(stderr, "mmap failed for CEN and END part of zip file\n"); |
|
622 |
goto Catch; |
|
623 |
} |
|
624 |
} |
|
625 |
cenbuf = zip->maddr + cenpos - offset; |
|
626 |
#else |
|
627 |
if ((cenbuf = malloc((size_t) cenlen)) == NULL || |
|
628 |
(readFullyAt(zip->zfd, cenbuf, cenlen, cenpos) == -1)) |
|
629 |
goto Catch; |
|
630 |
#endif |
|
631 |
cenend = cenbuf + cenlen; |
|
632 |
||
633 |
/* Initialize zip file data structures based on the total number |
|
634 |
* of central directory entries as stored in ENDTOT. Since this |
|
635 |
* is a 2-byte field, but we (and other zip implementations) |
|
636 |
* support approx. 2**31 entries, we do not trust ENDTOT, but |
|
637 |
* treat it only as a strong hint. When we call ourselves |
|
2438
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
638 |
* recursively, knownTotal will have the "true" value. |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
639 |
* |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
640 |
* Keep this path alive even with the Zip64 END support added, just |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
641 |
* for zip files that have more than 0xffff entries but don't have |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
642 |
* the Zip64 enabled. |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
643 |
*/ |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
644 |
total = (knownTotal != -1) ? knownTotal : total; |
2 | 645 |
entries = zip->entries = calloc(total, sizeof(entries[0])); |
646 |
tablelen = zip->tablelen = ((total/2) | 1); // Odd -> fewer collisions |
|
647 |
table = zip->table = malloc(tablelen * sizeof(table[0])); |
|
648 |
if (entries == NULL || table == NULL) goto Catch; |
|
649 |
for (j = 0; j < tablelen; j++) |
|
650 |
table[j] = ZIP_ENDCHAIN; |
|
651 |
||
652 |
/* Iterate through the entries in the central directory */ |
|
653 |
for (i = 0, cp = cenbuf; cp <= cenend - CENHDR; i++, cp += CENSIZE(cp)) { |
|
654 |
/* Following are unsigned 16-bit */ |
|
655 |
jint method, nlen; |
|
656 |
unsigned int hsh; |
|
657 |
||
658 |
if (i >= total) { |
|
659 |
/* This will only happen if the zip file has an incorrect |
|
660 |
* ENDTOT field, which usually means it contains more than |
|
661 |
* 65535 entries. */ |
|
662 |
cenpos = readCEN(zip, countCENHeaders(cenbuf, cenend)); |
|
663 |
goto Finally; |
|
664 |
} |
|
665 |
||
666 |
method = CENHOW(cp); |
|
667 |
nlen = CENNAM(cp); |
|
668 |
||
669 |
if (GETSIG(cp) != CENSIG) |
|
670 |
ZIP_FORMAT_ERROR("invalid CEN header (bad signature)"); |
|
671 |
if (CENFLG(cp) & 1) |
|
672 |
ZIP_FORMAT_ERROR("invalid CEN header (encrypted entry)"); |
|
673 |
if (method != STORED && method != DEFLATED) |
|
674 |
ZIP_FORMAT_ERROR("invalid CEN header (bad compression method)"); |
|
675 |
if (cp + CENHDR + nlen > cenend) |
|
676 |
ZIP_FORMAT_ERROR("invalid CEN header (bad header size)"); |
|
677 |
||
678 |
/* if the entry is metadata add it to our metadata names */ |
|
679 |
if (isMetaName((char *)cp+CENHDR, nlen)) |
|
680 |
if (addMetaName(zip, (char *)cp+CENHDR, nlen) != 0) |
|
681 |
goto Catch; |
|
682 |
||
683 |
/* Record the CEN offset and the name hash in our hash cell. */ |
|
684 |
entries[i].cenpos = cenpos + (cp - cenbuf); |
|
685 |
entries[i].hash = hashN((char *)cp+CENHDR, nlen); |
|
686 |
||
687 |
/* Add the entry to the hash table */ |
|
688 |
hsh = entries[i].hash % tablelen; |
|
689 |
entries[i].next = table[hsh]; |
|
690 |
table[hsh] = i; |
|
691 |
} |
|
692 |
if (cp != cenend) |
|
693 |
ZIP_FORMAT_ERROR("invalid CEN header (bad header size)"); |
|
694 |
||
695 |
zip->total = i; |
|
696 |
goto Finally; |
|
697 |
||
698 |
Catch: |
|
699 |
freeCEN(zip); |
|
700 |
cenpos = -1; |
|
701 |
||
702 |
Finally: |
|
703 |
#ifndef USE_MMAP |
|
704 |
free(cenbuf); |
|
705 |
#endif |
|
706 |
return cenpos; |
|
707 |
} |
|
708 |
||
709 |
/* |
|
710 |
* Opens a zip file with the specified mode. Returns the jzfile object |
|
711 |
* or NULL if an error occurred. If a zip error occurred then *pmsg will |
|
712 |
* be set to the error message text if pmsg != 0. Otherwise, *pmsg will be |
|
713 |
* set to NULL. |
|
714 |
*/ |
|
715 |
jzfile * |
|
716 |
ZIP_Open_Generic(const char *name, char **pmsg, int mode, jlong lastModified) |
|
717 |
{ |
|
718 |
jzfile *zip = NULL; |
|
719 |
||
720 |
/* Clear zip error message */ |
|
721 |
if (pmsg != 0) { |
|
722 |
*pmsg = NULL; |
|
723 |
} |
|
724 |
||
725 |
zip = ZIP_Get_From_Cache(name, pmsg, lastModified); |
|
726 |
||
727 |
if (zip == NULL && *pmsg == NULL) { |
|
728 |
ZFILE zfd = ZFILE_Open(name, mode); |
|
729 |
zip = ZIP_Put_In_Cache(name, zfd, pmsg, lastModified); |
|
730 |
} |
|
731 |
return zip; |
|
732 |
} |
|
733 |
||
734 |
/* |
|
735 |
* Returns the jzfile corresponding to the given file name from the cache of |
|
736 |
* zip files, or NULL if the file is not in the cache. If the name is longer |
|
737 |
* than PATH_MAX or a zip error occurred then *pmsg will be set to the error |
|
738 |
* message text if pmsg != 0. Otherwise, *pmsg will be set to NULL. |
|
739 |
*/ |
|
740 |
jzfile * |
|
741 |
ZIP_Get_From_Cache(const char *name, char **pmsg, jlong lastModified) |
|
742 |
{ |
|
743 |
static char errbuf[256]; |
|
744 |
char buf[PATH_MAX]; |
|
745 |
jzfile *zip; |
|
746 |
||
747 |
if (InitializeZip()) { |
|
748 |
return NULL; |
|
749 |
} |
|
750 |
||
751 |
/* Clear zip error message */ |
|
752 |
if (pmsg != 0) { |
|
753 |
*pmsg = NULL; |
|
754 |
} |
|
755 |
||
756 |
if (strlen(name) >= PATH_MAX) { |
|
757 |
if (pmsg) { |
|
758 |
*pmsg = "zip file name too long"; |
|
759 |
} |
|
760 |
return NULL; |
|
761 |
} |
|
762 |
strcpy(buf, name); |
|
763 |
JVM_NativePath(buf); |
|
764 |
name = buf; |
|
765 |
||
766 |
MLOCK(zfiles_lock); |
|
767 |
for (zip = zfiles; zip != NULL; zip = zip->next) { |
|
768 |
if (strcmp(name, zip->name) == 0 |
|
769 |
&& (zip->lastModified == lastModified || zip->lastModified == 0) |
|
770 |
&& zip->refs < MAXREFS) { |
|
771 |
zip->refs++; |
|
772 |
break; |
|
773 |
} |
|
774 |
} |
|
775 |
MUNLOCK(zfiles_lock); |
|
776 |
return zip; |
|
777 |
} |
|
778 |
||
779 |
/* |
|
780 |
* Reads data from the given file descriptor to create a jzfile, puts the |
|
781 |
* jzfile in a cache, and returns that jzfile. Returns NULL in case of error. |
|
782 |
* If a zip error occurs, then *pmsg will be set to the error message text if |
|
783 |
* pmsg != 0. Otherwise, *pmsg will be set to NULL. |
|
784 |
*/ |
|
785 |
jzfile * |
|
786 |
ZIP_Put_In_Cache(const char *name, ZFILE zfd, char **pmsg, jlong lastModified) |
|
787 |
{ |
|
788 |
static char errbuf[256]; |
|
789 |
jlong len; |
|
790 |
jzfile *zip; |
|
791 |
||
792 |
if ((zip = allocZip(name)) == NULL) { |
|
793 |
return NULL; |
|
794 |
} |
|
795 |
||
796 |
zip->refs = 1; |
|
797 |
zip->lastModified = lastModified; |
|
798 |
||
799 |
if (zfd == -1) { |
|
800 |
if (pmsg && JVM_GetLastErrorString(errbuf, sizeof(errbuf)) > 0) |
|
801 |
*pmsg = errbuf; |
|
802 |
freeZip(zip); |
|
803 |
return NULL; |
|
804 |
} |
|
805 |
||
2074
346a841b2dcc
6599383: Unable to open zip files more than 2GB in size
kevinw
parents:
1572
diff
changeset
|
806 |
len = zip->len = IO_Lseek(zfd, 0, SEEK_END); |
1228
1515928f48cd
6440786: Cannot create a ZIP file containing zero entries
bristor
parents:
2
diff
changeset
|
807 |
if (len <= 0) { |
1515928f48cd
6440786: Cannot create a ZIP file containing zero entries
bristor
parents:
2
diff
changeset
|
808 |
if (len == 0) { /* zip file is empty */ |
1515928f48cd
6440786: Cannot create a ZIP file containing zero entries
bristor
parents:
2
diff
changeset
|
809 |
if (pmsg) { |
1515928f48cd
6440786: Cannot create a ZIP file containing zero entries
bristor
parents:
2
diff
changeset
|
810 |
*pmsg = "zip file is empty"; |
1515928f48cd
6440786: Cannot create a ZIP file containing zero entries
bristor
parents:
2
diff
changeset
|
811 |
} |
1515928f48cd
6440786: Cannot create a ZIP file containing zero entries
bristor
parents:
2
diff
changeset
|
812 |
} else { /* error */ |
1515928f48cd
6440786: Cannot create a ZIP file containing zero entries
bristor
parents:
2
diff
changeset
|
813 |
if (pmsg && JVM_GetLastErrorString(errbuf, sizeof(errbuf)) > 0) |
1515928f48cd
6440786: Cannot create a ZIP file containing zero entries
bristor
parents:
2
diff
changeset
|
814 |
*pmsg = errbuf; |
1515928f48cd
6440786: Cannot create a ZIP file containing zero entries
bristor
parents:
2
diff
changeset
|
815 |
} |
2 | 816 |
ZFILE_Close(zfd); |
817 |
freeZip(zip); |
|
818 |
return NULL; |
|
819 |
} |
|
820 |
||
821 |
zip->zfd = zfd; |
|
1228
1515928f48cd
6440786: Cannot create a ZIP file containing zero entries
bristor
parents:
2
diff
changeset
|
822 |
if (readCEN(zip, -1) < 0) { |
2 | 823 |
/* An error occurred while trying to read the zip file */ |
824 |
if (pmsg != 0) { |
|
825 |
/* Set the zip error message */ |
|
826 |
*pmsg = zip->msg; |
|
827 |
} |
|
828 |
freeZip(zip); |
|
829 |
return NULL; |
|
830 |
} |
|
831 |
MLOCK(zfiles_lock); |
|
832 |
zip->next = zfiles; |
|
833 |
zfiles = zip; |
|
834 |
MUNLOCK(zfiles_lock); |
|
835 |
||
836 |
return zip; |
|
837 |
} |
|
838 |
||
839 |
/* |
|
840 |
* Opens a zip file for reading. Returns the jzfile object or NULL |
|
841 |
* if an error occurred. If a zip error occurred then *msg will be |
|
842 |
* set to the error message text if msg != 0. Otherwise, *msg will be |
|
843 |
* set to NULL. |
|
844 |
*/ |
|
845 |
jzfile * JNICALL |
|
846 |
ZIP_Open(const char *name, char **pmsg) |
|
847 |
{ |
|
848 |
return ZIP_Open_Generic(name, pmsg, O_RDONLY, 0); |
|
849 |
} |
|
850 |
||
851 |
/* |
|
852 |
* Closes the specified zip file object. |
|
853 |
*/ |
|
854 |
void JNICALL |
|
855 |
ZIP_Close(jzfile *zip) |
|
856 |
{ |
|
857 |
MLOCK(zfiles_lock); |
|
858 |
if (--zip->refs > 0) { |
|
859 |
/* Still more references so just return */ |
|
860 |
MUNLOCK(zfiles_lock); |
|
861 |
return; |
|
862 |
} |
|
863 |
/* No other references so close the file and remove from list */ |
|
864 |
if (zfiles == zip) { |
|
865 |
zfiles = zfiles->next; |
|
866 |
} else { |
|
867 |
jzfile *zp; |
|
868 |
for (zp = zfiles; zp->next != 0; zp = zp->next) { |
|
869 |
if (zp->next == zip) { |
|
870 |
zp->next = zip->next; |
|
871 |
break; |
|
872 |
} |
|
873 |
} |
|
874 |
} |
|
875 |
MUNLOCK(zfiles_lock); |
|
876 |
freeZip(zip); |
|
877 |
return; |
|
878 |
} |
|
879 |
||
880 |
#ifndef USE_MMAP |
|
881 |
||
882 |
/* Empirically, most CEN headers are smaller than this. */ |
|
883 |
#define AMPLE_CEN_HEADER_SIZE 160 |
|
884 |
||
885 |
/* A good buffer size when we want to read CEN headers sequentially. */ |
|
886 |
#define CENCACHE_PAGESIZE 8192 |
|
887 |
||
888 |
static char * |
|
889 |
readCENHeader(jzfile *zip, jlong cenpos, jint bufsize) |
|
890 |
{ |
|
891 |
jint censize; |
|
892 |
ZFILE zfd = zip->zfd; |
|
893 |
char *cen; |
|
894 |
if (bufsize > zip->len - cenpos) |
|
895 |
bufsize = zip->len - cenpos; |
|
896 |
if ((cen = malloc(bufsize)) == NULL) goto Catch; |
|
897 |
if (readFullyAt(zfd, cen, bufsize, cenpos) == -1) goto Catch; |
|
898 |
censize = CENSIZE(cen); |
|
899 |
if (censize <= bufsize) return cen; |
|
900 |
if ((cen = realloc(cen, censize)) == NULL) goto Catch; |
|
901 |
if (readFully(zfd, cen+bufsize, censize-bufsize) == -1) goto Catch; |
|
902 |
return cen; |
|
903 |
||
904 |
Catch: |
|
905 |
free(cen); |
|
906 |
return NULL; |
|
907 |
} |
|
908 |
||
909 |
static char * |
|
910 |
sequentialAccessReadCENHeader(jzfile *zip, jlong cenpos) |
|
911 |
{ |
|
912 |
cencache *cache = &zip->cencache; |
|
913 |
char *cen; |
|
914 |
if (cache->data != NULL |
|
915 |
&& (cenpos >= cache->pos) |
|
916 |
&& (cenpos + CENHDR <= cache->pos + CENCACHE_PAGESIZE)) |
|
917 |
{ |
|
918 |
cen = cache->data + cenpos - cache->pos; |
|
919 |
if (cenpos + CENSIZE(cen) <= cache->pos + CENCACHE_PAGESIZE) |
|
920 |
/* A cache hit */ |
|
921 |
return cen; |
|
922 |
} |
|
923 |
||
924 |
if ((cen = readCENHeader(zip, cenpos, CENCACHE_PAGESIZE)) == NULL) |
|
925 |
return NULL; |
|
926 |
free(cache->data); |
|
927 |
cache->data = cen; |
|
928 |
cache->pos = cenpos; |
|
929 |
return cen; |
|
930 |
} |
|
931 |
#endif /* not USE_MMAP */ |
|
932 |
||
933 |
typedef enum { ACCESS_RANDOM, ACCESS_SEQUENTIAL } AccessHint; |
|
934 |
||
935 |
/* |
|
936 |
* Return a new initialized jzentry corresponding to a given hash cell. |
|
937 |
* In case of error, returns NULL. |
|
938 |
* We already sanity-checked all the CEN headers for ZIP format errors |
|
939 |
* in readCEN(), so we don't check them again here. |
|
940 |
* The ZIP lock should be held here. |
|
941 |
*/ |
|
942 |
static jzentry * |
|
943 |
newEntry(jzfile *zip, jzcell *zc, AccessHint accessHint) |
|
944 |
{ |
|
2438
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
945 |
jlong locoff; |
2 | 946 |
jint nlen, elen, clen; |
947 |
jzentry *ze; |
|
948 |
char *cen; |
|
949 |
||
950 |
if ((ze = (jzentry *) malloc(sizeof(jzentry))) == NULL) return NULL; |
|
951 |
ze->name = NULL; |
|
952 |
ze->extra = NULL; |
|
953 |
ze->comment = NULL; |
|
954 |
||
955 |
#ifdef USE_MMAP |
|
956 |
cen = (char*) zip->maddr + zc->cenpos - zip->offset; |
|
957 |
#else |
|
958 |
if (accessHint == ACCESS_RANDOM) |
|
959 |
cen = readCENHeader(zip, zc->cenpos, AMPLE_CEN_HEADER_SIZE); |
|
960 |
else |
|
961 |
cen = sequentialAccessReadCENHeader(zip, zc->cenpos); |
|
962 |
if (cen == NULL) goto Catch; |
|
963 |
#endif |
|
964 |
||
965 |
nlen = CENNAM(cen); |
|
966 |
elen = CENEXT(cen); |
|
967 |
clen = CENCOM(cen); |
|
968 |
ze->time = CENTIM(cen); |
|
969 |
ze->size = CENLEN(cen); |
|
970 |
ze->csize = (CENHOW(cen) == STORED) ? 0 : CENSIZ(cen); |
|
971 |
ze->crc = CENCRC(cen); |
|
2438
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
972 |
locoff = CENOFF(cen); |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
973 |
ze->pos = -(zip->locpos + locoff); |
2592
ef26f663a2ba
4244499: ZipEntry() does not convert filenames from Unicode to platform
sherman
parents:
2438
diff
changeset
|
974 |
ze->flag = CENFLG(cen); |
2 | 975 |
|
976 |
if ((ze->name = malloc(nlen + 1)) == NULL) goto Catch; |
|
977 |
memcpy(ze->name, cen + CENHDR, nlen); |
|
978 |
ze->name[nlen] = '\0'; |
|
979 |
||
980 |
if (elen > 0) { |
|
2438
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
981 |
char *extra = cen + CENHDR + nlen; |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
982 |
|
2 | 983 |
/* This entry has "extra" data */ |
984 |
if ((ze->extra = malloc(elen + 2)) == NULL) goto Catch; |
|
985 |
ze->extra[0] = (unsigned char) elen; |
|
986 |
ze->extra[1] = (unsigned char) (elen >> 8); |
|
2438
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
987 |
memcpy(ze->extra+2, extra, elen); |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
988 |
if (ze->csize == ZIP64_MAGICVAL || ze->size == ZIP64_MAGICVAL || |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
989 |
locoff == ZIP64_MAGICVAL) { |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
990 |
jint off = 0; |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
991 |
while ((off + 4) < elen) { // spec: HeaderID+DataSize+Data |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
992 |
jint sz = SH(extra, off + 2); |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
993 |
if (SH(extra, off) == ZIP64_EXTID) { |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
994 |
off += 4; |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
995 |
if (ze->size == ZIP64_MAGICVAL) { |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
996 |
// if invalid zip64 extra fields, just skip |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
997 |
if (sz < 8 || (off + 8) > elen) |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
998 |
break; |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
999 |
ze->size = LL(extra, off); |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
1000 |
sz -= 8; |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
1001 |
off += 8; |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
1002 |
} |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
1003 |
if (ze->csize == ZIP64_MAGICVAL) { |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
1004 |
if (sz < 8 || (off + 8) > elen) |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
1005 |
break; |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
1006 |
ze->csize = LL(extra, off); |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
1007 |
sz -= 8; |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
1008 |
off += 8; |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
1009 |
} |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
1010 |
if (locoff == ZIP64_MAGICVAL) { |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
1011 |
if (sz < 8 || (off + 8) > elen) |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
1012 |
break; |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
1013 |
ze->pos = -(zip->locpos + LL(extra, off)); |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
1014 |
sz -= 8; |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
1015 |
off += 8; |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
1016 |
} |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
1017 |
break; |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
1018 |
} |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
1019 |
off += (sz + 4); |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
1020 |
} |
21c111b51aa8
4681995: Add support for large (> 4GB) zip/jar files
sherman
parents:
2163
diff
changeset
|
1021 |
} |
2 | 1022 |
} |
1023 |
||
1024 |
if (clen > 0) { |
|
1025 |
/* This entry has a comment */ |
|
1026 |
if ((ze->comment = malloc(clen + 1)) == NULL) goto Catch; |
|
1027 |
memcpy(ze->comment, cen + CENHDR + nlen + elen, clen); |
|
1028 |
ze->comment[clen] = '\0'; |
|
1029 |
} |
|
1030 |
goto Finally; |
|
1031 |
||
1032 |
Catch: |
|
1033 |
free(ze->name); |
|
1034 |
free(ze->extra); |
|
1035 |
free(ze->comment); |
|
1036 |
free(ze); |
|
1037 |
ze = NULL; |
|
1038 |
||
1039 |
Finally: |
|
1040 |
#ifndef USE_MMAP |
|
1041 |
if (cen != NULL && accessHint == ACCESS_RANDOM) free(cen); |
|
1042 |
#endif |
|
1043 |
return ze; |
|
1044 |
} |
|
1045 |
||
1046 |
/* |
|
1047 |
* Free the given jzentry. |
|
1048 |
* In fact we maintain a one-entry cache of the most recently used |
|
1049 |
* jzentry for each zip. This optimizes a common access pattern. |
|
1050 |
*/ |
|
1051 |
||
1052 |
void |
|
1053 |
ZIP_FreeEntry(jzfile *jz, jzentry *ze) |
|
1054 |
{ |
|
1055 |
jzentry *last; |
|
1056 |
ZIP_Lock(jz); |
|
1057 |
last = jz->cache; |
|
1058 |
jz->cache = ze; |
|
1059 |
ZIP_Unlock(jz); |
|
1060 |
if (last != NULL) { |
|
1061 |
/* Free the previously cached jzentry */ |
|
1062 |
free(last->name); |
|
1063 |
if (last->extra) free(last->extra); |
|
1064 |
if (last->comment) free(last->comment); |
|
1065 |
free(last); |
|
1066 |
} |
|
1067 |
} |
|
1068 |
||
1069 |
/* |
|
1070 |
* Returns the zip entry corresponding to the specified name, or |
|
1071 |
* NULL if not found. |
|
1072 |
*/ |
|
1073 |
jzentry * |
|
1074 |
ZIP_GetEntry(jzfile *zip, char *name, jint ulen) |
|
1075 |
{ |
|
1076 |
unsigned int hsh = hash(name); |
|
1228
1515928f48cd
6440786: Cannot create a ZIP file containing zero entries
bristor
parents:
2
diff
changeset
|
1077 |
jint idx; |
1515928f48cd
6440786: Cannot create a ZIP file containing zero entries
bristor
parents:
2
diff
changeset
|
1078 |
jzentry *ze = 0; |
2 | 1079 |
|
1080 |
ZIP_Lock(zip); |
|
1228
1515928f48cd
6440786: Cannot create a ZIP file containing zero entries
bristor
parents:
2
diff
changeset
|
1081 |
if (zip->total == 0) { |
1515928f48cd
6440786: Cannot create a ZIP file containing zero entries
bristor
parents:
2
diff
changeset
|
1082 |
goto Finally; |
1515928f48cd
6440786: Cannot create a ZIP file containing zero entries
bristor
parents:
2
diff
changeset
|
1083 |
} |
1515928f48cd
6440786: Cannot create a ZIP file containing zero entries
bristor
parents:
2
diff
changeset
|
1084 |
|
1515928f48cd
6440786: Cannot create a ZIP file containing zero entries
bristor
parents:
2
diff
changeset
|
1085 |
idx = zip->table[hsh % zip->tablelen]; |
2 | 1086 |
|
1087 |
/* |
|
1088 |
* This while loop is an optimization where a double lookup |
|
1089 |
* for name and name+/ is being performed. The name char |
|
1090 |
* array has enough room at the end to try again with a |
|
1091 |
* slash appended if the first table lookup does not succeed. |
|
1092 |
*/ |
|
1093 |
while(1) { |
|
1094 |
||
1095 |
/* Check the cached entry first */ |
|
1096 |
ze = zip->cache; |
|
1097 |
if (ze && strcmp(ze->name,name) == 0) { |
|
1098 |
/* Cache hit! Remove and return the cached entry. */ |
|
1099 |
zip->cache = 0; |
|
1100 |
ZIP_Unlock(zip); |
|
1101 |
return ze; |
|
1102 |
} |
|
1103 |
ze = 0; |
|
1104 |
||
1105 |
/* |
|
1106 |
* Search down the target hash chain for a cell whose |
|
1107 |
* 32 bit hash matches the hashed name. |
|
1108 |
*/ |
|
1109 |
while (idx != ZIP_ENDCHAIN) { |
|
1110 |
jzcell *zc = &zip->entries[idx]; |
|
1111 |
||
1112 |
if (zc->hash == hsh) { |
|
1113 |
/* |
|
1114 |
* OK, we've found a ZIP entry whose 32 bit hashcode |
|
1115 |
* matches the name we're looking for. Try to read |
|
1116 |
* its entry information from the CEN. If the CEN |
|
1117 |
* name matches the name we're looking for, we're |
|
1118 |
* done. |
|
1119 |
* If the names don't match (which should be very rare) |
|
1120 |
* we keep searching. |
|
1121 |
*/ |
|
1122 |
ze = newEntry(zip, zc, ACCESS_RANDOM); |
|
1123 |
if (ze && strcmp(ze->name, name)==0) { |
|
1124 |
break; |
|
1125 |
} |
|
1126 |
if (ze != 0) { |
|
1127 |
/* We need to release the lock across the free call */ |
|
1128 |
ZIP_Unlock(zip); |
|
1129 |
ZIP_FreeEntry(zip, ze); |
|
1130 |
ZIP_Lock(zip); |
|
1131 |
} |
|
1132 |
ze = 0; |
|
1133 |
} |
|
1134 |
idx = zc->next; |
|
1135 |
} |
|
1136 |
||
1137 |
/* Entry found, return it */ |
|
1138 |
if (ze != 0) { |
|
1139 |
break; |
|
1140 |
} |
|
1141 |
||
1142 |
/* If no real length was passed in, we are done */ |
|
1143 |
if (ulen == 0) { |
|
1144 |
break; |
|
1145 |
} |
|
1146 |
||
1147 |
/* Slash is already there? */ |
|
1148 |
if (name[ulen-1] == '/') { |
|
1149 |
break; |
|
1150 |
} |
|
1151 |
||
1152 |
/* Add slash and try once more */ |
|
1153 |
name[ulen] = '/'; |
|
1154 |
name[ulen+1] = '\0'; |
|
1155 |
hsh = hash_append(hsh, '/'); |
|
1156 |
idx = zip->table[hsh % zip->tablelen]; |
|
1157 |
ulen = 0; |
|
1158 |
} |
|
1159 |
||
1228
1515928f48cd
6440786: Cannot create a ZIP file containing zero entries
bristor
parents:
2
diff
changeset
|
1160 |
Finally: |
2 | 1161 |
ZIP_Unlock(zip); |
1162 |
return ze; |
|
1163 |
} |
|
1164 |
||
1165 |
/* |
|
1166 |
* Returns the n'th (starting at zero) zip file entry, or NULL if the |
|
1167 |
* specified index was out of range. |
|
1168 |
*/ |
|
1169 |
jzentry * JNICALL |
|
1170 |
ZIP_GetNextEntry(jzfile *zip, jint n) |
|
1171 |
{ |
|
1172 |
jzentry *result; |
|
1173 |
if (n < 0 || n >= zip->total) { |
|
1174 |
return 0; |
|
1175 |
} |
|
1176 |
ZIP_Lock(zip); |
|
1177 |
result = newEntry(zip, &zip->entries[n], ACCESS_SEQUENTIAL); |
|
1178 |
ZIP_Unlock(zip); |
|
1179 |
return result; |
|
1180 |
} |
|
1181 |
||
1182 |
/* |
|
1183 |
* Locks the specified zip file for reading. |
|
1184 |
*/ |
|
1185 |
void |
|
1186 |
ZIP_Lock(jzfile *zip) |
|
1187 |
{ |
|
1188 |
MLOCK(zip->lock); |
|
1189 |
} |
|
1190 |
||
1191 |
/* |
|
1192 |
* Unlocks the specified zip file. |
|
1193 |
*/ |
|
1194 |
void |
|
1195 |
ZIP_Unlock(jzfile *zip) |
|
1196 |
{ |
|
1197 |
MUNLOCK(zip->lock); |
|
1198 |
} |
|
1199 |
||
1200 |
/* |
|
1201 |
* Returns the offset of the entry data within the zip file. |
|
1202 |
* Returns -1 if an error occurred, in which case zip->msg will |
|
1203 |
* contain the error text. |
|
1204 |
*/ |
|
1205 |
jlong |
|
1206 |
ZIP_GetEntryDataOffset(jzfile *zip, jzentry *entry) |
|
1207 |
{ |
|
1208 |
/* The Zip file spec explicitly allows the LOC extra data size to |
|
1209 |
* be different from the CEN extra data size, although the JDK |
|
1210 |
* never creates such zip files. Since we cannot trust the CEN |
|
1211 |
* extra data size, we need to read the LOC to determine the entry |
|
1212 |
* data offset. We do this lazily to avoid touching the virtual |
|
1213 |
* memory page containing the LOC when initializing jzentry |
|
1214 |
* objects. (This speeds up javac by a factor of 10 when the JDK |
|
1215 |
* is installed on a very slow filesystem.) |
|
1216 |
*/ |
|
1217 |
if (entry->pos <= 0) { |
|
1218 |
unsigned char loc[LOCHDR]; |
|
1219 |
if (readFullyAt(zip->zfd, loc, LOCHDR, -(entry->pos)) == -1) { |
|
1220 |
zip->msg = "error reading zip file"; |
|
1221 |
return -1; |
|
1222 |
} |
|
1223 |
if (GETSIG(loc) != LOCSIG) { |
|
1224 |
zip->msg = "invalid LOC header (bad signature)"; |
|
1225 |
return -1; |
|
1226 |
} |
|
1227 |
entry->pos = (- entry->pos) + LOCHDR + LOCNAM(loc) + LOCEXT(loc); |
|
1228 |
} |
|
1229 |
return entry->pos; |
|
1230 |
} |
|
1231 |
||
1232 |
/* |
|
1233 |
* Reads bytes from the specified zip entry. Assumes that the zip |
|
1234 |
* file had been previously locked with ZIP_Lock(). Returns the |
|
1235 |
* number of bytes read, or -1 if an error occurred. If zip->msg != 0 |
|
1236 |
* then a zip error occurred and zip->msg contains the error text. |
|
1237 |
*/ |
|
1238 |
jint |
|
1239 |
ZIP_Read(jzfile *zip, jzentry *entry, jlong pos, void *buf, jint len) |
|
1240 |
{ |
|
1241 |
jlong entry_size = (entry->csize != 0) ? entry->csize : entry->size; |
|
1242 |
jlong start; |
|
1243 |
||
1244 |
/* Clear previous zip error */ |
|
1245 |
zip->msg = NULL; |
|
1246 |
||
1247 |
/* Check specified position */ |
|
1248 |
if (pos < 0 || pos > entry_size - 1) { |
|
1249 |
zip->msg = "ZIP_Read: specified offset out of range"; |
|
1250 |
return -1; |
|
1251 |
} |
|
1252 |
||
1253 |
/* Check specified length */ |
|
1254 |
if (len <= 0) |
|
1255 |
return 0; |
|
1256 |
if (len > entry_size - pos) |
|
1257 |
len = entry_size - pos; |
|
1258 |
||
1259 |
/* Get file offset to start reading data */ |
|
1260 |
start = ZIP_GetEntryDataOffset(zip, entry); |
|
1261 |
if (start < 0) |
|
1262 |
return -1; |
|
1263 |
start += pos; |
|
1264 |
||
1265 |
if (start + len > zip->len) { |
|
1266 |
zip->msg = "ZIP_Read: corrupt zip file: invalid entry size"; |
|
1267 |
return -1; |
|
1268 |
} |
|
1269 |
||
1270 |
if (readFullyAt(zip->zfd, buf, len, start) == -1) { |
|
1271 |
zip->msg = "ZIP_Read: error reading zip file"; |
|
1272 |
return -1; |
|
1273 |
} |
|
1274 |
return len; |
|
1275 |
} |
|
1276 |
||
1277 |
||
1278 |
/* The maximum size of a stack-allocated buffer. |
|
1279 |
*/ |
|
1280 |
#define BUF_SIZE 4096 |
|
1281 |
||
1282 |
/* |
|
1283 |
* This function is used by the runtime system to load compressed entries |
|
1284 |
* from ZIP/JAR files specified in the class path. It is defined here |
|
1285 |
* so that it can be dynamically loaded by the runtime if the zip library |
|
1286 |
* is found. |
|
1287 |
*/ |
|
1288 |
jboolean |
|
1289 |
InflateFully(jzfile *zip, jzentry *entry, void *buf, char **msg) |
|
1290 |
{ |
|
1291 |
z_stream strm; |
|
1292 |
char tmp[BUF_SIZE]; |
|
1293 |
jlong pos = 0; |
|
1294 |
jlong count = entry->csize; |
|
1295 |
jboolean status; |
|
1296 |
||
1297 |
*msg = 0; /* Reset error message */ |
|
1298 |
||
1299 |
if (count == 0) { |
|
1300 |
*msg = "inflateFully: entry not compressed"; |
|
1301 |
return JNI_FALSE; |
|
1302 |
} |
|
1303 |
||
1304 |
memset(&strm, 0, sizeof(z_stream)); |
|
1305 |
if (inflateInit2(&strm, -MAX_WBITS) != Z_OK) { |
|
1306 |
*msg = strm.msg; |
|
1307 |
return JNI_FALSE; |
|
1308 |
} |
|
1309 |
||
1310 |
strm.next_out = buf; |
|
1311 |
strm.avail_out = entry->size; |
|
1312 |
||
1313 |
while (count > 0) { |
|
1314 |
jint n = count > (jlong)sizeof(tmp) ? (jint)sizeof(tmp) : count; |
|
1315 |
ZIP_Lock(zip); |
|
1316 |
n = ZIP_Read(zip, entry, pos, tmp, n); |
|
1317 |
ZIP_Unlock(zip); |
|
1318 |
if (n <= 0) { |
|
1319 |
if (n == 0) { |
|
1320 |
*msg = "inflateFully: Unexpected end of file"; |
|
1321 |
} |
|
1322 |
inflateEnd(&strm); |
|
1323 |
return JNI_FALSE; |
|
1324 |
} |
|
1325 |
pos += n; |
|
1326 |
count -= n; |
|
1327 |
strm.next_in = (Bytef *)tmp; |
|
1328 |
strm.avail_in = n; |
|
1329 |
do { |
|
1330 |
switch (inflate(&strm, Z_PARTIAL_FLUSH)) { |
|
1331 |
case Z_OK: |
|
1332 |
break; |
|
1333 |
case Z_STREAM_END: |
|
1334 |
if (count != 0 || strm.total_out != entry->size) { |
|
1335 |
*msg = "inflateFully: Unexpected end of stream"; |
|
1336 |
inflateEnd(&strm); |
|
1337 |
return JNI_FALSE; |
|
1338 |
} |
|
1339 |
break; |
|
1340 |
default: |
|
1341 |
break; |
|
1342 |
} |
|
1343 |
} while (strm.avail_in > 0); |
|
1344 |
} |
|
1345 |
inflateEnd(&strm); |
|
1346 |
return JNI_TRUE; |
|
1347 |
} |
|
1348 |
||
1349 |
jzentry * JNICALL |
|
1350 |
ZIP_FindEntry(jzfile *zip, char *name, jint *sizeP, jint *nameLenP) |
|
1351 |
{ |
|
1352 |
jzentry *entry = ZIP_GetEntry(zip, name, 0); |
|
1353 |
if (entry) { |
|
1354 |
*sizeP = entry->size; |
|
1355 |
*nameLenP = strlen(entry->name); |
|
1356 |
} |
|
1357 |
return entry; |
|
1358 |
} |
|
1359 |
||
1360 |
/* |
|
1361 |
* Reads a zip file entry into the specified byte array |
|
1362 |
* When the method completes, it releases the jzentry. |
|
1363 |
* Note: this is called from the separately delivered VM (hotspot/classic) |
|
1364 |
* so we have to be careful to maintain the expected behaviour. |
|
1365 |
*/ |
|
1366 |
jboolean JNICALL |
|
1367 |
ZIP_ReadEntry(jzfile *zip, jzentry *entry, unsigned char *buf, char *entryname) |
|
1368 |
{ |
|
1369 |
char *msg; |
|
1370 |
||
1371 |
strcpy(entryname, entry->name); |
|
1372 |
if (entry->csize == 0) { |
|
1373 |
/* Entry is stored */ |
|
1374 |
jlong pos = 0; |
|
1375 |
jlong size = entry->size; |
|
1376 |
while (pos < size) { |
|
1377 |
jint n; |
|
1378 |
jlong limit = ((((jlong) 1) << 31) - 1); |
|
1379 |
jint count = (size - pos < limit) ? |
|
1380 |
/* These casts suppress a VC++ Internal Compiler Error */ |
|
1381 |
(jint) (size - pos) : |
|
1382 |
(jint) limit; |
|
1383 |
ZIP_Lock(zip); |
|
1384 |
n = ZIP_Read(zip, entry, pos, buf, count); |
|
1385 |
msg = zip->msg; |
|
1386 |
ZIP_Unlock(zip); |
|
1387 |
if (n == -1) { |
|
1388 |
jio_fprintf(stderr, "%s: %s\n", zip->name, |
|
1389 |
msg != 0 ? msg : strerror(errno)); |
|
1390 |
return JNI_FALSE; |
|
1391 |
} |
|
1392 |
buf += n; |
|
1393 |
pos += n; |
|
1394 |
} |
|
1395 |
} else { |
|
1396 |
/* Entry is compressed */ |
|
1397 |
int ok = InflateFully(zip, entry, buf, &msg); |
|
1398 |
if (!ok) { |
|
1399 |
if ((msg == NULL) || (*msg == 0)) { |
|
1400 |
msg = zip->msg; |
|
1401 |
} |
|
1402 |
jio_fprintf(stderr, "%s: %s\n", zip->name, |
|
1403 |
msg != 0 ? msg : strerror(errno)); |
|
1404 |
return JNI_FALSE; |
|
1405 |
} |
|
1406 |
} |
|
1407 |
||
1408 |
ZIP_FreeEntry(zip, entry); |
|
1409 |
||
1410 |
return JNI_TRUE; |
|
1411 |
} |