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