< prev index next >

src/java.base/share/classes/java/util/zip/ZipFile.java

Print this page
rev 59204 : imported patch jarf_signature

@@ -43,10 +43,12 @@
 import java.util.Collections;
 import java.util.Deque;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
 import java.util.Objects;
 import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.Spliterator;
 import java.util.Spliterators;

@@ -1008,33 +1010,53 @@
         }
 
     }
 
     /**
-     * Returns the names of all non-directory entries that begin with
-     * "META-INF/" (case ignored). This method is used in JarFile, via
-     * SharedSecrets, as an optimization when looking up manifest and
-     * signature file entries. Returns null if no entries were found.
+     * Returns the names of the META-INF/MANIFEST.MF entry - if exists -
+     * and any signature-related files under META-INF. This method is used in
+     * JarFile, via SharedSecrets, as an optimization.
      */
-    private String[] getMetaInfEntryNames() {
+    private List<String> getManifestAndSignatureRelatedFiles() {
         synchronized (this) {
             ensureOpen();
             Source zsrc = res.zsrc;
-            if (zsrc.metanames == null) {
-                return null;
+            int[] metanames = zsrc.signatureMetaNames;
+            List<String> files = null;
+            if (zsrc.manifestPos >= 0) {
+                files = new ArrayList<>();
+                files.add(getEntryName(zsrc.manifestPos));
+            }
+            if (metanames != null) {
+                if (files == null) {
+                    files = new ArrayList<>();
+                }
+                for (int i = 0; i < metanames.length; i++) {
+                    files.add(getEntryName(metanames[i]));
+                }
+            }
+            return files == null ? List.of() : files;
             }
-            String[] names = new String[zsrc.metanames.length];
-            byte[] cen = zsrc.cen;
-            for (int i = 0; i < names.length; i++) {
-                int pos = zsrc.metanames[i];
-                // This will only be invoked on JarFile, which is guaranteed
-                // to use (or be compatible with) UTF-8 encoding.
-                names[i] = new String(cen, pos + CENHDR, CENNAM(cen, pos),
-                                      UTF_8.INSTANCE);
             }
-            return names;
+
+    /**
+     * Returns the name of the META-INF/MANIFEST.MF entry, ignoring
+     * case. If {@code onlyIfSignatureRelatedFiles} is true, we only return the
+     * manifest if there is also at least one signature-related file.
+     * This method is used in JarFile, via SharedSecrets, as an optimization
+     * when looking up the manifest file.
+     */
+    private String getManifestName(boolean onlyIfSignatureRelatedFiles) {
+        synchronized (this) {
+            ensureOpen();
+            Source zsrc = res.zsrc;
+            int pos = zsrc.manifestPos;
+            if (pos >= 0 && (!onlyIfSignatureRelatedFiles || zsrc.signatureMetaNames != null)) {
+                return getEntryName(pos);
+            }
         }
+        return null;
     }
 
     /**
      * Returns the versions for which there exists a non-directory
      * entry that begin with "META-INF/versions/" (case ignored).

@@ -1057,12 +1079,16 @@
                 @Override
                 public boolean startsWithLocHeader(ZipFile zip) {
                     return zip.res.zsrc.startsWithLoc;
                 }
                 @Override
-                public String[] getMetaInfEntryNames(JarFile jar) {
-                    return ((ZipFile)jar).getMetaInfEntryNames();
+                public List<String> getManifestAndSignatureRelatedFiles(JarFile jar) {
+                    return ((ZipFile)jar).getManifestAndSignatureRelatedFiles();
+                }
+                @Override
+                public String getManifestName(JarFile jar, boolean onlyIfHasSignatureRelatedFiles) {
+                    return ((ZipFile)jar).getManifestName(onlyIfHasSignatureRelatedFiles);
                 }
                 @Override
                 public int[] getMetaInfVersions(JarFile jar) {
                     return ((ZipFile)jar).getMetaInfVersions();
                 }

@@ -1103,11 +1129,12 @@
         private RandomAccessFile zfile;      // zfile of the underlying zip file
         private byte[] cen;                  // CEN & ENDHDR
         private long locpos;                 // position of first LOC header (usually 0)
         private byte[] comment;              // zip file comment
                                              // list of meta entries in META-INF dir
-        private int[] metanames;
+        private int   manifestPos = -1;      // position of the META-INF/MANIFEST.MF, if exists
+        private int[] signatureMetaNames;    // positions of signature related entries, if such exist
         private int[] metaVersions;          // list of unique versions found in META-INF/versions/
         private final boolean startsWithLoc; // true, if zip file starts with LOCSIG (usually true)
 
         // A Hashmap for all entries.
         //

@@ -1252,11 +1279,12 @@
             zfile.close();
             zfile = null;
             cen = null;
             entries = null;
             table = null;
-            metanames = null;
+            manifestPos = -1;
+            signatureMetaNames = null;
             metaVersions = EMPTY_META_VERSIONS;
         }
 
         private static final int BUF_SIZE = 8192;
         private final int readFullyAt(byte[] buf, int off, int len, long pos)

@@ -1436,11 +1464,11 @@
             int idx = 0;
             int hash;
             int next;
 
             // list for all meta entries
-            ArrayList<Integer> metanamesList = null;
+            ArrayList<Integer> signatureNames = null;
             // Set of all version numbers seen in META-INF/versions/
             Set<Integer> metaVersionsSet = null;
 
             // Iterate through the entries in the central directory
             int i = 0;

@@ -1474,13 +1502,19 @@
                 next = table[hsh];
                 table[hsh] = idx;
                 idx = addEntry(idx, hash, next, pos);
                 // Adds name to metanames.
                 if (isMetaName(cen, entryPos, nlen)) {
-                    if (metanamesList == null)
-                        metanamesList = new ArrayList<>(4);
-                    metanamesList.add(pos);
+                    if (isManifestName(cen, entryPos + META_INF_LENGTH,
+                            nlen - META_INF_LENGTH)) {
+                        manifestPos = entryPos;
+                    } else {
+                        if (isSignatureRelated(cen, entryPos, nlen)) {
+                            if (signatureNames == null)
+                                signatureNames = new ArrayList<>(4);
+                            signatureNames.add(pos);
+                        }
 
                     // If this is a versioned entry, parse the version
                     // and store it for later. This optimizes lookup
                     // performance in multi-release jar files
                     int version = getMetaVersion(cen,

@@ -1489,20 +1523,22 @@
                         if (metaVersionsSet == null)
                             metaVersionsSet = new TreeSet<>();
                         metaVersionsSet.add(version);
                     }
                 }
+                }
                 // skip ext and comment
                 pos = entryPos + nlen + elen + clen;
                 entryPos = pos + CENHDR;
                 i++;
             }
             total = i;
-            if (metanamesList != null) {
-                metanames = new int[metanamesList.size()];
-                for (int j = 0, len = metanames.length; j < len; j++) {
-                    metanames[j] = metanamesList.get(j);
+            if (signatureNames != null) {
+                int len = signatureNames.size();
+                signatureMetaNames = new int[len];
+                for (int j = 0; j < len; j++) {
+                    signatureMetaNames[j] = signatureNames.get(j);
                 }
             }
             if (metaVersionsSet != null) {
                 metaVersions = new int[metaVersionsSet.size()];
                 int c = 0;

@@ -1593,10 +1629,49 @@
                 && (name[off++] | 0x20) == 'f'
                 && (name[off]         ) == '/';
         }
 
         /*
+         * Check if the bytes represents a name equals to MANIFEST.MF
+         */
+        private static boolean isManifestName(byte[] name, int off, int len) {
+            return (len == 11 // "MANIFEST.MF".length()
+                    && (name[off++] | 0x20) == 'm'
+                    && (name[off++] | 0x20) == 'a'
+                    && (name[off++] | 0x20) == 'n'
+                    && (name[off++] | 0x20) == 'i'
+                    && (name[off++] | 0x20) == 'f'
+                    && (name[off++] | 0x20) == 'e'
+                    && (name[off++] | 0x20) == 's'
+                    && (name[off++] | 0x20) == 't'
+                    && (name[off++] | 0x20) == '.'
+                    && (name[off++] | 0x20) == 'm'
+                    && (name[off++] | 0x20) == 'f');
+        }
+
+        private boolean isSignatureRelated(byte[] cen, int start, int len) {
+            // Check if entry ends with .EC and .SF
+            if (cen[start + len - 3] == '.') {
+                int b1 = cen[start + len - 2] | 0x20;
+                int b2 = cen[start + len - 1] | 0x20;
+                if ((b1 == 'e' && b2 == 'c') || (b1 == 's' && b2 == 'f')) {
+                    return true;
+                }
+            }
+            // Check if entry ends with .DSA and .RSA
+            if (cen[start + len - 4] == '.') {
+                int b1 = cen[start + len - 3] | 0x20;
+                int b2 = cen[start + len - 2] | 0x20;
+                int b3 = cen[start + len - 1] | 0x20;
+                if ((b1 == 'r' || b1 == 'd') && b2 == 's' && b3 == 'a') {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        /*
          * If the bytes represents a non-directory name beginning
          * with "versions/", continuing with a positive integer,
          * followed by a '/', then return that integer value.
          * Otherwise, return 0
          */
< prev index next >