Matthew Rooney created IO-692:
---------------------------------

             Summary: PathUtil delete throws an exception when deleting a 
"dead" symlink that points to a 
                 Key: IO-692
                 URL: https://issues.apache.org/jira/browse/IO-692
             Project: Commons IO
          Issue Type: Bug
    Affects Versions: 2.8.0
         Environment: {noformat}
java version "1.8.0_202" Java(TM) SE Runtime Environment (build 1.8.0_202-b08) 
Java HotSpot(TM) 64-Bit Server VM (build 25.202-b08, mixed mode)
{noformat}
            Reporter: Matthew Rooney


PathUtils.delete throws an Exception when deleting a symlink to a file that 
doesn't exist, in our case this was when the files were deleted out of sequence.

Minimal reproducing code running as a unit test (scala). This creates a symlink 
to a fail that does not exist at all.
{code:java}
val file = Files.createSymbolicLink(
  Paths.get("target", "x.txt"),
  Paths.get("target",  "y.txt").toAbsolutePath,
)
PathUtils.delete(file)
{code}

This throws the following exception


{noformat}
[error]    java.nio.file.NoSuchFileException: target/x.txt 
(UnixException.java:86)
[error] sun.nio.fs.UnixException.translateToIOException(UnixException.java:86)
[error] sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:102)
[error] sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:107)
[error] 
sun.nio.fs.UnixFileAttributeViews$Basic.readAttributes(UnixFileAttributeViews.java:55)
[error] 
sun.nio.fs.UnixFileSystemProvider.readAttributes(UnixFileSystemProvider.java:144)
[error] org.apache.commons.io.file.PathUtils.deleteFile(PathUtils.java:361)
[error] org.apache.commons.io.file.PathUtils.delete(PathUtils.java:304)
[error] 
org.apache.commons.io.file.PathUtils.delete(PathUtils.java:280){noformat}

The offending code is this in *PathUtils*


{code:java}
public static PathCounters deleteFile(final Path file, final DeleteOption... 
options) throws IOException {
    // Files.deleteIfExists() never follows links, so use 
LinkOption.NOFOLLOW_LINKS in other calls to Files.
    if (Files.isDirectory(file, LinkOption.NOFOLLOW_LINKS)) {
        throw new NoSuchFileException(file.toString());
    }
    final PathCounters pathCounts = Counters.longPathCounters();
    final boolean exists = Files.exists(file, LinkOption.NOFOLLOW_LINKS);
    final long size = exists ? Files.size(file) : 0;
    if (overrideReadOnly(options) && exists) {
        setReadOnly(file, false, LinkOption.NOFOLLOW_LINKS);
    }
    if (Files.deleteIfExists(file)) {
        pathCounts.getFileCounter().increment();
        pathCounts.getByteCounter().add(size);
    }
    return pathCounts;
}
{code}

This manifests because 
{code:java}
Files.exists(file, LinkOption.NOFOLLOW_LINKS); // this always returns true if 
the symlink exists

Files.size(file) // this throws an exception because there is no file to check 
the size of{code}

A guess at the solution would be to only check the size if the file exists and 
is not a symlink


{code:java}
final long size = exists && !Files.isSymbolicLink() ? Files.size(file) : 
0;{code}

This was discovered when using *FileUtils.deleteDirectory* where we have a 
structure like the following. We clean up these directories when the process 
finishes, since upgrading to 2.8.0 this fails if the parent directory is 
deleted before the child.


{code:java}
 work_dir/
   parent_dir/
     big_file.txt
   child_dir/
     symlink_to_big_file.txt{code}

As a work around using *PathUtils.deleteDirectory* seems to work regardless of 
the deletion order

 



--
This message was sent by Atlassian Jira
(v8.3.4#803005)

Reply via email to