You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2021/11/21 22:32:09 UTC

[commons-io] branch master updated: [IO-751] When deleting symlinks, File/PathUtils.deleteDirectory() changes file permissions of the target.

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-io.git


The following commit(s) were added to refs/heads/master by this push:
     new 62925e3  [IO-751] When deleting symlinks, File/PathUtils.deleteDirectory() changes file permissions of the target.
62925e3 is described below

commit 62925e3084c9e72ae4659c00364a490252368326
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sun Nov 21 17:32:06 2021 -0500

    [IO-751] When deleting symlinks, File/PathUtils.deleteDirectory()
    changes file permissions of the target.
    
    
    Fix file permission logic when deleting in override read-only mode.
---
 src/changes/changes.xml                            |   3 +
 .../java/org/apache/commons/io/file/PathUtils.java | 364 ++++++++++++++-------
 .../commons/io/file/StandardDeleteOption.java      |   2 +-
 .../org/apache/commons/io/function/IOStreams.java  |   2 +-
 .../org/apache/commons/io/DeleteDirectoryTest.java |  60 ++--
 .../apache/commons/io/FileCleaningTrackerTest.java |  23 +-
 .../commons/io/FileUtilsCleanDirectoryTest.java    |  58 ++--
 .../java/org/apache/commons/io/FileUtilsTest.java  | 324 +++++++++---------
 .../commons/io/file/AbstractTempDirTest.java       |  52 +++
 .../io/file/PathUtilsDeleteDirectoryTest.java      |  48 +--
 .../commons/io/file/PathUtilsDeleteTest.java       |  56 ++--
 .../org/apache/commons/io/file/PathUtilsTest.java  |  79 +++--
 12 files changed, 600 insertions(+), 471 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index ac75ea6..4330512 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -126,6 +126,9 @@ The <action> type attribute can be add,update,fix,remove.
       <action dev="ggregory" type="fix" due-to="Gary Gregory">
         Initialize the message of an IOExceptionList to a default if null.
       </action>
+      <action issue="IO-751" dev="ggregory" type="fix" due-to="Gary Gregory, Richard Cyganiak">
+        When deleting symlinks, File/PathUtils.deleteDirectory() changes file permissions of the target.
+      </action>
       <!-- ADD -->
       <action dev="ggregory" type="add" due-to="Gary Gregory">
         Add BrokenReader.INSTANCE.
diff --git a/src/main/java/org/apache/commons/io/file/PathUtils.java b/src/main/java/org/apache/commons/io/file/PathUtils.java
index 0e04e9d..014b777 100644
--- a/src/main/java/org/apache/commons/io/file/PathUtils.java
+++ b/src/main/java/org/apache/commons/io/file/PathUtils.java
@@ -26,6 +26,7 @@ import java.math.BigInteger;
 import java.net.URI;
 import java.net.URL;
 import java.nio.charset.Charset;
+import java.nio.file.AccessDeniedException;
 import java.nio.file.CopyOption;
 import java.nio.file.DirectoryStream;
 import java.nio.file.FileVisitOption;
@@ -72,6 +73,7 @@ import org.apache.commons.io.IOUtils;
 import org.apache.commons.io.UncheckedIOExceptions;
 import org.apache.commons.io.file.Counters.PathCounters;
 import org.apache.commons.io.filefilter.IOFileFilter;
