< 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 >