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)