1 /*
2  * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved.
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.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 /*
27  * Native method support for java.util.zip.ZipFile
28  */
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <ctype.h>
35 #include <assert.h>
36 #include <nativehelper/JNIHelp.h>
37 #include "jlong.h"
38 #include "jvm.h"
39 #include "jni.h"
40 #include "jni_util.h"
41 #include "zip_util.h"
42 #ifdef WIN32
43 #include "io_util_md.h"
44 #else
45 #include "io_util.h"
46 #endif
47 
48 #include "java_util_zip_ZipFile.h"
49 
50 #define NATIVE_METHOD(className, functionName, signature) \
51 { #functionName, signature, (void*)(className ## _ ## functionName) }
52 
53 #define DEFLATED 8
54 #define STORED 0
55 
56 static jfieldID jzfileID;
57 
58 static int OPEN_READ = java_util_zip_ZipFile_OPEN_READ;
59 static int OPEN_DELETE = java_util_zip_ZipFile_OPEN_DELETE;
60 
ZipFile_initIDs(JNIEnv * env)61 static void ZipFile_initIDs(JNIEnv *env)
62 {
63     jclass cls = (*env)->FindClass(env, "java/util/zip/ZipFile");
64     jzfileID = (*env)->GetFieldID(env, cls, "jzfile", "J");
65     assert(jzfileID != 0);
66 }
67 
68 
69 static void
ThrowZipException(JNIEnv * env,const char * msg)70 ThrowZipException(JNIEnv *env, const char *msg)
71 {
72     jstring s = NULL;
73     jobject x;
74 
75     if (msg != NULL) {
76         s = JNU_NewStringPlatform(env, msg);
77     }
78     x = JNU_NewObjectByName(env,
79                             "java/util/zip/ZipException",
80                             "(Ljava/lang/String;)V", s);
81     if (x != NULL) {
82         (*env)->Throw(env, x);
83     }
84 }
85 
86 JNIEXPORT jlong JNICALL
ZipFile_open(JNIEnv * env,jclass cls,jstring name,jint mode,jlong lastModified,jboolean usemmap)87 ZipFile_open(JNIEnv *env, jclass cls, jstring name,
88                                         jint mode, jlong lastModified,
89                                         jboolean usemmap)
90 {
91     const char *path = JNU_GetStringPlatformChars(env, name, 0);
92     char *msg = 0;
93     jlong result = 0;
94     int flag = 0;
95     jzfile *zip = 0;
96 
97     if (mode & OPEN_READ) flag |= O_RDONLY;
98     // Android changed, JVM_O_DELETE/unlink is problematic, see b/28901232.
99     //if (mode & OPEN_DELETE) flag |= JVM_O_DELETE;
100 
101     if (path != 0) {
102         zip = ZIP_Get_From_Cache(path, &msg, lastModified);
103         if (zip == 0 && msg == 0) {
104             ZFILE zfd = 0;
105 #ifdef WIN32
106             zfd = winFileHandleOpen(env, name, flag);
107             if (zfd == -1) {
108                 /* Exception already pending. */
109                 goto finally;
110             }
111 #else
112             zfd = JVM_Open(path, flag, 0);
113             if (zfd < 0) {
114                 throwFileNotFoundException(env, name);
115                 goto finally;
116             }
117 #endif
118             zip = ZIP_Put_In_Cache0(path, zfd, &msg, lastModified, usemmap);
119         }
120 
121         if (zip != 0) {
122             result = ptr_to_jlong(zip);
123         } else if (msg != 0) {
124             ThrowZipException(env, msg);
125             free(msg);
126         } else if (errno == ENOMEM) {
127             JNU_ThrowOutOfMemoryError(env, 0);
128         } else {
129             ThrowZipException(env, "error in opening zip file");
130         }
131 finally:
132         JNU_ReleaseStringPlatformChars(env, name, path);
133     }
134     return result;
135 }
136 
137 JNIEXPORT jint JNICALL
ZipFile_getTotal(JNIEnv * env,jclass cls,jlong zfile)138 ZipFile_getTotal(JNIEnv *env, jclass cls, jlong zfile)
139 {
140     jzfile *zip = jlong_to_ptr(zfile);
141 
142     return zip->total;
143 }
144 
145 JNIEXPORT jboolean JNICALL
ZipFile_startsWithLOC(JNIEnv * env,jclass cls,jlong zfile)146 ZipFile_startsWithLOC(JNIEnv *env, jclass cls, jlong zfile)
147 {
148     jzfile *zip = jlong_to_ptr(zfile);
149 
150     return zip->locsig;
151 }
152 
153 JNIEXPORT void JNICALL
ZipFile_close(JNIEnv * env,jclass cls,jlong zfile)154 ZipFile_close(JNIEnv *env, jclass cls, jlong zfile)
155 {
156     ZIP_Close(jlong_to_ptr(zfile));
157 }
158 
159 JNIEXPORT jint JNICALL
ZipFile_getFileDescriptor(JNIEnv * env,jclass cls,jlong zfile)160 ZipFile_getFileDescriptor(JNIEnv *env, jclass cls, jlong zfile) {
161     jzfile *zip = jlong_to_ptr(zfile);
162     return zip->zfd;
163 }
164 
165 JNIEXPORT jlong JNICALL
ZipFile_getEntry(JNIEnv * env,jclass cls,jlong zfile,jbyteArray name,jboolean addSlash)166 ZipFile_getEntry(JNIEnv *env, jclass cls, jlong zfile,
167                  jbyteArray name, jboolean addSlash)
168 {
169 #define MAXNAME 1024
170     jzfile *zip = jlong_to_ptr(zfile);
171     jsize ulen = (*env)->GetArrayLength(env, name);
172     char buf[MAXNAME+2], *path;
173     jzentry *ze;
174 
175     if (ulen > MAXNAME) {
176         path = malloc(ulen + 2);
177         if (path == 0) {
178             JNU_ThrowOutOfMemoryError(env, 0);
179             return 0;
180         }
181     } else {
182         path = buf;
183     }
184     (*env)->GetByteArrayRegion(env, name, 0, ulen, (jbyte *)path);
185     path[ulen] = '\0';
186     ze = ZIP_GetEntry2(zip, path, (jint)ulen, addSlash);
187     if (path != buf) {
188         free(path);
189     }
190     return ptr_to_jlong(ze);
191 }
192 
193 JNIEXPORT void JNICALL
ZipFile_freeEntry(JNIEnv * env,jclass cls,jlong zfile,jlong zentry)194 ZipFile_freeEntry(JNIEnv *env, jclass cls, jlong zfile,
195                                     jlong zentry)
196 {
197     jzfile *zip = jlong_to_ptr(zfile);
198     jzentry *ze = jlong_to_ptr(zentry);
199     ZIP_FreeEntry(zip, ze);
200 }
201 
202 JNIEXPORT jlong JNICALL
ZipFile_getNextEntry(JNIEnv * env,jclass cls,jlong zfile,jint n)203 ZipFile_getNextEntry(JNIEnv *env, jclass cls, jlong zfile,
204                                         jint n)
205 {
206     jzentry *ze = ZIP_GetNextEntry(jlong_to_ptr(zfile), n);
207     return ptr_to_jlong(ze);
208 }
209 
210 JNIEXPORT jint JNICALL
ZipFile_getEntryMethod(JNIEnv * env,jclass cls,jlong zentry)211 ZipFile_getEntryMethod(JNIEnv *env, jclass cls, jlong zentry)
212 {
213     jzentry *ze = jlong_to_ptr(zentry);
214     return ze->csize != 0 ? DEFLATED : STORED;
215 }
216 
217 JNIEXPORT jint JNICALL
ZipFile_getEntryFlag(JNIEnv * env,jclass cls,jlong zentry)218 ZipFile_getEntryFlag(JNIEnv *env, jclass cls, jlong zentry)
219 {
220     jzentry *ze = jlong_to_ptr(zentry);
221     return ze->flag;
222 }
223 
224 JNIEXPORT jlong JNICALL
ZipFile_getEntryCSize(JNIEnv * env,jclass cls,jlong zentry)225 ZipFile_getEntryCSize(JNIEnv *env, jclass cls, jlong zentry)
226 {
227     jzentry *ze = jlong_to_ptr(zentry);
228     return ze->csize != 0 ? ze->csize : ze->size;
229 }
230 
231 JNIEXPORT jlong JNICALL
ZipFile_getEntrySize(JNIEnv * env,jclass cls,jlong zentry)232 ZipFile_getEntrySize(JNIEnv *env, jclass cls, jlong zentry)
233 {
234     jzentry *ze = jlong_to_ptr(zentry);
235     return ze->size;
236 }
237 
238 JNIEXPORT jlong JNICALL
ZipFile_getEntryTime(JNIEnv * env,jclass cls,jlong zentry)239 ZipFile_getEntryTime(JNIEnv *env, jclass cls, jlong zentry)
240 {
241     jzentry *ze = jlong_to_ptr(zentry);
242     return (jlong)ze->time & 0xffffffffUL;
243 }
244 
245 JNIEXPORT jlong JNICALL
ZipFile_getEntryCrc(JNIEnv * env,jclass cls,jlong zentry)246 ZipFile_getEntryCrc(JNIEnv *env, jclass cls, jlong zentry)
247 {
248     jzentry *ze = jlong_to_ptr(zentry);
249     return (jlong)ze->crc & 0xffffffffUL;
250 }
251 
252 JNIEXPORT jbyteArray JNICALL
ZipFile_getCommentBytes(JNIEnv * env,jclass cls,jlong zfile)253 ZipFile_getCommentBytes(JNIEnv *env, jclass cls, jlong zfile)
254 {
255     jzfile *zip = jlong_to_ptr(zfile);
256     jbyteArray jba = NULL;
257 
258     if (zip->comment != NULL) {
259         if ((jba = (*env)->NewByteArray(env, zip->clen)) == NULL)
260             return NULL;
261         (*env)->SetByteArrayRegion(env, jba, 0, zip->clen, (jbyte*)zip->comment);
262     }
263     return jba;
264 }
265 
266 JNIEXPORT jbyteArray JNICALL
ZipFile_getEntryBytes(JNIEnv * env,jclass cls,jlong zentry,jint type)267 ZipFile_getEntryBytes(JNIEnv *env, jclass cls, jlong zentry, jint type)
268 {
269     jzentry *ze = jlong_to_ptr(zentry);
270     int len = 0;
271     jbyteArray jba = NULL;
272     switch (type) {
273     case java_util_zip_ZipFile_JZENTRY_NAME:
274         if (ze->name != 0) {
275             len = (int)ze->nlen;
276             if (len == 0 || (jba = (*env)->NewByteArray(env, len)) == NULL)
277                 break;
278             (*env)->SetByteArrayRegion(env, jba, 0, len, (jbyte *)ze->name);
279         }
280         break;
281     case java_util_zip_ZipFile_JZENTRY_EXTRA:
282         if (ze->extra != 0) {
283             unsigned char *bp = (unsigned char *)&ze->extra[0];
284             len = (bp[0] | (bp[1] << 8));
285             if (len <= 0 || (jba = (*env)->NewByteArray(env, len)) == NULL)
286                 break;
287             (*env)->SetByteArrayRegion(env, jba, 0, len, &ze->extra[2]);
288         }
289         break;
290     case java_util_zip_ZipFile_JZENTRY_COMMENT:
291         if (ze->comment != 0) {
292             len = (int)strlen(ze->comment);
293             if (len == 0 || (jba = (*env)->NewByteArray(env, len)) == NULL)
294                 break;
295             (*env)->SetByteArrayRegion(env, jba, 0, len, (jbyte*)ze->comment);
296         }
297         break;
298     }
299     return jba;
300 }
301 
302 JNIEXPORT jint JNICALL
ZipFile_read(JNIEnv * env,jclass cls,jlong zfile,jlong zentry,jlong pos,jbyteArray bytes,jint off,jint len)303 ZipFile_read(JNIEnv *env, jclass cls, jlong zfile,
304              jlong zentry, jlong pos, jbyteArray bytes,
305              jint off, jint len)
306 {
307     jzfile *zip = jlong_to_ptr(zfile);
308     char *msg;
309 
310     // BEGIN Android-changed: Removed tmp stack buffer.
311     long long length = (long long)(*env)->GetArrayLength(env, bytes);
312     if (off < 0 || len < 0 || off + len > length) {
313         char errmsg[128];
314         snprintf(errmsg, sizeof(errmsg), "len: %d, off: %d are not valid for array sized %lld\n",
315                  len, off, length);
316         JNU_ThrowArrayIndexOutOfBoundsException(env, errmsg);
317         return -1;
318     }
319 
320     jbyte *buf = (*env)->GetByteArrayElements(env, bytes, NULL);
321     ZIP_Lock(zip);
322     len = ZIP_Read(zip, jlong_to_ptr(zentry), pos, buf + off, len);
323     msg = zip->msg;
324     ZIP_Unlock(zip);
325     (*env)->ReleaseByteArrayElements(env, bytes, buf, 0);
326 
327     if (len == -1) {
328         if (msg != 0) {
329             ThrowZipException(env, msg);
330         } else {
331             char errmsg[128];
332             snprintf(errmsg, sizeof(errmsg), "errno: %d, error: %s\n", errno,
333                      "Error reading ZIP file");
334             JNU_ThrowIOExceptionWithLastError(env, errmsg);
335         }
336     }
337     // END Android-changed: Removed tmp stack buffer.
338 
339     return len;
340 }
341 
342 JNIEXPORT jstring JNICALL
ZipFile_getZipMessage(JNIEnv * env,jclass cls,jlong zfile)343 ZipFile_getZipMessage(JNIEnv *env, jclass cls, jlong zfile)
344 {
345     jzfile *zip = jlong_to_ptr(zfile);
346     char *msg = zip->msg;
347     if (msg == NULL) {
348         return NULL;
349     }
350     return JNU_NewStringPlatform(env, msg);
351 }
352 
353 JNIEXPORT jobjectArray JNICALL
JarFile_getMetaInfEntryNames(JNIEnv * env,jobject obj)354 JarFile_getMetaInfEntryNames(JNIEnv *env, jobject obj)
355 {
356     jlong zfile = (*env)->GetLongField(env, obj, jzfileID);
357     jzfile *zip;
358     int i, count;
359     jobjectArray result = 0;
360 
361     if (zfile == 0) {
362         JNU_ThrowByName(env,
363                         "java/lang/IllegalStateException", "zip file closed");
364         return NULL;
365     }
366     zip = jlong_to_ptr(zfile);
367 
368     /* count the number of valid ZIP metanames */
369     count = 0;
370     if (zip->metanames != 0) {
371         for (i = 0; i < zip->metacount; i++) {
372             if (zip->metanames[i] != 0) {
373                 count++;
374             }
375         }
376     }
377 
378     /* If some names were found then build array of java strings */
379     if (count > 0) {
380         jclass cls = (*env)->FindClass(env, "java/lang/String");
381         result = (*env)->NewObjectArray(env, count, cls, 0);
382         if (result != 0) {
383             for (i = 0; i < count; i++) {
384                 jstring str = (*env)->NewStringUTF(env, zip->metanames[i]);
385                 if (str == 0) {
386                     break;
387                 }
388                 (*env)->SetObjectArrayElement(env, result, i, str);
389                 (*env)->DeleteLocalRef(env, str);
390             }
391         }
392     }
393     return result;
394 }
395 
396 static JNINativeMethod gMethods[] = {
397   NATIVE_METHOD(ZipFile, getFileDescriptor, "(J)I"),
398   NATIVE_METHOD(ZipFile, getEntry, "(J[BZ)J"),
399   NATIVE_METHOD(ZipFile, freeEntry, "(JJ)V"),
400   NATIVE_METHOD(ZipFile, getNextEntry, "(JI)J"),
401   NATIVE_METHOD(ZipFile, close, "(J)V"),
402   NATIVE_METHOD(ZipFile, open, "(Ljava/lang/String;IJZ)J"),
403   NATIVE_METHOD(ZipFile, getTotal, "(J)I"),
404   NATIVE_METHOD(ZipFile, startsWithLOC, "(J)Z"),
405   NATIVE_METHOD(ZipFile, read, "(JJJ[BII)I"),
406   NATIVE_METHOD(ZipFile, getEntryTime, "(J)J"),
407   NATIVE_METHOD(ZipFile, getEntryCrc, "(J)J"),
408   NATIVE_METHOD(ZipFile, getEntryCSize, "(J)J"),
409   NATIVE_METHOD(ZipFile, getEntrySize, "(J)J"),
410   NATIVE_METHOD(ZipFile, getEntryMethod, "(J)I"),
411   NATIVE_METHOD(ZipFile, getEntryFlag, "(J)I"),
412   NATIVE_METHOD(ZipFile, getCommentBytes, "(J)[B"),
413   NATIVE_METHOD(ZipFile, getEntryBytes, "(JI)[B"),
414   NATIVE_METHOD(ZipFile, getZipMessage, "(J)Ljava/lang/String;"),
415 };
416 
417 static JNINativeMethod gJarFileMethods[] = {
418   NATIVE_METHOD(JarFile, getMetaInfEntryNames, "()[Ljava/lang/String;"),
419 };
420 
register_java_util_zip_ZipFile(JNIEnv * env)421 void register_java_util_zip_ZipFile(JNIEnv* env) {
422   jniRegisterNativeMethods(env, "java/util/zip/ZipFile", gMethods, NELEM(gMethods));
423   ZipFile_initIDs(env);
424 
425   jniRegisterNativeMethods(env, "java/util/jar/JarFile", gJarFileMethods, NELEM(gJarFileMethods));
426 }
427