Home | History | Annotate | Download | only in native
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one or more
      3  * contributor license agreements.  See the NOTICE file distributed with
      4  * this work for additional information regarding copyright ownership.
      5  * The ASF licenses this file to You under the Apache License, Version 2.0
      6  * (the "License"); you may not use this file except in compliance with
      7  * the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 #define LOG_TAG "StrictJarFile"
     19 
     20 #include <string>
     21 
     22 #include "JNIHelp.h"
     23 #include "JniConstants.h"
     24 #include "ScopedLocalRef.h"
     25 #include "ScopedUtfChars.h"
     26 #include "UniquePtr.h"
     27 #include "jni.h"
     28 #include "ziparchive/zip_archive.h"
     29 #include "cutils/log.h"
     30 
     31 static void throwIoException(JNIEnv* env, const int32_t errorCode) {
     32   jniThrowException(env, "java/io/IOException", ErrorCodeString(errorCode));
     33 }
     34 
     35 // Constructs a string out of |name| with the default charset (UTF-8 on android).
     36 // We prefer this to JNI's NewStringUTF because the string constructor will
     37 // replace unmappable and malformed bytes instead of throwing. See b/18584205
     38 //
     39 // Returns |NULL| iff. we couldn't allocate the string object or its constructor
     40 // arguments.
     41 //
     42 // TODO: switch back to NewStringUTF after libziparchive is modified to reject
     43 // files whose names aren't valid UTF-8.
     44 static jobject constructString(JNIEnv* env, const char* name, const uint16_t nameLength) {
     45   jbyteArray javaNameBytes = env->NewByteArray(nameLength);
     46   if (javaNameBytes == NULL) {
     47       return NULL;
     48   }
     49   env->SetByteArrayRegion(javaNameBytes, 0, nameLength, reinterpret_cast<const jbyte*>(name));
     50 
     51   ScopedLocalRef<jclass> stringClass(env, env->FindClass("java/lang/String"));
     52   const jmethodID stringCtor = env->GetMethodID(stringClass.get(), "<init>", "([B)V");
     53   return env->NewObject(stringClass.get(), stringCtor, javaNameBytes);
     54 }
     55 
     56 static jobject newZipEntry(JNIEnv* env, const ZipEntry& entry, const jobject entryName,
     57                            const uint16_t nameLength) {
     58   ScopedLocalRef<jclass> zipEntryClass(env, env->FindClass("java/util/zip/ZipEntry"));
     59   const jmethodID zipEntryCtor = env->GetMethodID(zipEntryClass.get(), "<init>",
     60                                    "(Ljava/lang/String;Ljava/lang/String;JJJIII[BIJJ)V");
     61 
     62   return env->NewObject(zipEntryClass.get(),
     63                         zipEntryCtor,
     64                         entryName,
     65                         NULL,  // comment
     66                         static_cast<jlong>(entry.crc32),
     67                         static_cast<jlong>(entry.compressed_length),
     68                         static_cast<jlong>(entry.uncompressed_length),
     69                         static_cast<jint>(entry.method),
     70                         static_cast<jint>(0),  // time
     71                         static_cast<jint>(0),  // modData
     72                         NULL,  // byte[] extra
     73                         static_cast<jint>(nameLength),
     74                         static_cast<jlong>(-1),  // local header offset
     75                         static_cast<jlong>(entry.offset));
     76 }
     77 
     78 static jobject newZipEntry(JNIEnv* env, const ZipEntry& entry, const char* name,
     79                            const uint16_t nameLength) {
     80   return newZipEntry(env, entry, constructString(env, name, nameLength), nameLength);
     81 }
     82 
     83 static jlong StrictJarFile_nativeOpenJarFile(JNIEnv* env, jobject, jstring fileName) {
     84   ScopedUtfChars fileChars(env, fileName);
     85   if (fileChars.c_str() == NULL) {
     86     return static_cast<jlong>(-1);
     87   }
     88 
     89   ZipArchiveHandle handle;
     90   int32_t error = OpenArchive(fileChars.c_str(), &handle);
     91   if (error) {
     92     throwIoException(env, error);
     93     return static_cast<jlong>(-1);
     94   }
     95 
     96   return reinterpret_cast<jlong>(handle);
     97 }
     98 
     99 class IterationHandle {
    100  public:
    101   IterationHandle(const char* prefix) :
    102     cookie_(NULL), prefix_(strdup(prefix)) {
    103   }
    104 
    105   void** CookieAddress() {
    106     return &cookie_;
    107   }
    108 
    109   const char* Prefix() const {
    110     return prefix_;
    111   }
    112 
    113   ~IterationHandle() {
    114     free(prefix_);
    115   }
    116 
    117  private:
    118   void* cookie_;
    119   char* prefix_;
    120 };
    121 
    122 
    123 static jlong StrictJarFile_nativeStartIteration(JNIEnv* env, jobject, jlong nativeHandle,
    124                                                 jstring prefix) {
    125   ScopedUtfChars prefixChars(env, prefix);
    126   if (prefixChars.c_str() == NULL) {
    127     return static_cast<jlong>(-1);
    128   }
    129 
    130   IterationHandle* handle = new IterationHandle(prefixChars.c_str());
    131   int32_t error = 0;
    132   if (prefixChars.size() == 0) {
    133     error = StartIteration(reinterpret_cast<ZipArchiveHandle>(nativeHandle),
    134                            handle->CookieAddress(), NULL);
    135   } else {
    136     error = StartIteration(reinterpret_cast<ZipArchiveHandle>(nativeHandle),
    137                            handle->CookieAddress(), handle->Prefix());
    138   }
    139 
    140   if (error) {
    141     throwIoException(env, error);
    142     return static_cast<jlong>(-1);
    143   }
    144 
    145   return reinterpret_cast<jlong>(handle);
    146 }
    147 
    148 static jobject StrictJarFile_nativeNextEntry(JNIEnv* env, jobject, jlong iterationHandle) {
    149   ZipEntry data;
    150   ZipEntryName entryName;
    151 
    152   IterationHandle* handle = reinterpret_cast<IterationHandle*>(iterationHandle);
    153   const int32_t error = Next(*handle->CookieAddress(), &data, &entryName);
    154   if (error) {
    155     delete handle;
    156     return NULL;
    157   }
    158 
    159   UniquePtr<char[]> entryNameCString(new char[entryName.name_length + 1]);
    160   memcpy(entryNameCString.get(), entryName.name, entryName.name_length);
    161   entryNameCString[entryName.name_length] = '\0';
    162 
    163   return newZipEntry(env, data, entryNameCString.get(), entryName.name_length);
    164 }
    165 
    166 static jobject StrictJarFile_nativeFindEntry(JNIEnv* env, jobject, jlong nativeHandle,
    167                                              jstring entryName) {
    168   ScopedUtfChars entryNameChars(env, entryName);
    169   if (entryNameChars.c_str() == NULL) {
    170     return NULL;
    171   }
    172 
    173   ZipEntry data;
    174   const int32_t error = FindEntry(reinterpret_cast<ZipArchiveHandle>(nativeHandle),
    175                                   entryNameChars.c_str(), &data);
    176   if (error) {
    177     return NULL;
    178   }
    179 
    180   return newZipEntry(env, data, entryName, entryNameChars.size());
    181 }
    182 
    183 static void StrictJarFile_nativeClose(JNIEnv*, jobject, jlong nativeHandle) {
    184   CloseArchive(reinterpret_cast<ZipArchiveHandle>(nativeHandle));
    185 }
    186 
    187 static JNINativeMethod gMethods[] = {
    188   NATIVE_METHOD(StrictJarFile, nativeOpenJarFile, "(Ljava/lang/String;)J"),
    189   NATIVE_METHOD(StrictJarFile, nativeStartIteration, "(JLjava/lang/String;)J"),
    190   NATIVE_METHOD(StrictJarFile, nativeNextEntry, "(J)Ljava/util/zip/ZipEntry;"),
    191   NATIVE_METHOD(StrictJarFile, nativeFindEntry, "(JLjava/lang/String;)Ljava/util/zip/ZipEntry;"),
    192   NATIVE_METHOD(StrictJarFile, nativeClose, "(J)V"),
    193 };
    194 
    195 void register_java_util_jar_StrictJarFile(JNIEnv* env) {
    196   jniRegisterNativeMethods(env, "java/util/jar/StrictJarFile", gMethods, NELEM(gMethods));
    197 }
    198