22
22
23
23
import java .io .CharArrayWriter ;
24
24
import java .io .File ;
25
+ import java .io .FileInputStream ;
25
26
import java .io .FileOutputStream ;
26
27
import java .io .IOException ;
27
28
import java .io .InputStream ;
28
29
import java .io .PrintWriter ;
29
30
import java .nio .channels .Channels ;
30
31
import java .nio .channels .ReadableByteChannel ;
32
+ import java .security .DigestInputStream ;
33
+ import java .security .MessageDigest ;
34
+ import java .security .NoSuchAlgorithmException ;
31
35
import java .util .ArrayList ;
32
36
import java .util .Arrays ;
33
37
import java .util .Collection ;
@@ -297,19 +301,27 @@ static void load() {
297
301
try {
298
302
loadFromJar (jffiExtractDir );
299
303
return ;
304
+ } catch (SecurityException se ) {
305
+ throw se ;
300
306
} catch (Throwable t1 ) {
301
- throw new UnsatisfiedLinkError ("could not load jffi library from " + jffiExtractDir );
307
+ UnsatisfiedLinkError ule = new UnsatisfiedLinkError ("could not load jffi library from " + jffiExtractDir );
308
+ ule .initCause (t1 );
309
+ throw ule ;
302
310
}
303
311
}
304
312
305
313
// try default tmp location with failover to current directory
306
314
try {
307
315
loadFromJar (null );
308
316
return ;
317
+ } catch (SecurityException se ) {
318
+ throw se ;
309
319
} catch (Throwable t ) {
310
- try {
320
+ try {
311
321
loadFromJar (new File (System .getProperty ("user.dir" )));
312
- }catch (Throwable t1 ){
322
+ } catch (SecurityException se ) {
323
+ throw se ;
324
+ } catch (Throwable t1 ){
313
325
errors .add (t1 );
314
326
}
315
327
}
@@ -395,7 +407,7 @@ private static boolean loadFromBootPath(String libName, String bootPath, Collect
395
407
return false ;
396
408
}
397
409
398
- private static String dlExtension () {
410
+ static String dlExtension () {
399
411
switch (getOS ()) {
400
412
case WINDOWS :
401
413
return "dll" ;
@@ -407,35 +419,23 @@ private static String dlExtension() {
407
419
}
408
420
409
421
private static void loadFromJar (File tmpDirFile ) throws IOException , LinkageError {
410
- InputStream is = getStubLibraryStream ();
411
422
File dstFile ;
412
423
413
- // Install the stub library to a temporary location
424
+ // Install the stub library
414
425
String jffiExtractName = StubLoader .jffiExtractName ;
415
- try {
426
+
427
+ try (InputStream sourceIS = getStubLibraryStream ()) {
416
428
dstFile = calculateExtractPath (tmpDirFile , jffiExtractName );
417
429
418
- if (dstFile .exists ()) {
419
- // attempt to load existing file
420
- // TODO: verify file matches bundled library
430
+ if (jffiExtractName != null && dstFile .exists ()) {
431
+ // unpacking to a specific name and that file exists, verify it
432
+ verifyExistingLibrary ( dstFile , sourceIS );
421
433
} else {
422
- // Write the library to the tempfile
423
- FileOutputStream os = new FileOutputStream (dstFile );
424
- try {
425
- ReadableByteChannel srcChannel = Channels .newChannel (is );
426
-
427
- for (long pos = 0 ; is .available () > 0 ; ) {
428
- pos += os .getChannel ().transferFrom (srcChannel , pos , Math .max (4096 , is .available ()));
429
- }
430
- } finally {
431
- os .close ();
432
- }
434
+ unpackLibrary (dstFile , sourceIS );
433
435
}
434
436
} catch (IOException ioe ) {
435
437
// If we get here it means we are unable to write the stub library to the system default temp location.
436
438
throw tempReadonlyError (ioe );
437
- } finally {
438
- is .close ();
439
439
}
440
440
441
441
try {
@@ -447,25 +447,87 @@ private static void loadFromJar(File tmpDirFile) throws IOException, LinkageErro
447
447
}
448
448
}
449
449
450
+ private static void unpackLibrary (File dstFile , InputStream sourceIS ) throws IOException {
451
+ int sourceSize = sourceIS .available ();
452
+
453
+ // Write the library to the tempfile
454
+ try (FileOutputStream os = new FileOutputStream (dstFile )) {
455
+ ReadableByteChannel srcChannel = Channels .newChannel (sourceIS );
456
+
457
+ for (long pos = 0 ; sourceSize > 0 ; ) {
458
+ pos += os .getChannel ().transferFrom (srcChannel , pos , Math .max (4096 , sourceIS .available ()));
459
+ }
460
+ }
461
+ }
462
+
463
+ private static void verifyExistingLibrary (File dstFile , InputStream sourceIS ) throws IOException {
464
+ int sourceSize = sourceIS .available ();
465
+
466
+ try (FileInputStream targetIS = new FileInputStream (dstFile )) {
467
+ // perform minimal verification of the found file
468
+ int targetSize = targetIS .available ();
469
+ if (targetSize != sourceSize ) throw sizeMismatchError (dstFile , sourceSize , targetSize );
470
+
471
+ // compare sha-256 for existing file
472
+ MessageDigest sourceMD = MessageDigest .getInstance ("SHA-256" );
473
+ MessageDigest targetMD = MessageDigest .getInstance ("SHA-256" );
474
+ DigestInputStream sourceDIS = new DigestInputStream (sourceIS , sourceMD );
475
+ DigestInputStream targetDIS = new DigestInputStream (targetIS , targetMD );
476
+ byte [] buf = new byte [8192 ];
477
+ while (sourceIS .available () > 0 ) {
478
+ sourceDIS .read (buf );
479
+ targetDIS .read (buf );
480
+ }
481
+ byte [] sourceDigest = sourceMD .digest ();
482
+ byte [] targetDigest = targetMD .digest ();
483
+
484
+ if (!Arrays .equals (sourceDigest , targetDigest )) throw digestMismatchError (dstFile );
485
+ } catch (NoSuchAlgorithmException nsae ) {
486
+ throw new IOException (nsae );
487
+ }
488
+ }
489
+
490
+ private static SecurityException digestMismatchError (File dstFile ) {
491
+ return new SecurityException ("digest mismatch: " + dstFile + " does not match packaged library" );
492
+ }
493
+
450
494
static File calculateExtractPath (File tmpDirFile , String jffiExtractName ) throws IOException {
495
+ if (jffiExtractName == null ) return calculateExtractPath (tmpDirFile );
496
+
451
497
File dstFile ;
498
+
499
+ // allow empty name to mean "jffi-#.#"
500
+ if (jffiExtractName .isEmpty ()) {
501
+ jffiExtractName = "jffi-" + VERSION_MAJOR + "." + VERSION_MINOR ;
502
+ }
503
+
504
+ // add extension if necessary
505
+ if (!jffiExtractName .endsWith (dlExtension ())) {
506
+ jffiExtractName = jffiExtractName + "." + dlExtension ();
507
+ }
508
+
509
+ // use tmpdir if no dir was specified
452
510
if (null == tmpDirFile ) {
453
- if (null == jffiExtractName ) {
454
- // Create tempfile.
455
- dstFile = File .createTempFile ("jffi" , "." + dlExtension ());
456
- dstFile .deleteOnExit ();
457
- } else {
458
- dstFile = new File (System .getProperty ("java.io.tmpdir" ), jffiExtractName );
459
- }
511
+ dstFile = new File (TMPDIR , jffiExtractName );
460
512
} else {
461
- if (null == jffiExtractName ) {
462
- // Create tempfile.
463
- dstFile = File .createTempFile ("jffi" , "." + dlExtension (), tmpDirFile );
464
- dstFile .deleteOnExit ();
465
- } else {
466
- dstFile = new File (tmpDirFile , jffiExtractName );
467
- }
513
+ dstFile = new File (tmpDirFile , jffiExtractName );
514
+ }
515
+
516
+ return dstFile ;
517
+ }
518
+
519
+ static File calculateExtractPath (File tmpDirFile ) throws IOException {
520
+ File dstFile ;
521
+
522
+ // create tempfile
523
+ if (null == tmpDirFile ) {
524
+ dstFile = File .createTempFile ("jffi" , "." + dlExtension ());
525
+ } else {
526
+ dstFile = File .createTempFile ("jffi" , "." + dlExtension (), tmpDirFile );
468
527
}
528
+
529
+ dstFile .deleteOnExit ();
530
+
469
531
return dstFile ;
470
532
}
471
533
0 commit comments