3

I was using Apache Commons' FileUtils.deleteDirectory() to recursively delete a folder, and I noticed it follows symlinks.

Is there an alternative that will not follow symlinks, but only delete real files and folders? Alternatively, can I tweak FileUtils to do this?

ripper234
  • 222,824
  • 274
  • 634
  • 905
  • Related question: http://stackoverflow.com/questions/2175673/java-check-symbolic-link-file-existence – Lukas Eder Dec 29 '11 at 10:13
  • @LukasEder - well, yeah. I'll implement it myself, was just wondering if something like this already existed. – ripper234 Dec 29 '11 at 10:15
  • I remember having seen an OS-specific implementation for IO in Java, but I forgot where... Let's wait for another answer – Lukas Eder Dec 29 '11 at 10:20
  • I don't think you can tweak `FileUtils` to do what you want. If you want to implement it your self maybe [DirectoryWalker](http://commons.apache.org/io/apidocs/org/apache/commons/io/DirectoryWalker.html) and `FileUtils.isSymlink(File file)` will speed you up. – Ulrich von Poblotzki Dec 29 '11 at 10:32
  • @LukasEder - odd, it seems that Apache's `FileUtils.isSymlink()` misses a symlink. Java 7's `FileUtils.isSymlink()` identifies it correctly though. – ripper234 Dec 29 '11 at 10:34
  • @UlrichvonPoblotzki - I'm copy-pasting FileUtils.deleteDirectory() - should have the result posted soon. – ripper234 Dec 29 '11 at 10:34
  • http://stackoverflow.com/questions/8666770/are-there-known-discrepencies-between-apache-fileutils-issymlink-and-java7s-fil – ripper234 Dec 29 '11 at 10:37

2 Answers2

2

I shortened ripper234's answer.

/**
 * Recursively deletes `item`, which may be a directory.
 * Symbolic links will be deleted instead of their referents.
 * Returns a boolean indicating whether `item` still exists.
 * http://stackoverflow.com/questions/8666420
 */
public static boolean deleteRecursiveIfExists(File item) {
    if (!item.exists()) return true;
    if (!Files.isSymbolicLink(item.toPath()) && item.isDirectory()) {
        File[] subitems = item.listFiles();
        for (File subitem : subitems)
            if (!deleteRecursiveIfExists(subitem)) return false;
    }
    return item.delete();
}
interestinglythere
  • 1,230
  • 13
  • 16
1

The entire problems seems to stem from an apparant bug in FileUtils.isSymlink() (I just reported it). I copy pasted the code for deleteDirectory(), and used Java 7's API to check for symlinks instead, and it works:

public static void deleteDirectory(File directory) throws IOException {
    // See http://stackoverflow.com/questions/8666420/how-to-recursively-delete-a-folder-without-following-symlinks
    // Copied from http://grepcode.com/file/repo1.maven.org/maven2/commons-io/commons-io/2.1/org/apache/commons/io/FileUtils.java#FileUtils.deleteDirectory%28java.io.File%29
    if (!directory.exists()) {
        return;
    }

    if (!Files.isSymbolicLink(directory.toPath())) {
        cleanDirectory(directory);
    }

    if (!directory.delete()) {
        String message = "Unable to delete directory " + directory + ".";
        throw new IOException(message);
    }
}

private static void cleanDirectory(File directory) throws IOException {
    // Copied from http://grepcode.com/file/repo1.maven.org/maven2/commons-io/commons-io/2.1/org/apache/commons/io/FileUtils.java#FileUtils.cleanDirectory%28java.io.File%29
    if (!directory.exists()) {
        String message = directory + " does not exist";
        throw new IllegalArgumentException(message);
    }

    if (!directory.isDirectory()) {
        String message = directory + " is not a directory";
        throw new IllegalArgumentException(message);
    }

    File[] files = directory.listFiles();
    if (files == null) {  // null if security restricted
        throw new IOException("Failed to list contents of " + directory);
    }

    IOException exception = null;
    for (File file : files) {
        try {
            forceDelete(file);
        } catch (IOException ioe) {
            exception = ioe;
        }
    }

    if (exception != null) {
        throw exception;
    }
}

private static void forceDelete(File file) throws IOException {
    if (file.isDirectory()) {
        deleteDirectory(file);
    } else {
        boolean filePresent = file.exists();
        if (!file.delete()) {
            if (!filePresent) {
                throw new FileNotFoundException("File does not exist: " + file);
            }
            String message =
                    "Unable to delete file: " + file;
            throw new IOException(message);
        }
    }
}
ripper234
  • 222,824
  • 274
  • 634
  • 905