1 /*
   2  * Copyright (c) 2003, 2012, 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 static jlong read_input_via_jni(unpacker* self,
  60                                 void* buf, jlong minlen, jlong maxlen);
  61 
  62 static unpacker* get_unpacker(JNIEnv *env, jobject pObj, bool noCreate=false) {
  63   unpacker* uPtr;
  64   jlong p = env->CallLongMethod(pObj, getUnpackerPtrMID);
  65   uPtr = (unpacker*)jlong2ptr(p);
  66   if (uPtr == null) {
  67     if (noCreate)  return null;
  68     uPtr = new unpacker();
  69     if (uPtr == null) {
  70       THROW_IOE(ERROR_ENOMEM);
  71       return null;
  72     }
  73     //fprintf(stderr, "get_unpacker(%p) uPtr=%p initializing\n", pObj, uPtr);
  74     uPtr->init(read_input_via_jni);
  75     uPtr->jniobj = (void*) env->NewGlobalRef(pObj);
  76     env->SetLongField(pObj, unpackerPtrFID, ptr2jlong(uPtr));
  77   }
  78   uPtr->jnienv = env;  // keep refreshing this in case of MT access
  79   return uPtr;
  80 }
  81 
  82 // This is the harder trick:  Pull the current state out of mid-air.
  83 static unpacker* get_unpacker() {
  84   //fprintf(stderr, "get_unpacker()\n");
  85   JavaVM* vm = null;
  86   jsize nVM = 0;
  87   jint retval = JNI_GetCreatedJavaVMs(&vm, 1, &nVM);
  88   // other VM implements may differ, thus for correctness, we need these checks
  89   if (retval != JNI_OK || nVM != 1)
  90     return null;
  91   void* envRaw = null;
  92   vm->GetEnv(&envRaw, JNI_VERSION_1_1);
  93   JNIEnv* env = (JNIEnv*) envRaw;
  94   //fprintf(stderr, "get_unpacker() env=%p\n", env);
  95   if (env == null)
  96     return null;
  97   jobject pObj = env->CallStaticObjectMethod(NIclazz, currentInstMID);
  98   //fprintf(stderr, "get_unpacker0() pObj=%p\n", pObj);
  99   if (pObj != null) {
 100     // Got pObj and env; now do it the easy way.
 101     return get_unpacker(env, pObj);
 102   }
 103   // this should really not happen, if it does something is seriously
 104   // wrong throw an exception
 105   THROW_IOE(ERROR_INTERNAL);
 106   return null;
 107 }
 108 
 109 static void free_unpacker(JNIEnv *env, jobject pObj, unpacker* uPtr) {
 110   if (uPtr != null) {
 111     //fprintf(stderr, "free_unpacker(%p) uPtr=%p\n", pObj, uPtr);
 112     env->DeleteGlobalRef((jobject) uPtr->jniobj);
 113     uPtr->jniobj = null;
 114     uPtr->free();
 115     delete uPtr;
 116     env->SetLongField(pObj, unpackerPtrFID, (jlong)null);
 117    }
 118 }
 119 
 120 unpacker* unpacker::current() {
 121   return get_unpacker();
 122 }
 123 
 124 // Callback for fetching data, Java style.  Calls NativeUnpack.readInputFn().
 125 static jlong read_input_via_jni(unpacker* self,
 126                                 void* buf, jlong minlen, jlong maxlen) {
 127   JNIEnv* env = (JNIEnv*) self->jnienv;
 128   jobject pbuf = env->NewDirectByteBuffer(buf, maxlen);
 129   return env->CallLongMethod((jobject) self->jniobj, readInputMID,
 130                              pbuf, minlen);
 131 }
 132 
 133 JNIEXPORT void JNICALL
 134 Java_com_sun_java_util_jar_pack_NativeUnpack_initIDs(JNIEnv *env, jclass clazz) {
 135 #ifndef PRODUCT
 136   dbg = getenv("DEBUG_ATTACH");
 137   while( dbg != null) { sleep(10); }
 138 #endif
 139   NIclazz = (jclass) env->NewGlobalRef(clazz);
 140   unpackerPtrFID = env->GetFieldID(clazz, "unpackerPtr", "J");
 141   currentInstMID = env->GetStaticMethodID(clazz, "currentInstance",
 142                                           "()Ljava/lang/Object;");
 143   readInputMID = env->GetMethodID(clazz, "readInputFn",
 144                                   "(Ljava/nio/ByteBuffer;J)J");
 145   getUnpackerPtrMID = env->GetMethodID(clazz, "getUnpackerPtr", "()J");
 146 
 147   if (unpackerPtrFID == null ||
 148       currentInstMID == null ||
 149       readInputMID == null ||
 150       NIclazz == null ||
 151       getUnpackerPtrMID == null) {
 152     THROW_IOE("cannot init class members");
 153   }
 154 }
 155 
 156 JNIEXPORT jlong JNICALL
 157 Java_com_sun_java_util_jar_pack_NativeUnpack_start(JNIEnv *env, jobject pObj,
 158                                    jobject pBuf, jlong offset) {
 159   // try to get the unpacker pointer the hard way first, we do this to ensure
 160   // valid object pointers and env is intact, if not now is good time to bail.
 161   unpacker* uPtr = get_unpacker();
 162   //fprintf(stderr, "start(%p) uPtr=%p initializing\n", pObj, uPtr);
 163   if (uPtr == null) {
 164       return -1;
 165   }
 166   // redirect our io to the default log file or whatever.
 167   uPtr->redirect_stdio();
 168 
 169   void*  buf    = null;
 170   size_t buflen = 0;
 171   if (pBuf != null) {
 172     buf    = env->GetDirectBufferAddress(pBuf);
 173     buflen = (size_t)env->GetDirectBufferCapacity(pBuf);
 174     if (buflen == 0)  buf = null;
 175     if (buf == null) { THROW_IOE(ERROR_INTERNAL); return 0; }
 176     if ((size_t)offset >= buflen)
 177       { buf = null; buflen = 0; }
 178     else
 179       { buf = (char*)buf + (size_t)offset; buflen -= (size_t)offset; }
 180   }
 181   // before we start off we make sure there is no other error by the time we
 182   // get here
 183   if (uPtr->aborting()) {
 184     THROW_IOE(uPtr->get_abort_message());
 185     return 0;
 186   }
 187   uPtr->start(buf, buflen);
 188   if (uPtr->aborting()) {
 189     THROW_IOE(uPtr->get_abort_message());
 190     return 0;
 191   }
 192 
 193   return ((jlong)
 194           uPtr->get_segments_remaining() << 32)
 195     + uPtr->get_files_remaining();
 196 }
 197 
 198 JNIEXPORT jboolean JNICALL
 199 Java_com_sun_java_util_jar_pack_NativeUnpack_getNextFile(JNIEnv *env, jobject pObj,
 200                                          jobjectArray pParts) {
 201 
 202   unpacker* uPtr = get_unpacker(env, pObj);
 203   unpacker::file* filep = uPtr->get_next_file();
 204 
 205   if (uPtr->aborting()) {
 206     THROW_IOE(uPtr->get_abort_message());
 207     return false;
 208   }
 209 
 210   if (filep == null) {
 211     return false;   // end of the sequence
 212   }
 213   assert(filep == &uPtr->cur_file);
 214 
 215   int pidx = 0, iidx = 0;
 216   jintArray pIntParts = (jintArray) env->GetObjectArrayElement(pParts, pidx++);
 217   jint*     intParts  = env->GetIntArrayElements(pIntParts, null);
 218   intParts[iidx++] = (jint)( (julong)filep->size >> 32 );
 219   intParts[iidx++] = (jint)( (julong)filep->size >>  0 );
 220   intParts[iidx++] = filep->modtime;
 221   intParts[iidx++] = filep->deflate_hint() ? 1 : 0;
 222   env->ReleaseIntArrayElements(pIntParts, intParts, JNI_COMMIT);
 223 
 224   env->SetObjectArrayElement(pParts, pidx++, env->NewStringUTF(filep->name));
 225 
 226   jobject pDataBuf = null;
 227   if (filep->data[0].len > 0)
 228     pDataBuf = env->NewDirectByteBuffer(filep->data[0].ptr,
 229                                         filep->data[0].len);
 230   env->SetObjectArrayElement(pParts, pidx++, pDataBuf);
 231   pDataBuf = null;
 232   if (filep->data[1].len > 0)
 233     pDataBuf = env->NewDirectByteBuffer(filep->data[1].ptr,
 234                                         filep->data[1].len);
 235   env->SetObjectArrayElement(pParts, pidx++, pDataBuf);
 236 
 237   return true;
 238 }
 239 
 240 
 241 JNIEXPORT jobject JNICALL
 242 Java_com_sun_java_util_jar_pack_NativeUnpack_getUnusedInput(JNIEnv *env, jobject pObj) {
 243   unpacker* uPtr = get_unpacker(env, pObj);
 244   unpacker::file* filep = &uPtr->cur_file;
 245 
 246   if (uPtr->aborting()) {
 247     THROW_IOE(uPtr->get_abort_message());
 248     return false;
 249   }
 250 
 251   // We have fetched all the files.
 252   // Now swallow up any remaining input.
 253   if (uPtr->input_remaining() == 0) {
 254     return null;
 255   } else {
 256     bytes remaining_bytes;
 257     remaining_bytes.malloc(uPtr->input_remaining());
 258     remaining_bytes.copyFrom(uPtr->input_scan(), uPtr->input_remaining());
 259     return env->NewDirectByteBuffer(remaining_bytes.ptr, remaining_bytes.len);
 260   }
 261 }
 262 
 263 JNIEXPORT jlong JNICALL
 264 Java_com_sun_java_util_jar_pack_NativeUnpack_finish(JNIEnv *env, jobject pObj) {
 265   unpacker* uPtr = get_unpacker(env, pObj, false);
 266   if (uPtr == null)  return 0;
 267   size_t consumed = uPtr->input_consumed();
 268   free_unpacker(env, pObj, uPtr);
 269   return consumed;
 270 }
 271 
 272 JNIEXPORT jboolean JNICALL
 273 Java_com_sun_java_util_jar_pack_NativeUnpack_setOption(JNIEnv *env, jobject pObj,
 274                                        jstring pProp, jstring pValue) {
 275   unpacker*   uPtr  = get_unpacker(env, pObj);
 276   const char* prop  = env->GetStringUTFChars(pProp, JNI_FALSE);
 277   const char* value = env->GetStringUTFChars(pValue, JNI_FALSE);
 278   jboolean   retval = uPtr->set_option(prop, value);
 279   env->ReleaseStringUTFChars(pProp,  prop);
 280   env->ReleaseStringUTFChars(pValue, value);
 281   return retval;
 282 }
 283 
 284 JNIEXPORT jstring JNICALL
 285 Java_com_sun_java_util_jar_pack_NativeUnpack_getOption(JNIEnv *env, jobject pObj,
 286                                        jstring pProp) {
 287 
 288   unpacker*   uPtr  = get_unpacker(env, pObj);
 289   const char* prop  = env->GetStringUTFChars(pProp, JNI_FALSE);
 290   const char* value = uPtr->get_option(prop);
 291   env->ReleaseStringUTFChars(pProp, prop);
 292   if (value == null)  return null;
 293   return env->NewStringUTF(value);
 294 }