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