< prev index next >

src/java.base/share/classes/java/util/jar/JarFile.java

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


  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.util.jar;
  27 
  28 import java.io.*;
  29 import java.lang.ref.SoftReference;
  30 import java.net.URL;
  31 import java.util.*;
  32 import java.util.stream.Stream;
  33 import java.util.stream.StreamSupport;
  34 import java.util.zip.*;
  35 import java.security.CodeSigner;
  36 import java.security.cert.Certificate;
  37 import java.security.CodeSource;
  38 import jdk.internal.misc.SharedSecrets;

  39 import sun.security.action.GetPropertyAction;
  40 import sun.security.util.ManifestEntryVerifier;
  41 import sun.security.util.SignatureFileVerifier;
  42 
  43 /**
  44  * The {@code JarFile} class is used to read the contents of a jar file
  45  * from any file that can be opened with {@code java.io.RandomAccessFile}.
  46  * It extends the class {@code java.util.zip.ZipFile} with support
  47  * for reading an optional {@code Manifest} entry, and support for
  48  * processing multi-release jar files.  The {@code Manifest} can be used
  49  * to specify meta-information about the jar file and its entries.
  50  *
  51  * <p><a name="multirelease">A multi-release jar file</a> is a jar file that
  52  * contains a manifest with a main attribute named "Multi-Release",
  53  * a set of "base" entries, some of which are public classes with public
  54  * or protected methods that comprise the public interface of the jar file,
  55  * and a set of "versioned" entries contained in subdirectories of the
  56  * "META-INF/versions" directory.  The versioned entries are partitioned by the
  57  * major version of the Java platform.  A versioned entry, with a version
  58  * {@code n}, {@code 8 < n}, in the "META-INF/versions/{n}" directory overrides


 810         // wrap a verifier stream around the real stream
 811         return new JarVerifier.VerifierStream(
 812             getManifestFromReference(),
 813             verifiableEntry(ze),
 814             super.getInputStream(ze),
 815             jv);
 816     }
 817 
 818     private JarEntry verifiableEntry(ZipEntry ze) {
 819         if (ze instanceof JarFileEntry) {
 820             // assure the name and entry match for verification
 821             return ((JarFileEntry)ze).realEntry();
 822         }
 823         ze = getJarEntry(ze.getName());
 824         if (ze instanceof JarFileEntry) {
 825             return ((JarFileEntry)ze).realEntry();
 826         }
 827         return (JarEntry)ze;
 828     }
 829 
 830     // Statics for hand-coded Boyer-Moore search
 831     private static final byte[] CLASSPATH_CHARS =
 832             {'C','L','A','S','S','-','P','A','T','H', ':', ' '};
 833 
 834     // The bad character shift for "class-path: "
 835     private static final byte[] CLASSPATH_LASTOCC;
 836 
 837     // The good suffix shift for "class-path: "
 838     private static final byte[] CLASSPATH_OPTOSFT;
 839 
 840     private static final byte[] MULTIRELEASE_CHARS =
 841             {'M','U','L','T','I','-','R','E','L','E', 'A', 'S', 'E', ':',
 842                     ' ', 'T', 'R', 'U', 'E'};
 843 
 844     // The bad character shift for "multi-release: true"
 845     private static final byte[] MULTIRELEASE_LASTOCC;
 846 
 847     // The good suffix shift for "multi-release: true"
 848     private static final byte[] MULTIRELEASE_OPTOSFT;
 849 
 850     static {
 851         CLASSPATH_LASTOCC = new byte[64];
 852         CLASSPATH_OPTOSFT = new byte[12];
 853         CLASSPATH_LASTOCC[(int)'C' - 32] = 1;
 854         CLASSPATH_LASTOCC[(int)'L' - 32] = 2;
 855         CLASSPATH_LASTOCC[(int)'S' - 32] = 5;
 856         CLASSPATH_LASTOCC[(int)'-' - 32] = 6;
 857         CLASSPATH_LASTOCC[(int)'P' - 32] = 7;
 858         CLASSPATH_LASTOCC[(int)'A' - 32] = 8;
 859         CLASSPATH_LASTOCC[(int)'T' - 32] = 9;
 860         CLASSPATH_LASTOCC[(int)'H' - 32] = 10;
 861         CLASSPATH_LASTOCC[(int)':' - 32] = 11;
 862         CLASSPATH_LASTOCC[(int)' ' - 32] = 12;
 863         for (int i = 0; i < 11; i++) {
 864             CLASSPATH_OPTOSFT[i] = 12;
 865         }
 866         CLASSPATH_OPTOSFT[11] = 1;
 867 
 868         MULTIRELEASE_LASTOCC = new byte[64];
 869         MULTIRELEASE_OPTOSFT = new byte[19];
 870         MULTIRELEASE_LASTOCC[(int)'M' - 32] = 1;
 871         MULTIRELEASE_LASTOCC[(int)'I' - 32] = 5;
 872         MULTIRELEASE_LASTOCC[(int)'-' - 32] = 6;
 873         MULTIRELEASE_LASTOCC[(int)'L' - 32] = 9;
 874         MULTIRELEASE_LASTOCC[(int)'A' - 32] = 11;
 875         MULTIRELEASE_LASTOCC[(int)'S' - 32] = 12;
 876         MULTIRELEASE_LASTOCC[(int)':' - 32] = 14;
 877         MULTIRELEASE_LASTOCC[(int)' ' - 32] = 15;
 878         MULTIRELEASE_LASTOCC[(int)'T' - 32] = 16;
 879         MULTIRELEASE_LASTOCC[(int)'R' - 32] = 17;
 880         MULTIRELEASE_LASTOCC[(int)'U' - 32] = 18;
 881         MULTIRELEASE_LASTOCC[(int)'E' - 32] = 19;
 882         for (int i = 0; i < 17; i++) {
 883             MULTIRELEASE_OPTOSFT[i] = 19;
 884         }
 885         MULTIRELEASE_OPTOSFT[17] = 6;
 886         MULTIRELEASE_OPTOSFT[18] = 1;
 887     }
 888 
 889     private JarEntry getManEntry() {
 890         if (manEntry == null) {
 891             // First look up manifest entry using standard name
 892             ZipEntry manEntry = super.getEntry(MANIFEST_NAME);
 893             if (manEntry == null) {
 894                 // If not found, then iterate through all the "META-INF/"
 895                 // entries to find a match.
 896                 String[] names = getMetaInfEntryNames();
 897                 if (names != null) {
 898                     for (String name : names) {
 899                         if (MANIFEST_NAME.equals(name.toUpperCase(Locale.ENGLISH))) {
 900                             manEntry = super.getEntry(name);
 901                             break;
 902                         }
 903                     }
 904                 }
 905             }
 906             this.manEntry = (manEntry == null)
 907                     ? null
 908                     : new JarFileEntry(manEntry.getName(), manEntry);
 909         }
 910         return manEntry;
 911     }
 912 
 913    /**
 914     * Returns {@code true} iff this JAR file has a manifest with the
 915     * Class-Path attribute
 916     */
 917     boolean hasClassPathAttribute() throws IOException {
 918         checkForSpecialAttributes();
 919         return hasClassPathAttribute;
 920     }
 921 
 922     /**
 923      * Returns true if the pattern {@code src} is found in {@code b}.
 924      * The {@code lastOcc} array is the precomputed bad character shifts.
 925      * Since there are no repeated substring in our search strings,
 926      * the good suffix shifts can be replaced with a comparison.
 927      */
 928     private int match(byte[] src, byte[] b, byte[] lastOcc, byte[] optoSft) {
 929         int len = src.length;
 930         int last = b.length - len;
 931         int i = 0;
 932         next:
 933         while (i <= last) {
 934             for (int j = (len - 1); j >= 0; j--) {
 935                 byte c = b[i + j];
 936                 if (c >= ' ' && c <= 'z') {
 937                     if (c >= 'a') c -= 32; // Canonicalize
 938 
 939                     if (c != src[j]) {
 940                         // no match
 941                         int badShift = lastOcc[c - 32];
 942                         i += Math.max(j + 1 - badShift, optoSft[j]);
 943                         continue next;
 944                     }
 945                 } else {
 946                     // no match, character not valid for name
 947                     i += len;
 948                     continue next;
 949                 }
 950             }
 951             return i;
 952         }
 953         return -1;
 954     }
 955 
 956     /**
 957      * On first invocation, check if the JAR file has the Class-Path
 958      * and the Multi-Release attribute. A no-op on subsequent calls.
 959      */
 960     private void checkForSpecialAttributes() throws IOException {
 961         if (hasCheckedSpecialAttributes) {
 962             return;
 963         }
 964         synchronized (this) {
 965             if (hasCheckedSpecialAttributes) {
 966                 return;
 967             }
 968             JarEntry manEntry = getManEntry();
 969             if (manEntry != null) {
 970                 byte[] b = getBytes(manEntry);
 971                 hasClassPathAttribute = match(CLASSPATH_CHARS, b,
 972                         CLASSPATH_LASTOCC, CLASSPATH_OPTOSFT) != -1;
 973                 // is this a multi-release jar file
 974                 if (MULTI_RELEASE_ENABLED) {
 975                     int i = match(MULTIRELEASE_CHARS, b, MULTIRELEASE_LASTOCC,
 976                             MULTIRELEASE_OPTOSFT);
 977                     if (i != -1) {
 978                         i += MULTIRELEASE_CHARS.length;
 979                         if (i < b.length) {
 980                             byte c = b[i++];
 981                             // Check that the value is followed by a newline
 982                             // and does not have a continuation
 983                             if (c == '\n' &&
 984                                     (i == b.length || b[i] != ' ')) {
 985                                 isMultiRelease = true;
 986                             } else if (c == '\r') {
 987                                 if (i == b.length) {
 988                                     isMultiRelease = true;
 989                                 } else {
 990                                     c = b[i++];
 991                                     if (c == '\n') {
 992                                         if (i == b.length || b[i] != ' ') {
 993                                             isMultiRelease = true;
 994                                         }
 995                                     } else if (c != ' ') {
 996                                         isMultiRelease = true;
 997                                     }
 998                                 }
 999                             }
1000                         }
1001                     }
1002                 }
1003             }
1004             hasCheckedSpecialAttributes = true;
1005         }
1006     }
1007 
1008     private synchronized void ensureInitialization() {
1009         try {
1010             maybeInstantiateVerifier();
1011         } catch (IOException e) {
1012             throw new RuntimeException(e);
1013         }
1014         if (jv != null && !jvInitialized) {
1015             initializeVerifier();
1016             jvInitialized = true;
1017         }
1018     }
1019 
1020     JarEntry newEntry(ZipEntry ze) {
1021         return new JarFileEntry(ze);




  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.util.jar;
  27 
  28 import java.io.*;
  29 import java.lang.ref.SoftReference;
  30 import java.net.URL;
  31 import java.util.*;
  32 import java.util.stream.Stream;
  33 import java.util.stream.StreamSupport;
  34 import java.util.zip.*;
  35 import java.security.CodeSigner;
  36 import java.security.cert.Certificate;
  37 import java.security.CodeSource;
  38 import jdk.internal.misc.SharedSecrets;
  39 import jdk.internal.util.jar.JarAttributes;
  40 import sun.security.action.GetPropertyAction;
  41 import sun.security.util.ManifestEntryVerifier;
  42 import sun.security.util.SignatureFileVerifier;
  43 
  44 /**
  45  * The {@code JarFile} class is used to read the contents of a jar file
  46  * from any file that can be opened with {@code java.io.RandomAccessFile}.
  47  * It extends the class {@code java.util.zip.ZipFile} with support
  48  * for reading an optional {@code Manifest} entry, and support for
  49  * processing multi-release jar files.  The {@code Manifest} can be used
  50  * to specify meta-information about the jar file and its entries.
  51  *
  52  * <p><a name="multirelease">A multi-release jar file</a> is a jar file that
  53  * contains a manifest with a main attribute named "Multi-Release",
  54  * a set of "base" entries, some of which are public classes with public
  55  * or protected methods that comprise the public interface of the jar file,
  56  * and a set of "versioned" entries contained in subdirectories of the
  57  * "META-INF/versions" directory.  The versioned entries are partitioned by the
  58  * major version of the Java platform.  A versioned entry, with a version
  59  * {@code n}, {@code 8 < n}, in the "META-INF/versions/{n}" directory overrides


 811         // wrap a verifier stream around the real stream
 812         return new JarVerifier.VerifierStream(
 813             getManifestFromReference(),
 814             verifiableEntry(ze),
 815             super.getInputStream(ze),
 816             jv);
 817     }
 818 
 819     private JarEntry verifiableEntry(ZipEntry ze) {
 820         if (ze instanceof JarFileEntry) {
 821             // assure the name and entry match for verification
 822             return ((JarFileEntry)ze).realEntry();
 823         }
 824         ze = getJarEntry(ze.getName());
 825         if (ze instanceof JarFileEntry) {
 826             return ((JarFileEntry)ze).realEntry();
 827         }
 828         return (JarEntry)ze;
 829     }
 830 



























































 831     private JarEntry getManEntry() {
 832         if (manEntry == null) {
 833             // First look up manifest entry using standard name
 834             ZipEntry manEntry = super.getEntry(MANIFEST_NAME);
 835             if (manEntry == null) {
 836                 // If not found, then iterate through all the "META-INF/"
 837                 // entries to find a match.
 838                 String[] names = getMetaInfEntryNames();
 839                 if (names != null) {
 840                     for (String name : names) {
 841                         if (MANIFEST_NAME.equals(name.toUpperCase(Locale.ENGLISH))) {
 842                             manEntry = super.getEntry(name);
 843                             break;
 844                         }
 845                     }
 846                 }
 847             }
 848             this.manEntry = (manEntry == null)
 849                     ? null
 850                     : new JarFileEntry(manEntry.getName(), manEntry);
 851         }
 852         return manEntry;
 853     }
 854 
 855    /**
 856     * Returns {@code true} iff this JAR file has a manifest with the
 857     * Class-Path attribute
 858     */
 859     boolean hasClassPathAttribute() throws IOException {
 860         checkForSpecialAttributes();
 861         return hasClassPathAttribute;
 862     }
 863 

































 864 
 865     /**
 866      * On first invocation, check if the JAR file has the Class-Path
 867      * and the Multi-Release attribute. A no-op on subsequent calls.
 868      */
 869     private void checkForSpecialAttributes() throws IOException {
 870         if (hasCheckedSpecialAttributes) {
 871             return;
 872         }
 873         synchronized (this) {
 874             if (hasCheckedSpecialAttributes) {
 875                 return;
 876             }
 877             JarEntry manEntry = getManEntry();
 878             if (manEntry != null) {
 879                 byte[] b = getBytes(manEntry);
 880                 hasClassPathAttribute = JarAttributes.hasClassPathAttribute(b);

 881                 // is this a multi-release jar file
 882                 if (MULTI_RELEASE_ENABLED) {
 883                     isMultiRelease = JarAttributes.isMultiRelease(b);


























 884                 }
 885             }
 886             hasCheckedSpecialAttributes = true;
 887         }
 888     }
 889 
 890     private synchronized void ensureInitialization() {
 891         try {
 892             maybeInstantiateVerifier();
 893         } catch (IOException e) {
 894             throw new RuntimeException(e);
 895         }
 896         if (jv != null && !jvInitialized) {
 897             initializeVerifier();
 898             jvInitialized = true;
 899         }
 900     }
 901 
 902     JarEntry newEntry(ZipEntry ze) {
 903         return new JarFileEntry(ze);


< prev index next >