diff options
Diffstat (limited to 'luni/src/main')
-rw-r--r-- | luni/src/main/java/java/util/jar/JarFile.java | 3 | ||||
-rw-r--r-- | luni/src/main/java/java/util/jar/JarInputStream.java | 3 | ||||
-rw-r--r-- | luni/src/main/java/java/util/jar/JarVerifier.java | 5 | ||||
-rw-r--r-- | luni/src/main/java/java/util/jar/StrictJarFile.java | 197 | ||||
-rw-r--r-- | luni/src/main/java/java/util/zip/ZipEntry.java | 33 | ||||
-rw-r--r-- | luni/src/main/java/java/util/zip/ZipFile.java | 16 | ||||
-rw-r--r-- | luni/src/main/native/Register.cpp | 1 | ||||
-rw-r--r-- | luni/src/main/native/java_util_jar_StrictJarFile.cpp | 173 | ||||
-rw-r--r-- | luni/src/main/native/sub.mk | 4 |
9 files changed, 422 insertions, 13 deletions
diff --git a/luni/src/main/java/java/util/jar/JarFile.java b/luni/src/main/java/java/util/jar/JarFile.java index b219637..6b147f6 100644 --- a/luni/src/main/java/java/util/jar/JarFile.java +++ b/luni/src/main/java/java/util/jar/JarFile.java @@ -215,8 +215,7 @@ public class JarFile extends ZipFile { // We create the manifest straight away, so that we can create // the jar verifier as well. manifest = new Manifest(metaEntries.get(MANIFEST_NAME), true); - verifier = new JarVerifier(getName(), manifest, metaEntries, - manifest.getMainAttributesEnd()); + verifier = new JarVerifier(getName(), manifest, metaEntries); } else { verifier = null; manifestBytes = metaEntries.get(MANIFEST_NAME); diff --git a/luni/src/main/java/java/util/jar/JarInputStream.java b/luni/src/main/java/java/util/jar/JarInputStream.java index 31bd05f..585c135 100644 --- a/luni/src/main/java/java/util/jar/JarInputStream.java +++ b/luni/src/main/java/java/util/jar/JarInputStream.java @@ -90,8 +90,7 @@ public class JarInputStream extends ZipInputStream { if (verify) { HashMap<String, byte[]> metaEntries = new HashMap<String, byte[]>(); metaEntries.put(JarFile.MANIFEST_NAME, manifestBytes); - verifier = new JarVerifier("JarInputStream", manifest, - metaEntries, manifest.getMainAttributesEnd()); + verifier = new JarVerifier("JarInputStream", manifest, metaEntries); } } diff --git a/luni/src/main/java/java/util/jar/JarVerifier.java b/luni/src/main/java/java/util/jar/JarVerifier.java index eb88990..c545a02 100644 --- a/luni/src/main/java/java/util/jar/JarVerifier.java +++ b/luni/src/main/java/java/util/jar/JarVerifier.java @@ -152,12 +152,11 @@ class JarVerifier { * @param name * the name of the JAR file being verified. */ - JarVerifier(String name, Manifest manifest, HashMap<String, byte[]> metaEntries, - int mainAttributesEnd) { + JarVerifier(String name, Manifest manifest, HashMap<String, byte[]> metaEntries) { jarName = name; this.manifest = manifest; this.metaEntries = metaEntries; - this.mainAttributesEnd = mainAttributesEnd; + this.mainAttributesEnd = manifest.getMainAttributesEnd(); } /** diff --git a/luni/src/main/java/java/util/jar/StrictJarFile.java b/luni/src/main/java/java/util/jar/StrictJarFile.java new file mode 100644 index 0000000..fa175d8 --- /dev/null +++ b/luni/src/main/java/java/util/jar/StrictJarFile.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package java.util.jar; + +import dalvik.system.CloseGuard; +import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.security.cert.Certificate; +import java.util.HashMap; +import java.util.Iterator; +import java.util.zip.Inflater; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import libcore.io.IoUtils; +import libcore.io.Streams; + +/** + * A subset of the JarFile API implemented as a thin wrapper over + * system/core/libziparchive. + * + * @hide for internal use only. Not API compatible (or as forgiving) as + * {@link java.util.jar.JarFile} + */ +public final class StrictJarFile { + + private final long nativeHandle; + + // NOTE: It's possible to share a file descriptor with the native + // code, at the cost of some additional complexity. + private final RandomAccessFile raf; + + private final Manifest manifest; + private final JarVerifier verifier; + + private final boolean isSigned; + + private final CloseGuard guard = CloseGuard.get(); + private boolean closed; + + public StrictJarFile(String fileName) throws IOException { + this.nativeHandle = nativeOpenJarFile(fileName); + this.raf = new RandomAccessFile(fileName, "r"); + + try { + // Read the MANIFEST and signature files up front and try to + // parse them. We never want to accept a JAR File with broken signatures + // or manifests, so it's best to throw as early as possible. + HashMap<String, byte[]> metaEntries = getMetaEntries(); + this.manifest = new Manifest(metaEntries.get(JarFile.MANIFEST_NAME), true); + this.verifier = new JarVerifier(fileName, manifest, metaEntries); + + isSigned = verifier.readCertificates() && verifier.isSignedJar(); + } catch (IOException ioe) { + nativeClose(this.nativeHandle); + throw ioe; + } + + guard.open("close"); + } + + public Manifest getManifest() { + return manifest; + } + + public Iterator<ZipEntry> iterator() throws IOException { + return new EntryIterator(nativeHandle, ""); + } + + public ZipEntry findEntry(String name) { + return nativeFindEntry(nativeHandle, name); + } + + /** + * Return all certificates for a given {@link ZipEntry} belonging to this jar. + * This method MUST be called only after fully exhausting the InputStream belonging + * to this entry. + * + * Returns {@code null} if this jar file isn't signed or if this method is + * called before the stream is processed. + */ + public Certificate[] getCertificates(ZipEntry ze) { + if (isSigned) { + return verifier.getCertificates(ze.getName()); + } + + return null; + } + + public InputStream getInputStream(ZipEntry ze) { + final InputStream is = getZipInputStream(ze); + + if (isSigned) { + JarVerifier.VerifierEntry entry = verifier.initEntry(ze.getName()); + if (entry == null) { + return is; + } + + return new JarFile.JarFileInputStream(is, ze.getSize(), entry); + } + + return is; + } + + public void close() throws IOException { + if (!closed) { + guard.close(); + + nativeClose(nativeHandle); + IoUtils.closeQuietly(raf); + closed = true; + } + } + + private InputStream getZipInputStream(ZipEntry ze) { + if (ze.getMethod() == ZipEntry.STORED) { + return new ZipFile.RAFStream(raf, ze.getDataOffset(), + ze.getDataOffset() + ze.getSize()); + } else { + final ZipFile.RAFStream wrapped = new ZipFile.RAFStream( + raf, ze.getDataOffset(), ze.getDataOffset() + ze.getCompressedSize()); + + int bufSize = Math.max(1024, (int) Math.min(ze.getSize(), 65535L)); + return new ZipFile.ZipInflaterInputStream(wrapped, new Inflater(true), bufSize, ze); + } + } + + static final class EntryIterator implements Iterator<ZipEntry> { + private final long iterationHandle; + private ZipEntry nextEntry; + + EntryIterator(long nativeHandle, String prefix) throws IOException { + iterationHandle = nativeStartIteration(nativeHandle, prefix); + } + + public ZipEntry next() { + if (nextEntry != null) { + final ZipEntry ze = nextEntry; + nextEntry = null; + return ze; + } + + return nativeNextEntry(iterationHandle); + } + + public boolean hasNext() { + if (nextEntry != null) { + return true; + } + + final ZipEntry ze = nativeNextEntry(iterationHandle); + if (ze == null) { + return false; + } + + nextEntry = ze; + return true; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + } + + private HashMap<String, byte[]> getMetaEntries() throws IOException { + HashMap<String, byte[]> metaEntries = new HashMap<String, byte[]>(); + + Iterator<ZipEntry> entryIterator = new EntryIterator(nativeHandle, "META-INF/"); + while (entryIterator.hasNext()) { + final ZipEntry entry = entryIterator.next(); + metaEntries.put(entry.getName(), Streams.readFully(getInputStream(entry))); + } + + return metaEntries; + } + + private static native long nativeOpenJarFile(String fileName) throws IOException; + private static native long nativeStartIteration(long nativeHandle, String prefix); + private static native ZipEntry nativeNextEntry(long iterationHandle); + private static native ZipEntry nativeFindEntry(long nativeHandle, String entryName); + private static native void nativeClose(long nativeHandle); +} diff --git a/luni/src/main/java/java/util/zip/ZipEntry.java b/luni/src/main/java/java/util/zip/ZipEntry.java index f64c717..69f027a 100644 --- a/luni/src/main/java/java/util/zip/ZipEntry.java +++ b/luni/src/main/java/java/util/zip/ZipEntry.java @@ -25,9 +25,9 @@ import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; -import libcore.io.Streams; import libcore.io.BufferIterator; import libcore.io.HeapBufferIterator; +import libcore.io.Streams; /** * An entry within a zip file. @@ -54,6 +54,8 @@ public class ZipEntry implements ZipConstants, Cloneable { int nameLength = -1; long localHeaderRelOffset = -1; + long dataOffset = -1; + /** * Zip entry state: Deflated. */ @@ -64,6 +66,23 @@ public class ZipEntry implements ZipConstants, Cloneable { */ public static final int STORED = 0; + ZipEntry(String name, String comment, long crc, long compressedSize, + long size, int compressionMethod, int time, int modDate, byte[] extra, + int nameLength, long localHeaderRelOffset, long dataOffset) { + this.name = name; + this.comment = comment; + this.crc = crc; + this.compressedSize = compressedSize; + this.size = size; + this.compressionMethod = compressionMethod; + this.time = time; + this.modDate = modDate; + this.extra = extra; + this.nameLength = nameLength; + this.localHeaderRelOffset = localHeaderRelOffset; + this.dataOffset = dataOffset; + } + /** * Constructs a new {@code ZipEntry} with the specified name. The name is actually a path, * and may contain {@code /} characters. @@ -287,6 +306,17 @@ public class ZipEntry implements ZipConstants, Cloneable { } } + + /** @hide */ + public void setDataOffset(long value) { + dataOffset = value; + } + + /** @hide */ + public long getDataOffset() { + return dataOffset; + } + /** * Returns the string representation of this {@code ZipEntry}. * @@ -316,6 +346,7 @@ public class ZipEntry implements ZipConstants, Cloneable { extra = ze.extra; nameLength = ze.nameLength; localHeaderRelOffset = ze.localHeaderRelOffset; + dataOffset = ze.dataOffset; } /** diff --git a/luni/src/main/java/java/util/zip/ZipFile.java b/luni/src/main/java/java/util/zip/ZipFile.java index c25bbc1..40036cf 100644 --- a/luni/src/main/java/java/util/zip/ZipFile.java +++ b/luni/src/main/java/java/util/zip/ZipFile.java @@ -434,16 +434,23 @@ public class ZipFile implements Closeable, ZipConstants { * collisions.) * * <p>We could support mark/reset, but we don't currently need them. + * + * @hide */ - static class RAFStream extends InputStream { + public static class RAFStream extends InputStream { private final RandomAccessFile sharedRaf; private long endOffset; private long offset; - public RAFStream(RandomAccessFile raf, long initialOffset) throws IOException { + + public RAFStream(RandomAccessFile raf, long initialOffset, long endOffset) { sharedRaf = raf; offset = initialOffset; - endOffset = raf.length(); + this.endOffset = endOffset; + } + + public RAFStream(RandomAccessFile raf, long initialOffset) throws IOException { + this(raf, initialOffset, raf.length()); } @Override public int available() throws IOException { @@ -491,7 +498,8 @@ public class ZipFile implements Closeable, ZipConstants { } } - static class ZipInflaterInputStream extends InflaterInputStream { + /** @hide */ + public static class ZipInflaterInputStream extends InflaterInputStream { private final ZipEntry entry; private long bytesRead = 0; diff --git a/luni/src/main/native/Register.cpp b/luni/src/main/native/Register.cpp index 15f6953..68c59c3 100644 --- a/luni/src/main/native/Register.cpp +++ b/luni/src/main/native/Register.cpp @@ -49,6 +49,7 @@ jint JNI_OnLoad(JavaVM* vm, void*) { REGISTER(register_java_nio_ByteOrder); REGISTER(register_java_nio_charset_Charsets); REGISTER(register_java_text_Bidi); + REGISTER(register_java_util_jar_StrictJarFile); REGISTER(register_java_util_regex_Matcher); REGISTER(register_java_util_regex_Pattern); REGISTER(register_java_util_zip_Adler32); diff --git a/luni/src/main/native/java_util_jar_StrictJarFile.cpp b/luni/src/main/native/java_util_jar_StrictJarFile.cpp new file mode 100644 index 0000000..7611749 --- /dev/null +++ b/luni/src/main/native/java_util_jar_StrictJarFile.cpp @@ -0,0 +1,173 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "StrictJarFile" + +#include <string> + +#include "JNIHelp.h" +#include "JniConstants.h" +#include "ScopedLocalRef.h" +#include "ScopedUtfChars.h" +#include "UniquePtr.h" +#include "jni.h" +#include "ziparchive/zip_archive.h" +#include "cutils/log.h" + +static void throwIoException(JNIEnv* env, const int32_t errorCode) { + jniThrowException(env, "java/io/IOException", ErrorCodeString(errorCode)); +} + +static jobject newZipEntry(JNIEnv* env, const ZipEntry& entry, jstring entryName, + const uint16_t nameLength) { + ScopedLocalRef<jclass> zipEntryClass(env, env->FindClass("java/util/zip/ZipEntry")); + const jmethodID zipEntryCtor = env->GetMethodID(zipEntryClass.get(), "<init>", + "(Ljava/lang/String;Ljava/lang/String;JJJIII[BIJJ)V"); + + return env->NewObject(zipEntryClass.get(), + zipEntryCtor, + entryName, + NULL, // comment + static_cast<jlong>(entry.crc32), + static_cast<jlong>(entry.compressed_length), + static_cast<jlong>(entry.uncompressed_length), + static_cast<jint>(entry.method), + static_cast<jint>(0), // time + static_cast<jint>(0), // modData + NULL, // byte[] extra + static_cast<jint>(nameLength), + static_cast<jlong>(-1), // local header offset + static_cast<jlong>(entry.offset)); +} + +static jlong StrictJarFile_nativeOpenJarFile(JNIEnv* env, jobject, jstring fileName) { + ScopedUtfChars fileChars(env, fileName); + if (fileChars.c_str() == NULL) { + return static_cast<jlong>(-1); + } + + ZipArchiveHandle handle; + int32_t error = OpenArchive(fileChars.c_str(), &handle); + if (error) { + throwIoException(env, error); + return static_cast<jlong>(-1); + } + + return reinterpret_cast<jlong>(handle); +} + +class IterationHandle { + public: + IterationHandle(const char* prefix) : + cookie_(NULL), prefix_(strdup(prefix)) { + } + + void** CookieAddress() { + return &cookie_; + } + + const char* Prefix() const { + return prefix_; + } + + ~IterationHandle() { + free(prefix_); + } + + private: + void* cookie_; + char* prefix_; +}; + + +static jlong StrictJarFile_nativeStartIteration(JNIEnv* env, jobject, jlong nativeHandle, + jstring prefix) { + ScopedUtfChars prefixChars(env, prefix); + if (prefixChars.c_str() == NULL) { + return static_cast<jlong>(-1); + } + + IterationHandle* handle = new IterationHandle(prefixChars.c_str()); + int32_t error = 0; + if (prefixChars.size() == 0) { + error = StartIteration(reinterpret_cast<ZipArchiveHandle>(nativeHandle), + handle->CookieAddress(), NULL); + } else { + error = StartIteration(reinterpret_cast<ZipArchiveHandle>(nativeHandle), + handle->CookieAddress(), handle->Prefix()); + } + + if (error) { + throwIoException(env, error); + return static_cast<jlong>(-1); + } + + return reinterpret_cast<jlong>(handle); +} + +static jobject StrictJarFile_nativeNextEntry(JNIEnv* env, jobject, jlong iterationHandle) { + ZipEntry data; + ZipEntryName entryName; + + IterationHandle* handle = reinterpret_cast<IterationHandle*>(iterationHandle); + const int32_t error = Next(*handle->CookieAddress(), &data, &entryName); + if (error) { + delete handle; + return NULL; + } + + UniquePtr<char[]> entryNameCString(new char[entryName.name_length + 1]); + memcpy(entryNameCString.get(), entryName.name, entryName.name_length); + entryNameCString[entryName.name_length] = '\0'; + ScopedLocalRef<jstring> entryNameString(env, env->NewStringUTF(entryNameCString.get())); + + return newZipEntry(env, data, entryNameString.get(), entryName.name_length); +} + +static jobject StrictJarFile_nativeFindEntry(JNIEnv* env, jobject, jlong nativeHandle, + jstring entryName) { + ScopedUtfChars entryNameChars(env, entryName); + if (entryNameChars.c_str() == NULL) { + return NULL; + } + + ZipEntry data; + const int32_t error = FindEntry(reinterpret_cast<ZipArchiveHandle>(nativeHandle), + entryNameChars.c_str(), &data); + if (error) { + return NULL; + } + + return newZipEntry(env, data, entryName, entryNameChars.size()); +} + +static void StrictJarFile_nativeClose(JNIEnv*, jobject, jlong nativeHandle) { + CloseArchive(reinterpret_cast<ZipArchiveHandle>(nativeHandle)); +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(StrictJarFile, nativeOpenJarFile, "(Ljava/lang/String;)J"), + NATIVE_METHOD(StrictJarFile, nativeStartIteration, "(JLjava/lang/String;)J"), + NATIVE_METHOD(StrictJarFile, nativeNextEntry, "(J)Ljava/util/zip/ZipEntry;"), + NATIVE_METHOD(StrictJarFile, nativeFindEntry, "(JLjava/lang/String;)Ljava/util/zip/ZipEntry;"), + NATIVE_METHOD(StrictJarFile, nativeClose, "(J)V"), +}; + +void register_java_util_jar_StrictJarFile(JNIEnv* env) { + jniRegisterNativeMethods(env, "java/util/jar/StrictJarFile", gMethods, NELEM(gMethods)); + +} diff --git a/luni/src/main/native/sub.mk b/luni/src/main/native/sub.mk index 9e1a05b..4e10cc7 100644 --- a/luni/src/main/native/sub.mk +++ b/luni/src/main/native/sub.mk @@ -28,6 +28,7 @@ LOCAL_SRC_FILES := \ java_nio_ByteOrder.cpp \ java_nio_charset_Charsets.cpp \ java_text_Bidi.cpp \ + java_util_jar_StrictJarFile.cpp \ java_util_regex_Matcher.cpp \ java_util_regex_Pattern.cpp \ java_util_zip_Adler32.cpp \ @@ -60,7 +61,8 @@ LOCAL_C_INCLUDES += \ external/icu4c/common \ external/icu4c/i18n \ external/openssl/include \ - external/zlib + external/zlib \ + system/core/include LOCAL_STATIC_LIBRARIES += \ libfdlibm |