41 switch (type) {
42 case MODULE_INFO:
43 case SIGNATURE:
44 return ".";
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
106 return -1;
107 n = Math.min(n, count);
108 long skipped = super.skip(n);
109 if (n > 0)
110 count-=skipped;
111 return skipped;
112 }
113 }
114
115 public Reader(DataInputStream stream) {
116 hashtype = HashType.SHA256;
117 // Ensure that mark/reset is supported
118 if (stream.markSupported()) {
119 this.stream = stream;
120 } else {
121 this.stream =
122 new DataInputStream(new BufferedInputStream(stream));
123 }
124 }
125
126 private void checkHashMatch(byte[] expected, byte[] computed)
127 throws IOException
128 {
129 if (!MessageDigest.isEqual(expected, computed))
130 throw new IOException("Expected hash "
131 + hashHexString(expected)
132 + " instead of "
133 + hashHexString(computed));
134 }
135
136 private ModuleFileHeader fileHeader = null;
137 private MessageDigest fileDigest = null;
138 private MessageDigest sectionDigest = null;
139 private DataInputStream fileIn = null;
140 private byte[] moduleInfoBytes = null;
141 private Integer moduleSignatureType = null;
142 private byte[] moduleSignatureBytes = null;
143 private final int MAX_SECTION_HEADER_LENGTH = 128;
144 private List<byte[]> calculatedHashes = new ArrayList<>();
145 private boolean extract = true;
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
366 }
367 break;
368 case GZIP:
369 readGZIPCompressedFile(in, type);
370 break;
371 case PACK200_GZIP:
372 readClasses(
373 new DataInputStream(new CountingInputStream(in, csize)));
374 break;
375 default:
376 throw new IOException("Unsupported Compressor for files: " +
377 compressor);
378 }
379 }
380
381 public void readClasses(DataInputStream in) throws IOException {
382 unpack200gzip(in);
383 }
384
385 private File currentPath = null;
386
387 private OutputStream openOutputStream(SectionType type,
388 String path)
389 throws IOException
390 {
391 if (!extract)
392 return new NullOutputStream();
393 currentPath = null;
394 assert type != SectionType.CLASSES;
395 if (type == SectionType.RESOURCES)
396 return Files.newOutputStream(contentStream(), path);
397 currentPath = computeRealPath(type, path);
398 File parent = currentPath.getParentFile();
399 if (!parent.exists())
400 Files.mkdirs(parent, currentPath.getName());
401 return new BufferedOutputStream(new FileOutputStream(currentPath));
402 }
403
404 private static class NullOutputStream extends OutputStream {
405 @Override
412
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();
|
41 switch (type) {
42 case MODULE_INFO:
43 case SIGNATURE:
44 return ".";
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 // The library where this module is to be installed, or null if
62 // simply extracting ( jmod Extract, or jsign )
63 private SimpleLibrary lib;
64 private DataInputStream stream;
65 private File destination;
66 private boolean deflate;
67 private HashType hashtype;
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
109 return -1;
110 n = Math.min(n, count);
111 long skipped = super.skip(n);
112 if (n > 0)
113 count-=skipped;
114 return skipped;
115 }
116 }
117
118 public Reader(DataInputStream stream) {
119 hashtype = HashType.SHA256;
120 // Ensure that mark/reset is supported
121 if (stream.markSupported()) {
122 this.stream = stream;
123 } else {
124 this.stream =
125 new DataInputStream(new BufferedInputStream(stream));
126 }
127 }
128
129 public Reader(DataInputStream stream, SimpleLibrary lib) {
130 this(stream);
131 this.lib = lib;
132 }
133
134 private void checkHashMatch(byte[] expected, byte[] computed)
135 throws IOException
136 {
137 if (!MessageDigest.isEqual(expected, computed))
138 throw new IOException("Expected hash "
139 + hashHexString(expected)
140 + " instead of "
141 + hashHexString(computed));
142 }
143
144 private ModuleFileHeader fileHeader = null;
145 private MessageDigest fileDigest = null;
146 private MessageDigest sectionDigest = null;
147 private DataInputStream fileIn = null;
148 private byte[] moduleInfoBytes = null;
149 private Integer moduleSignatureType = null;
150 private byte[] moduleSignatureBytes = null;
151 private final int MAX_SECTION_HEADER_LENGTH = 128;
152 private List<byte[]> calculatedHashes = new ArrayList<>();
153 private boolean extract = true;
251 }
252
253 private JarOutputStream contentStream = null;
254
255 private JarOutputStream contentStream() throws IOException {
256 if (contentStream == null) {
257 if (extract) {
258 FileOutputStream fos
259 = new FileOutputStream(computeRealPath("classes"));
260 contentStream
261 = new JarOutputStream(new BufferedOutputStream(fos));
262 } else {
263 contentStream = new JarOutputStream(new NullOutputStream());
264 }
265 }
266 return contentStream;
267 }
268
269 public void close() throws IOException {
270 try {
271 try {
272 if (contentStream != null) {
273 contentStream.close();
274 contentStream = null;
275 }
276 } finally {
277 if (fileIn != null) {
278 fileIn.close();
279 fileIn = null;
280 }
281 }
282 } finally {
283 if (oomFilesWriter != null) {
284 oomFilesWriter.close();
285 oomFilesWriter = null;
286 }
287 }
288 }
289
290
291 public void readModule() throws IOException {
292 extract = false;
293 readStart();
294 readRest();
295 }
296
297 public void readModule(File dst) throws IOException {
298 readStart();
299 readRest(dst, false);
300 }
301
302 private void readSignatureSection(DataInputStream stream,
303 DigestInputStream dis)
304 throws IOException
305 {
306
307 // Turn off digest computation before reading Signature Section
308 dis.on(false);
309
310 // Mark the starting position
382 }
383 break;
384 case GZIP:
385 readGZIPCompressedFile(in, type);
386 break;
387 case PACK200_GZIP:
388 readClasses(
389 new DataInputStream(new CountingInputStream(in, csize)));
390 break;
391 default:
392 throw new IOException("Unsupported Compressor for files: " +
393 compressor);
394 }
395 }
396
397 public void readClasses(DataInputStream in) throws IOException {
398 unpack200gzip(in);
399 }
400
401 private File currentPath = null;
402 // true if currentPath points a path outside the module directory
403 private boolean oomPath; // false
404
405 private OutputStream openOutputStream(SectionType type,
406 String path)
407 throws IOException
408 {
409 if (!extract)
410 return new NullOutputStream();
411 currentPath = null;
412 assert type != SectionType.CLASSES;
413 if (type == SectionType.RESOURCES)
414 return Files.newOutputStream(contentStream(), path);
415 currentPath = computeRealPath(type, path);
416 File parent = currentPath.getParentFile();
417 if (!parent.exists())
418 Files.mkdirs(parent, currentPath.getName());
419 return new BufferedOutputStream(new FileOutputStream(currentPath));
420 }
421
422 private static class NullOutputStream extends OutputStream {
423 @Override
430
431 public void readGZIPCompressedFile(DataInputStream in,
432 SectionType type)
433 throws IOException
434 {
435 SubSectionFileHeader header = SubSectionFileHeader.read(in);
436 int csize = header.getCSize();
437
438 // Splice off the compressed file from input stream
439 ByteArrayOutputStream baos = new ByteArrayOutputStream();
440 copyStream(new CountingInputStream(in, csize), baos, csize);
441
442 byte[] compressedfile = baos.toByteArray();
443 ByteArrayInputStream bain
444 = new ByteArrayInputStream(compressedfile);
445 try (GZIPInputStream gin = new GZIPInputStream(bain);
446 OutputStream out = openOutputStream(type, header.getPath())) {
447 copyStream(gin, out);
448 }
449
450 if (extract) {
451 markNativeCodeExecutable(type, currentPath);
452 if (oomPath)
453 trackOutOfModuleContent(currentPath);
454 }
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 markNativeCodeExecutable(type, currentPath);
474 if (oomPath)
475 trackOutOfModuleContent(currentPath);
476 }
477 }
478
479 public byte[] readModuleInfo(DataInputStream in, int csize)
480 throws IOException
481 {
482 CountingInputStream cin = new CountingInputStream(in, csize);
483 ByteArrayOutputStream out = new ByteArrayOutputStream();
484 byte[] buf = new byte[8192];
485 int n;
486 while ((n = cin.read(buf)) >= 0)
487 out.write(buf, 0, n);
488 return out.toByteArray();
489 }
490
491 public byte[] readModuleSignature(DataInputStream in, int csize)
492 throws IOException
493 {
494 return readModuleInfo(in, csize); // signature has the same format
495 }
496
497 // Returns the path for the section type, if the library
498 // has one configured.
499 private File librarySectionPath(SectionType type) {
500 if (lib != null && type == SectionType.NATIVE_LIBS
501 && lib.natlibs() != null)
502 return lib.natlibs();
503 if (lib != null && type == SectionType.NATIVE_CMDS
504 && lib.natcmds() != null)
505 return lib.natcmds();
506
507 return null;
508 }
509
510 // Track files installed outside the module library. For later removal.
511 private PrintWriter oomFilesWriter;
512
513 private void trackOutOfModuleContent(File file)
514 throws IOException
515 {
516 if (file == null)
517 return;
518
519 // Lazy construction, not all modules will need this.
520 if (oomFilesWriter == null) {
521 oomFilesWriter = new PrintWriter(computeRealPath("oomfiles"));
522 }
523 oomFilesWriter.println(file);
524 oomFilesWriter.flush();
525 }
526
527 void remove() throws IOException {
528 ModuleFile.Reader.remove(destination);
529 }
530
531 // Removes a module, given its module install directory
532 static void remove(File moduleDir) throws IOException {
533 // Firstly remove any files installed outside of the module dir
534 File oomfiles = new File(moduleDir, "oomfiles");
535 if (oomfiles.exists()) {
536 try (FileInputStream fis = new FileInputStream(oomfiles);
537 BufferedReader in = new BufferedReader(new InputStreamReader(fis))) {
538 String filename;
539 while ((filename = in.readLine()) != null)
540 Files.delete(new File(filename));
541 }
542 }
543
544 Files.deleteTree(moduleDir);
545 }
546
547 private File computeRealPath(String storedpath) throws IOException {
548
549 String convertedpath = storedpath.replace('/', File.separatorChar);
550 File path = new File(convertedpath);
551
552 // Absolute path names are not permitted.
553 ensureNonAbsolute(path);
554 path = resolveAndNormalize(destination, convertedpath);
555 // Create the parent directories if necessary
556 File parent = path.getParentFile();
557 if (!parent.exists())
558 Files.mkdirs(parent, path.getName());
559
560 return path;
561 }
562
563 private File computeRealPath(SectionType type,
564 String storedpath)
565 throws IOException
566 {
567 File lsp = librarySectionPath(type);
568 if (lsp != null) {
569 // The library has a configured path for this section
570 File realpath = new File(lsp, storedpath);
571
572 if (realpath.exists()) {
573 // conflict, for now just fail
574 throw new IOException("File " + realpath + " already exists");
575 }
576
577 // Create the parent directories if necessary
578 File parent = realpath.getParentFile();
579 if (!parent.exists())
580 Files.mkdirs(parent, realpath.getName());
581
582 oomPath = true;
583 return realpath;
584 }
585
586 // reset since the path must be within the module directory
587 oomPath = false;
588 String dir = getSubdirOfSection(type);
589 return computeRealPath(dir + File.separatorChar + storedpath);
590 }
591
592 private static void markNativeCodeExecutable(SectionType type,
593 File file)
594 {
595 if (type == SectionType.NATIVE_CMDS
596 || (type == SectionType.NATIVE_LIBS
597 && System.getProperty("os.name").startsWith("Windows")))
598 {
599 file.setExecutable(true);
600 }
601 }
602
603 private void unpack200gzip(DataInputStream in) throws IOException {
604 GZIPInputStream gis = new GZIPInputStream(in) {
605 public void close() throws IOException {}
606 };
607 Pack200.Unpacker unpacker = Pack200.newUnpacker();
|