Skip to content

Commit f9d2ea0

Browse files
committed
Improve "Windows NTFS junction fix"
See #155
1 parent 88e971e commit f9d2ea0

File tree

6 files changed

+67
-115
lines changed

6 files changed

+67
-115
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# 2.2.1
2+
* Windows NTFS junction fix
3+
* Automatically disable it on Java 26+ as [JDK-8364277](https://bugs.openjdk.org/browse/JDK-8364277) is fixed there
4+
* Backport changes from Java 25 to ensure compatibility and get performance improvements (JEP 486)
5+
* Updated dependencies
6+
17
# 2.2.0
28
* Added an explicit option for enabling the Windows NTFS junction fix: ``useWinNTFSJunctionFixIfApplicable`` #155
39
* Enabling it also requires adding ``--add-exports java.base/sun.nio.fs=ALL-UNNAMED`` or performance will be impacted by ~20x due to non-accessible file attributes cache

testcontainers-advanced-imagebuilder/src/main/java/software/xdev/testcontainers/imagebuilder/transfer/java/nio/file/winntfs/FileTreeIterator.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616
/*
17-
* Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved.
17+
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
1818
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
1919
*
2020
* This code is free software; you can redistribute it and/or modify it
@@ -74,7 +74,6 @@ public class FileTreeIterator implements Iterator<Event>, Closeable
7474
*
7575
* @throws IllegalArgumentException if {@code maxDepth} is negative
7676
* @throws IOException if an I/O errors occurs opening the starting file
77-
* @throws SecurityException if the security manager denies access to the starting file
7877
* @throws NullPointerException if {@code start} or {@code options} is {@code null} or the options array
7978
* contains a {@code null} element
8079
*/

testcontainers-advanced-imagebuilder/src/main/java/software/xdev/testcontainers/imagebuilder/transfer/java/nio/file/winntfs/FileTreeWalker.java

Lines changed: 42 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616
/*
17-
* Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved.
17+
* Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved.
1818
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
1919
*
2020
* This code is free software; you can redistribute it and/or modify it
@@ -162,21 +162,8 @@ enum EventType
162162
/**
163163
* Events returned by the {@link #walk} and {@link #next} methods.
164164
*/
165-
static class Event
165+
record Event(EventType type, Path file, BasicFileAttributes attributes, IOException ioeException)
166166
{
167-
private final EventType type;
168-
private final Path file;
169-
private final BasicFileAttributes attrs;
170-
private final IOException ioe;
171-
172-
private Event(final EventType type, final Path file, final BasicFileAttributes attrs, final IOException ioe)
173-
{
174-
this.type = type;
175-
this.file = file;
176-
this.attrs = attrs;
177-
this.ioe = ioe;
178-
}
179-
180167
Event(final EventType type, final Path file, final BasicFileAttributes attrs)
181168
{
182169
this(type, file, attrs, null);
@@ -186,26 +173,6 @@ private Event(final EventType type, final Path file, final BasicFileAttributes a
186173
{
187174
this(type, file, null, ioe);
188175
}
189-
190-
EventType type()
191-
{
192-
return this.type;
193-
}
194-
195-
Path file()
196-
{
197-
return this.file;
198-
}
199-
200-
BasicFileAttributes attributes()
201-
{
202-
return this.attrs;
203-
}
204-
205-
IOException ioeException()
206-
{
207-
return this.ioe;
208-
}
209176
}
210177

211178
/**
@@ -307,16 +274,13 @@ private static BasicFileAttributes extractFromBasicFileAttributesHolder(final Pa
307274
* The
308275
* {@code canUseCached} argument determines whether this method can use cached attributes.
309276
*/
310-
@SuppressWarnings("removal")
311277
private BasicFileAttributes getAttributes(final Path file, final boolean canUseCached)
312278
throws IOException
313279
{
314280
// if attributes are cached then use them if possible
315-
if(canUseCached
316-
&& this.isBasicFileAttributesHolder(file)
317-
&& System.getSecurityManager() == null)
281+
if(canUseCached && isBasicFileAttributesHolder(file))
318282
{
319-
final BasicFileAttributes cached = this.extractFromBasicFileAttributesHolder(file);
283+
final BasicFileAttributes cached = extractFromBasicFileAttributesHolder(file);
320284
if(cached != null && (!this.followLinks || !cached.isSymbolicLink()))
321285
{
322286
return cached;
@@ -337,7 +301,7 @@ private BasicFileAttributes getAttributes(final Path file, final boolean canUseC
337301
throw ioe;
338302
}
339303

340-
// attempt to get attrmptes without following links
304+
// attempt to get attributes without following links
341305
attrs = Files.readAttributes(
342306
file,
343307
BasicFileAttributes.class,
@@ -374,7 +338,7 @@ private boolean wouldLoop(final Path dir, final Object key)
374338
return true;
375339
}
376340
}
377-
catch(final IOException | SecurityException x)
341+
catch(final IOException e)
378342
{
379343
// ignore
380344
}
@@ -386,13 +350,9 @@ private boolean wouldLoop(final Path dir, final Object key)
386350
/**
387351
* Visits the given file, returning the {@code Event} corresponding to that visit.
388352
* <p>
389-
* The {@code ignoreSecurityException} parameter determines whether any SecurityException should be ignored or not.
390-
* If a SecurityException is thrown, and is ignored, then this method returns {@code null} to mean that there is no
391-
* event corresponding to a visit to the file.
392-
* <p>
393353
* The {@code canUseCached} parameter determines whether cached attributes for the file can be used or not.
394354
*/
395-
private Event visit(final Path entry, final boolean ignoreSecurityException, final boolean canUseCached)
355+
private Event visit(final Path entry, final boolean canUseCached)
396356
{
397357
// need the file attributes
398358
final BasicFileAttributes attrs;
@@ -404,14 +364,6 @@ private Event visit(final Path entry, final boolean ignoreSecurityException, fin
404364
{
405365
return new Event(EventType.ENTRY, entry, ioe);
406366
}
407-
catch(final SecurityException se)
408-
{
409-
if(ignoreSecurityException)
410-
{
411-
return null;
412-
}
413-
throw se;
414-
}
415367

416368
// at maximum depth or file is not a directory
417369
final int depth = this.stack.size();
@@ -439,14 +391,6 @@ private Event visit(final Path entry, final boolean ignoreSecurityException, fin
439391
{
440392
return new Event(EventType.ENTRY, entry, ioe);
441393
}
442-
catch(final SecurityException se)
443-
{
444-
if(ignoreSecurityException)
445-
{
446-
return null;
447-
}
448-
throw se;
449-
}
450394

451395
// push a directory node to the stack and return an event
452396
this.stack.push(new DirectoryNode(entry, attrs.fileKey(), stream));
@@ -463,12 +407,9 @@ Event walk(final Path file)
463407
throw new IllegalStateException("Closed");
464408
}
465409

466-
final Event ev = this.visit(
410+
return this.visit(
467411
file,
468-
false, // ignoreSecurityException
469412
false); // canUseCached
470-
assert ev != null;
471-
return ev;
472413
}
473414

474415
/**
@@ -483,61 +424,53 @@ Event next()
483424
}
484425

485426
// continue iteration of the directory at the top of the stack
486-
Event ev;
487-
do
427+
Path entry = null;
428+
IOException ioe = null;
429+
430+
// get next entry in the directory
431+
if(!top.skipped())
488432
{
489-
Path entry = null;
490-
IOException ioe = null;
491-
492-
// get next entry in the directory
493-
if(!top.skipped())
433+
final Iterator<Path> iterator = top.iterator();
434+
try
494435
{
495-
final Iterator<Path> iterator = top.iterator();
496-
try
436+
if(iterator.hasNext())
497437
{
498-
if(iterator.hasNext())
499-
{
500-
entry = iterator.next();
501-
}
502-
}
503-
catch(final DirectoryIteratorException x)
504-
{
505-
ioe = x.getCause();
438+
entry = iterator.next();
506439
}
507440
}
508-
509-
// no next entry so close and pop directory,
510-
// creating corresponding event
511-
if(entry == null)
441+
catch(final DirectoryIteratorException x)
512442
{
513-
try
443+
ioe = x.getCause();
444+
}
445+
}
446+
447+
// no next entry so close and pop directory,
448+
// creating corresponding event
449+
if(entry == null)
450+
{
451+
try
452+
{
453+
top.stream().close();
454+
}
455+
catch(final IOException e)
456+
{
457+
if(ioe == null)
514458
{
515-
top.stream().close();
459+
ioe = e;
516460
}
517-
catch(final IOException e)
461+
else
518462
{
519-
if(ioe == null)
520-
{
521-
ioe = e;
522-
}
523-
else
524-
{
525-
ioe.addSuppressed(e);
526-
}
463+
ioe.addSuppressed(e);
527464
}
528-
this.stack.pop();
529-
return new Event(EventType.END_DIRECTORY, top.directory(), ioe);
530465
}
531-
532-
// visit the entry
533-
ev = this.visit(
534-
entry,
535-
true, // ignoreSecurityException
536-
true); // canUseCached
466+
this.stack.pop();
467+
return new Event(EventType.END_DIRECTORY, top.directory(), ioe);
537468
}
538-
while(ev == null);
539469

540-
return ev;
470+
// visit the entry
471+
return this.visit(
472+
entry,
473+
true); // canUseCached
541474
}
542475

543476
/**

testcontainers-advanced-imagebuilder/src/main/java/software/xdev/testcontainers/imagebuilder/transfer/java/nio/file/winntfs/WinNTFSJunctionFiles.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,27 @@
5252
import java.util.stream.Stream;
5353
import java.util.stream.StreamSupport;
5454

55+
import org.slf4j.Logger;
56+
import org.slf4j.LoggerFactory;
57+
5558

5659
/**
5760
* @see software.xdev.testcontainers.imagebuilder.transfer.java.nio.file.winntfs
5861
*/
5962
public final class WinNTFSJunctionFiles
6063
{
64+
private static final Logger LOG = LoggerFactory.getLogger(WinNTFSJunctionFiles.class);
65+
66+
@SuppressWarnings("checkstyle:MagicNumber")
6167
public static boolean shouldBeApplied(final Path path)
6268
{
69+
// JDK-8364277 was fixed in Java 26
70+
if(Runtime.version().feature() >= 26)
71+
{
72+
LOG.info("WindowsNTFSJunctionFix is no longer required "
73+
+ "as this is a Java version where JDK-8364277 is fixed");
74+
return false;
75+
}
6376
try
6477
{
6578
final FileStore store = Files.getFileStore(path);

testcontainers-advanced-imagebuilder/src/main/java/software/xdev/testcontainers/imagebuilder/transfer/java/nio/file/winntfs/package-info.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,17 @@
1414
* limitations under the License.
1515
*/
1616
/**
17-
* This package was forked from the OpenJDK (as of 21.0.7) to resolve a crash/infinite loop when encountering
17+
* This package was forked from the OpenJDK (25) to resolve a crash/infinite loop when encountering
1818
* <a href="https://github.com/xdev-software/testcontainers-advanced-imagebuilder/issues/155">
1919
* recursive NTFS junctions on Windows
2020
* </a>.
2121
* <p>
2222
* <b>Please note:</b>
23-
* Enabling it also requires adding ``--add-exports java.base/sun.nio.fs=ALL-UNNAMED`` or performance will be
24-
* impacted by ~20x due non-accessible file attributes cache
23+
* Enabling/Using it also requires adding <code>--add-exports java.base/sun.nio.fs=ALL-UNNAMED</code> as arguments
24+
* or performance will be impacted by ~20x due non-accessible file attributes cache.
2525
* </p>
2626
*
27+
* @see <a href="https://bugs.openjdk.org/browse/JDK-8364277">JDK-8364277</a>
2728
* @see <a href="https://en.wikipedia.org/wiki/NTFS_links#Junction_points">NTFS junction</a>
2829
*/
2930
package software.xdev.testcontainers.imagebuilder.transfer.java.nio.file.winntfs;

testcontainers-advanced-imagebuilder/src/test/java/software/xdev/testcontainers/imagebuilder/transfer/java/nio/file/winntfs/WinNTFSJunctionFilesTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ void junctionNoCrash(@TempDir final Path tempDir) throws IOException
5252

5353
if(!WinNTFSJunctionFiles.shouldBeApplied(tempDir))
5454
{
55-
Assumptions.abort("Test is not applicable: Not WIN + NTFS");
55+
Assumptions.abort("Test is not applicable");
5656
}
5757

5858
final Path testFile = sub.resolve("test.txt");

0 commit comments

Comments
 (0)