1/*
2 * Copyright (c) 2003, 2015, 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#include <sys/types.h>
26
27#include <stdio.h>
28#include <string.h>
29#include <stdlib.h>
30#include <stdarg.h>
31
32
33#include <limits.h>
34
35#include <com_sun_java_util_jar_pack_NativeUnpack.h>
36
37#include "jni_util.h"
38
39#include "defines.h"
40#include "bytes.h"
41#include "utils.h"
42#include "coding.h"
43#include "bands.h"
44#include "constants.h"
45#include "zip.h"
46#include "unpack.h"
47
48
49static jfieldID unpackerPtrFID;
50static jmethodID currentInstMID;
51static jmethodID readInputMID;
52static jclass NIclazz;
53static jmethodID getUnpackerPtrMID;
54
55static char* dbg = null;
56
57#define THROW_IOE(x) JNU_ThrowIOException(env,x)
58
59#define CHECK_EXCEPTION_RETURN_VOID_THROW_IOE(CERVTI_exception, CERVTI_message) \
60 do { \
61 if ((env)->ExceptionOccurred()) { \
62 THROW_IOE(CERVTI_message); \
63 return; \
64 } \
65 if ((CERVTI_exception) == NULL) { \
66 THROW_IOE(CERVTI_message); \
67 return; \
68 } \
69 } while (JNI_FALSE)
70
71
72#define CHECK_EXCEPTION_RETURN_VALUE(CERL_exception, CERL_return_value) \
73 do { \
74 if ((env)->ExceptionOccurred()) { \
75 return CERL_return_value; \
76 } \
77 if ((CERL_exception) == NULL) { \
78 return CERL_return_value; \
79 } \
80 } while (JNI_FALSE)
81
82
83// If these useful macros aren't defined in jni_util.h then define them here
84#ifndef CHECK_NULL_RETURN
85#define CHECK_NULL_RETURN(x, y) \
86 do { \
87 if ((x) == NULL) return (y); \
88 } while (JNI_FALSE)
89#endif
90
91#ifndef CHECK_EXCEPTION_RETURN
92#define CHECK_EXCEPTION_RETURN(env, y) \
93 do { \
94 if ((*env)->ExceptionCheck(env)) return (y); \
95 } while (JNI_FALSE)
96#endif
97
98/*
99 * Declare library specific JNI_Onload entry if static build
100 */
101DEF_STATIC_JNI_OnLoad
102
103static jlong read_input_via_jni(unpacker* self,
104 void* buf, jlong minlen, jlong maxlen);
105
106static unpacker* get_unpacker(JNIEnv *env, jobject pObj, bool noCreate=false) {
107 unpacker* uPtr;
108 jlong p = env->CallLongMethod(pObj, getUnpackerPtrMID);
109 uPtr = (unpacker*)jlong2ptr(p);
110 if (uPtr == null) {
111 if (noCreate) return null;
112 uPtr = new unpacker();
113 if (uPtr == null) {
114 THROW_IOE(ERROR_ENOMEM);
115 return null;
116 }
117 //fprintf(stderr, "get_unpacker(%p) uPtr=%p initializing\n", pObj, uPtr);
118 uPtr->init(read_input_via_jni);
119 uPtr->jniobj = (void*) env->NewGlobalRef(pObj);
120 env->SetLongField(pObj, unpackerPtrFID, ptr2jlong(uPtr));
121 }
122 uPtr->jnienv = env; // keep refreshing this in case of MT access
123 return uPtr;
124}
125
126// This is the harder trick: Pull the current state out of mid-air.
127static unpacker* get_unpacker() {
128 //fprintf(stderr, "get_unpacker()\n");
129 JavaVM* vm = null;
130 jsize nVM = 0;
131 jint retval = JNI_GetCreatedJavaVMs(&vm, 1, &nVM);
132 // other VM implements may differ, thus for correctness, we need these checks
133 if (retval != JNI_OK || nVM != 1)
134 return null;
135 void* envRaw = null;
136 vm->GetEnv(&envRaw, JNI_VERSION_1_1);
137 JNIEnv* env = (JNIEnv*) envRaw;
138 //fprintf(stderr, "get_unpacker() env=%p\n", env);
139 CHECK_NULL_RETURN(env, NULL);
140 jobject pObj = env->CallStaticObjectMethod(NIclazz, currentInstMID);
141 // We should check upon the known non-null variable because here we want to check
142 // only for pending exceptions. If pObj is null we'll deal with it later.
143 CHECK_EXCEPTION_RETURN_VALUE(env, NULL);
144 //fprintf(stderr, "get_unpacker0() pObj=%p\n", pObj);
145 if (pObj != null) {
146 // Got pObj and env; now do it the easy way.
147 return get_unpacker(env, pObj);
148 }
149 // this should really not happen, if it does something is seriously
150 // wrong throw an exception
151 THROW_IOE(ERROR_INTERNAL);
152 return null;
153}
154
155static void free_unpacker(JNIEnv *env, jobject pObj, unpacker* uPtr) {
156 if (uPtr != null) {
157 //fprintf(stderr, "free_unpacker(%p) uPtr=%p\n", pObj, uPtr);
158 env->DeleteGlobalRef((jobject) uPtr->jniobj);
159 uPtr->jniobj = null;
160 uPtr->free();
161 delete uPtr;
162 env->SetLongField(pObj, unpackerPtrFID, (jlong)null);
163 }
164}
165
166unpacker* unpacker::current() {
167 return get_unpacker();
168}
169
170// Callback for fetching data, Java style. Calls NativeUnpack.readInputFn().
171static jlong read_input_via_jni(unpacker* self,
172 void* buf, jlong minlen, jlong maxlen) {
173 JNIEnv* env = (JNIEnv*) self->jnienv;
174 jobject pbuf = env->NewDirectByteBuffer(buf, maxlen);
175 return env->CallLongMethod((jobject) self->jniobj, readInputMID,
176 pbuf, minlen);
177}
178
179JNIEXPORT void JNICALL
180Java_com_sun_java_util_jar_pack_NativeUnpack_initIDs(JNIEnv *env, jclass clazz) {
181#ifndef PRODUCT
182 dbg = getenv("DEBUG_ATTACH");
183 while( dbg != null) { sleep(10); }
184#endif
185 NIclazz = (jclass) env->NewGlobalRef(clazz);
186
187 unpackerPtrFID = env->GetFieldID(clazz, "unpackerPtr", "J");
188 CHECK_EXCEPTION_RETURN_VOID_THROW_IOE(unpackerPtrFID, ERROR_INIT);
189
190 currentInstMID = env->GetStaticMethodID(clazz, "currentInstance",
191 "()Ljava/lang/Object;");
192 CHECK_EXCEPTION_RETURN_VOID_THROW_IOE(currentInstMID, ERROR_INIT);
193
194 readInputMID = env->GetMethodID(clazz, "readInputFn",
195 "(Ljava/nio/ByteBuffer;J)J");
196 CHECK_EXCEPTION_RETURN_VOID_THROW_IOE(readInputMID, ERROR_INIT);
197
198 getUnpackerPtrMID = env->GetMethodID(clazz, "getUnpackerPtr", "()J");
199 CHECK_EXCEPTION_RETURN_VOID_THROW_IOE(getUnpackerPtrMID, ERROR_INIT);
200}
201
202JNIEXPORT jlong JNICALL
203Java_com_sun_java_util_jar_pack_NativeUnpack_start(JNIEnv *env, jobject pObj,
204 jobject pBuf, jlong offset) {
205 // try to get the unpacker pointer the hard way first, we do this to ensure
206 // valid object pointers and env is intact, if not now is good time to bail.
207 unpacker* uPtr = get_unpacker();
208 //fprintf(stderr, "start(%p) uPtr=%p initializing\n", pObj, uPtr);
209 CHECK_EXCEPTION_RETURN_VALUE(uPtr, -1);
210 // redirect our io to the default log file or whatever.
211 uPtr->redirect_stdio();
212
213 void* buf = null;
214 size_t buflen = 0;
215 if (pBuf != null) {
216 buf = env->GetDirectBufferAddress(pBuf);
217 buflen = (size_t)env->GetDirectBufferCapacity(pBuf);
218 if (buflen == 0) buf = null;
219 if (buf == null) { THROW_IOE(ERROR_INTERNAL); return 0; }
220 if ((size_t)offset >= buflen)
221 { buf = null; buflen = 0; }
222 else
223 { buf = (char*)buf + (size_t)offset; buflen -= (size_t)offset; }
224 }
225 // before we start off we make sure there is no other error by the time we
226 // get here
227 if (uPtr->aborting()) {
228 THROW_IOE(uPtr->get_abort_message());
229 return 0;
230 }
231 uPtr->start(buf, buflen);
232 if (uPtr->aborting()) {
233 THROW_IOE(uPtr->get_abort_message());
234 return 0;
235 }
236
237 return ((jlong)
238 uPtr->get_segments_remaining() << 32)
239 + uPtr->get_files_remaining();
240}
241
242JNIEXPORT jboolean JNICALL
243Java_com_sun_java_util_jar_pack_NativeUnpack_getNextFile(JNIEnv *env, jobject pObj,
244 jobjectArray pParts) {
245
246 unpacker* uPtr = get_unpacker(env, pObj);
247 CHECK_EXCEPTION_RETURN_VALUE(uPtr, false);
248 unpacker::file* filep = uPtr->get_next_file();
249
250 if (uPtr->aborting()) {
251 THROW_IOE(uPtr->get_abort_message());
252 return false;
253 }
254
255 CHECK_NULL_RETURN(filep, false);
256 assert(filep == &uPtr->cur_file);
257
258 int pidx = 0, iidx = 0;
259 jintArray pIntParts = (jintArray) env->GetObjectArrayElement(pParts, pidx++);
260 CHECK_EXCEPTION_RETURN_VALUE(pIntParts, false);
261 jint* intParts = env->GetIntArrayElements(pIntParts, null);
262 intParts[iidx++] = (jint)( (julong)filep->size >> 32 );
263 intParts[iidx++] = (jint)( (julong)filep->size >> 0 );
264 intParts[iidx++] = filep->modtime;
265 intParts[iidx++] = filep->deflate_hint() ? 1 : 0;
266 env->ReleaseIntArrayElements(pIntParts, intParts, JNI_COMMIT);
267 jstring filename = env->NewStringUTF(filep->name);
268 CHECK_EXCEPTION_RETURN_VALUE(filename, false);
269 env->SetObjectArrayElement(pParts, pidx++, filename);
270 CHECK_EXCEPTION_RETURN_VALUE(uPtr, false);
271 jobject pDataBuf = null;
272 if (filep->data[0].len > 0) {
273 pDataBuf = env->NewDirectByteBuffer(filep->data[0].ptr,
274 filep->data[0].len);
275 CHECK_EXCEPTION_RETURN_VALUE(pDataBuf, false);
276 }
277 env->SetObjectArrayElement(pParts, pidx++, pDataBuf);
278 CHECK_EXCEPTION_RETURN_VALUE(uPtr, false);
279 pDataBuf = null;
280 if (filep->data[1].len > 0) {
281 pDataBuf = env->NewDirectByteBuffer(filep->data[1].ptr,
282 filep->data[1].len);
283 CHECK_EXCEPTION_RETURN_VALUE(pDataBuf, false);
284 }
285 env->SetObjectArrayElement(pParts, pidx++, pDataBuf);
286 CHECK_EXCEPTION_RETURN_VALUE(uPtr, false);
287
288 return true;
289}
290
291
292JNIEXPORT jobject JNICALL
293Java_com_sun_java_util_jar_pack_NativeUnpack_getUnusedInput(JNIEnv *env, jobject pObj) {
294 unpacker* uPtr = get_unpacker(env, pObj);
295 CHECK_EXCEPTION_RETURN_VALUE(uPtr, NULL);
296 unpacker::file* filep = &uPtr->cur_file;
297
298 if (uPtr->aborting()) {
299 THROW_IOE(uPtr->get_abort_message());
300 return null;
301 }
302
303 // We have fetched all the files.
304 // Now swallow up any remaining input.
305 if (uPtr->input_remaining() == 0) {
306 return null;
307 } else {
308 bytes remaining_bytes;
309 remaining_bytes.malloc(uPtr->input_remaining());
310 remaining_bytes.copyFrom(uPtr->input_scan(), uPtr->input_remaining());
311 return env->NewDirectByteBuffer(remaining_bytes.ptr, remaining_bytes.len);
312 }
313}
314
315JNIEXPORT jlong JNICALL
316Java_com_sun_java_util_jar_pack_NativeUnpack_finish(JNIEnv *env, jobject pObj) {
317 unpacker* uPtr = get_unpacker(env, pObj, false);
318 CHECK_EXCEPTION_RETURN_VALUE(uPtr, 0);
319 size_t consumed = uPtr->input_consumed();
320 free_unpacker(env, pObj, uPtr);
321 return consumed;
322}
323
324JNIEXPORT jboolean JNICALL
325Java_com_sun_java_util_jar_pack_NativeUnpack_setOption(JNIEnv *env, jobject pObj,
326 jstring pProp, jstring pValue) {
327 unpacker* uPtr = get_unpacker(env, pObj);
328 CHECK_EXCEPTION_RETURN_VALUE(uPtr, false);
329 const char* prop = env->GetStringUTFChars(pProp, JNI_FALSE);
330 CHECK_EXCEPTION_RETURN_VALUE(prop, false);
331 const char* value = env->GetStringUTFChars(pValue, JNI_FALSE);
332 CHECK_EXCEPTION_RETURN_VALUE(value, false);
333 jboolean retval = uPtr->set_option(prop, value);
334 env->ReleaseStringUTFChars(pProp, prop);
335 env->ReleaseStringUTFChars(pValue, value);
336 return retval;
337}
338
339JNIEXPORT jstring JNICALL
340Java_com_sun_java_util_jar_pack_NativeUnpack_getOption(JNIEnv *env, jobject pObj,
341 jstring pProp) {
342
343 unpacker* uPtr = get_unpacker(env, pObj);
344 CHECK_EXCEPTION_RETURN_VALUE(uPtr, NULL);
345 const char* prop = env->GetStringUTFChars(pProp, JNI_FALSE);
346 CHECK_EXCEPTION_RETURN_VALUE(prop, NULL);
347 const char* value = uPtr->get_option(prop);
348 CHECK_EXCEPTION_RETURN_VALUE(value, NULL);
349 env->ReleaseStringUTFChars(pProp, prop);
350 return env->NewStringUTF(value);
351}
352