< 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,103 **** } } 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 } catch (IOException x) { return false; } } /** * 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 --- 86,197 ---- } } private boolean isMultiReleaseJar() { try (InputStream is = newInputStream(getBytes("/META-INF/MANIFEST.MF"))) { ! 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 >