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