+import org.apache.commons.io.function.IOFunction;
 
 /**
  * NIO Path utilities.
@@ -81,8 +83,7 @@ import org.apache.commons.io.filefilter.IOFileFilter;
 public final class PathUtils {
 
     /**
-     * Private worker/holder that computes and tracks relative path names and their equality. We reuse the sorted relative
-     * lists when comparing directories.
+     * Private worker/holder that computes and tracks relative path names and their equality. We reuse the sorted relative lists when comparing directories.
      */
     private static class RelativeSortedPaths {
 
@@ -103,7 +104,7 @@ public final class PathUtils {
          * @throws IOException if an I/O error is thrown by a visitor method.
          */
         private RelativeSortedPaths(final Path dir1, final Path dir2, final int maxDepth, final LinkOption[] linkOptions,
-            final FileVisitOption[] fileVisitOptions) throws IOException {
+                final FileVisitOption[] fileVisitOptions) throws IOException {
             final List<Path> tmpRelativeDirList1;
             final List<Path> tmpRelativeDirList2;
             List<Path> tmpRelativeFileList1 = null;
@@ -142,9 +143,9 @@ public final class PathUtils {
         }
     }
 
-    private static final OpenOption[] OPEN_OPTIONS_TRUNCATE = {StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING};
+    private static final OpenOption[] OPEN_OPTIONS_TRUNCATE = { StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING };
 
-    private static final OpenOption[] OPEN_OPTIONS_APPEND = {StandardOpenOption.CREATE, StandardOpenOption.APPEND};
+    private static final OpenOption[] OPEN_OPTIONS_APPEND = { StandardOpenOption.CREATE, StandardOpenOption.APPEND };
 
     /**
      * Empty {@link CopyOption} array.
@@ -154,7 +155,7 @@ public final class PathUtils {
     public static final CopyOption[] EMPTY_COPY_OPTIONS = {};
 
     /**
-     * Empty {@link LinkOption} array.
+     * Empty {@link DeleteOption} array.
      *
      * @since 2.8.0
      */
@@ -175,7 +176,7 @@ public final class PathUtils {
      *
      * @since 2.9.0
      */
-    public static final LinkOption[] NOFOLLOW_LINK_OPTION_ARRAY = {LinkOption.NOFOLLOW_LINKS};
+    public static final LinkOption[] NOFOLLOW_LINK_OPTION_ARRAY = { LinkOption.NOFOLLOW_LINKS };
 
     /**
      * Empty {@link OpenOption} array.
@@ -252,7 +253,7 @@ public final class PathUtils {
     public static PathCounters copyDirectory(final Path sourceDirectory, final Path targetDirectory, final CopyOption... copyOptions) throws IOException {
         final Path absoluteSource = sourceDirectory.toAbsolutePath();
         return visitFileTree(new CopyDirectoryVisitor(Counters.longPathCounters(), absoluteSource, targetDirectory, copyOptions), absoluteSource)
-            .getPathCounters();
+                .getPathCounters();
     }
 
     /**
@@ -436,8 +437,10 @@ public final class PathUtils {
      * @since 2.8.0
      */
     public static PathCounters deleteDirectory(final Path directory, final DeleteOption... deleteOptions) throws IOException {
-        return visitFileTree(new DeletingPathVisitor(Counters.longPathCounters(), PathUtils.NOFOLLOW_LINK_OPTION_ARRAY, deleteOptions), directory)
-            .getPathCounters();
+        final LinkOption[] linkOptions = PathUtils.NOFOLLOW_LINK_OPTION_ARRAY;
+        // POSIX ops will noop on non-POSIX.
+        return withPosixFileAttributes(directory.getParent(), linkOptions, overrideReadOnly(deleteOptions),
+                pfa -> visitFileTree(new DeletingPathVisitor(Counters.longPathCounters(), linkOptions, deleteOptions), directory).getPathCounters());
     }
 
     /**
@@ -493,26 +496,62 @@ public final class PathUtils {
      * @since 2.9.0
      */
     public static PathCounters deleteFile(final Path file, final LinkOption[] linkOptions, final DeleteOption... deleteOptions)
-        throws NoSuchFileException, IOException {
+            throws NoSuchFileException, IOException {
+        //
+        // TODO Needs clean up
+        //
         if (Files.isDirectory(file, linkOptions)) {
             throw new NoSuchFileException(file.toString());
         }
         final PathCounters pathCounts = Counters.longPathCounters();
-        final boolean exists = exists(file, linkOptions);
-        final long size = exists && !Files.isSymbolicLink(file) ? Files.size(file) : 0;
-        if (overrideReadOnly(deleteOptions) && exists) {
-            setReadOnly(file, false, linkOptions);
+        boolean exists = exists(file, linkOptions);
+        long size = exists && !Files.isSymbolicLink(file) ? Files.size(file) : 0;
+        try {
+            if (Files.deleteIfExists(file)) {
+                pathCounts.getFileCounter().increment();
+                pathCounts.getByteCounter().add(size);
+                return pathCounts;
+            }
+        } catch (AccessDeniedException e) {
+            // Ignore and try again below.
         }
-        if (Files.deleteIfExists(file)) {
-            pathCounts.getFileCounter().increment();
-            pathCounts.getByteCounter().add(size);
+        final Path parent = file.getParent();
+        PosixFileAttributes posixFileAttributes = null;
+        try {
+            if (overrideReadOnly(deleteOptions)) {
+                posixFileAttributes = readPosixFileAttributes(parent, linkOptions);
+                setReadOnly(file, false, linkOptions);
+            }
+            // Read size _after_ having read/execute access on POSIX.
+            exists = exists(file, linkOptions);
+            size = exists && !Files.isSymbolicLink(file) ? Files.size(file) : 0;
+            if (Files.deleteIfExists(file)) {
+                pathCounts.getFileCounter().increment();
+                pathCounts.getByteCounter().add(size);
+            }
+        } finally {
+            if (posixFileAttributes != null) {
+                Files.setPosixFilePermissions(parent, posixFileAttributes.permissions());
+            }
         }
         return pathCounts;
     }
 
+    private static <R> R withPosixFileAttributes(final Path path, final LinkOption[] linkOptions, final boolean overrideReadOnly,
+            IOFunction<PosixFileAttributes, R> function) throws IOException {
+        final PosixFileAttributes posixFileAttributes = overrideReadOnly ? readPosixFileAttributes(path, linkOptions) : null;
+        try {
+            return function.apply(posixFileAttributes);
+        } finally {
+            if (posixFileAttributes != null && path != null && Files.exists(path, linkOptions)) {
+                Files.setPosixFilePermissions(path, posixFileAttributes.permissions());
+            }
+        }
+    }
+
     /**
-     * Compares the file sets of two Paths to determine if they are equal or not while considering file contents. The
-     * comparison includes all files in all sub-directories.
+     * Compares the file sets of two Paths to determine if they are equal or not while considering file contents. The comparison includes all files in all
+     * sub-directories.
      *
      * @param path1 The first directory.
      * @param path2 The second directory.
@@ -524,8 +563,8 @@ public final class PathUtils {
     }
 
     /**
-     * Compares the file sets of two Paths to determine if they are equal or not while considering file contents. The
-     * comparison includes all files in all sub-directories.
+     * Compares the file sets of two Paths to determine if they are equal or not while considering file contents. The comparison includes all files in all
+     * sub-directories.
      *
      * @param path1 The first directory.
      * @param path2 The second directory.
@@ -536,7 +575,7 @@ public final class PathUtils {
      * @throws IOException if an I/O error is thrown by a visitor method.
      */
     public static boolean directoryAndFileContentEquals(final Path path1, final Path path2, final LinkOption[] linkOptions, final OpenOption[] openOptions,
-        final FileVisitOption[] fileVisitOption) throws IOException {
+            final FileVisitOption[] fileVisitOption) throws IOException {
         // First walk both file trees and gather normalized paths.
         if (path1 == null && path2 == null) {
             return true;
@@ -568,8 +607,8 @@ public final class PathUtils {
     }
 
     /**
-     * Compares the file sets of two Paths to determine if they are equal or not without considering file contents. The
-     * comparison includes all files in all sub-directories.
+     * Compares the file sets of two Paths to determine if they are equal or not without considering file contents. The comparison includes all files in all
+     * sub-directories.
      *
      * @param path1 The first directory.
      * @param path2 The second directory.
@@ -581,8 +620,8 @@ public final class PathUtils {
     }
 
     /**
-     * Compares the file sets of two Paths to determine if they are equal or not without considering file contents. The
-     * comparison includes all files in all sub-directories.
+     * Compares the file sets of two Paths to determine if they are equal or not without considering file contents. The comparison includes all files in all
+     * sub-directories.
      *
      * @param path1 The first directory.
      * @param path2 The second directory.
@@ -593,7 +632,7 @@ public final class PathUtils {
      * @throws IOException if an I/O error is thrown by a visitor method.
      */
     public static boolean directoryContentEquals(final Path path1, final Path path2, final int maxDepth, final LinkOption[] linkOptions,
-        final FileVisitOption[] fileVisitOptions) throws IOException {
+            final FileVisitOption[] fileVisitOptions) throws IOException {
         return new RelativeSortedPaths(path1, path2, maxDepth, linkOptions, fileVisitOptions).equals;
     }
 
@@ -634,7 +673,7 @@ public final class PathUtils {
      * @see org.apache.commons.io.FileUtils#contentEquals(java.io.File, java.io.File)
      */
     public static boolean fileContentEquals(final Path path1, final Path path2, final LinkOption[] linkOptions, final OpenOption[] openOptions)
-        throws IOException {
+            throws IOException {
         if (path1 == null && path2 == null) {
             return true;
         }
@@ -669,15 +708,15 @@ public final class PathUtils {
             return true;
         }
         try (final InputStream inputStream1 = Files.newInputStream(nPath1, openOptions);
-            final InputStream inputStream2 = Files.newInputStream(nPath2, openOptions)) {
+                final InputStream inputStream2 = Files.newInputStream(nPath2, openOptions)) {
             return IOUtils.contentEquals(inputStream1, inputStream2);
         }
     }
 
     /**
      * <p>
-     * Applies an {@link IOFileFilter} to the provided {@link File} objects. The resulting array is a subset of the original
-     * file list that matches the provided filter.
+     * Applies an {@link IOFileFilter} to the provided {@link File} objects. The resulting array is a subset of the original file list that matches the provided
+     * filter.
      * </p>
      *
      * <p>
@@ -790,10 +829,10 @@ public final class PathUtils {
      *
      * @param path the path to the file.
      * @param options options indicating how to handle symbolic links
-     * @return {@code true} if the file is a directory; {@code false} if the path is null, the file does not exist, is not a
-     *         directory, or it cannot be determined if the file is a directory or not.
-     * @throws SecurityException In the case of the default provider, and a security manager is installed, the
-     *         {@link SecurityManager#checkRead(String) checkRead} method is invoked to check read access to the directory.
+     * @return {@code true} if the file is a directory; {@code false} if the path is null, the file does not exist, is not a directory, or it cannot be
+     *         determined if the file is a directory or not.
+     * @throws SecurityException In the case of the default provider, and a security manager is installed, the {@link SecurityManager#checkRead(String)
+     *         checkRead} method is invoked to check read access to the directory.
      * @since 2.9.0
      */
     public static boolean isDirectory(final Path path, final LinkOption... options) {
@@ -816,11 +855,10 @@ public final class PathUtils {
      *
      * @param directory the directory to query.
      * @return whether the directory is empty.
-     * @throws NotDirectoryException if the file could not otherwise be opened because it is not a directory <i>(optional
-     *         specific exception)</i>.
+     * @throws NotDirectoryException if the file could not otherwise be opened because it is not a directory <i>(optional specific exception)</i>.
      * @throws IOException if an I/O error occurs.
-     * @throws SecurityException In the case of the default provider, and a security manager is installed, the
-     *         {@link SecurityManager#checkRead(String) checkRead} method is invoked to check read access to the directory.
+     * @throws SecurityException In the case of the default provider, and a security manager is installed, the {@link SecurityManager#checkRead(String)
+     *         checkRead} method is invoked to check read access to the directory.
      */
     public static boolean isEmptyDirectory(final Path directory) throws IOException {
         try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(directory)) {
@@ -834,8 +872,8 @@ public final class PathUtils {
      * @param file the file to query.
      * @return whether the file is empty.
      * @throws IOException if an I/O error occurs.
-     * @throws SecurityException In the case of the default provider, and a security manager is installed, its
-     *         {@link SecurityManager#checkRead(String) checkRead} method denies read access to the file.
+     * @throws SecurityException In the case of the default provider, and a security manager is installed, its {@link SecurityManager#checkRead(String)
+     *         checkRead} method denies read access to the file.
      */
     public static boolean isEmptyFile(final Path file) throws IOException {
         return Files.size(file) <= 0;
@@ -908,10 +946,9 @@ public final class PathUtils {
     /**
      * Tests if the given {@code Path} is newer than the reference {@code Path}.
      *
-     * @param file      the {@code File} to test.
+     * @param file the {@code File} to test.
      * @param reference the {@code File} of which the modification date is used.
-     * @return true if the {@code File} exists and has been modified more
-     * recently than the reference {@code File}.
+     * @return true if the {@code File} exists and has been modified more recently than the reference {@code File}.
      * @throws IOException if an I/O error occurs.
      * @since 2.12.0
      */
@@ -952,7 +989,6 @@ public final class PathUtils {
         return isOlder(file, FileTime.from(instant), options);
     }
 
-
     /**
      * Tests if the given {@code Path} is older than the given time reference.
      *
@@ -999,10 +1035,10 @@ public final class PathUtils {
      *
      * @param path the path to the file.
      * @param options options indicating how to handle symbolic links.
-     * @return {@code true} if the file is a regular file; {@code false} if the path is null, the file does not exist, is
-     *         not a directory, or it cannot be determined if the file is a regular file or not.
-     * @throws SecurityException In the case of the default provider, and a security manager is installed, the
-     *         {@link SecurityManager#checkRead(String) checkRead} method is invoked to check read access to the directory.
+     * @return {@code true} if the file is a regular file; {@code false} if the path is null, the file does not exist, is not a directory, or it cannot be
+     *         determined if the file is a regular file or not.
+     * @throws SecurityException In the case of the default provider, and a security manager is installed, the {@link SecurityManager#checkRead(String)
+     *         checkRead} method is invoked to check read access to the directory.
      * @since 2.9.0
      */
     public static boolean isRegularFile(final Path path, final LinkOption... options) {
@@ -1022,8 +1058,7 @@ public final class PathUtils {
     }
 
     /**
-     * Creates a new OutputStream by opening or creating a file, returning an output stream that may be used to write bytes
-     * to the file.
+     * Creates a new OutputStream by opening or creating a file, returning an output stream that may be used to write bytes to the file.
      *
      * @param path the Path.
      * @param append Whether or not to append.
@@ -1061,45 +1096,44 @@ public final class PathUtils {
     }
 
     /**
-     * Reads the BasicFileAttributes from the given path.
-     *
-     * @param path the path to read.
-     * @return the path attributes.
-     * @throws IOException if an I/O error occurs.
-     * @since 2.9.0
-     * @deprecated Will be removed in 3.0.0 in favor of {@link #readBasicFileAttributes(Path, LinkOption...)}.
-     */
-    @Deprecated
-    public static BasicFileAttributes readBasicFileAttributes(final Path path) throws IOException {
-        return Files.readAttributes(path, BasicFileAttributes.class);
-    }
-
-    /**
-     * Reads the BasicFileAttributes from the given path. Returns null instead of throwing
-     * {@link UnsupportedOperationException}. Throws {@link UncheckedIOExceptions} instead of {@link IOException}.
+     * Reads the BasicFileAttributes from the given path. Returns null instead of throwing {@link UnsupportedOperationException}. Throws
+     * {@link UncheckedIOExceptions} instead of {@link IOException}.
      *
      * @param <A> The {@code BasicFileAttributes} type
-     * @param test The Path to test.
+     * @param path The Path to test.
      * @param type the {@code Class} of the file attributes required to read.
      * @param options options indicating how to handle symbolic links.
      * @return the file attributes.
      * @see Files#readAttributes(Path, Class, LinkOption...)
      * @since 2.12.0
      */
-    public static <A extends BasicFileAttributes> A readAttributes(final Path test, final Class<A> type, final LinkOption... options) {
+    public static <A extends BasicFileAttributes> A readAttributes(final Path path, final Class<A> type, final LinkOption... options) {
         try {
-            return Files.readAttributes(test, type, options);
+            return path == null ? null : Files.readAttributes(path, type, options);
         } catch (final UnsupportedOperationException e) {
             // For example, on Windows.
             return null;
         } catch (final IOException e) {
-            throw UncheckedIOExceptions.create(test, e);
+            throw UncheckedIOExceptions.create(path, e);
         }
     }
 
     /**
-     * Reads the BasicFileAttributes from the given path. Returns null instead of throwing
-     * {@link UnsupportedOperationException}.
+     * Reads the BasicFileAttributes from the given path.
+     *
+     * @param path the path to read.
+     * @return the path attributes.
+     * @throws IOException if an I/O error occurs.
+     * @since 2.9.0
+     * @deprecated Will be removed in 3.0.0 in favor of {@link #readBasicFileAttributes(Path, LinkOption...)}.
+     */
+    @Deprecated
+    public static BasicFileAttributes readBasicFileAttributes(final Path path) throws IOException {
+        return Files.readAttributes(path, BasicFileAttributes.class);
+    }
+
+    /**
+     * Reads the BasicFileAttributes from the given path. Returns null instead of throwing {@link UnsupportedOperationException}.
      *
      * @param path the path to read.
      * @param options options indicating how to handle symbolic links.
@@ -1111,8 +1145,7 @@ public final class PathUtils {
     }
 
     /**
-     * Reads the BasicFileAttributes from the given path. Returns null instead of throwing
-     * {@link UnsupportedOperationException}.
+     * Reads the BasicFileAttributes from the given path. Returns null instead of throwing {@link UnsupportedOperationException}.
      *
      * @param path the path to read.
      * @return the path attributes.
@@ -1126,8 +1159,7 @@ public final class PathUtils {
     }
 
     /**
-     * Reads the DosFileAttributes from the given path. Returns null instead of throwing
-     * {@link UnsupportedOperationException}.
+     * Reads the DosFileAttributes from the given path. Returns null instead of throwing {@link UnsupportedOperationException}.
      *
      * @param path the path to read.
      * @param options options indicating how to handle symbolic links.
@@ -1139,16 +1171,28 @@ public final class PathUtils {
     }
 
     /**
-     * Reads the PosixFileAttributes from the given path. Returns null instead of throwing
-     * {@link UnsupportedOperationException}.
+     * Reads the PosixFileAttributes or DosFileAttributes from the given path. Returns null instead of throwing {@link UnsupportedOperationException}.
      *
-     * @param test The Path to test.
+     * @param path The Path to read.
+     * @param options options indicating how to handle symbolic links.
+     * @return the file attributes.
+     * @since 2.12.0
+     */
+    public static BasicFileAttributes readOsFileAttributes(final Path path, final LinkOption... options) {
+        final PosixFileAttributes fileAttributes = readPosixFileAttributes(path, options);
+        return fileAttributes != null ? fileAttributes : readDosFileAttributes(path, options);
+    }
+
+    /**
+     * Reads the PosixFileAttributes from the given path. Returns null instead of throwing {@link UnsupportedOperationException}.
+     *
+     * @param path The Path to read.
      * @param options options indicating how to handle symbolic links.
      * @return the file attributes.
      * @since 2.12.0
      */
-    public static PosixFileAttributes readPosixFileAttributes(final Path test, final LinkOption... options) {
-        return readAttributes(test, PosixFileAttributes.class, options);
+    public static PosixFileAttributes readPosixFileAttributes(final Path path, final LinkOption... options) {
+        return readAttributes(path, PosixFileAttributes.class, options);
     }
 
     /**
@@ -1183,8 +1227,7 @@ public final class PathUtils {
     }
 
     /**
-     * Throws an {@link IllegalArgumentException} if the file is not writable. This provides a more precise exception
-     * message than a plain access denied.
+     * Throws an {@link IllegalArgumentException} if the file is not writable. This provides a more precise exception message than a plain access denied.
      *
      * @param file The file to test.
      * @param name The parameter name to use in the exception message.
@@ -1262,55 +1305,132 @@ public final class PathUtils {
      * @since 2.8.0
      */
     public static Path setReadOnly(final Path path, final boolean readOnly, final LinkOption... linkOptions) throws IOException {
-        final List<Exception> causeList = new ArrayList<>(2);
-        final DosFileAttributeView fileAttributeView = getDosFileAttributeView(path, linkOptions);
-        if (fileAttributeView != null) {
-            // Windows 10
-            try {
-                fileAttributeView.setReadOnly(readOnly);
+        final List<Exception> causeList = new ArrayList<>(3);
+        try {
+            // Windows is simplest
+            if (setDosReadOnly(path, readOnly, linkOptions)) {
                 return path;
-            } catch (final IOException e) {
-                // Remember and retry with PosixFileAttributeView
-                causeList.add(e);
             }
+        } catch (final IOException e) {
+            // Remember and retry with POSIX
+            causeList.add(e);
         }
+        // POSIX
+        if (readOnly) {
+            // RO
+            // File, then parent dir (if any).
+            final boolean set = setPosixReadOnly(path, readOnly, linkOptions);
+            setPosixDeletePermissions(path.getParent(), false, linkOptions);
+            if (set) {
+                return path;
+            }
+        } else {
+            // RE
+            // Parent dir (if any), then file.
+            if (setPosixDeletePermissions(path.getParent(), true, linkOptions)) {
+                return path;
+            }
+        }
+        IOExceptionList.checkEmpty(causeList, path);
+        throw new IOException(String.format("No DosFileAttributeView or PosixFileAttributeView for '%s' (linkOptions=%s)", path, Arrays.toString(linkOptions)));
+    }
+
+    private static boolean setPosixReadOnly(final Path path, final boolean readOnly, final LinkOption... linkOptions) throws IOException {
         final PosixFileAttributeView posixFileAttributeView = getPosixFileAttributeView(path, linkOptions);
         if (posixFileAttributeView != null) {
             // Not Windows 10
             final PosixFileAttributes readAttributes = posixFileAttributeView.readAttributes();
             final Set<PosixFilePermission> permissions = readAttributes.permissions();
             permissions.add(PosixFilePermission.OWNER_READ);
-            permissions.add(PosixFilePermission.GROUP_READ);
-            permissions.add(PosixFilePermission.OTHERS_READ);
+            // permissions.add(PosixFilePermission.GROUP_READ);
+            // permissions.add(PosixFilePermission.OTHERS_READ);
             // @formatter:off
             final List<PosixFilePermission> writePermissions = Arrays.asList(
-                PosixFilePermission.OWNER_WRITE,
-                PosixFilePermission.GROUP_WRITE,
-                PosixFilePermission.OTHERS_WRITE);
+                PosixFilePermission.OWNER_WRITE
+                //PosixFilePermission.GROUP_WRITE,
+                //PosixFilePermission.
+            );
             // @formatter:on
             if (readOnly) {
                 permissions.removeAll(writePermissions);
             } else {
                 permissions.addAll(writePermissions);
             }
-            try {
-                return Files.setPosixFilePermissions(path, permissions);
-            } catch (final IOException e) {
-                causeList.add(e);
-            }
+            Files.setPosixFilePermissions(path, permissions);
+            return true;
         }
-        if (!causeList.isEmpty()) {
-            throw new IOExceptionList(path.toString(), causeList);
+        return false;
+    }
+
+    private static boolean setDosReadOnly(final Path path, final boolean readOnly, final LinkOption... linkOptions) throws IOException {
+        final DosFileAttributeView dosFileAttributeView = getDosFileAttributeView(path, linkOptions);
+        if (dosFileAttributeView != null) {
+            dosFileAttributeView.setReadOnly(readOnly);
+            return true;
         }
-        throw new IOException(String.format("No DosFileAttributeView or PosixFileAttributeView for '%s' (linkOptions=%s)", path, Arrays.toString(linkOptions)));
+        return false;
+    }
+
+    /**
+     * To delete a file in POSIX, you need Write and Execute permissions on its parent directory.
+     *
+     * @param parent The parent path for a file element to delete which needs RW permissions.
+     * @param enableDelete true to set permissions to delete.
+     * @param linkOptions options indicating how handle symbolic links.
+     * @return true if the operation was attempted and succeeded, false if parent is null.
+     * @throws IOException if an I/O error occurs.
+     */
+    private static boolean setPosixDeletePermissions(final Path parent, final boolean enableDelete, final LinkOption... linkOptions) throws IOException {
+        if (parent != null) {
+            // To delete a file in POSIX, you need write and execute permissions on its parent directory.
+            // @formatter:off
+            return setPosixPermissions(parent, enableDelete, Arrays.asList(
+                PosixFilePermission.OWNER_WRITE,
+                //PosixFilePermission.GROUP_WRITE,
+                //PosixFilePermission.OTHERS_WRITE,
+                PosixFilePermission.OWNER_EXECUTE
+                //PosixFilePermission.GROUP_EXECUTE,
+                //PosixFilePermission.OTHERS_EXECUTE
+                ), linkOptions);
+            // @formatter:on
+        }
+        return false;
+    }
+
+    /**
+     * Low-level POSIX permission operation to set permissions.
+     *
+     * @param path Set this path's permissions.
+     * @param addPermissions true to add, false to remove.
+     * @param updatePermissions the List of PosixFilePermission to add or remove.
+     * @param linkOptions options indicating how handle symbolic links.
+     * @return true if the operation was attempted and succeeded, false if parent is null.
+     * @throws IOException if an I/O error occurs.
+     */
+    private static boolean setPosixPermissions(final Path path, final boolean addPermissions, final List<PosixFilePermission> updatePermissions,
+            final LinkOption... linkOptions) throws IOException {
+        if (path != null) {
+            final PosixFileAttributeView posixFileAttributeView = getPosixFileAttributeView(path, linkOptions);
+            if (posixFileAttributeView != null) {
+                final Set<PosixFilePermission> currentPermissions = posixFileAttributeView.readAttributes().permissions();
+                if (addPermissions) {
+                    currentPermissions.addAll(updatePermissions);
+                } else {
+                    currentPermissions.removeAll(updatePermissions);
+                }
+                Files.setPosixFilePermissions(path, currentPermissions);
+                return true;
+            }
+        }
+        return false;
     }
 
     /**
-     * Returns the size of the given file or directory. If the provided {@link Path} is a regular file, then the file's size
-     * is returned. If the argument is a directory, then the size of the directory is calculated recursively.
+     * Returns the size of the given file or directory. If the provided {@link Path} is a regular file, then the file's size is returned. If the argument is a
+     * directory, then the size of the directory is calculated recursively.
      * <p>
-     * Note that overflow is not detected, and the return value may be negative if overflow occurs. See
-     * {@link #sizeOfAsBigInteger(Path)} for an alternative method that does not overflow.
+     * Note that overflow is not detected, and the return value may be negative if overflow occurs. See {@link #sizeOfAsBigInteger(Path)} for an alternative
+     * method that does not overflow.
      * </p>
      *
      * @param path the regular file or directory to return the size of, must not be {@code null}.
@@ -1326,8 +1446,8 @@ public final class PathUtils {
     }
 
     /**
-     * Returns the size of the given file or directory. If the provided {@link Path} is a regular file, then the file's size
-     * is returned. If the argument is a directory, then the size of the directory is calculated recursively.
+     * Returns the size of the given file or directory. If the provided {@link Path} is a regular file, then the file's size is returned. If the argument is a
+     * directory, then the size of the directory is calculated recursively.
      *
      * @param path the regular file or directory to return the size of (must not be {@code null}).
      * @return the length of the file, or recursive size of the directory, provided (in bytes).
@@ -1344,13 +1464,12 @@ public final class PathUtils {
     /**
      * Counts the size of a directory recursively (sum of the size of all files).
      * <p>
-     * Note that overflow is not detected, and the return value may be negative if overflow occurs. See
-     * {@link #sizeOfDirectoryAsBigInteger(Path)} for an alternative method that does not overflow.
+     * Note that overflow is not detected, and the return value may be negative if overflow occurs. See {@link #sizeOfDirectoryAsBigInteger(Path)} for an
+     * alternative method that does not overflow.
      * </p>
      *
      * @param directory directory to inspect, must not be {@code null}.
-     * @return size of directory in bytes, 0 if directory is security restricted, a negative number when the real total is
-     *         greater than {@link Long#MAX_VALUE}.
+     * @return size of directory in bytes, 0 if directory is security restricted, a negative number when the real total is greater than {@link Long#MAX_VALUE}.
      * @throws NullPointerException if the directory is {@code null}.
      * @throws IOException if an I/O error occurs.
      * @since 2.12.0
@@ -1415,7 +1534,7 @@ public final class PathUtils {
      * @throws IOException if an I/O error is thrown by a visitor method.
      */
     public static <T extends FileVisitor<? super Path>> T visitFileTree(final T visitor, final Path start, final Set<FileVisitOption> options,
-        final int maxDepth) throws IOException {
+            final int maxDepth) throws IOException {
         Files.walkFileTree(start, options, maxDepth, visitor);
         return visitor;
     }
@@ -1456,8 +1575,7 @@ public final class PathUtils {
     /**
      * Waits for the file system to propagate a file creation, with a timeout.
      * <p>
-     * This method repeatedly tests {@link Files#exists(Path,LinkOption...)} until it returns true up to the maximum time
-     * given.
+     * This method repeatedly tests {@link Files#exists(Path,LinkOption...)} until it returns true up to the maximum time given.
      * </p>
      *
      * @param file the file to check, must not be {@code null}.
@@ -1507,9 +1625,9 @@ public final class PathUtils {
      * @since 2.9.0
      */
     public static Stream<Path> walk(final Path start, final PathFilter pathFilter, final int maxDepth, final boolean readAttributes,
-        final FileVisitOption... options) throws IOException {
+            final FileVisitOption... options) throws IOException {
         return Files.walk(start, maxDepth, options)
-            .filter(path -> pathFilter.accept(path, readAttributes ? readBasicFileAttributesUnchecked(path) : null) == FileVisitResult.CONTINUE);
+                .filter(path -> pathFilter.accept(path, readAttributes ? readBasicFileAttributesUnchecked(path) : null) == FileVisitResult.CONTINUE);
     }
 
     /**
@@ -1524,7 +1642,7 @@ public final class PathUtils {
      * @since 2.12.0
      */
     public static Path writeString(final Path path, final CharSequence charSequence, final Charset charset, final OpenOption... openOptions)
-        throws IOException {
+            throws IOException {
         // Check the text is not null before opening file.
         Objects.requireNonNull(path, "path");
         Objects.requireNonNull(charSequence, "charSequence");
diff --git a/src/main/java/org/apache/commons/io/file/StandardDeleteOption.java b/src/main/java/org/apache/commons/io/file/StandardDeleteOption.java
index 7dd3f73..4c31b7f 100644
--- a/src/main/java/org/apache/commons/io/file/StandardDeleteOption.java
+++ b/src/main/java/org/apache/commons/io/file/StandardDeleteOption.java
@@ -27,7 +27,7 @@ import org.apache.commons.io.IOUtils;
 public enum StandardDeleteOption implements DeleteOption {
 
     /**
-     * Overrides the read-only attribute to allow deletion.
+     * Overrides the read-only attribute to allow deletion, on POSIX, this means Write and Execute on the parent.
      */
     OVERRIDE_READ_ONLY;
 
diff --git a/src/main/java/org/apache/commons/io/function/IOStreams.java b/src/main/java/org/apache/commons/io/function/IOStreams.java
index 52654cb..7437ec3 100644
--- a/src/main/java/org/apache/commons/io/function/IOStreams.java
+++ b/src/main/java/org/apache/commons/io/function/IOStreams.java
@@ -65,7 +65,7 @@ class IOStreams {
             }
             index.incrementAndGet();
         });
-        IOExceptionList.checkEmpty(causeList.get(), "forEach");
+        IOExceptionList.checkEmpty(causeList.get(), null);
     }
 
 }
diff --git a/src/test/java/org/apache/commons/io/DeleteDirectoryTest.java b/src/test/java/org/apache/commons/io/DeleteDirectoryTest.java
index f27d5dd..bb0e290 100644
--- a/src/test/java/org/apache/commons/io/DeleteDirectoryTest.java
+++ b/src/test/java/org/apache/commons/io/DeleteDirectoryTest.java
@@ -18,9 +18,9 @@ package org.apache.commons.io;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
-import java.io.File;
 import java.io.IOException;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
@@ -29,35 +29,24 @@ import java.nio.file.Path;
 import java.nio.file.attribute.PosixFilePermission;
 import java.util.Set;
 
+import org.apache.commons.io.file.AbstractTempDirTest;
 import org.apache.commons.io.file.PathUtils;
 import org.apache.commons.io.file.StandardDeleteOption;
 import org.apache.commons.io.function.IOConsumer;
-import org.junit.jupiter.api.Assumptions;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
+import org.junit.jupiter.api.condition.DisabledOnOs;
+import org.junit.jupiter.api.condition.OS;
 
 /**
  * Tests <a href="https://issues.apache.org/jira/browse/IO-751">IO-751</a>.
  * <p>
- * Must be run on macOS or Linux, not Windows.
+ * Must be run on a POSIX file system, macOS or Linux, disbled on Windows.
  * </p>
  */
-public class DeleteDirectoryTest {
-
-    @BeforeAll
-    public static void beforeAll() throws IOException {
-        // This test requires a POSIX file system, so not stock Windows 10.
-        Assumptions.assumeTrue(PathUtils.isPosix(PathUtils.current()));
-    }
-
-    @TempDir
-    public File tempDir;
+@DisabledOnOs(OS.WINDOWS)
+public class DeleteDirectoryTest extends AbstractTempDirTest {
 
     private void testDeleteDirectory(final IOConsumer<Path> deleter) throws IOException {
-        final Path tempDirPath = tempDir.toPath();
-
         // Create a test file
         final String contents = "Hello!";
         final Path file = tempDirPath.resolve("file.txt");
@@ -77,18 +66,17 @@ public class DeleteDirectoryTest {
         // Sanity check: The symlink really points to the test file
         assertEquals(contents, PathUtils.readString(symLink, charset));
 
-        // Delete the test directory with the given implementation
+        // Delete the test directory using the given implementation
         deleter.accept(testDir);
         // Symlink is gone -- passes
         assertFalse(Files.exists(symLink), symLink::toString);
         // The test file still exists -- passes
         assertTrue(Files.exists(file), file::toString);
-        // The permissions of the test file should still be the same -- fails
+        // The permissions of the test file should still be the same
         assertEquals(permissions, Files.getPosixFilePermissions(file), file::toString);
     }
 
     @Test
-    @Disabled
     public void testDeleteDirectoryWithFileUtils() throws IOException {
         testDeleteDirectory(dir -> FileUtils.deleteDirectory(dir.toFile()));
     }
@@ -99,7 +87,35 @@ public class DeleteDirectoryTest {
     }
 
     @Test
-    @Disabled
+    public void testDeleteFileCheckParentAccess() throws IOException {
+        // Create a test directory
+        final Path testDir = tempDirPath.resolve("dir");
+        Files.createDirectory(testDir);
+
+        // Create a test file
+        final Path file = testDir.resolve("file.txt");
+        final Charset charset = StandardCharsets.UTF_8;
+        PathUtils.writeString(file, "Hello!", charset);
+
+        // A file is RO in POSIX if the parent is not W & E.
+        PathUtils.setReadOnly(file, true);
+        final Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(testDir);
+        assertFalse(Files.isWritable(testDir));
+        assertFalse(Files.isExecutable(testDir));
+
+        assertThrows(IOException.class, () -> PathUtils.delete(file));
+        // Nothing happened, we're not even allowed to test attributes, so the file seems deleted, but it is not.
+
+        PathUtils.delete(file, StandardDeleteOption.OVERRIDE_READ_ONLY);
+
+        assertFalse(Files.exists(file));
+
+        assertEquals(permissions, Files.getPosixFilePermissions(testDir), testDir::toString);
+        assertFalse(Files.isWritable(testDir));
+        assertFalse(Files.isExecutable(testDir));
+    }
+
+    @Test
     public void testDeleteDirectoryWithPathUtilsOverrideReadOnly() throws IOException {
         testDeleteDirectory(dir -> PathUtils.deleteDirectory(dir, StandardDeleteOption.OVERRIDE_READ_ONLY));
     }
diff --git a/src/test/java/org/apache/commons/io/FileCleaningTrackerTest.java b/src/test/java/org/apache/commons/io/FileCleaningTrackerTest.java
index 438e461..8a18e05 100644
--- a/src/test/java/org/apache/commons/io/FileCleaningTrackerTest.java
+++ b/src/test/java/org/apache/commons/io/FileCleaningTrackerTest.java
@@ -32,21 +32,18 @@ import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.apache.commons.io.file.AbstractTempDirTest;
 import org.apache.commons.io.test.TestUtils;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
 
 /**
  * This is used to test {@link FileCleaningTracker} for correctness.
  *
  * @see FileCleaningTracker
  */
-public class FileCleaningTrackerTest {
-
-    @TempDir
-    public File temporaryFolder;
+public class FileCleaningTrackerTest extends AbstractTempDirTest {
 
     private File testFile;
 
@@ -68,11 +65,9 @@ public class FileCleaningTrackerTest {
         }
     }
 
-    /**
-     */
     @BeforeEach
     public void setUp() {
-        testFile = new File(temporaryFolder, "file-test.txt");
+        testFile = new File(tempDirFile, "file-test.txt");
         theInstance = newInstance();
     }
 
@@ -110,11 +105,11 @@ public class FileCleaningTrackerTest {
     public void testFileCleanerDirectory() throws Exception {
         TestUtils.createFile(testFile, 100);
         assertTrue(testFile.exists());
-        assertTrue(temporaryFolder.exists());
+        assertTrue(tempDirFile.exists());
 
         Object obj = new Object();
         assertEquals(0, theInstance.getTrackCount());
-        theInstance.track(temporaryFolder, obj);
+        theInstance.track(tempDirFile, obj);
         assertEquals(1, theInstance.getTrackCount());
 
         obj = null;
@@ -137,11 +132,11 @@ public class FileCleaningTrackerTest {
             TestUtils.generateTestData(output, 100);
         }
         assertTrue(testFile.exists());
-        assertTrue(temporaryFolder.exists());
+        assertTrue(tempDirFile.exists());
 
         Object obj = new Object();
         assertEquals(0, theInstance.getTrackCount());
-        theInstance.track(temporaryFolder, obj, FileDeleteStrategy.FORCE);
+        theInstance.track(tempDirFile, obj, FileDeleteStrategy.FORCE);
         assertEquals(1, theInstance.getTrackCount());
 
         obj = null;
@@ -158,11 +153,11 @@ public class FileCleaningTrackerTest {
     public void testFileCleanerDirectory_NullStrategy() throws Exception {
         TestUtils.createFile(testFile, 100);
         assertTrue(testFile.exists());
-        assertTrue(temporaryFolder.exists());
+        assertTrue(tempDirFile.exists());
 
         Object obj = new Object();
         assertEquals(0, theInstance.getTrackCount());
-        theInstance.track(temporaryFolder, obj, null);
+        theInstance.track(tempDirFile, obj, null);
         assertEquals(1, theInstance.getTrackCount());
 
         obj = null;
diff --git a/src/test/java/org/apache/commons/io/FileUtilsCleanDirectoryTest.java b/src/test/java/org/apache/commons/io/FileUtilsCleanDirectoryTest.java
index f835a7b..38da2f8 100644
--- a/src/test/java/org/apache/commons/io/FileUtilsCleanDirectoryTest.java
+++ b/src/test/java/org/apache/commons/io/FileUtilsCleanDirectoryTest.java
@@ -27,10 +27,10 @@ import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.apache.commons.io.file.AbstractTempDirTest;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.condition.DisabledOnOs;
 import org.junit.jupiter.api.condition.OS;
-import org.junit.jupiter.api.io.TempDir;
 
 /**
  * Test cases for FileUtils.cleanDirectory() method.
@@ -38,10 +38,7 @@ import org.junit.jupiter.api.io.TempDir;
  * TODO Redo this test using
  * {@link Files#createSymbolicLink(java.nio.file.Path, java.nio.file.Path, java.nio.file.attribute.FileAttribute...)}.
  */
-public class FileUtilsCleanDirectoryTest {
-
-    @TempDir
-    public File top;
+public class FileUtilsCleanDirectoryTest extends AbstractTempDirTest {
 
     /** Only runs on Linux. */
     private boolean chmod(final File file, final int mode, final boolean recurse) throws InterruptedException {
@@ -65,61 +62,52 @@ public class FileUtilsCleanDirectoryTest {
         return proc.waitFor() == 0;
     }
 
-    // -----------------------------------------------------------------------
     @Test
     public void testCleanEmpty() throws Exception {
-        assertEquals(0, top.list().length);
+        assertEquals(0, tempDirFile.list().length);
 
-        FileUtils.cleanDirectory(top);
+        FileUtils.cleanDirectory(tempDirFile);
 
-        assertEquals(0, top.list().length);
+        assertEquals(0, tempDirFile.list().length);
     }
 
     @Test
     public void testDeletesNested() throws Exception {
-        final File nested = new File(top, "nested");
+        final File nested = new File(tempDirFile, "nested");
 
         assertTrue(nested.mkdirs());
 
         FileUtils.touch(new File(nested, "file"));
 
-        assertEquals(1, top.list().length);
+        assertEquals(1, tempDirFile.list().length);
 
-        FileUtils.cleanDirectory(top);
+        FileUtils.cleanDirectory(tempDirFile);
 
-        assertEquals(0, top.list().length);
+        assertEquals(0, tempDirFile.list().length);
     }
 
     @Test
     public void testDeletesRegular() throws Exception {
-        FileUtils.touch(new File(top, "regular"));
-        FileUtils.touch(new File(top, ".hidden"));
+        FileUtils.touch(new File(tempDirFile, "regular"));
+        FileUtils.touch(new File(tempDirFile, ".hidden"));
 
-        assertEquals(2, top.list().length);
+        assertEquals(2, tempDirFile.list().length);
 
-        FileUtils.cleanDirectory(top);
+        FileUtils.cleanDirectory(tempDirFile);
 
-        assertEquals(0, top.list().length);
+        assertEquals(0, tempDirFile.list().length);
     }
 
     @DisabledOnOs(OS.WINDOWS)
     @Test
-    public void testThrowsOnCannotDeleteFile() throws Exception {
-        final File file = new File(top, "restricted");
+    public void testCleanDirectoryToForceDelete() throws Exception {
+        final File file = new File(tempDirFile, "restricted");
         FileUtils.touch(file);
 
-        assumeTrue(chmod(top, 500, false));
+        assumeTrue(chmod(tempDirFile, 500, false));
 
-        try {
-            // cleanDirectory calls forceDelete
-            FileUtils.cleanDirectory(top);
-            fail("expected IOException");
-        } catch (final IOException e) {
-            final IOExceptionList list = (IOExceptionList) e;
-            assertEquals("Cannot delete file: " + file.getAbsolutePath(), list.getCause(0).getMessage());
-        } finally {
-            chmod(top, 755, false);
-        }
+        // cleanDirectory calls forceDelete
+        FileUtils.cleanDirectory(tempDirFile);
     }
 
     @DisabledOnOs(OS.WINDOWS)
@@ -127,16 +115,16 @@ public class FileUtilsCleanDirectoryTest {
     public void testThrowsOnNullList() throws Exception {
         // test wont work if we can't restrict permissions on the
         // directory, so skip it.
-        assumeTrue(chmod(top, 0, false));
+        assumeTrue(chmod(tempDirFile, 0, false));
 
         try {
             // cleanDirectory calls forceDelete
-            FileUtils.cleanDirectory(top);
+            FileUtils.cleanDirectory(tempDirFile);
             fail("expected IOException");
         } catch (final IOException e) {
-            assertEquals("Unknown I/O error listing contents of directory: " + top.getAbsolutePath(), e.getMessage());
+            assertEquals("Unknown I/O error listing contents of directory: " + tempDirFile.getAbsolutePath(), e.getMessage());
         } finally {
-            chmod(top, 755, false);
+            chmod(tempDirFile, 755, false);
         }
     }
 
diff --git a/src/test/java/org/apache/commons/io/FileUtilsTest.java b/src/test/java/org/apache/commons/io/FileUtilsTest.java
index 5d13cf1..1cf9026 100644
--- a/src/test/java/org/apache/commons/io/FileUtilsTest.java
+++ b/src/test/java/org/apache/commons/io/FileUtilsTest.java
@@ -63,6 +63,7 @@ import java.util.Map;
 import java.util.zip.CRC32;
 import java.util.zip.Checksum;
 
+import org.apache.commons.io.file.AbstractTempDirTest;
 import org.apache.commons.io.file.PathUtils;
 import org.apache.commons.io.file.PathUtilsIsEmptyTest;
 import org.apache.commons.io.filefilter.IOFileFilter;
@@ -74,7 +75,6 @@ import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
 
 /**
  * This is used to test FileUtils for correctness.
@@ -82,7 +82,7 @@ import org.junit.jupiter.api.io.TempDir;
  * @see FileUtils
  */
 @SuppressWarnings({"deprecation", "ResultOfMethodCallIgnored"}) // unit tests include tests of many deprecated methods
-public class FileUtilsTest {
+public class FileUtilsTest extends AbstractTempDirTest {
 
     /**
      * DirectoryWalker implementation that recursively lists all files and directories.
@@ -156,12 +156,6 @@ public class FileUtilsTest {
     private static final ListDirectoryWalker LIST_WALKER = new ListDirectoryWalker();
 
     /**
-     * The test temporary folder managed by JUnit.
-     */
-    @TempDir
-    public File temporaryFolder;
-
-    /**
      * Delay in milliseconds to make sure test for "last modified date" are accurate
      */
     //private static final int LAST_MODIFIED_DELAY = 600;
@@ -307,8 +301,8 @@ public class FileUtilsTest {
 
     @BeforeEach
     public void setUp() throws Exception {
-        testFile1 = new File(temporaryFolder, "file1-test.txt");
-        testFile2 = new File(temporaryFolder, "file1a-test.txt");
+        testFile1 = new File(tempDirFile, "file1-test.txt");
+        testFile2 = new File(tempDirFile, "file1a-test.txt");
 
         testFile1Size = testFile1.length();
         testFile2Size = testFile2.length();
@@ -328,8 +322,8 @@ public class FileUtilsTest {
                 new BufferedOutputStream(Files.newOutputStream(testFile2.toPath()))) {
             TestUtils.generateTestData(output2, testFile2Size);
         }
-        FileUtils.deleteDirectory(temporaryFolder);
-        temporaryFolder.mkdirs();
+        FileUtils.deleteDirectory(tempDirFile);
+        tempDirFile.mkdirs();
         if (!testFile1.getParentFile().exists()) {
             throw new IOException("Cannot create file " + testFile1
                     + " as the parent directory does not exist");
@@ -350,7 +344,7 @@ public class FileUtilsTest {
 
     @Test
     public void test_openInputStream_exists() throws Exception {
-        final File file = new File(temporaryFolder, "test.txt");
+        final File file = new File(tempDirFile, "test.txt");
         TestUtils.createLineBasedFile(file, new String[]{"Hello"});
         try (FileInputStream in = FileUtils.openInputStream(file)) {
             assertEquals('H', in.read());
@@ -359,20 +353,20 @@ public class FileUtilsTest {
 
     @Test
     public void test_openInputStream_existsButIsDirectory() {
-        final File directory = new File(temporaryFolder, "subdir");
+        final File directory = new File(tempDirFile, "subdir");
         directory.mkdirs();
         assertThrows(IOException.class, () -> FileUtils.openInputStream(directory));
     }
 
     @Test
     public void test_openInputStream_notExists() {
-        final File directory = new File(temporaryFolder, "test.txt");
+        final File directory = new File(tempDirFile, "test.txt");
         assertThrows(IOException.class, () -> FileUtils.openInputStream(directory));
     }
 
     @Test
     public void test_openOutputStream_exists() throws Exception {
-        final File file = new File(temporaryFolder, "test.txt");
+        final File file = new File(tempDirFile, "test.txt");
         TestUtils.createLineBasedFile(file, new String[]{"Hello"});
         try (FileOutputStream out = FileUtils.openOutputStream(file)) {
             out.write(0);
@@ -382,7 +376,7 @@ public class FileUtilsTest {
 
     @Test
     public void test_openOutputStream_existsButIsDirectory() {
-        final File directory = new File(temporaryFolder, "subdir");
+        final File directory = new File(tempDirFile, "subdir");
         directory.mkdirs();
         assertThrows(IllegalArgumentException.class, () -> FileUtils.openOutputStream(directory));
     }
@@ -399,7 +393,7 @@ public class FileUtilsTest {
 
     @Test
     public void test_openOutputStream_notExists() throws Exception {
-        final File file = new File(temporaryFolder, "a/test.txt");
+        final File file = new File(tempDirFile, "a/test.txt");
         try (FileOutputStream out = FileUtils.openOutputStream(file)) {
             out.write(0);
         }
@@ -416,7 +410,7 @@ public class FileUtilsTest {
                         "abcdevwxyzabcdevwxyzabcdevwxyzabcdevwxyzabcdevwxyz" +
                         "abcdevwxyzabcdevwxyzabcdevwxyzabcdevwxyzabcdevwxyz" +
                         "abcdevwxyzabcdevwxyzabcdevwxyzabcdevwxyzabcdevwxyz";  // 300 chars
-        final File file = new File(temporaryFolder, "a/" + longStr + "/test.txt");
+        final File file = new File(tempDirFile, "a/" + longStr + "/test.txt");
         assertThrows(IOException.class, () -> FileUtils.openOutputStream(file));
     }
 
@@ -510,7 +504,7 @@ public class FileUtilsTest {
     public void testChecksum() throws Exception {
         // create a test file
         final String text = "Imagination is more important than knowledge - Einstein";
-        final File file = new File(temporaryFolder, "checksum-test.txt");
+        final File file = new File(tempDirFile, "checksum-test.txt");
         FileUtils.writeStringToFile(file, text, "US-ASCII");
 
         // compute the expected checksum
@@ -531,7 +525,7 @@ public class FileUtilsTest {
     public void testChecksumCRC32() throws Exception {
         // create a test file
         final String text = "Imagination is more important than knowledge - Einstein";
-        final File file = new File(temporaryFolder, "checksum-test.txt");
+        final File file = new File(tempDirFile, "checksum-test.txt");
         FileUtils.writeStringToFile(file, text, "US-ASCII");
 
         // compute the expected checksum
@@ -549,12 +543,12 @@ public class FileUtilsTest {
     public void testChecksumDouble() throws Exception {
         // create a test file
         final String text1 = "Imagination is more important than knowledge - Einstein";
-        final File file1 = new File(temporaryFolder, "checksum-test.txt");
+        final File file1 = new File(tempDirFile, "checksum-test.txt");
         FileUtils.writeStringToFile(file1, text1, "US-ASCII");
 
         // create a second test file
         final String text2 = "To be or not to be - Shakespeare";
-        final File file2 = new File(temporaryFolder, "checksum-test2.txt");
+        final File file2 = new File(tempDirFile, "checksum-test2.txt");
         FileUtils.writeStringToFile(file2, text2, "US-ASCII");
 
         // compute the expected checksum
@@ -581,7 +575,7 @@ public class FileUtilsTest {
     public void testChecksumOnNullChecksum() throws Exception {
         // create a test file
         final String text = "Imagination is more important than knowledge - Einstein";
-        final File file = new File(temporaryFolder, "checksum-test.txt");
+        final File file = new File(tempDirFile, "checksum-test.txt");
         FileUtils.writeStringToFile(file, text, "US-ASCII");
         assertThrows(NullPointerException.class, () -> FileUtils.checksum(file, null));
     }
@@ -605,8 +599,8 @@ public class FileUtilsTest {
     @Test
     public void testContentEquals() throws Exception {
         // Non-existent files
-        final File file = new File(temporaryFolder, getName());
-        final File file2 = new File(temporaryFolder, getName() + "2");
+        final File file = new File(tempDirFile, getName());
+        final File file2 = new File(tempDirFile, getName() + "2");
         assertTrue(FileUtils.contentEquals(null, null));
         assertFalse(FileUtils.contentEquals(null, file));
         assertFalse(FileUtils.contentEquals(file, null));
@@ -617,25 +611,25 @@ public class FileUtilsTest {
         assertTrue(FileUtils.contentEquals(file2, file));
 
         // Directories
-        assertThrows(IllegalArgumentException.class, () -> FileUtils.contentEquals(temporaryFolder, temporaryFolder));
+        assertThrows(IllegalArgumentException.class, () -> FileUtils.contentEquals(tempDirFile, tempDirFile));
 
         // Different files
         final File objFile1 =
-                new File(temporaryFolder, getName() + ".object");
+                new File(tempDirFile, getName() + ".object");
         objFile1.deleteOnExit();
         FileUtils.copyURLToFile(
                 getClass().getResource("/java/lang/Object.class"),
                 objFile1);
 
         final File objFile1b =
-                new File(temporaryFolder, getName() + ".object2");
+                new File(tempDirFile, getName() + ".object2");
         objFile1.deleteOnExit();
         FileUtils.copyURLToFile(
                 getClass().getResource("/java/lang/Object.class"),
                 objFile1b);
 
         final File objFile2 =
-                new File(temporaryFolder, getName() + ".collection");
+                new File(tempDirFile, getName() + ".collection");
         objFile2.deleteOnExit();
         FileUtils.copyURLToFile(
                 getClass().getResource("/java/util/Collection.class"),
@@ -661,8 +655,8 @@ public class FileUtilsTest {
     @Test
     public void testContentEqualsIgnoreEOL() throws Exception {
         // Non-existent files
-        final File file1 = new File(temporaryFolder, getName());
-        final File file2 = new File(temporaryFolder, getName() + "2");
+        final File file1 = new File(tempDirFile, getName());
+        final File file2 = new File(tempDirFile, getName() + "2");
         assertTrue(FileUtils.contentEqualsIgnoreEOL(null, null, null));
         assertFalse(FileUtils.contentEqualsIgnoreEOL(null, file1, null));
         assertFalse(FileUtils.contentEqualsIgnoreEOL(file1, null, null));
@@ -674,18 +668,18 @@ public class FileUtilsTest {
 
         // Directories
         assertThrows(IllegalArgumentException.class,
-            () -> FileUtils.contentEqualsIgnoreEOL(temporaryFolder, temporaryFolder, null));
+            () -> FileUtils.contentEqualsIgnoreEOL(tempDirFile, tempDirFile, null));
 
         // Different files
-        final File tfile1 = new File(temporaryFolder, getName() + ".txt1");
+        final File tfile1 = new File(tempDirFile, getName() + ".txt1");
         tfile1.deleteOnExit();
         FileUtils.write(tfile1, "123\r");
 
-        final File tfile2 = new File(temporaryFolder, getName() + ".txt2");
+        final File tfile2 = new File(tempDirFile, getName() + ".txt2");
         tfile1.deleteOnExit();
         FileUtils.write(tfile2, "123\n");
 
-        final File tfile3 = new File(temporaryFolder, getName() + ".collection");
+        final File tfile3 = new File(tempDirFile, getName() + ".collection");
         tfile3.deleteOnExit();
         FileUtils.write(tfile3, "123\r\n2");
 
@@ -748,22 +742,22 @@ public class FileUtilsTest {
         // IllegalArgumentException
         assertThrows(IllegalArgumentException.class, () -> FileUtils.copyDirectory(testFile1, new File("a")));
         assertThrows(IllegalArgumentException.class, () -> FileUtils.copyDirectory(testFile1, new File("a")));
-        assertThrows(IllegalArgumentException.class, () -> FileUtils.copyDirectory(temporaryFolder, temporaryFolder));
+        assertThrows(IllegalArgumentException.class, () -> FileUtils.copyDirectory(tempDirFile, tempDirFile));
         //
         // IOException
         assertThrows(IOException.class, () -> FileUtils.copyDirectory(new File("doesnt-exist"), new File("a")));
-        assertThrows(IllegalArgumentException.class, () -> FileUtils.copyDirectory(temporaryFolder, testFile1));
+        assertThrows(IllegalArgumentException.class, () -> FileUtils.copyDirectory(tempDirFile, testFile1));
     }
 
     @Test
     public void testCopyDirectoryFiltered() throws Exception {
-        final File grandParentDir = new File(temporaryFolder, "grandparent");
+        final File grandParentDir = new File(tempDirFile, "grandparent");
         final File parentDir = new File(grandParentDir, "parent");
         final File childDir = new File(parentDir, "child");
         createFilesForTestCopyDirectory(grandParentDir, parentDir, childDir);
 
         final NameFileFilter filter = new NameFileFilter("parent", "child", "file3.txt");
-        final File destDir = new File(temporaryFolder, "copydest");
+        final File destDir = new File(tempDirFile, "copydest");
 
         FileUtils.copyDirectory(grandParentDir, destDir, filter);
         final List<File> files = LIST_WALKER.list(destDir);
@@ -775,7 +769,7 @@ public class FileUtilsTest {
 
     @Test
     public void testCopyDirectoryPreserveDates() throws Exception {
-        final File source = new File(temporaryFolder, "source");
+        final File source = new File(tempDirFile, "source");
         final File sourceDirectory = new File(source, "directory");
         final File sourceFile = new File(sourceDirectory, "hello.txt");
 
@@ -790,7 +784,7 @@ public class FileUtilsTest {
         assertTrue(setLastModifiedMillis(sourceDirectory, DATE2));
         assertTrue(setLastModifiedMillis(source, DATE1));
 
-        final File target = new File(temporaryFolder, "target");
+        final File target = new File(tempDirFile, "target");
         final File targetDirectory = new File(target, "directory");
         final File targetFile = new File(targetDirectory, "hello.txt");
 
@@ -831,7 +825,7 @@ public class FileUtilsTest {
     /* Test for IO-141 */
     @Test
     public void testCopyDirectoryToChild() throws Exception {
-        final File grandParentDir = new File(temporaryFolder, "grandparent");
+        final File grandParentDir = new File(tempDirFile, "grandparent");
         final File parentDir = new File(grandParentDir, "parent");
         final File childDir = new File(parentDir, "child");
         createFilesForTestCopyDirectory(grandParentDir, parentDir, childDir);
@@ -871,7 +865,7 @@ public class FileUtilsTest {
         } finally {
             IOUtils.closeQuietly(output);
         }
-        final File srcDir = temporaryFolder;
+        final File srcDir = tempDirFile;
         final File subDir = new File(srcDir, "sub");
         subDir.mkdir();
         final File subFile = new File(subDir, "A.txt");
@@ -935,7 +929,7 @@ public class FileUtilsTest {
         } finally {
             IOUtils.closeQuietly(output);
         }
-        final File srcDir = temporaryFolder;
+        final File srcDir = tempDirFile;
         final File subDir = new File(srcDir, "sub");
         subDir.mkdir();
         final File subFile = new File(subDir, "A.txt");
@@ -957,7 +951,7 @@ public class FileUtilsTest {
     /* Test for IO-141 */
     @Test
     public void testCopyDirectoryToGrandChild() throws Exception {
-        final File grandParentDir = new File(temporaryFolder, "grandparent");
+        final File grandParentDir = new File(tempDirFile, "grandparent");
         final File parentDir = new File(grandParentDir, "parent");
         final File childDir = new File(parentDir, "child");
         createFilesForTestCopyDirectory(grandParentDir, parentDir, childDir);
@@ -973,7 +967,7 @@ public class FileUtilsTest {
     /* Test for IO-217 FileUtils.copyDirectoryToDirectory makes infinite loops */
     @Test
     public void testCopyDirectoryToItself() throws Exception {
-        final File dir = new File(temporaryFolder, "itself");
+        final File dir = new File(tempDirFile, "itself");
         dir.mkdirs();
         FileUtils.copyDirectoryToDirectory(dir, dir);
         assertEquals(1, LIST_WALKER.list(dir).size());
@@ -1005,7 +999,7 @@ public class FileUtilsTest {
         } finally {
             IOUtils.closeQuietly(output);
         }
-        final File srcDir = temporaryFolder;
+        final File srcDir = tempDirFile;
         final File subDir = new File(srcDir, "sub");
         subDir.mkdir();
         final File subFile = new File(subDir, "A.txt");
@@ -1025,7 +1019,7 @@ public class FileUtilsTest {
 
     @Test
     public void testCopyFile1() throws Exception {
-        final File destination = new File(temporaryFolder, "copy1.txt");
+        final File destination = new File(tempDirFile, "copy1.txt");
 
         backDateFile10Minutes(testFile1); // set test file back 10 minutes
 
@@ -1039,7 +1033,7 @@ public class FileUtilsTest {
 
     @Test
     public void testCopyFile1ToDir() throws Exception {
-        final File directory = new File(temporaryFolder, "subdir");
+        final File directory = new File(tempDirFile, "subdir");
         if (!directory.exists()) {
             directory.mkdirs();
         }
@@ -1058,7 +1052,7 @@ public class FileUtilsTest {
 
     @Test
     public void testCopyFile2() throws Exception {
-        final File destination = new File(temporaryFolder, "copy2.txt");
+        final File destination = new File(tempDirFile, "copy2.txt");
 
         backDateFile10Minutes(testFile1); // set test file back 10 minutes
 
@@ -1072,7 +1066,7 @@ public class FileUtilsTest {
 
     @Test
     public void testCopyFile2ToDir() throws Exception {
-        final File directory = new File(temporaryFolder, "subdir");
+        final File directory = new File(tempDirFile, "subdir");
         if (!directory.exists()) {
             directory.mkdirs();
         }
@@ -1088,7 +1082,7 @@ public class FileUtilsTest {
 
     @Test
     public void testCopyFile2WithoutFileDatePreservation() throws Exception {
-        final File destFile = new File(temporaryFolder, "copy2.txt");
+        final File destFile = new File(tempDirFile, "copy2.txt");
 
         backDateFile10Minutes(testFile1); // set test file back 10 minutes
 
@@ -1113,8 +1107,8 @@ public class FileUtilsTest {
     @Disabled
     public void testCopyFileLarge() throws Exception {
 
-        final File largeFile = new File(temporaryFolder, "large.txt");
-        final File destination = new File(temporaryFolder, "copylarge.txt");
+        final File largeFile = new File(tempDirFile, "large.txt");
+        final File destination = new File(tempDirFile, "copylarge.txt");
 
         System.out.println("START:   " + new java.util.Date());
         if (!largeFile.getParentFile().exists()) {
@@ -1147,13 +1141,13 @@ public class FileUtilsTest {
 
     @Test
     public void testCopyToDirectoryWithDirectory() throws IOException {
-        final File destDirectory = new File(temporaryFolder, "destination");
+        final File destDirectory = new File(tempDirFile, "destination");
         if (!destDirectory.exists()) {
             destDirectory.mkdirs();
         }
 
         // Create a test directory
-        final File inputDirectory = new File(temporaryFolder, "input");
+        final File inputDirectory = new File(tempDirFile, "input");
         if (!inputDirectory.exists()) {
             inputDirectory.mkdirs();
         }
@@ -1178,7 +1172,7 @@ public class FileUtilsTest {
 
     @Test
     public void testCopyToDirectoryWithFile() throws IOException {
-        final File directory = new File(temporaryFolder, "subdir");
+        final File directory = new File(tempDirFile, "subdir");
         if (!directory.exists()) {
             directory.mkdirs();
         }
@@ -1192,19 +1186,19 @@ public class FileUtilsTest {
     @Test
     public void testCopyToDirectoryWithFileSourceDoesNotExist() {
         assertThrows(IOException.class,
-                () -> FileUtils.copyToDirectory(new File(temporaryFolder, "doesNotExists"), temporaryFolder));
+                () -> FileUtils.copyToDirectory(new File(tempDirFile, "doesNotExists"), tempDirFile));
     }
 
     // copyFile
 
     @Test
     public void testCopyToDirectoryWithFileSourceIsNull() {
-        assertThrows(NullPointerException.class, () -> FileUtils.copyToDirectory((File) null, temporaryFolder));
+        assertThrows(NullPointerException.class, () -> FileUtils.copyToDirectory((File) null, tempDirFile));
     }
 
     @Test
     public void testCopyToDirectoryWithIterable() throws IOException {
-        final File directory = new File(temporaryFolder, "subdir");
+        final File directory = new File(tempDirFile, "subdir");
         if (!directory.exists()) {
             directory.mkdirs();
         }
@@ -1227,18 +1221,18 @@ public class FileUtilsTest {
     @Test
     public void testCopyToDirectoryWithIterableSourceDoesNotExist() {
         assertThrows(IOException.class,
-                () -> FileUtils.copyToDirectory(Collections.singleton(new File(temporaryFolder, "doesNotExists")),
-                        temporaryFolder));
+                () -> FileUtils.copyToDirectory(Collections.singleton(new File(tempDirFile, "doesNotExists")),
+                        tempDirFile));
     }
 
     @Test
     public void testCopyToDirectoryWithIterableSourceIsNull() {
-        assertThrows(NullPointerException.class, () -> FileUtils.copyToDirectory((List<File>) null, temporaryFolder));
+        assertThrows(NullPointerException.class, () -> FileUtils.copyToDirectory((List<File>) null, tempDirFile));
     }
 
     @Test
     public void testCopyToSelf() throws Exception {
-        final File destination = new File(temporaryFolder, "copy3.txt");
+        final File destination = new File(tempDirFile, "copy3.txt");
         //Prepare a test file
         FileUtils.copyFile(testFile1, destination);
         assertThrows(IllegalArgumentException.class, () -> FileUtils.copyFile(destination, destination));
@@ -1247,7 +1241,7 @@ public class FileUtilsTest {
     @Test
     public void testCopyURLToFile() throws Exception {
         // Creates file
-        final File file = new File(temporaryFolder, getName());
+        final File file = new File(tempDirFile, getName());
         file.deleteOnExit();
 
         // Loads resource
@@ -1265,7 +1259,7 @@ public class FileUtilsTest {
     @Test
     public void testCopyURLToFileWithTimeout() throws Exception {
         // Creates file
-        final File file = new File(temporaryFolder, "testCopyURLToFileWithTimeout");
+        final File file = new File(tempDirFile, "testCopyURLToFileWithTimeout");
         file.deleteOnExit();
 
         // Loads resource
@@ -1356,7 +1350,7 @@ public class FileUtilsTest {
 
     @Test
     public void testDeleteQuietlyDir() throws IOException {
-        final File testDirectory = new File(temporaryFolder, "testDeleteQuietlyDir");
+        final File testDirectory = new File(tempDirFile, "testDeleteQuietlyDir");
         final File testFile = new File(testDirectory, "testDeleteQuietlyFile");
         testDirectory.mkdirs();
         if (!testFile.getParentFile().exists()) {
@@ -1382,7 +1376,7 @@ public class FileUtilsTest {
 
     @Test
     public void testDeleteQuietlyFile() throws IOException {
-        final File testFile = new File(temporaryFolder, "testDeleteQuietlyFile");
+        final File testFile = new File(tempDirFile, "testDeleteQuietlyFile");
         if (!testFile.getParentFile().exists()) {
             throw new IOException("Cannot create file " + testFile
                     + " as the parent directory does not exist");
@@ -1418,7 +1412,7 @@ public class FileUtilsTest {
     @Test
     public void testFileUtils() throws Exception {
         // Loads file from classpath
-        final File file1 = new File(temporaryFolder, "test.txt");
+        final File file1 = new File(tempDirFile, "test.txt");
         final String filename = file1.getAbsolutePath();
 
         //Create test file on-the-fly (used to be in CVS)
@@ -1426,7 +1420,7 @@ public class FileUtilsTest {
             out.write("This is a test".getBytes(StandardCharsets.UTF_8));
         }
 
-        final File file2 = new File(temporaryFolder, "test2.txt");
+        final File file2 = new File(tempDirFile, "test2.txt");
 
         FileUtils.writeStringToFile(file2, filename, "UTF-8");
         assertTrue(file2.exists());
@@ -1444,7 +1438,7 @@ public class FileUtilsTest {
 
     @Test
     public void testForceDeleteAFile1() throws Exception {
-        final File destination = new File(temporaryFolder, "copy1.txt");
+        final File destination = new File(tempDirFile, "copy1.txt");
         destination.createNewFile();
         assertTrue(destination.exists(), "Copy1.txt doesn't exist to delete");
         FileUtils.forceDelete(destination);
@@ -1453,7 +1447,7 @@ public class FileUtilsTest {
 
     @Test
     public void testForceDeleteAFile2() throws Exception {
-        final File destination = new File(temporaryFolder, "copy2.txt");
+        final File destination = new File(tempDirFile, "copy2.txt");
         destination.createNewFile();
         assertTrue(destination.exists(), "Copy2.txt doesn't exist to delete");
         FileUtils.forceDelete(destination);
@@ -1462,15 +1456,15 @@ public class FileUtilsTest {
 
     @Test
     public void testForceDeleteAFile3() throws Exception {
-        final File destination = new File(temporaryFolder, "no_such_file");
+        final File destination = new File(tempDirFile, "no_such_file");
         assertFalse(destination.exists(), "Check No Exist");
-        assertThrows(FileNotFoundException.class, () -> FileUtils.forceDelete(destination));
+        assertThrows(IOException.class, () -> FileUtils.forceDelete(destination));
 
     }
 
     @Test
     public void testForceDeleteDir() throws Exception {
-        final File testDirectory = temporaryFolder;
+        final File testDirectory = tempDirFile;
         assertTrue(testDirectory.exists(), "TestDirectory must exist");
         FileUtils.forceDelete(testDirectory);
         assertFalse(testDirectory.exists(), "TestDirectory must not exist");
@@ -1497,10 +1491,10 @@ public class FileUtilsTest {
     @Test
     public void testForceMkdir() throws Exception {
         // Tests with existing directory
-        FileUtils.forceMkdir(temporaryFolder);
+        FileUtils.forceMkdir(tempDirFile);
 
         // Creates test file
-        final File testFile = new File(temporaryFolder, getName());
+        final File testFile = new File(tempDirFile, getName());
         testFile.deleteOnExit();
         testFile.createNewFile();
         assertTrue(testFile.exists(), "Test file does not exist.");
@@ -1521,8 +1515,8 @@ public class FileUtilsTest {
     @Test
     public void testForceMkdirParent() throws Exception {
         // Tests with existing directory
-        assertTrue(temporaryFolder.exists());
-        final File testParentDir = new File(temporaryFolder, "testForceMkdirParent");
+        assertTrue(tempDirFile.exists());
+        final File testParentDir = new File(tempDirFile, "testForceMkdirParent");
         testParentDir.delete();
         assertFalse(testParentDir.exists());
         final File testFile = new File(testParentDir, "test.txt");
@@ -1613,7 +1607,7 @@ public class FileUtilsTest {
 
     @Test
     public void testIO300() {
-        final File testDirectory = temporaryFolder;
+        final File testDirectory = tempDirFile;
         final File src = new File(testDirectory, "dir1");
         final File dest = new File(src, "dir2");
         assertTrue(dest.mkdirs());
@@ -1626,7 +1620,7 @@ public class FileUtilsTest {
     public void testIsDirectory() throws IOException {
         assertFalse(FileUtils.isDirectory(null));
 
-        assertTrue(FileUtils.isDirectory(temporaryFolder));
+        assertTrue(FileUtils.isDirectory(tempDirFile));
         assertFalse(FileUtils.isDirectory(testFile1));
 
         final Path tempDir = Files.createTempDirectory(getClass().getCanonicalName());
@@ -1650,10 +1644,10 @@ public class FileUtilsTest {
     // isFileNewer / isFileOlder
     @Test
     public void testIsFileNewerOlder() throws Exception {
-        final File reference = new File(temporaryFolder, "FileUtils-reference.txt");
-        final File oldFile = new File(temporaryFolder, "FileUtils-old.txt");
-        final File newFile = new File(temporaryFolder, "FileUtils-new.txt");
-        final File invalidFile = new File(temporaryFolder, "FileUtils-invalid-file.txt");
+        final File reference = new File(tempDirFile, "FileUtils-reference.txt");
+        final File oldFile = new File(tempDirFile, "FileUtils-old.txt");
+        final File newFile = new File(tempDirFile, "FileUtils-new.txt");
+        final File invalidFile = new File(tempDirFile, "FileUtils-invalid-file.txt");
 
         // Create Files
         if (!oldFile.getParentFile().exists()) {
@@ -1798,7 +1792,7 @@ public class FileUtilsTest {
     public void testIsRegularFile() throws IOException {
         assertFalse(FileUtils.isRegularFile(null));
 
-        assertFalse(FileUtils.isRegularFile(temporaryFolder));
+        assertFalse(FileUtils.isRegularFile(tempDirFile));
         assertTrue(FileUtils.isRegularFile(testFile1));
 
         Files.delete(testFile1.toPath());
@@ -1807,7 +1801,7 @@ public class FileUtilsTest {
 
     @Test
     public void testIterateFiles() throws Exception {
-        final File srcDir = temporaryFolder;
+        final File srcDir = tempDirFile;
         final File subDir = new File(srcDir, "list_test");
         final File subSubDir = new File(subDir, "subSubDir");
         final File notSubSubDir = new File(subDir, "notSubSubDir");
@@ -1867,7 +1861,7 @@ public class FileUtilsTest {
 
     @Test
     public void testIterateFilesAndDirs() throws IOException {
-        final File srcDir = temporaryFolder;
+        final File srcDir = tempDirFile;
         // temporaryFolder/srcDir
         // - subdir1
         // -- subdir2
@@ -1918,7 +1912,7 @@ public class FileUtilsTest {
 
     @Test
     public void testIterateFilesOnlyNoDirs() throws IOException {
-        final File directory = temporaryFolder;
+        final File directory = tempDirFile;
         assertTrue(new File(directory, "TEST").mkdir());
         assertTrue(new File(directory, "test.txt").createNewFile());
 
@@ -1931,7 +1925,7 @@ public class FileUtilsTest {
 
     @Test
     public void testListFiles() throws Exception {
-        final File srcDir = temporaryFolder;
+        final File srcDir = tempDirFile;
         final File subDir = new File(srcDir, "list_test");
         final File subDir2 = new File(subDir, "subdir");
         subDir.mkdir();
@@ -1981,7 +1975,7 @@ public class FileUtilsTest {
 
     @Test
     public void testListFilesOnlyNoDirs() throws IOException {
-        final File directory = temporaryFolder;
+        final File directory = tempDirFile;
         assertTrue(new File(directory, "TEST").mkdir());
         assertTrue(new File(directory, "test.txt").createNewFile());
 
@@ -1993,7 +1987,7 @@ public class FileUtilsTest {
 
     @Test
     public void testListFilesWithDirs() throws IOException {
-        final File srcDir = temporaryFolder;
+        final File srcDir = tempDirFile;
 
         final File subDir1 = new File(srcDir, "subdir");
         final File subDir2 = new File(subDir1, "subdir2");
@@ -2030,7 +2024,7 @@ public class FileUtilsTest {
     @Test
     public void testMoveDirectory_CopyDelete() throws Exception {
 
-        final File dir = temporaryFolder;
+        final File dir = tempDirFile;
         final File src = new File(dir, "testMoveDirectory2Source") {
             private static final long serialVersionUID = 1L;
 
@@ -2075,7 +2069,7 @@ public class FileUtilsTest {
         assertThrows(NullPointerException.class, () -> FileUtils.moveDirectory(new File("foo"), null));
         assertThrows(FileNotFoundException.class, () -> FileUtils.moveDirectory(new File("nonexistant"), new File("foo")));
 
-        final File testFile = new File(temporaryFolder, "testMoveDirectoryFile");
+        final File testFile = new File(tempDirFile, "testMoveDirectoryFile");
         if (!testFile.getParentFile().exists()) {
             throw new IOException("Cannot create file " + testFile + " as the parent directory does not exist");
         }
@@ -2086,8 +2080,8 @@ public class FileUtilsTest {
             IOUtils.closeQuietly(output);
         }
         assertThrows(IllegalArgumentException.class, () -> FileUtils.moveDirectory(testFile, new File("foo")));
-        final File testSrcFile = new File(temporaryFolder, "testMoveDirectorySource");
-        final File testDestFile = new File(temporaryFolder, "testMoveDirectoryDest");
+        final File testSrcFile = new File(tempDirFile, "testMoveDirectorySource");
+        final File testDestFile = new File(tempDirFile, "testMoveDirectoryDest");
         testSrcFile.mkdir();
         testDestFile.mkdir();
         assertThrows(FileExistsException.class, () -> FileUtils.moveDirectory(testSrcFile, testDestFile),
@@ -2097,7 +2091,7 @@ public class FileUtilsTest {
 
     @Test
     public void testMoveDirectory_Rename() throws Exception {
-        final File dir = temporaryFolder;
+        final File dir = tempDirFile;
         final File src = new File(dir, "testMoveDirectory1Source");
         final File testDir = new File(src, "foo");
         final File testFile = new File(testDir, "bar");
@@ -2130,7 +2124,7 @@ public class FileUtilsTest {
 
     @Test
     public void testMoveDirectoryToDirectory() throws Exception {
-        final File dir = temporaryFolder;
+        final File dir = tempDirFile;
         final File src = new File(dir, "testMoveDirectory1Source");
         final File testChildDir = new File(src, "foo");
         final File testFile = new File(testChildDir, "bar");
@@ -2168,8 +2162,8 @@ public class FileUtilsTest {
     public void testMoveDirectoryToDirectory_Errors() throws Exception {
         assertThrows(NullPointerException.class, () -> FileUtils.moveDirectoryToDirectory(null, new File("foo"), true));
         assertThrows(NullPointerException.class, () -> FileUtils.moveDirectoryToDirectory(new File("foo"), null, true));
-        final File testFile1 = new File(temporaryFolder, "testMoveFileFile1");
-        final File testFile2 = new File(temporaryFolder, "testMoveFileFile2");
+        final File testFile1 = new File(tempDirFile, "testMoveFileFile1");
+        final File testFile2 = new File(tempDirFile, "testMoveFileFile2");
         if (!testFile1.getParentFile().exists()) {
             throw new IOException("Cannot create file " + testFile1 + " as the parent directory does not exist");
         }
@@ -2190,13 +2184,13 @@ public class FileUtilsTest {
         }
         assertThrows(IOException.class, () -> FileUtils.moveDirectoryToDirectory(testFile1, testFile2, true));
 
-        final File nonexistant = new File(temporaryFolder, "testMoveFileNonExistant");
+        final File nonexistant = new File(tempDirFile, "testMoveFileNonExistant");
         assertThrows(IOException.class, () -> FileUtils.moveDirectoryToDirectory(testFile1, nonexistant, false));
     }
 
     @Test
     public void testMoveFile_CopyDelete() throws Exception {
-        final File destination = new File(temporaryFolder, "move2.txt");
+        final File destination = new File(tempDirFile, "move2.txt");
         final File src = new File(testFile1.getAbsolutePath()) {
             private static final long serialVersionUID = 1L;
 
@@ -2214,7 +2208,7 @@ public class FileUtilsTest {
 
     @Test
     public void testMoveFile_CopyDelete_Failed() throws Exception {
-        final File destination = new File(temporaryFolder, "move3.txt");
+        final File destination = new File(tempDirFile, "move3.txt");
         final File src = new File(testFile1.getAbsolutePath()) {
             private static final long serialVersionUID = 1L;
 
@@ -2240,7 +2234,7 @@ public class FileUtilsTest {
 
     @Test
     public void testMoveFile_CopyDelete_WithFileDatePreservation() throws Exception {
-        final File destination = new File(temporaryFolder, "move2.txt");
+        final File destination = new File(tempDirFile, "move2.txt");
 
         backDateFile10Minutes(testFile1); // set test file back 10 minutes
 
@@ -2267,7 +2261,7 @@ public class FileUtilsTest {
 
     @Test
     public void testMoveFile_CopyDelete_WithoutFileDatePreservation() throws Exception {
-        final File destination = new File(temporaryFolder, "move2.txt");
+        final File destination = new File(tempDirFile, "move2.txt");
 
         backDateFile10Minutes(testFile1); // set test file back 10 minutes
 
@@ -2306,9 +2300,9 @@ public class FileUtilsTest {
         assertThrows(NullPointerException.class, () -> FileUtils.moveFile(null, new File("foo")));
         assertThrows(NullPointerException.class, () -> FileUtils.moveFile(new File("foo"), null));
         assertThrows(FileNotFoundException.class, () -> FileUtils.moveFile(new File("nonexistant"), new File("foo")));
-        assertThrows(IllegalArgumentException.class, () -> FileUtils.moveFile(temporaryFolder, new File("foo")));
-        final File testSourceFile = new File(temporaryFolder, "testMoveFileSource");
-        final File testDestFile = new File(temporaryFolder, "testMoveFileSource");
+        assertThrows(IllegalArgumentException.class, () -> FileUtils.moveFile(tempDirFile, new File("foo")));
+        final File testSourceFile = new File(tempDirFile, "testMoveFileSource");
+        final File testDestFile = new File(tempDirFile, "testMoveFileSource");
         if (!testSourceFile.getParentFile().exists()) {
             throw new IOException("Cannot create file " + testSourceFile + " as the parent directory does not exist");
         }
@@ -2331,7 +2325,7 @@ public class FileUtilsTest {
 
     @Test
     public void testMoveFile_Rename() throws Exception {
-        final File destination = new File(temporaryFolder, "move1.txt");
+        final File destination = new File(tempDirFile, "move1.txt");
 
         FileUtils.moveFile(testFile1, destination);
         assertTrue(destination.exists(), "Check Exist");
@@ -2340,7 +2334,7 @@ public class FileUtilsTest {
 
     @Test
     public void testMoveFileToDirectory() throws Exception {
-        final File destDir = new File(temporaryFolder, "moveFileDestDir");
+        final File destDir = new File(tempDirFile, "moveFileDestDir");
         final File movedFile = new File(destDir, testFile1.getName());
         assertFalse(destDir.exists(), "Check Exist before");
         assertFalse(movedFile.exists(), "Check Exist before");
@@ -2354,8 +2348,8 @@ public class FileUtilsTest {
     public void testMoveFileToDirectory_Errors() throws Exception {
         assertThrows(NullPointerException.class, () -> FileUtils.moveFileToDirectory(null, new File("foo"), true));
         assertThrows(NullPointerException.class, () -> FileUtils.moveFileToDirectory(new File("foo"), null, true));
-        final File testFile1 = new File(temporaryFolder, "testMoveFileFile1");
-        final File testFile2 = new File(temporaryFolder, "testMoveFileFile2");
+        final File testFile1 = new File(tempDirFile, "testMoveFileFile1");
+        final File testFile2 = new File(tempDirFile, "testMoveFileFile2");
         if (!testFile1.getParentFile().exists()) {
             throw new IOException("Cannot create file " + testFile1
                     + " as the parent directory does not exist");
@@ -2380,15 +2374,15 @@ public class FileUtilsTest {
         }
         assertThrows(IllegalArgumentException.class, () -> FileUtils.moveFileToDirectory(testFile1, testFile2, true));
 
-        final File nonexistant = new File(temporaryFolder, "testMoveFileNonExistant");
+        final File nonexistant = new File(tempDirFile, "testMoveFileNonExistant");
         assertThrows(IOException.class, () -> FileUtils.moveFileToDirectory(testFile1, nonexistant, false));
     }
 
     @Test
     public void testMoveToDirectory() throws Exception {
-        final File destDir = new File(temporaryFolder, "testMoveToDirectoryDestDir");
-        final File testDir = new File(temporaryFolder, "testMoveToDirectoryTestDir");
-        final File testFile = new File(temporaryFolder, "testMoveToDirectoryTestFile");
+        final File destDir = new File(tempDirFile, "testMoveToDirectoryDestDir");
+        final File testDir = new File(tempDirFile, "testMoveToDirectoryTestDir");
+        final File testFile = new File(tempDirFile, "testMoveToDirectoryTestFile");
         testDir.mkdirs();
         if (!testFile.getParentFile().exists()) {
             throw new IOException("Cannot create file " + testFile
@@ -2422,15 +2416,15 @@ public class FileUtilsTest {
     public void testMoveToDirectory_Errors() throws Exception {
         assertThrows(NullPointerException.class, () -> FileUtils.moveDirectoryToDirectory(null, new File("foo"), true));
         assertThrows(NullPointerException.class, () -> FileUtils.moveDirectoryToDirectory(new File("foo"), null, true));
-        final File nonexistant = new File(temporaryFolder, "nonexistant");
-        final File destDir = new File(temporaryFolder, "MoveToDirectoryDestDir");
+        final File nonexistant = new File(tempDirFile, "nonexistant");
+        final File destDir = new File(tempDirFile, "MoveToDirectoryDestDir");
         assertThrows(IOException.class, () -> FileUtils.moveToDirectory(nonexistant, destDir, true), "Expected IOException when source does not exist");
 
     }
 
     @Test
     public void testReadFileToByteArray() throws Exception {
-        final File file = new File(temporaryFolder, "read.txt");
+        final File file = new File(tempDirFile, "read.txt");
         final OutputStream out = Files.newOutputStream(file.toPath());
         out.write(11);
         out.write(21);
@@ -2446,7 +2440,7 @@ public class FileUtilsTest {
 
     @Test
     public void testReadFileToStringWithDefaultEncoding() throws Exception {
-        final File file = new File(temporaryFolder, "read.obj");
+        final File file = new File(tempDirFile, "read.obj");
         final OutputStream out = Files.newOutputStream(file.toPath());
         final byte[] text = "Hello /u1234".getBytes();
         out.write(text);
@@ -2459,7 +2453,7 @@ public class FileUtilsTest {
 
     @Test
     public void testReadFileToStringWithEncoding() throws Exception {
-        final File file = new File(temporaryFolder, "read.obj");
+        final File file = new File(tempDirFile, "read.obj");
         final OutputStream out = Files.newOutputStream(file.toPath());
         final byte[] text = "Hello /u1234".getBytes(StandardCharsets.UTF_8);
         out.write(text);
@@ -2471,7 +2465,7 @@ public class FileUtilsTest {
 
     @Test
     public void testReadLines() throws Exception {
-        final File file = TestUtils.newFile(temporaryFolder, "lines.txt");
+        final File file = TestUtils.newFile(tempDirFile, "lines.txt");
         try {
             final String[] data = {"hello", "/u1234", "", "this is", "some text"};
             TestUtils.createLineBasedFile(file, data);
@@ -2485,7 +2479,7 @@ public class FileUtilsTest {
 
     @Test
     public void testSizeOf() throws Exception {
-        final File file = new File(temporaryFolder, getName());
+        final File file = new File(tempDirFile, getName());
 
         // Null argument
         assertThrows(NullPointerException.class, () -> FileUtils.sizeOf(null));
@@ -2505,12 +2499,12 @@ public class FileUtilsTest {
         assertEquals(testFile1Size, FileUtils.sizeOf(testFile1), "Unexpected files size");
 
         // Existing directory
-        assertEquals(TEST_DIRECTORY_SIZE, FileUtils.sizeOf(temporaryFolder), "Unexpected directory size");
+        assertEquals(TEST_DIRECTORY_SIZE, FileUtils.sizeOf(tempDirFile), "Unexpected directory size");
     }
 
     @Test
     public void testSizeOfAsBigInteger() throws Exception {
-        final File file = new File(temporaryFolder, getName());
+        final File file = new File(tempDirFile, getName());
 
         // Null argument
         assertThrows(NullPointerException.class, () -> FileUtils.sizeOfAsBigInteger(null));
@@ -2530,13 +2524,13 @@ public class FileUtilsTest {
                 "Unexpected files size");
 
         // Existing directory
-        assertEquals(TEST_DIRECTORY_SIZE_BI, FileUtils.sizeOfAsBigInteger(temporaryFolder),
+        assertEquals(TEST_DIRECTORY_SIZE_BI, FileUtils.sizeOfAsBigInteger(tempDirFile),
                 "Unexpected directory size");
     }
 
     @Test
     public void testSizeOfDirectory() throws Exception {
-        final File file = new File(temporaryFolder, getName());
+        final File file = new File(tempDirFile, getName());
 
         // Null argument
         assertThrows(NullPointerException.class, () -> FileUtils.sizeOfDirectory(null));
@@ -2561,7 +2555,7 @@ public class FileUtilsTest {
 
     @Test
     public void testSizeOfDirectoryAsBigInteger() throws Exception {
-        final File file = new File(temporaryFolder, getName());
+        final File file = new File(tempDirFile, getName());
 
         // Null argument
         assertThrows(NullPointerException.class, () -> FileUtils.sizeOfDirectoryAsBigInteger(null));
@@ -2698,7 +2692,7 @@ public class FileUtilsTest {
 
     @Test
     public void testTouch() throws IOException {
-        final File file = new File(temporaryFolder, "touch.txt");
+        final File file = new File(tempDirFile, "touch.txt");
         if (file.exists()) {
             file.delete();
         }
@@ -2726,9 +2720,9 @@ public class FileUtilsTest {
     @Test
     public void testToURLs1() throws Exception {
         final File[] files = {
-                new File(temporaryFolder, "file1.txt"),
-                new File(temporaryFolder, "file2.txt"),
-                new File(temporaryFolder, "test file.txt"),
+                new File(tempDirFile, "file1.txt"),
+                new File(tempDirFile, "file2.txt"),
+                new File(tempDirFile, "test file.txt"),
         };
         final URL[] urls = FileUtils.toURLs(files);
 
@@ -2753,7 +2747,7 @@ public class FileUtilsTest {
 
     @Test
     public void testWrite_WithAppendOptionFalse_ShouldDeletePreviousFileLines() throws Exception {
-        final File file = TestUtils.newFile(temporaryFolder, "lines.txt");
+        final File file = TestUtils.newFile(tempDirFile, "lines.txt");
         FileUtils.writeStringToFile(file, "This line was there before you...");
 
         FileUtils.write(file, "this is brand new data", false);
@@ -2765,7 +2759,7 @@ public class FileUtilsTest {
 
     @Test
     public void testWrite_WithAppendOptionTrue_ShouldNotDeletePreviousFileLines() throws Exception {
-        final File file = TestUtils.newFile(temporaryFolder, "lines.txt");
+        final File file = TestUtils.newFile(tempDirFile, "lines.txt");
         FileUtils.writeStringToFile(file, "This line was there before you...");
 
         FileUtils.write(file, "this is brand new data", true);
@@ -2778,7 +2772,7 @@ public class FileUtilsTest {
 
     @Test
     public void testWriteByteArrayToFile() throws Exception {
-        final File file = new File(temporaryFolder, "write.obj");
+        final File file = new File(tempDirFile, "write.obj");
         final byte[] data = {11, 21, 31};
         FileUtils.writeByteArrayToFile(file, data);
         TestUtils.assertEqualContent(data, file);
@@ -2786,7 +2780,7 @@ public class FileUtilsTest {
 
     @Test
     public void testWriteByteArrayToFile_WithAppendOptionFalse_ShouldDeletePreviousFileLines() throws Exception {
-        final File file = TestUtils.newFile(temporaryFolder, "lines.txt");
+        final File file = TestUtils.newFile(tempDirFile, "lines.txt");
         FileUtils.writeStringToFile(file, "This line was there before you...");
 
         FileUtils.writeByteArrayToFile(file, "this is brand new data".getBytes(), false);
@@ -2798,7 +2792,7 @@ public class FileUtilsTest {
 
     @Test
     public void testWriteByteArrayToFile_WithAppendOptionTrue_ShouldNotDeletePreviousFileLines() throws Exception {
-        final File file = TestUtils.newFile(temporaryFolder, "lines.txt");
+        final File file = TestUtils.newFile(tempDirFile, "lines.txt");
         FileUtils.writeStringToFile(file, "This line was there before you...");
 
         FileUtils.writeByteArrayToFile(file, "this is brand new data".getBytes(), true);
@@ -2811,7 +2805,7 @@ public class FileUtilsTest {
 
     @Test
     public void testWriteByteArrayToFile_WithOffsetAndLength() throws Exception {
-        final File file = new File(temporaryFolder, "write.obj");
+        final File file = new File(tempDirFile, "write.obj");
         final byte[] data = {11, 21, 32, 41, 51};
         final byte[] writtenData = new byte[3];
         System.arraycopy(data, 1, writtenData, 0, 3);
@@ -2821,7 +2815,7 @@ public class FileUtilsTest {
 
     @Test
     public void testWriteByteArrayToFile_WithOffsetAndLength_WithAppendOptionTrue_ShouldDeletePreviousFileLines() throws Exception {
-        final File file = TestUtils.newFile(temporaryFolder, "lines.txt");
+        final File file = TestUtils.newFile(tempDirFile, "lines.txt");
         FileUtils.writeStringToFile(file, "This line was there before you...");
 
         final byte[] data = "SKIP_THIS_this is brand new data_AND_SKIP_THIS".getBytes(StandardCharsets.UTF_8);
@@ -2834,7 +2828,7 @@ public class FileUtilsTest {
 
     @Test
     public void testWriteByteArrayToFile_WithOffsetAndLength_WithAppendOptionTrue_ShouldNotDeletePreviousFileLines() throws Exception {
-        final File file = TestUtils.newFile(temporaryFolder, "lines.txt");
+        final File file = TestUtils.newFile(tempDirFile, "lines.txt");
         FileUtils.writeStringToFile(file, "This line was there before you...");
 
         final byte[] data = "SKIP_THIS_this is brand new data_AND_SKIP_THIS".getBytes(StandardCharsets.UTF_8);
@@ -2847,7 +2841,7 @@ public class FileUtilsTest {
 
     @Test
     public void testWriteCharSequence1() throws Exception {
-        final File file = new File(temporaryFolder, "write.txt");
+        final File file = new File(tempDirFile, "write.txt");
         FileUtils.write(file, "Hello /u1234", "UTF8");
         final byte[] text = "Hello /u1234".getBytes(StandardCharsets.UTF_8);
         TestUtils.assertEqualContent(text, file);
@@ -2855,7 +2849,7 @@ public class FileUtilsTest {
 
     @Test
     public void testWriteCharSequence2() throws Exception {
-        final File file = new File(temporaryFolder, "write.txt");
+        final File file = new File(tempDirFile, "write.txt");
         FileUtils.write(file, "Hello /u1234", (String) null);
         final byte[] text = "Hello /u1234".getBytes();
         TestUtils.assertEqualContent(text, file);
@@ -2867,7 +2861,7 @@ public class FileUtilsTest {
                 "hello", new StringBuffer("world"), "", "this is", null, "some text"};
         final List<Object> list = Arrays.asList(data);
 
-        final File file = TestUtils.newFile(temporaryFolder, "lines.txt");
+        final File file = TestUtils.newFile(tempDirFile, "lines.txt");
         FileUtils.writeLines(file, "US-ASCII", list);
 
         final String expected = "hello" + System.lineSeparator() + "world" + System.lineSeparator() +
@@ -2879,7 +2873,7 @@ public class FileUtilsTest {
 
     @Test
     public void testWriteLines_3argsWithAppendOptionFalse_ShouldDeletePreviousFileLines() throws Exception {
-        final File file = TestUtils.newFile(temporaryFolder, "lines.txt");
+        final File file = TestUtils.newFile(tempDirFile, "lines.txt");
         FileUtils.writeStringToFile(file, "This line was there before you...");
 
         final List<String> linesToAppend = Arrays.asList("my first line", "The second Line");
@@ -2894,7 +2888,7 @@ public class FileUtilsTest {
 
     @Test
     public void testWriteLines_3argsWithAppendOptionTrue_ShouldNotDeletePreviousFileLines() throws Exception {
-        final File file = TestUtils.newFile(temporaryFolder, "lines.txt");
+        final File file = TestUtils.newFile(tempDirFile, "lines.txt");
         FileUtils.writeStringToFile(file, "This line was there before you...");
 
         final List<String> linesToAppend = Arrays.asList("my first line", "The second Line");
@@ -2915,7 +2909,7 @@ public class FileUtilsTest {
                 "hello", new StringBuffer("world"), "", "this is", null, "some text"};
         final List<Object> list = Arrays.asList(data);
 
-        final File file = TestUtils.newFile(temporaryFolder, "lines.txt");
+        final File file = TestUtils.newFile(tempDirFile, "lines.txt");
         FileUtils.writeLines(file, "US-ASCII", list, "*");
 
         final String expected = "hello*world**this is**some text*";
@@ -2929,7 +2923,7 @@ public class FileUtilsTest {
                 "hello", new StringBuffer("world"), "", "this is", null, "some text"};
         final List<Object> list = Arrays.asList(data);
 
-        final File file = TestUtils.newFile(temporaryFolder, "lines.txt");
+        final File file = TestUtils.newFile(tempDirFile, "lines.txt");
         FileUtils.writeLines(file, "US-ASCII", list, null);
 
         final String expected = "hello" + System.lineSeparator() + "world" + System.lineSeparator() +
@@ -2941,7 +2935,7 @@ public class FileUtilsTest {
 
     @Test
     public void testWriteLines_4arg_Writer_nullData() throws Exception {
-        final File file = TestUtils.newFile(temporaryFolder, "lines.txt");
+        final File file = TestUtils.newFile(tempDirFile, "lines.txt");
         FileUtils.writeLines(file, "US-ASCII", null, "*");
 
         assertEquals(0, file.length(), "Sizes differ");
@@ -2949,7 +2943,7 @@ public class FileUtilsTest {
 
     @Test
     public void testWriteLines_4argsWithAppendOptionFalse_ShouldDeletePreviousFileLines() throws Exception {
-        final File file = TestUtils.newFile(temporaryFolder, "lines.txt");
+        final File file = TestUtils.newFile(tempDirFile, "lines.txt");
         FileUtils.writeStringToFile(file, "This line was there before you...");
 
         final List<String> linesToAppend = Arrays.asList("my first line", "The second Line");
@@ -2964,7 +2958,7 @@ public class FileUtilsTest {
 
     @Test
     public void testWriteLines_4argsWithAppendOptionTrue_ShouldNotDeletePreviousFileLines() throws Exception {
-        final File file = TestUtils.newFile(temporaryFolder, "lines.txt");
+        final File file = TestUtils.newFile(tempDirFile, "lines.txt");
         FileUtils.writeStringToFile(file, "This line was there before you...");
 
         final List<String> linesToAppend = Arrays.asList("my first line", "The second Line");
@@ -2980,7 +2974,7 @@ public class FileUtilsTest {
 
     @Test
     public void testWriteLines_5argsWithAppendOptionFalse_ShouldDeletePreviousFileLines() throws Exception {
-        final File file = TestUtils.newFile(temporaryFolder, "lines.txt");
+        final File file = TestUtils.newFile(tempDirFile, "lines.txt");
         FileUtils.writeStringToFile(file, "This line was there before you...");
 
         final List<String> linesToAppend = Arrays.asList("my first line", "The second Line");
@@ -2995,7 +2989,7 @@ public class FileUtilsTest {
 
     @Test
     public void testWriteLines_5argsWithAppendOptionTrue_ShouldNotDeletePreviousFileLines() throws Exception {
-        final File file = TestUtils.newFile(temporaryFolder, "lines.txt");
+        final File file = TestUtils.newFile(tempDirFile, "lines.txt");
         FileUtils.writeStringToFile(file, "This line was there before you...");
 
         final List<String> linesToAppend = Arrays.asList("my first line", "The second Line");
@@ -3011,7 +3005,7 @@ public class FileUtilsTest {
 
     @Test
     public void testWriteLinesEncoding_WithAppendOptionFalse_ShouldDeletePreviousFileLines() throws Exception {
-        final File file = TestUtils.newFile(temporaryFolder, "lines.txt");
+        final File file = TestUtils.newFile(tempDirFile, "lines.txt");
         FileUtils.writeStringToFile(file, "This line was there before you...");
 
         final List<String> linesToAppend = Arrays.asList("my first line", "The second Line");
@@ -3026,7 +3020,7 @@ public class FileUtilsTest {
 
     @Test
     public void testWriteLinesEncoding_WithAppendOptionTrue_ShouldNotDeletePreviousFileLines() throws Exception {
-        final File file = TestUtils.newFile(temporaryFolder, "lines.txt");
+        final File file = TestUtils.newFile(tempDirFile, "lines.txt");
         FileUtils.writeStringToFile(file, "This line was there before you...");
 
         final List<String> linesToAppend = Arrays.asList("my first line", "The second Line");
@@ -3042,7 +3036,7 @@ public class FileUtilsTest {
 
     @Test
     public void testWriteStringToFile_WithAppendOptionFalse_ShouldDeletePreviousFileLines() throws Exception {
-        final File file = TestUtils.newFile(temporaryFolder, "lines.txt");
+        final File file = TestUtils.newFile(tempDirFile, "lines.txt");
         FileUtils.writeStringToFile(file, "This line was there before you...");
 
         FileUtils.writeStringToFile(file, "this is brand new data", false);
@@ -3054,7 +3048,7 @@ public class FileUtilsTest {
 
     @Test
     public void testWriteStringToFile_WithAppendOptionTrue_ShouldNotDeletePreviousFileLines() throws Exception {
-        final File file = TestUtils.newFile(temporaryFolder, "lines.txt");
+        final File file = TestUtils.newFile(tempDirFile, "lines.txt");
         FileUtils.writeStringToFile(file, "This line was there before you...");
 
         FileUtils.writeStringToFile(file, "this is brand new data", true);
@@ -3067,7 +3061,7 @@ public class FileUtilsTest {
 
     @Test
     public void testWriteStringToFile1() throws Exception {
-        final File file = new File(temporaryFolder, "write.txt");
+        final File file = new File(tempDirFile, "write.txt");
         FileUtils.writeStringToFile(file, "Hello /u1234", "UTF8");
         final byte[] text = "Hello /u1234".getBytes(StandardCharsets.UTF_8);
         TestUtils.assertEqualContent(text, file);
@@ -3075,7 +3069,7 @@ public class FileUtilsTest {
 
     @Test
     public void testWriteStringToFile2() throws Exception {
-        final File file = new File(temporaryFolder, "write.txt");
+        final File file = new File(tempDirFile, "write.txt");
         FileUtils.writeStringToFile(file, "Hello /u1234", (String) null);
         final byte[] text = "Hello /u1234".getBytes();
         TestUtils.assertEqualContent(text, file);
@@ -3083,7 +3077,7 @@ public class FileUtilsTest {
 
     @Test
     public void testWriteStringToFile3() throws Exception {
-        final File file = new File(temporaryFolder, "write.txt");
+        final File file = new File(tempDirFile, "write.txt");
         FileUtils.writeStringToFile(file, "Hello /u1234", (Charset) null);
         final byte[] text = "Hello /u1234".getBytes();
         TestUtils.assertEqualContent(text, file);
@@ -3091,7 +3085,7 @@ public class FileUtilsTest {
 
     @Test
     public void testWriteStringToFile4() throws Exception {
-        final File file = new File(temporaryFolder, "subdir/write.txt");
+        final File file = new File(tempDirFile, "subdir/write.txt");
         FileUtils.writeStringToFile(file, "Hello /u1234", (Charset) null);
         final byte[] text = "Hello /u1234".getBytes();
         TestUtils.assertEqualContent(text, file);
@@ -3099,7 +3093,7 @@ public class FileUtilsTest {
 
     @Test
     public void testWriteStringToFileWithEncoding_WithAppendOptionFalse_ShouldDeletePreviousFileLines() throws Exception {
-        final File file = TestUtils.newFile(temporaryFolder, "lines.txt");
+        final File file = TestUtils.newFile(tempDirFile, "lines.txt");
         FileUtils.writeStringToFile(file, "This line was there before you...");
 
         FileUtils.writeStringToFile(file, "this is brand new data", (String) null, false);
@@ -3111,7 +3105,7 @@ public class FileUtilsTest {
 
     @Test
     public void testWriteStringToFileWithEncoding_WithAppendOptionTrue_ShouldNotDeletePreviousFileLines() throws Exception {
-        final File file = TestUtils.newFile(temporaryFolder, "lines.txt");
+        final File file = TestUtils.newFile(tempDirFile, "lines.txt");
         FileUtils.writeStringToFile(file, "This line was there before you...");
 
         FileUtils.writeStringToFile(file, "this is brand new data", (String) null, true);
@@ -3124,7 +3118,7 @@ public class FileUtilsTest {
 
     @Test
     public void testWriteWithEncoding_WithAppendOptionFalse_ShouldDeletePreviousFileLines() throws Exception {
-        final File file = TestUtils.newFile(temporaryFolder, "lines.txt");
+        final File file = TestUtils.newFile(tempDirFile, "lines.txt");
         FileUtils.writeStringToFile(file, "This line was there before you...");
 
         FileUtils.write(file, "this is brand new data", (String) null, false);
@@ -3136,7 +3130,7 @@ public class FileUtilsTest {
 
     @Test
     public void testWriteWithEncoding_WithAppendOptionTrue_ShouldNotDeletePreviousFileLines() throws Exception {
-        final File file = TestUtils.newFile(temporaryFolder, "lines.txt");
+        final File file = TestUtils.newFile(tempDirFile, "lines.txt");
         FileUtils.writeStringToFile(file, "This line was there before you...");
 
         FileUtils.write(file, "this is brand new data", (String) null, true);
diff --git a/src/test/java/org/apache/commons/io/file/AbstractTempDirTest.java b/src/test/java/org/apache/commons/io/file/AbstractTempDirTest.java
new file mode 100644
index 0000000..b25ad58
--- /dev/null
+++ b/src/test/java/org/apache/commons/io/file/AbstractTempDirTest.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.io.file;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.io.TempDir;
+
+public abstract class AbstractTempDirTest {
+
+    /**
+     * A temporary directory managed by JUnit.
+     */
+    @TempDir
+    public Path managedTempDirPath;
+
+    /**
+     * A temporary directory managed by each test so we can optionally fiddle with its permissions independently.
+     */
+    public Path tempDirPath;
+
+    /**
+     * A File version of this test's Path object.
+     */
+    public File tempDirFile;
+
+    @BeforeEach
+    public void beforeEachCreateTempDirs() throws IOException {
+        tempDirPath = Files.createTempDirectory(managedTempDirPath, getClass().getSimpleName());
+        tempDirFile = tempDirPath.toFile();
+    }
+
+}
diff --git a/src/test/java/org/apache/commons/io/file/PathUtilsDeleteDirectoryTest.java b/src/test/java/org/apache/commons/io/file/PathUtilsDeleteDirectoryTest.java
index 441b9bd..121b2dc 100644
--- a/src/test/java/org/apache/commons/io/file/PathUtilsDeleteDirectoryTest.java
+++ b/src/test/java/org/apache/commons/io/file/PathUtilsDeleteDirectoryTest.java
@@ -21,42 +21,24 @@ import static org.apache.commons.io.file.CounterAssertions.assertCounts;
 
 import java.io.IOException;
 import java.nio.file.Files;
-import java.nio.file.Path;
 import java.nio.file.Paths;
 
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 /**
  * Tests {@link DeletingPathVisitor}.
  */
-public class PathUtilsDeleteDirectoryTest {
-
-    private Path tempDir;
-
-    @AfterEach
-    public void afterEach() throws IOException {
-        // backstop
-        if (Files.exists(tempDir) && PathUtils.isEmptyDirectory(tempDir)) {
-            Files.deleteIfExists(tempDir);
-        }
-    }
-
-    @BeforeEach
-    public void beforeEach() throws IOException {
-        tempDir = Files.createTempDirectory(getClass().getCanonicalName());
-    }
+public class PathUtilsDeleteDirectoryTest extends AbstractTempDirTest {
 
     /**
      * Tests a directory with one file of size 0.
      */
     @Test
     public void testDeleteDirectory1FileSize0() throws IOException {
-        PathUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-0"), tempDir);
-        assertCounts(1, 1, 0, PathUtils.deleteDirectory(tempDir));
+        PathUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-0"), tempDirPath);
+        assertCounts(1, 1, 0, PathUtils.deleteDirectory(tempDirPath));
         // This will throw if not empty.
-        Files.deleteIfExists(tempDir);
+        Files.deleteIfExists(tempDirPath);
     }
 
     /**
@@ -64,10 +46,10 @@ public class PathUtilsDeleteDirectoryTest {
      */
     private void testDeleteDirectory1FileSize0(final DeleteOption... options) throws IOException {
         // TODO Setup the test to use FileVisitOption.
-        PathUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-0"), tempDir);
-        assertCounts(1, 1, 0, PathUtils.deleteDirectory(tempDir, options));
+        PathUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-0"), tempDirPath);
+        assertCounts(1, 1, 0, PathUtils.deleteDirectory(tempDirPath, options));
         // This will throw if not empty.
-        Files.deleteIfExists(tempDir);
+        Files.deleteIfExists(tempDirPath);
     }
 
     @Test
@@ -85,10 +67,10 @@ public class PathUtilsDeleteDirectoryTest {
      */
     @Test
     public void testDeleteDirectory1FileSize1() throws IOException {
-        PathUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1"), tempDir);
-        assertCounts(1, 1, 1, PathUtils.deleteDirectory(tempDir));
+        PathUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1"), tempDirPath);
+        assertCounts(1, 1, 1, PathUtils.deleteDirectory(tempDirPath));
         // This will throw if not empty.
-        Files.deleteIfExists(tempDir);
+        Files.deleteIfExists(tempDirPath);
     }
 
     /**
@@ -96,10 +78,10 @@ public class PathUtilsDeleteDirectoryTest {
      */
     @Test
     public void testDeleteDirectory2FileSize2() throws IOException {
-        PathUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-2-file-size-2"), tempDir);
-        assertCounts(3, 2, 2, PathUtils.deleteDirectory(tempDir));
+        PathUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-2-file-size-2"), tempDirPath);
+        assertCounts(3, 2, 2, PathUtils.deleteDirectory(tempDirPath));
         // This will throw if not empty.
-        Files.deleteIfExists(tempDir);
+        Files.deleteIfExists(tempDirPath);
     }
 
     /**
@@ -107,8 +89,8 @@ public class PathUtilsDeleteDirectoryTest {
      */
     @Test
     public void testDeleteEmptyDirectory() throws IOException {
-        assertCounts(1, 0, 0, PathUtils.deleteDirectory(tempDir));
+        assertCounts(1, 0, 0, PathUtils.deleteDirectory(tempDirPath));
         // This will throw if not empty.
-        Files.deleteIfExists(tempDir);
+        Files.deleteIfExists(tempDirPath);
     }
 }
diff --git a/src/test/java/org/apache/commons/io/file/PathUtilsDeleteTest.java b/src/test/java/org/apache/commons/io/file/PathUtilsDeleteTest.java
index 61651f2..f916102 100644
--- a/src/test/java/org/apache/commons/io/file/PathUtilsDeleteTest.java
+++ b/src/test/java/org/apache/commons/io/file/PathUtilsDeleteTest.java
@@ -21,54 +21,36 @@ import static org.apache.commons.io.file.CounterAssertions.assertCounts;
 
 import java.io.IOException;
 import java.nio.file.Files;
-import java.nio.file.Path;
 import java.nio.file.Paths;
 
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.file.Counters.PathCounters;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 /**
  * Tests {@link DeletingPathVisitor}.
  */
-public class PathUtilsDeleteTest {
-
-    private Path tempDir;
-
-    @AfterEach
-    public void afterEach() throws IOException {
-        // backstop
-        if (Files.exists(tempDir) && PathUtils.isEmptyDirectory(tempDir)) {
-            Files.deleteIfExists(tempDir);
-        }
-    }
-
-    @BeforeEach
-    public void beforeEach() throws IOException {
-        tempDir = Files.createTempDirectory(getClass().getCanonicalName());
-    }
+public class PathUtilsDeleteTest extends AbstractTempDirTest {
 
     @Test
     public void testDeleteDirectory1FileSize0() throws IOException {
         final String fileName = "file-size-0.bin";
         FileUtils.copyFileToDirectory(
             Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-0/" + fileName).toFile(),
-            tempDir.toFile());
-        assertCounts(0, 1, 0, PathUtils.delete(tempDir.resolve(fileName)));
+            tempDirPath.toFile());
+        assertCounts(0, 1, 0, PathUtils.delete(tempDirPath.resolve(fileName)));
         // This will throw if not empty.
-        Files.deleteIfExists(tempDir);
+        Files.deleteIfExists(tempDirPath);
     }
 
     private void testDeleteDirectory1FileSize0(final DeleteOption... options) throws IOException {
         final String fileName = "file-size-0.bin";
         FileUtils.copyFileToDirectory(
             Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-0/" + fileName).toFile(),
-            tempDir.toFile());
-        assertCounts(0, 1, 0, PathUtils.delete(tempDir.resolve(fileName), options));
+            tempDirPath.toFile());
+        assertCounts(0, 1, 0, PathUtils.delete(tempDirPath.resolve(fileName), options));
         // This will throw if not empty.
-        Files.deleteIfExists(tempDir);
+        Files.deleteIfExists(tempDirPath);
     }
 
     /**
@@ -102,10 +84,10 @@ public class PathUtilsDeleteTest {
         final String fileName = "file-size-1.bin";
         FileUtils.copyFileToDirectory(
             Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1/" + fileName).toFile(),
-            tempDir.toFile());
-        assertCounts(0, 1, 1, PathUtils.delete(tempDir.resolve(fileName)));
+            tempDirPath.toFile());
+        assertCounts(0, 1, 1, PathUtils.delete(tempDirPath.resolve(fileName)));
         // This will throw if not empty.
-        Files.deleteIfExists(tempDir);
+        Files.deleteIfExists(tempDirPath);
     }
 
     private void testDeleteDirectory1FileSize1(final DeleteOption... options) throws IOException {
@@ -113,10 +95,10 @@ public class PathUtilsDeleteTest {
         final String fileName = "file-size-1.bin";
         FileUtils.copyFileToDirectory(
             Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1/" + fileName).toFile(),
-            tempDir.toFile());
-        assertCounts(0, 1, 1, PathUtils.delete(tempDir.resolve(fileName), options));
+            tempDirPath.toFile());
+        assertCounts(0, 1, 1, PathUtils.delete(tempDirPath.resolve(fileName), options));
         // This will throw if not empty.
-        Files.deleteIfExists(tempDir);
+        Files.deleteIfExists(tempDirPath);
     }
 
     /**
@@ -150,18 +132,18 @@ public class PathUtilsDeleteTest {
      */
     @Test
     public void testDeleteEmptyDirectory() throws IOException {
-        testDeleteEmptyDirectory(PathUtils.delete(tempDir));
+        testDeleteEmptyDirectory(PathUtils.delete(tempDirPath));
         // This will throw if not empty.
-        Files.deleteIfExists(tempDir);
+        Files.deleteIfExists(tempDirPath);
     }
 
     /**
      * Tests an empty folder.
      */
     private void testDeleteEmptyDirectory(final DeleteOption... options) throws IOException {
-        testDeleteEmptyDirectory(PathUtils.delete(tempDir, options));
+        testDeleteEmptyDirectory(PathUtils.delete(tempDirPath, options));
         // This will throw if not empty.
-        Files.deleteIfExists(tempDir);
+        Files.deleteIfExists(tempDirPath);
     }
 
     private void testDeleteEmptyDirectory(final PathCounters pathCounts) {
@@ -199,8 +181,8 @@ public class PathUtilsDeleteTest {
      */
     @Test
     public void testDeleteFileDoesNotExist() throws IOException {
-        assertCounts(0, 0, 0, PathUtils.deleteFile(tempDir.resolve("file-does-not-exist.bin")));
+        assertCounts(0, 0, 0, PathUtils.deleteFile(tempDirPath.resolve("file-does-not-exist.bin")));
         // This will throw if not empty.
-        Files.deleteIfExists(tempDir);
+        Files.deleteIfExists(tempDirPath);
     }
 }
diff --git a/src/test/java/org/apache/commons/io/file/PathUtilsTest.java b/src/test/java/org/apache/commons/io/file/PathUtilsTest.java
index 6f311be..40b93a7 100644
--- a/src/test/java/org/apache/commons/io/file/PathUtilsTest.java
+++ b/src/test/java/org/apache/commons/io/file/PathUtilsTest.java
@@ -34,13 +34,10 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.nio.file.attribute.DosFileAttributeView;
-import java.nio.file.attribute.PosixFileAttributeView;
 import java.nio.file.attribute.PosixFileAttributes;
-import java.nio.file.attribute.PosixFilePermission;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
-import java.util.Set;
 
 import org.apache.commons.io.filefilter.NameFileFilter;
 import org.apache.commons.io.test.TestUtils;
@@ -48,12 +45,11 @@ import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.SystemUtils;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
 
 /**
  * Tests {@link PathUtils}.
  */
-public class PathUtilsTest extends TestArguments {
+public class PathUtilsTest extends AbstractTempDirTest {
 
     private static final String STRING_FIXTURE = "Hello World";
 
@@ -65,12 +61,6 @@ public class PathUtilsTest extends TestArguments {
 
     private static final String PATH_FIXTURE = "NOTICE.txt";
 
-    /**
-     * A temporary directory managed by JUnit.
-     */
-    @TempDir
-    public Path tempDir;
-
     private FileSystem openArchive(final Path p, final boolean createNew) throws IOException {
         if (createNew) {
             final Map<String, String> env = new HashMap<>();
@@ -88,19 +78,19 @@ public class PathUtilsTest extends TestArguments {
         try (final FileSystem archive = openArchive(archivePath, false)) {
             // relative jar -> absolute dir
             Path sourceDir = archive.getPath("dir1");
-            PathUtils.copyDirectory(sourceDir, tempDir);
-            assertTrue(Files.exists(tempDir.resolve("f1")));
+            PathUtils.copyDirectory(sourceDir, tempDirPath);
+            assertTrue(Files.exists(tempDirPath.resolve("f1")));
 
             // absolute jar -> absolute dir
             sourceDir = archive.getPath("/next");
-            PathUtils.copyDirectory(sourceDir, tempDir);
-            assertTrue(Files.exists(tempDir.resolve("dir")));
+            PathUtils.copyDirectory(sourceDir, tempDirPath);
+            assertTrue(Files.exists(tempDirPath.resolve("dir")));
         }
     }
 
     @Test
     public void testCopyDirectoryForDifferentFilesystemsWithAbsolutePathReverse() throws IOException {
-        try (final FileSystem archive = openArchive(tempDir.resolve(TEST_JAR_NAME), true)) {
+        try (final FileSystem archive = openArchive(tempDirPath.resolve(TEST_JAR_NAME), true)) {
             // absolute dir -> relative jar
             Path targetDir = archive.getPath("target");
             Files.createDirectory(targetDir);
@@ -118,7 +108,8 @@ public class PathUtilsTest extends TestArguments {
     @Test
     public void testCopyDirectoryForDifferentFilesystemsWithRelativePath() throws IOException {
         final Path archivePath = Paths.get(TEST_JAR_PATH);
-        try (final FileSystem archive = openArchive(archivePath, false); final FileSystem targetArchive = openArchive(tempDir.resolve(TEST_JAR_NAME), true)) {
+        try (final FileSystem archive = openArchive(archivePath, false);
+                final FileSystem targetArchive = openArchive(tempDirPath.resolve(TEST_JAR_NAME), true)) {
             final Path targetDir = targetArchive.getPath("targetDir");
             Files.createDirectory(targetDir);
             // relative jar -> relative dir
@@ -135,7 +126,7 @@ public class PathUtilsTest extends TestArguments {
 
     @Test
     public void testCopyDirectoryForDifferentFilesystemsWithRelativePathReverse() throws IOException {
-        try (final FileSystem archive = openArchive(tempDir.resolve(TEST_JAR_NAME), true)) {
+        try (final FileSystem archive = openArchive(tempDirPath.resolve(TEST_JAR_NAME), true)) {
             // relative dir -> relative jar
             Path targetDir = archive.getPath("target");
             Files.createDirectory(targetDir);
@@ -153,19 +144,19 @@ public class PathUtilsTest extends TestArguments {
     @Test
     public void testCopyFile() throws IOException {
         final Path sourceFile = Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1/file-size-1.bin");
-        final Path targetFile = PathUtils.copyFileToDirectory(sourceFile, tempDir);
+        final Path targetFile = PathUtils.copyFileToDirectory(sourceFile, tempDirPath);
         assertTrue(Files.exists(targetFile));
         assertEquals(Files.size(sourceFile), Files.size(targetFile));
     }
 
     @Test
     public void testCreateDirectoriesAlreadyExists() throws IOException {
-        assertEquals(tempDir.getParent(), PathUtils.createParentDirectories(tempDir));
+        assertEquals(tempDirPath.getParent(), PathUtils.createParentDirectories(tempDirPath));
     }
 
     @Test
     public void testCreateDirectoriesNew() throws IOException {
-        assertEquals(tempDir, PathUtils.createParentDirectories(tempDir.resolve("child")));
+        assertEquals(tempDirPath, PathUtils.createParentDirectories(tempDirPath.resolve("child")));
     }
 
     @Test
@@ -178,8 +169,8 @@ public class PathUtilsTest extends TestArguments {
     public void testIsDirectory() throws IOException {
         assertFalse(PathUtils.isDirectory(null));
 
-        assertTrue(PathUtils.isDirectory(tempDir));
-        final Path testFile1 = Files.createTempFile(tempDir, "prefix", null);
+        assertTrue(PathUtils.isDirectory(tempDirPath));
+        final Path testFile1 = Files.createTempFile(tempDirPath, "prefix", null);
         assertFalse(PathUtils.isDirectory(testFile1));
 
         final Path tempDir = Files.createTempDirectory(getClass().getCanonicalName());
@@ -203,8 +194,8 @@ public class PathUtilsTest extends TestArguments {
     public void testIsRegularFile() throws IOException {
         assertFalse(PathUtils.isRegularFile(null));
 
-        assertFalse(PathUtils.isRegularFile(tempDir));
-        final Path testFile1 = Files.createTempFile(tempDir, "prefix", null);
+        assertFalse(PathUtils.isRegularFile(tempDirPath));
+        final Path testFile1 = Files.createTempFile(tempDirPath, "prefix", null);
         assertTrue(PathUtils.isRegularFile(testFile1));
 
         Files.delete(testFile1);
@@ -279,7 +270,9 @@ public class PathUtilsTest extends TestArguments {
 
     @Test
     public void testSetReadOnlyFile() throws IOException {
-        final Path resolved = tempDir.resolve("testSetReadOnlyFile.txt");
+        final Path resolved = tempDirPath.resolve("testSetReadOnlyFile.txt");
+        // Ask now, as we are allowed before editing parent permissions.
+        final boolean isPosix = PathUtils.isPosix(tempDirPath);
 
         // TEMP HACK
         assumeFalse(SystemUtils.IS_OS_LINUX);
@@ -297,8 +290,13 @@ public class PathUtilsTest extends TestArguments {
         assertTrue(writable);
         // Test A
         PathUtils.setReadOnly(resolved, false);
-        assertTrue(Files.isReadable(resolved));
-        assertTrue(Files.isWritable(resolved));
+        assertTrue(Files.isReadable(resolved), "isReadable");
+        assertTrue(Files.isWritable(resolved), "isWritable");
+        // Again, shouldn't blow up.
+        PathUtils.setReadOnly(resolved, false);
+        assertTrue(Files.isReadable(resolved), "isReadable");
+        assertTrue(Files.isWritable(resolved), "isWritable");
+        //
         assertEquals(regularFile, Files.isReadable(resolved));
         assertEquals(executable, Files.isExecutable(resolved));
         assertEquals(hidden, Files.isHidden(resolved));
@@ -306,21 +304,22 @@ public class PathUtilsTest extends TestArguments {
         assertEquals(symbolicLink, Files.isSymbolicLink(resolved));
         // Test B
         PathUtils.setReadOnly(resolved, true);
-        assertTrue(Files.isReadable(resolved));
-        assertFalse(Files.isWritable(resolved));
+        if (isPosix) {
+            // On POSIX, now that the parent is not WX, the file is not readable.
+            assertFalse(Files.isReadable(resolved), "isReadable");
+        } else {
+            assertTrue(Files.isReadable(resolved), "isReadable");
+        }
+        assertFalse(Files.isWritable(resolved), "isWritable");
         final DosFileAttributeView dosFileAttributeView = PathUtils.getDosFileAttributeView(resolved);
         if (dosFileAttributeView != null) {
             assertTrue(dosFileAttributeView.readAttributes().isReadOnly());
         }
-        final PosixFileAttributeView posixFileAttributeView = PathUtils.getPosixFileAttributeView(resolved);
-        if (posixFileAttributeView != null) {
-            // Not Windows
-            final Set<PosixFilePermission> permissions = posixFileAttributeView.readAttributes().permissions();
-            assertFalse(permissions.contains(PosixFilePermission.GROUP_WRITE), permissions::toString);
-            assertFalse(permissions.contains(PosixFilePermission.OTHERS_WRITE), permissions::toString);
-            assertFalse(permissions.contains(PosixFilePermission.OWNER_WRITE), permissions::toString);
+        if (isPosix) {
+            assertFalse(Files.isReadable(resolved));
+        } else {
+            assertEquals(regularFile, Files.isReadable(resolved));
         }
-        assertEquals(regularFile, Files.isReadable(resolved));
         assertEquals(executable, Files.isExecutable(resolved));
         assertEquals(hidden, Files.isHidden(resolved));
         assertEquals(directory, Files.isDirectory(resolved));
@@ -332,7 +331,7 @@ public class PathUtilsTest extends TestArguments {
 
     @Test
     public void testWriteStringToFile1() throws Exception {
-        final Path file = tempDir.resolve("write.txt");
+        final Path file = tempDirPath.resolve("write.txt");
         PathUtils.writeString(file, "Hello /u1234", StandardCharsets.UTF_8);
         final byte[] text = "Hello /u1234".getBytes(StandardCharsets.UTF_8);
         TestUtils.assertEqualContent(text, file);
@@ -342,7 +341,7 @@ public class PathUtilsTest extends TestArguments {
      * Tests newOutputStream() here and don't use Files.write obviously.
      */
     private Path writeToNewOutputStream(final boolean append) throws IOException {
-        final Path file = tempDir.resolve("test1.txt");
+        final Path file = tempDirPath.resolve("test1.txt");
         try (OutputStream os = PathUtils.newOutputStream(file, append)) {
             os.write(BYTE_ARRAY_FIXTURE);
         }