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 <memory>
21#include <string>
22
23#include <log/log.h>
24
25#include <nativehelper/JNIHelp.h>
26#include <nativehelper/JniConstants.h>
27#include <nativehelper/ScopedLocalRef.h>
28#include <nativehelper/ScopedUtfChars.h>
29#include "jni.h"
30#include "ziparchive/zip_archive.h"
31
32namespace android {
33
34// The method ID for ZipEntry.<init>(String,String,JJJIII[BJJ)
35static jmethodID zipEntryCtor;
36
37static void throwIoException(JNIEnv* env, const int32_t errorCode) {
38  jniThrowException(env, "java/io/IOException", ErrorCodeString(errorCode));
39}
40
41static jobject newZipEntry(JNIEnv* env, const ZipEntry& entry, jstring entryName) {
42  return env->NewObject(JniConstants::zipEntryClass,
43                        zipEntryCtor,
44                        entryName,
45                        NULL,  // comment
46                        static_cast<jlong>(entry.crc32),
47                        static_cast<jlong>(entry.compressed_length),
48                        static_cast<jlong>(entry.uncompressed_length),
49                        static_cast<jint>(entry.method),
50                        static_cast<jint>(0),  // time
51                        NULL,  // byte[] extra
52                        static_cast<jlong>(entry.offset));
53}
54
55static jlong StrictJarFile_nativeOpenJarFile(JNIEnv* env, jobject, jstring name, jint fd) {
56  // Name argument is used for logging, and can be any string.
57  ScopedUtfChars nameChars(env, name);
58  if (nameChars.c_str() == NULL) {
59    return static_cast<jlong>(-1);
60  }
61
62  ZipArchiveHandle handle;
63  int32_t error = OpenArchiveFd(fd, nameChars.c_str(), &handle,
64      false /* owned by Java side */);
65  if (error) {
66    CloseArchive(handle);
67    throwIoException(env, error);
68    return static_cast<jlong>(-1);
69  }
70
71  return reinterpret_cast<jlong>(handle);
72}
73
74class IterationHandle {
75 public:
76  IterationHandle() :
77    cookie_(NULL) {
78  }
79
80  void** CookieAddress() {
81    return &cookie_;
82  }
83
84  ~IterationHandle() {
85    EndIteration(cookie_);
86  }
87
88 private:
89  void* cookie_;
90};
91
92
93static jlong StrictJarFile_nativeStartIteration(JNIEnv* env, jobject, jlong nativeHandle,
94                                                jstring prefix) {
95  ScopedUtfChars prefixChars(env, prefix);
96  if (prefixChars.c_str() == NULL) {
97    return static_cast<jlong>(-1);
98  }
99
100  IterationHandle* handle = new IterationHandle();
101  int32_t error = 0;
102  if (prefixChars.size() == 0) {
103    error = StartIteration(reinterpret_cast<ZipArchiveHandle>(nativeHandle),
104                           handle->CookieAddress(), NULL, NULL);
105  } else {
106    ZipString entry_name(prefixChars.c_str());
107    error = StartIteration(reinterpret_cast<ZipArchiveHandle>(nativeHandle),
108                           handle->CookieAddress(), &entry_name, NULL);
109  }
110
111  if (error) {
112    throwIoException(env, error);
113    return static_cast<jlong>(-1);
114  }
115
116  return reinterpret_cast<jlong>(handle);
117}
118
119static jobject StrictJarFile_nativeNextEntry(JNIEnv* env, jobject, jlong iterationHandle) {
120  ZipEntry data;
121  ZipString entryName;
122
123  IterationHandle* handle = reinterpret_cast<IterationHandle*>(iterationHandle);
124  const int32_t error = Next(*handle->CookieAddress(), &data, &entryName);
125  if (error) {
126    delete handle;
127    return NULL;
128  }
129
130  std::unique_ptr<char[]> entryNameCString(new char[entryName.name_length + 1]);
131  memcpy(entryNameCString.get(), entryName.name, entryName.name_length);
132  entryNameCString[entryName.name_length] = '\0';
133  ScopedLocalRef<jstring> entryNameString(env, env->NewStringUTF(entryNameCString.get()));
134
135  return newZipEntry(env, data, entryNameString.get());
136}
137
138static jobject StrictJarFile_nativeFindEntry(JNIEnv* env, jobject, jlong nativeHandle,
139                                             jstring entryName) {
140  ScopedUtfChars entryNameChars(env, entryName);
141  if (entryNameChars.c_str() == NULL) {
142    return NULL;
143  }
144
145  ZipEntry data;
146  const int32_t error = FindEntry(reinterpret_cast<ZipArchiveHandle>(nativeHandle),
147                                  ZipString(entryNameChars.c_str()), &data);
148  if (error) {
149    return NULL;
150  }
151
152  return newZipEntry(env, data, entryName);
153}
154
155static void StrictJarFile_nativeClose(JNIEnv*, jobject, jlong nativeHandle) {
156  CloseArchive(reinterpret_cast<ZipArchiveHandle>(nativeHandle));
157}
158
159static JNINativeMethod gMethods[] = {
160  NATIVE_METHOD(StrictJarFile, nativeOpenJarFile, "(Ljava/lang/String;I)J"),
161  NATIVE_METHOD(StrictJarFile, nativeStartIteration, "(JLjava/lang/String;)J"),
162  NATIVE_METHOD(StrictJarFile, nativeNextEntry, "(J)Ljava/util/zip/ZipEntry;"),
163  NATIVE_METHOD(StrictJarFile, nativeFindEntry, "(JLjava/lang/String;)Ljava/util/zip/ZipEntry;"),
164  NATIVE_METHOD(StrictJarFile, nativeClose, "(J)V"),
165};
166
167int register_android_util_jar_StrictJarFile(JNIEnv* env) {
168  jniRegisterNativeMethods(env, "android/util/jar/StrictJarFile", gMethods, NELEM(gMethods));
169
170  zipEntryCtor = env->GetMethodID(JniConstants::zipEntryClass, "<init>",
171      "(Ljava/lang/String;Ljava/lang/String;JJJII[BJ)V");
172  LOG_ALWAYS_FATAL_IF(zipEntryCtor == NULL, "Unable to find ZipEntry.<init>");
173
174  return 0;
175}
176
177}; // namespace android
178