< prev index next >

src/jdk.zipfs/share/classes/jdk/nio/zipfs/JarFileSystem.java

Print this page
rev 16859 : 8176709: JarFileSystem::isMultiReleaseJar is incorrect
Reviewed-by: alanb, sherman, psandoz, mchung

@@ -86,18 +86,112 @@
         }
     }
 
     private boolean isMultiReleaseJar() {
         try (InputStream is = newInputStream(getBytes("/META-INF/MANIFEST.MF"))) {
-            return (new Manifest(is)).getMainAttributes()
-                    .containsKey(new Attributes.Name("Multi-Release"));
-            // fixme change line above after JarFile integration to contain Attributes.Name.MULTI_RELEASE
+            byte[] b = is.readAllBytes();
+            // Keep this implementation up to date with
+            // JarFile::checkForSpecialAttributes
+            int i = match(MULTIRELEASE_CHARS, b, MULTIRELEASE_LASTOCC,
+                          MULTIRELEASE_OPTOSFT);
+            if (i != -1) {
+                i += MULTIRELEASE_CHARS.length;
+                if (i < b.length) {
+                    byte c = b[i++];
+                    // Check that the value is followed by a newline
+                    // and does not have a continuation
+                    if (c == '\n' &&
+                            (i == b.length || b[i] != ' ')) {
+                        return true;
+                    } else if (c == '\r') {
+                        if (i == b.length) {
+                            return true;
+                        } else {
+                            c = b[i++];
+                            if (c == '\n') {
+                                if (i == b.length || b[i] != ' ') {
+                                    return true;
+                                }
+                            } else if (c != ' ') {
+                                return true;
+                            }
+                        }
+                    }
+                }
+            }
+            return false;
         } catch (IOException x) {
             return false;
         }
     }
 
+    private static final byte[] MULTIRELEASE_CHARS =
+            {'M','U','L','T','I','-','R','E','L','E', 'A', 'S', 'E', ':',
+                    ' ', 'T', 'R', 'U', 'E'};
+
+    // The bad character shift for "multi-release: true"
+    private static final byte[] MULTIRELEASE_LASTOCC;
+
+    // The good suffix shift for "multi-release: true"
+    private static final byte[] MULTIRELEASE_OPTOSFT;
+
+    static {
+        MULTIRELEASE_LASTOCC = new byte[64];
+        MULTIRELEASE_OPTOSFT = new byte[19];
+        MULTIRELEASE_LASTOCC[(int)'M' - 32] = 1;
+        MULTIRELEASE_LASTOCC[(int)'I' - 32] = 5;
+        MULTIRELEASE_LASTOCC[(int)'-' - 32] = 6;
+        MULTIRELEASE_LASTOCC[(int)'L' - 32] = 9;
+        MULTIRELEASE_LASTOCC[(int)'A' - 32] = 11;
+        MULTIRELEASE_LASTOCC[(int)'S' - 32] = 12;
+        MULTIRELEASE_LASTOCC[(int)':' - 32] = 14;
+        MULTIRELEASE_LASTOCC[(int)' ' - 32] = 15;
+        MULTIRELEASE_LASTOCC[(int)'T' - 32] = 16;
+        MULTIRELEASE_LASTOCC[(int)'R' - 32] = 17;
+        MULTIRELEASE_LASTOCC[(int)'U' - 32] = 18;
+        MULTIRELEASE_LASTOCC[(int)'E' - 32] = 19;
+        for (int i = 0; i < 17; i++) {
+            MULTIRELEASE_OPTOSFT[i] = 19;
+        }
+        MULTIRELEASE_OPTOSFT[17] = 6;
+        MULTIRELEASE_OPTOSFT[18] = 1;
+    }
+
+    /**
+     * Returns true if the pattern {@code src} is found in {@code b}.
+     * The {@code lastOcc} array is the precomputed bad character shifts.
+     * Since there are no repeated substring in our search strings,
+     * the good suffix shifts can be replaced with a comparison.
+     */
+    private static int match(byte[] src, byte[] b, byte[] lastOcc, byte[] optoSft) {
+        int len = src.length;
+        int last = b.length - len;
+        int i = 0;
+        next:
+        while (i <= last) {
+            for (int j = (len - 1); j >= 0; j--) {
+                byte c = b[i + j];
+                if (c >= ' ' && c <= 'z') {
+                    if (c >= 'a') c -= 32; // Canonicalize
+
+                    if (c != src[j]) {
+                        // no match
+                        int badShift = lastOcc[c - 32];
+                        i += Math.max(j + 1 - badShift, optoSft[j]);
+                        continue next;
+                    }
+                } else {
+                    // no match, character not valid for name
+                    i += len;
+                    continue next;
+                }
+            }
+            return i;
+        }
+        return -1;
+    }
+
     /**
      * create a map of aliases for versioned entries, for example:
      *   version/PackagePrivate.class -> META-INF/versions/9/version/PackagePrivate.class
      *   version/PackagePrivate.java -> META-INF/versions/9/version/PackagePrivate.java
      *   version/Version.class -> META-INF/versions/10/version/Version.class
< prev index next >