src/share/classes/org/openjdk/jigsaw/ModuleFile.java

Print this page




  45         case CLASSES:
  46         case RESOURCES:
  47             return "classes";
  48         case NATIVE_LIBS:
  49             return "lib";
  50         case NATIVE_CMDS:
  51             return "bin";
  52         case CONFIG:
  53             return "etc";
  54         default:
  55             throw new AssertionError(type);
  56         }
  57     }
  58 
  59     public final static class Reader implements Closeable {
  60 
  61         private DataInputStream stream;
  62         private File destination;
  63         private boolean deflate;
  64         private HashType hashtype;



  65 
  66         private static class CountingInputStream extends FilterInputStream {
  67             int count;
  68             public CountingInputStream(InputStream stream, int count) {
  69                 super(stream);
  70                 this.count = count;
  71             }
  72 
  73             public int available() throws IOException {
  74                 return count;
  75             }
  76 
  77             public boolean markSupported() {
  78                 return false;
  79             }
  80 
  81             public int read() throws IOException {
  82                 if (count == 0)
  83                     return -1;
  84                 int read = super.read();


 163                 calculatedHashes.add(sectionDigest.digest());
 164 
 165                 fileIn = new DataInputStream(dis);
 166                 if (readSection(fileIn) != SectionType.MODULE_INFO)
 167                     throw new IOException("First module-file section"
 168                                           + " is not MODULE_INFO");
 169                 assert moduleInfoBytes != null;
 170 
 171                 // Read the Signature Section, if present
 172                 readSignatureSection(fileIn, dis);
 173 
 174                 return moduleInfoBytes.clone();
 175             } catch (IOException x) {
 176                 close();
 177                 throw x;
 178             }
 179         }
 180 
 181         public void readRest() throws IOException {
 182             extract = false;
 183             readRest(null, false);
 184         }
 185 
 186         public void readRest(File dst, boolean deflate) throws IOException {
 187             this.destination = dst;







 188             this.deflate = deflate;



 189             try {
 190                 if (extract)
 191                     Files.store(moduleInfoBytes, computeRealPath("info"));
 192                 // Module-Info and Signature, if present, have been consumed
 193 
 194                 // Read rest of file until all sections have been read
 195                 stream.mark(1);
 196                 while (-1 != stream.read()) {
 197                     stream.reset();
 198                     readSection(fileIn);
 199                     stream.mark(1);
 200                 }
 201 
 202                 close();
 203                 byte[] fileHeaderHash = fileHeader.getHashNoClone();
 204                 checkHashMatch(fileHeaderHash, fileDigest.digest());
 205                 calculatedHashes.add(fileHeaderHash);
 206             } finally {
 207                 close();
 208             }


 243         }
 244 
 245         private JarOutputStream contentStream = null;
 246 
 247         private JarOutputStream contentStream() throws IOException {
 248             if (contentStream == null) {
 249                 if (extract) {
 250                     FileOutputStream fos
 251                         = new FileOutputStream(computeRealPath("classes"));
 252                     contentStream
 253                         = new JarOutputStream(new BufferedOutputStream(fos));
 254                 } else {
 255                     contentStream = new JarOutputStream(new NullOutputStream());
 256                 }
 257             }
 258             return contentStream;
 259         }
 260 
 261         public void close() throws IOException {
 262             try {

 263                 if (contentStream != null) {
 264                     contentStream.close();
 265                     contentStream = null;
 266                 }
 267             } finally {
 268                 if (fileIn != null) {
 269                     fileIn.close();
 270                     fileIn = null;
 271                 }
 272             }




 273         }


 274 

 275         public void readModule() throws IOException {
 276             extract = false;
 277             readStart();
 278             readRest();
 279         }
 280 
 281         public void readModule(File dst) throws IOException {
 282             readStart();
 283             readRest(dst, false);
 284         }
 285 
 286         private void readSignatureSection(DataInputStream stream,
 287                                           DigestInputStream dis)
 288             throws IOException
 289         {
 290 
 291             // Turn off digest computation before reading Signature Section
 292             dis.on(false);
 293 
 294             // Mark the starting position


 413         public void readGZIPCompressedFile(DataInputStream in,
 414                                            SectionType type)
 415             throws IOException
 416         {
 417             SubSectionFileHeader header = SubSectionFileHeader.read(in);
 418             int csize = header.getCSize();
 419 
 420             // Splice off the compressed file from input stream
 421             ByteArrayOutputStream baos = new ByteArrayOutputStream();
 422             copyStream(new CountingInputStream(in, csize), baos, csize);
 423 
 424             byte[] compressedfile = baos.toByteArray();
 425             ByteArrayInputStream bain
 426                 = new ByteArrayInputStream(compressedfile);
 427             try (GZIPInputStream gin = new GZIPInputStream(bain);
 428                  OutputStream out = openOutputStream(type, header.getPath())) {
 429                 copyStream(gin, out);
 430             }
 431 
 432             if (extract)
 433                 markNativeCodeExecutable(type, currentPath);
 434         }
 435 
 436         public void readUncompressedFile(DataInputStream in,
 437                                          SectionType type,
 438                                          int csize)
 439             throws IOException
 440         {
 441             assert type != SectionType.MODULE_INFO;
 442             SubSectionFileHeader header = SubSectionFileHeader.read(in);
 443             csize = header.getCSize();
 444             try (OutputStream out = openOutputStream(type, header.getPath())) {
 445                 CountingInputStream cin = new CountingInputStream(in, csize);
 446                 byte[] buf = new byte[8192];
 447                 int n;
 448                 while ((n = cin.read(buf)) >= 0)
 449                     out.write(buf, 0, n);
 450             }
 451             markNativeCodeExecutable(type, currentPath);

 452          }

 453 
 454         public byte[] readModuleInfo(DataInputStream in, int csize)
 455             throws IOException
 456         {
 457             CountingInputStream cin = new CountingInputStream(in, csize);
 458             ByteArrayOutputStream out = new ByteArrayOutputStream();
 459             byte[] buf = new byte[8192];
 460             int n;
 461             while ((n = cin.read(buf)) >= 0)
 462                 out.write(buf, 0, n);
 463             return out.toByteArray();
 464         }
 465 
 466         public byte[] readModuleSignature(DataInputStream in, int csize)
 467             throws IOException
 468         {
 469             return readModuleInfo(in, csize); // signature has the same format
 470         }
 471 
 472         private File computeRealPath(String storedpath) throws IOException {


 473 
 474             String convertedpath = storedpath.replace('/', File.separatorChar);
 475             File path = new File(convertedpath);



 476 
 477             // Absolute path names are not permitted.
 478             ensureNonAbsolute(path);
 479             path = resolveAndNormalize(destination, convertedpath);
 480             // Create the parent directories if necessary
 481             File parent = path.getParentFile();
 482             if (!parent.exists())
 483                 Files.mkdirs(parent, path.getName());
 484 
 485             return path;

 486         }
 487 
 488         private File computeRealPath(SectionType type,
 489                                      String storedpath)






































 490             throws IOException
 491         {
 492             String dir = getSubdirOfSection(type);
 493             return computeRealPath(dir + File.separatorChar + storedpath);










 494         }
 495 
 496         private static void markNativeCodeExecutable(SectionType type,
 497                                                      File file)
 498         {
 499             if (type == SectionType.NATIVE_CMDS
 500                 || (type == SectionType.NATIVE_LIBS
 501                     && System.getProperty("os.name").startsWith("Windows")))
 502                 {
 503                     file.setExecutable(true);
 504                 }
 505         }
 506 







 507         private void unpack200gzip(DataInputStream in) throws IOException {
 508             GZIPInputStream gis = new GZIPInputStream(in) {
 509                     public void close() throws IOException {}
 510                 };
 511             Pack200.Unpacker unpacker = Pack200.newUnpacker();
 512             if (deflate) {
 513                 Map<String,String> p = unpacker.properties();
 514                 p.put(Pack200.Unpacker.DEFLATE_HINT, Pack200.Unpacker.TRUE);
 515             }
 516             unpacker.unpack(gis, contentStream());
 517         }
 518 
 519     }
 520 
 521     private static void checkCompressor(SectionType type,
 522                                         Compressor compressor) {
 523 
 524         if ((SectionType.MODULE_INFO == type &&
 525              Compressor.NONE != compressor)
 526             || (SectionType.CLASSES == type &&


 562         throws IOException
 563     {
 564         byte[] buffer = new byte[1024 * 8];
 565 
 566         while(count > 0) {
 567             int b_read = in.read(buffer, 0, Math.min(count, buffer.length));
 568             if (-1 == b_read)
 569                 return;
 570             out.write(buffer, 0, b_read);
 571             count-=b_read;
 572         }
 573     }
 574 
 575     private static void copyStream(InputStream in, OutputStream out,
 576                                    int count)
 577         throws IOException
 578     {
 579         copyStream(in, (DataOutput) new DataOutputStream(out), count);
 580     }
 581 
 582     private static void ensureNonAbsolute(File path) throws IOException {
 583         if (path.isAbsolute())
 584             throw new IOException("Abolute path instead of relative: " + path);
 585     }
 586 
 587     private static void ensureNonNegativity(long size, String parameter) {
 588         if (size < 0)
 589             throw new IllegalArgumentException(parameter + "<0: " + size);
 590     }
 591 
 592     private static void ensureNonNull(Object reference, String parameter) {
 593         if (null == reference)
 594             throw new IllegalArgumentException(parameter + " == null");
 595     }
 596 
 597     private static void ensureMatch(int found, int expected, String field)
 598         throws IOException
 599     {
 600         if (found != expected)
 601             throw new IOException(field + " expected : "
 602                 + Integer.toHexString(expected) + " found: "
 603                 + Integer.toHexString(found));
 604     }
 605 
 606     private static void ensureShortNativePath(File path, String name)


 659         for (int i = 0; i < hash.length; i++) {
 660             int val = (hash[i] & 0xFF);
 661             if (val <= 16)
 662                 hex.append("0");
 663             hex.append(Integer.toHexString(val));
 664         }
 665         return hex.toString();
 666     }
 667 
 668     private static File resolveAndNormalize(File directory, String path)
 669         throws IOException
 670     {
 671         File realpath = new File(directory, path);
 672         if (directory != null &&
 673             ! realpath.toPath().startsWith(directory.toPath()))
 674             throw new IOException("Bogus relative path: " + path);
 675 
 676         return realpath;
 677     }
 678 
















 679     private static short readHashLength(DataInputStream in) throws IOException {
 680         final short hashLength = in.readShort();
 681         ensureNonNegativity(hashLength, "hashLength");
 682 
 683         return hashLength;
 684     }
 685 
 686     private static byte[] readHashBytes(DataInputStream in, short hashLength)
 687         throws IOException
 688     {
 689 
 690         final byte[] hash = new byte[hashLength];
 691         in.readFully(hash);
 692 
 693         return hash;
 694     }
 695 
 696     private static byte[] readHash(DataInputStream in) throws IOException {
 697         return readHashBytes(in, readHashLength(in));
 698     }




  45         case CLASSES:
  46         case RESOURCES:
  47             return "classes";
  48         case NATIVE_LIBS:
  49             return "lib";
  50         case NATIVE_CMDS:
  51             return "bin";
  52         case CONFIG:
  53             return "etc";
  54         default:
  55             throw new AssertionError(type);
  56         }
  57     }
  58 
  59     public final static class Reader implements Closeable {
  60 
  61         private DataInputStream stream;
  62         private File destination;
  63         private boolean deflate;
  64         private HashType hashtype;
  65         private File natlibs;
  66         private File natcmds;
  67         private File configs;
  68 
  69         private static class CountingInputStream extends FilterInputStream {
  70             int count;
  71             public CountingInputStream(InputStream stream, int count) {
  72                 super(stream);
  73                 this.count = count;
  74             }
  75 
  76             public int available() throws IOException {
  77                 return count;
  78             }
  79 
  80             public boolean markSupported() {
  81                 return false;
  82             }
  83 
  84             public int read() throws IOException {
  85                 if (count == 0)
  86                     return -1;
  87                 int read = super.read();


 166                 calculatedHashes.add(sectionDigest.digest());
 167 
 168                 fileIn = new DataInputStream(dis);
 169                 if (readSection(fileIn) != SectionType.MODULE_INFO)
 170                     throw new IOException("First module-file section"
 171                                           + " is not MODULE_INFO");
 172                 assert moduleInfoBytes != null;
 173 
 174                 // Read the Signature Section, if present
 175                 readSignatureSection(fileIn, dis);
 176 
 177                 return moduleInfoBytes.clone();
 178             } catch (IOException x) {
 179                 close();
 180                 throw x;
 181             }
 182         }
 183 
 184         public void readRest() throws IOException {
 185             extract = false;
 186             readRest(null, false, null, null, null);
 187         }
 188 
 189         public void readRest(File dst, boolean deflate) throws IOException {
 190             readRest(dst, deflate, null, null, null);
 191         }
 192 
 193         public void readRest(File dst, boolean deflate, File natlibs,
 194                              File natcmds, File configs)
 195                 throws IOException
 196         {
 197             this.destination = dst.getCanonicalFile();
 198             this.deflate = deflate;
 199             this.natlibs = natlibs != null ? natlibs : new File(destination, "lib");
 200             this.natcmds = natcmds != null ? natcmds : new File(destination, "bin");
 201             this.configs = configs != null ? configs : new File(destination, "etc");
 202             try {
 203                 if (extract)
 204                     Files.store(moduleInfoBytes, computeRealPath("info"));
 205                 // Module-Info and Signature, if present, have been consumed
 206 
 207                 // Read rest of file until all sections have been read
 208                 stream.mark(1);
 209                 while (-1 != stream.read()) {
 210                     stream.reset();
 211                     readSection(fileIn);
 212                     stream.mark(1);
 213                 }
 214 
 215                 close();
 216                 byte[] fileHeaderHash = fileHeader.getHashNoClone();
 217                 checkHashMatch(fileHeaderHash, fileDigest.digest());
 218                 calculatedHashes.add(fileHeaderHash);
 219             } finally {
 220                 close();
 221             }


 256         }
 257 
 258         private JarOutputStream contentStream = null;
 259 
 260         private JarOutputStream contentStream() throws IOException {
 261             if (contentStream == null) {
 262                 if (extract) {
 263                     FileOutputStream fos
 264                         = new FileOutputStream(computeRealPath("classes"));
 265                     contentStream
 266                         = new JarOutputStream(new BufferedOutputStream(fos));
 267                 } else {
 268                     contentStream = new JarOutputStream(new NullOutputStream());
 269                 }
 270             }
 271             return contentStream;
 272         }
 273 
 274         public void close() throws IOException {
 275             try {
 276                 try {
 277                     if (contentStream != null) {
 278                         contentStream.close();
 279                         contentStream = null;
 280                     }
 281                 } finally {
 282                     if (fileIn != null) {
 283                         fileIn.close();
 284                         fileIn = null;
 285                     }
 286                 }
 287             } finally {
 288                 if (filesWriter != null) {
 289                     filesWriter.close();
 290                     filesWriter = null;
 291                 }
 292             }
 293         }
 294 
 295 
 296         public void readModule() throws IOException {
 297             extract = false;
 298             readStart();
 299             readRest();
 300         }
 301 
 302         public void readModule(File dst) throws IOException {
 303             readStart();
 304             readRest(dst, false);
 305         }
 306 
 307         private void readSignatureSection(DataInputStream stream,
 308                                           DigestInputStream dis)
 309             throws IOException
 310         {
 311 
 312             // Turn off digest computation before reading Signature Section
 313             dis.on(false);
 314 
 315             // Mark the starting position


 434         public void readGZIPCompressedFile(DataInputStream in,
 435                                            SectionType type)
 436             throws IOException
 437         {
 438             SubSectionFileHeader header = SubSectionFileHeader.read(in);
 439             int csize = header.getCSize();
 440 
 441             // Splice off the compressed file from input stream
 442             ByteArrayOutputStream baos = new ByteArrayOutputStream();
 443             copyStream(new CountingInputStream(in, csize), baos, csize);
 444 
 445             byte[] compressedfile = baos.toByteArray();
 446             ByteArrayInputStream bain
 447                 = new ByteArrayInputStream(compressedfile);
 448             try (GZIPInputStream gin = new GZIPInputStream(bain);
 449                  OutputStream out = openOutputStream(type, header.getPath())) {
 450                 copyStream(gin, out);
 451             }
 452 
 453             if (extract)
 454                 postExtract(type, currentPath);
 455         }
 456 
 457         public void readUncompressedFile(DataInputStream in,
 458                                          SectionType type,
 459                                          int csize)
 460             throws IOException
 461         {
 462             assert type != SectionType.MODULE_INFO;
 463             SubSectionFileHeader header = SubSectionFileHeader.read(in);
 464             csize = header.getCSize();
 465             try (OutputStream out = openOutputStream(type, header.getPath())) {
 466                 CountingInputStream cin = new CountingInputStream(in, csize);
 467                 byte[] buf = new byte[8192];
 468                 int n;
 469                 while ((n = cin.read(buf)) >= 0)
 470                     out.write(buf, 0, n);
 471             }
 472             if (extract) {
 473                 postExtract(type, currentPath);
 474             }
 475          }
 476 
 477         public byte[] readModuleInfo(DataInputStream in, int csize)
 478             throws IOException
 479         {
 480             CountingInputStream cin = new CountingInputStream(in, csize);
 481             ByteArrayOutputStream out = new ByteArrayOutputStream();
 482             byte[] buf = new byte[8192];
 483             int n;
 484             while ((n = cin.read(buf)) >= 0)
 485                 out.write(buf, 0, n);
 486             return out.toByteArray();
 487         }
 488 
 489         public byte[] readModuleSignature(DataInputStream in, int csize)
 490             throws IOException
 491         {
 492             return readModuleInfo(in, csize); // signature has the same format
 493         }
 494 
 495         // Track files installed outside the module library. For later removal.
 496         // files are relative to the modules directory.
 497         private PrintWriter filesWriter;
 498 
 499         private void trackFiles(SectionType type, File file)
 500             throws IOException
 501         {
 502             if (file == null || file.toPath().startsWith(destination.toPath()))
 503                 return;
 504 
 505             // Lazy construction, not all modules will need this.
 506             if (filesWriter == null)
 507                 filesWriter = new PrintWriter(computeRealPath("files"), "UTF-8");




 508 
 509             filesWriter.println(Files.convertSeparator(relativize(destination, file)));
 510             filesWriter.flush();
 511         }
 512 
 513         void remove() throws IOException {
 514             ModuleFile.Reader.remove(destination);
 515         }
 516 
 517         // Removes a module, given its module install directory
 518         static void remove(File moduleDir) throws IOException {
 519             // Firstly remove any files installed outside of the module dir
 520             File files = new File(moduleDir, "files");
 521             if (files.exists()) {
 522                 try (FileInputStream fis = new FileInputStream(files);
 523                      InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
 524                      BufferedReader in = new BufferedReader(isr)) {
 525                     String filename;
 526                     while ((filename = in.readLine()) != null)
 527                         Files.delete(new File(moduleDir,
 528                                               Files.platformSeparator(filename)));
 529                 }
 530             }
 531 
 532             Files.deleteTree(moduleDir);
 533         }
 534 
 535         // Returns the absolute path of the given section type.
 536         private File getDirOfSection(SectionType type) {
 537             if (type == SectionType.NATIVE_LIBS)
 538                 return natlibs; 
 539             else if (type == SectionType.NATIVE_CMDS)
 540                 return natcmds;
 541             else if (type == SectionType.CONFIG)
 542                 return configs;
 543 
 544             // resolve sub dir section paths against the modules directory
 545             return new File(destination, ModuleFile.getSubdirOfSection(type));
 546         }
 547 
 548         private File computeRealPath(String path) throws IOException {
 549             return resolveAndNormalize(destination, path);
 550         }
 551 
 552         private File computeRealPath(SectionType type, String storedpath)
 553             throws IOException
 554         {
 555             File sectionPath = getDirOfSection(type);
 556             File realpath = new File(sectionPath,
 557                  Files.ensureNonAbsolute(Files.platformSeparator(storedpath)));
 558 
 559             validatePath(sectionPath, realpath);
 560 
 561             // Create the parent directories if necessary
 562             File parent = realpath.getParentFile();
 563             if (!parent.exists())
 564                 Files.mkdirs(parent, realpath.getName());
 565 
 566             return realpath;
 567         }
 568 
 569         private static void markNativeCodeExecutable(SectionType type,
 570                                                      File file)
 571         {
 572             if (type == SectionType.NATIVE_CMDS
 573                 || (type == SectionType.NATIVE_LIBS
 574                     && System.getProperty("os.name").startsWith("Windows")))
 575                 {
 576                     file.setExecutable(true);
 577                 }
 578         }
 579 
 580         private void postExtract(SectionType type, File path)
 581             throws IOException
 582         {
 583             markNativeCodeExecutable(type, path);
 584             trackFiles(type, path);
 585         }
 586 
 587         private void unpack200gzip(DataInputStream in) throws IOException {
 588             GZIPInputStream gis = new GZIPInputStream(in) {
 589                     public void close() throws IOException {}
 590                 };
 591             Pack200.Unpacker unpacker = Pack200.newUnpacker();
 592             if (deflate) {
 593                 Map<String,String> p = unpacker.properties();
 594                 p.put(Pack200.Unpacker.DEFLATE_HINT, Pack200.Unpacker.TRUE);
 595             }
 596             unpacker.unpack(gis, contentStream());
 597         }
 598 
 599     }
 600 
 601     private static void checkCompressor(SectionType type,
 602                                         Compressor compressor) {
 603 
 604         if ((SectionType.MODULE_INFO == type &&
 605              Compressor.NONE != compressor)
 606             || (SectionType.CLASSES == type &&


 642         throws IOException
 643     {
 644         byte[] buffer = new byte[1024 * 8];
 645 
 646         while(count > 0) {
 647             int b_read = in.read(buffer, 0, Math.min(count, buffer.length));
 648             if (-1 == b_read)
 649                 return;
 650             out.write(buffer, 0, b_read);
 651             count-=b_read;
 652         }
 653     }
 654 
 655     private static void copyStream(InputStream in, OutputStream out,
 656                                    int count)
 657         throws IOException
 658     {
 659         copyStream(in, (DataOutput) new DataOutputStream(out), count);
 660     }
 661 





 662     private static void ensureNonNegativity(long size, String parameter) {
 663         if (size < 0)
 664             throw new IllegalArgumentException(parameter + "<0: " + size);
 665     }
 666 
 667     private static void ensureNonNull(Object reference, String parameter) {
 668         if (null == reference)
 669             throw new IllegalArgumentException(parameter + " == null");
 670     }
 671 
 672     private static void ensureMatch(int found, int expected, String field)
 673         throws IOException
 674     {
 675         if (found != expected)
 676             throw new IOException(field + " expected : "
 677                 + Integer.toHexString(expected) + " found: "
 678                 + Integer.toHexString(found));
 679     }
 680 
 681     private static void ensureShortNativePath(File path, String name)


 734         for (int i = 0; i < hash.length; i++) {
 735             int val = (hash[i] & 0xFF);
 736             if (val <= 16)
 737                 hex.append("0");
 738             hex.append(Integer.toHexString(val));
 739         }
 740         return hex.toString();
 741     }
 742 
 743     private static File resolveAndNormalize(File directory, String path)
 744         throws IOException
 745     {
 746         File realpath = new File(directory, path);
 747         if (directory != null &&
 748             ! realpath.toPath().startsWith(directory.toPath()))
 749             throw new IOException("Bogus relative path: " + path);
 750 
 751         return realpath;
 752     }
 753 
 754 
 755     private static String relativize(File directory, File path) throws IOException {
 756         return (directory.toPath().relativize(path.toPath().toRealPath())).toString();
 757     }
 758 
 759     private static void validatePath(File parent, File child)
 760         throws IOException
 761     {
 762         if (!child.toPath().startsWith(parent.toPath()) )
 763             throw new IOException("Bogus relative path: " + child);
 764         if (child.exists()) {
 765             // conflict, for now just fail
 766             throw new IOException("File " + child + " already exists");
 767         }
 768     }
 769 
 770     private static short readHashLength(DataInputStream in) throws IOException {
 771         final short hashLength = in.readShort();
 772         ensureNonNegativity(hashLength, "hashLength");
 773 
 774         return hashLength;
 775     }
 776 
 777     private static byte[] readHashBytes(DataInputStream in, short hashLength)
 778         throws IOException
 779     {
 780 
 781         final byte[] hash = new byte[hashLength];
 782         in.readFully(hash);
 783 
 784         return hash;
 785     }
 786 
 787     private static byte[] readHash(DataInputStream in) throws IOException {
 788         return readHashBytes(in, readHashLength(in));
 789     }