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 
  49 static jfieldID  unpackerPtrFID;
  50 static jmethodID currentInstMID;
  51 static jmethodID readInputMID;
  52 static jclass    NIclazz;
  53 static jmethodID getUnpackerPtrMID;
  54 
  55 static 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  */
 101 DEF_STATIC_JNI_OnLoad
 102 
 103 static jlong read_input_via_jni(unpacker* self,
 104                                 void* buf, jlong minlen, jlong maxlen);
 105 
 106 static 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.
 127 static 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 
 155 static 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 
 166 unpacker* unpacker::current() {
 167   return get_unpacker();
 168 }
 169 
 170 // Callback for fetching data, Java style.  Calls NativeUnpack.readInputFn().
 171 static 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 
 179 JNIEXPORT void JNICALL
 180 Java_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 
 202 JNIEXPORT jlong JNICALL
 203 Java_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 
 242 JNIEXPORT jboolean JNICALL
 243 Java_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 
 292 JNIEXPORT jobject JNICALL
 293 Java_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 
 315 JNIEXPORT jlong JNICALL
 316 Java_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 
 324 JNIEXPORT jboolean JNICALL
 325 Java_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 
 339 JNIEXPORT jstring JNICALL
 340 Java_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 }