Skip to content

Commit b5b7cb0

Browse files
committed
Try to port MCLEAN-93: Windows NTFS junctions support.
1 parent 26a0ca6 commit b5b7cb0

File tree

1 file changed

+39
-9
lines changed

1 file changed

+39
-9
lines changed

src/main/java/org/apache/maven/plugins/clean/Cleaner.java

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@
4949
* @author Martin Desruisseaux
5050
*/
5151
final class Cleaner implements FileVisitor<Path> {
52-
52+
/**
53+
* Whether the host operating system is from the Windows family.
54+
*/
5355
private static final boolean ON_WINDOWS = (File.separatorChar == '\\');
5456

5557
private static final SessionData.Key<Path> LAST_DIRECTORY_TO_DELETE =
@@ -193,7 +195,7 @@ public void delete(Path basedir) throws IOException {
193195
var options = EnumSet.noneOf(FileVisitOption.class);
194196
if (followSymlinks) {
195197
options.add(FileVisitOption.FOLLOW_LINKS);
196-
basedir = getCanonicalPath(basedir);
198+
basedir = getCanonicalPath(basedir, null);
197199
}
198200
if (selector == null && !followSymlinks && fastDir != null && session != null) {
199201
// If anything wrong happens, we'll just use the usual deletion mechanism
@@ -275,12 +277,21 @@ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) th
275277
*/
276278
@Override
277279
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
278-
if ((selector == null || selector.matches(file)) && tryDelete(file)) {
279-
if (listDeletedFiles) {
280-
logDelete(file, attrs);
281-
}
280+
if (ON_WINDOWS && followSymlinks && attrs.isDirectory() && attrs.isOther()) {
281+
/*
282+
* MCLEAN-93: NTFS junctions have isDirectory() and isOther() attributes set.
283+
* Handle as a directory walked by a new `FileVisitor`.
284+
*/
285+
file = getCanonicalPath(file, null);
286+
Files.walkFileTree(file, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, this);
282287
} else {
283-
nonEmptyDirectoryLevels.set(currentDepth); // Remember that the directory will not be empty.
288+
if ((selector == null || selector.matches(file)) && tryDelete(file)) {
289+
if (listDeletedFiles) {
290+
logDelete(file, attrs);
291+
}
292+
} else {
293+
nonEmptyDirectoryLevels.set(currentDepth); // Remember that the directory will not be empty.
294+
}
284295
}
285296
return FileVisitResult.CONTINUE;
286297
}
@@ -333,11 +344,30 @@ public FileVisitResult postVisitDirectory(Path dir, IOException failure) throws
333344
return FileVisitResult.CONTINUE;
334345
}
335346

336-
private static Path getCanonicalPath(Path path) {
347+
/**
348+
* Returns the real path of the given file. If the real path cannot be obtained,
349+
* this method tries to get the real path of the parent and to append the rest of
350+
* the filename.
351+
*
352+
* @param path the path to get as a canonical path
353+
* @param mainError should be {@code null} (reserved to recursive calls of this method)
354+
* @return the real path of the given path
355+
* @throws IOException if the canonical path cannot be obtained
356+
*/
357+
private static Path getCanonicalPath(final Path path, IOException mainError) throws IOException {
337358
try {
338359
return path.toRealPath();
339360
} catch (IOException e) {
340-
return getCanonicalPath(path.getParent()).resolve(path.getFileName());
361+
if (mainError == null) {
362+
mainError = e;
363+
} else {
364+
mainError.addSuppressed(e);
365+
}
366+
final Path parent = path.getParent();
367+
if (parent != null) {
368+
return getCanonicalPath(parent, mainError).resolve(path.getFileName());
369+
}
370+
throw e;
341371
}
342372
}
343373

0 commit comments

Comments
 (0)