Author: niallp
Date: Mon Aug 11 04:21:38 2008
New Revision: 684715

URL: http://svn.apache.org/viewvc?rev=684715&view=rev
Log:
Fix IO-168 Symbolic links (symlinks) followed when deleting directory - 
reported by Apostolos Lerios, thanks to Attila Szegedi for the solution and 
Brydie McCoy for the patch

Added:
    
commons/proper/io/trunk/src/test/org/apache/commons/io/FileUtilsCleanSymlinksTestCase.java
   (with props)
Modified:
    commons/proper/io/trunk/src/java/org/apache/commons/io/FileUtils.java

Modified: commons/proper/io/trunk/src/java/org/apache/commons/io/FileUtils.java
URL: 
http://svn.apache.org/viewvc/commons/proper/io/trunk/src/java/org/apache/commons/io/FileUtils.java?rev=684715&r1=684714&r2=684715&view=diff
==============================================================================
--- commons/proper/io/trunk/src/java/org/apache/commons/io/FileUtils.java 
(original)
+++ commons/proper/io/trunk/src/java/org/apache/commons/io/FileUtils.java Mon 
Aug 11 04:21:38 2008
@@ -979,7 +979,10 @@
             return;
         }
 
-        cleanDirectory(directory);
+        if (!isSymlink(directory)) {
+            cleanDirectory(directory);
+        }
+
         if (!directory.delete()) {
             String message =
                 "Unable to delete directory " + directory + ".";
@@ -1921,4 +1924,31 @@
         }
     }
 
+    /**
+     * Determines whether the specified file is a link rather than an actual 
file.
+     * Will not return true if there is a symlink anywhere in the path, only 
if the specific file is.
+     *
+     * @param file the file to check
+     * @return true iff the file is a symlink
+     * @throws IOException if an IO error occurs while checking the file
+     * @since 2.0
+     */
+    public static boolean isSymlink(File file) throws IOException {
+        if (file == null) {
+            throw new NullPointerException("File must not be null");
+        }
+        File fileInCanonicalDir = null;
+        if (file.getParent() == null) {
+            fileInCanonicalDir = file;
+        } else {
+            File canonicalDir = file.getParentFile().getCanonicalFile();
+            fileInCanonicalDir = new File(canonicalDir, file.getName());
+        }
+        
+        if 
(fileInCanonicalDir.getCanonicalFile().equals(fileInCanonicalDir.getAbsoluteFile()))
 {
+            return false;
+        } else {
+            return true;
+        }
+    }
 }

Added: 
commons/proper/io/trunk/src/test/org/apache/commons/io/FileUtilsCleanSymlinksTestCase.java
URL: 
http://svn.apache.org/viewvc/commons/proper/io/trunk/src/test/org/apache/commons/io/FileUtilsCleanSymlinksTestCase.java?rev=684715&view=auto
==============================================================================
--- 
commons/proper/io/trunk/src/test/org/apache/commons/io/FileUtilsCleanSymlinksTestCase.java
 (added)
+++ 
commons/proper/io/trunk/src/test/org/apache/commons/io/FileUtilsCleanSymlinksTestCase.java
 Mon Aug 11 04:21:38 2008
@@ -0,0 +1,253 @@
+/*
+ * 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;
+
+import org.apache.commons.io.testtools.FileBasedTestCase;
+
+import java.io.File;
+import java.util.List;
+import java.util.ArrayList;
+
+import junit.textui.TestRunner;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Test cases for FileUtils.cleanDirectory() method that involve symlinks.
+ * & FileUtils.isSymlink(File file)
+ */
+public class FileUtilsCleanSymlinksTestCase extends FileBasedTestCase {
+
+    final File top = getTestDirectory();
+
+    public static void main(String[] args) {
+        TestRunner.run(suite());
+    }
+
+    public static Test suite() {
+        return new TestSuite(FileUtilsCleanSymlinksTestCase.class);
+    }
+
+    public FileUtilsCleanSymlinksTestCase(String name) {
+        super(name);
+    }
+
+    protected void setUp() throws Exception {
+        top.mkdirs();
+    }
+
+    protected void tearDown() throws Exception {
+        FileUtils.deleteDirectory(top);
+    }
+
+    public void testCleanDirWithSymlinkFile() throws Exception {
+        if (System.getProperty("os.name").startsWith("Win")) {
+            // cant create symlinks in windows.
+            return;
+        }
+
+        final File realOuter = new File(top, "realouter");
+        assertTrue(realOuter.mkdirs());
+
+        final File realInner = new File(realOuter, "realinner");
+        assertTrue(realInner.mkdirs());
+
+        final File realFile = new File(realInner, "file1");
+        FileUtils.touch(realFile);
+        assertEquals(1, realInner.list().length);
+
+        final File randomFile = new File(top, "randomfile");
+        FileUtils.touch(randomFile);
+
+        final File symlinkFile = new File(realInner, "fakeinner");
+        setupSymlink(randomFile, symlinkFile);
+
+        assertEquals(2, realInner.list().length);
+
+        // assert contents of the real directory were removed including the 
symlink
+        FileUtils.cleanDirectory(realOuter);
+        assertEquals(0, realOuter.list().length);
+
+        // ensure that the contents of the symlink were NOT removed.
+        assertTrue(randomFile.exists());
+        assertFalse(symlinkFile.exists());
+    }
+
+
+    public void testCleanDirWithASymlinkDir() throws Exception {
+        if (System.getProperty("os.name").startsWith("Win")) {
+            // cant create symlinks in windows.
+            return;
+        }
+
+        final File realOuter = new File(top, "realouter");
+        assertTrue(realOuter.mkdirs());
+
+        final File realInner = new File(realOuter, "realinner");
+        assertTrue(realInner.mkdirs());
+
+        FileUtils.touch(new File(realInner, "file1"));
+        assertEquals(1, realInner.list().length);
+
+        final File randomDirectory = new File(top, "randomDir");
+        assertTrue(randomDirectory.mkdirs());
+
+        FileUtils.touch(new File(randomDirectory, "randomfile"));
+        assertEquals(1, randomDirectory.list().length);
+
+        final File symlinkDirectory = new File(realOuter, "fakeinner");
+        setupSymlink(randomDirectory, symlinkDirectory);
+
+        assertEquals(1, symlinkDirectory.list().length);
+
+        // assert contents of the real directory were removed including the 
symlink
+        FileUtils.cleanDirectory(realOuter);
+        assertEquals(0, realOuter.list().length);
+
+        // ensure that the contents of the symlink were NOT removed.
+        assertEquals("Contents of sym link should not have been removed", 1, 
randomDirectory.list().length);
+    }
+
+    public void testCleanDirWithParentSymlinks() throws Exception {
+        if (System.getProperty("os.name").startsWith("Win")) {
+            // cant create symlinks in windows.
+            return;
+        }
+
+        final File realParent = new File(top, "realparent");
+        assertTrue(realParent.mkdirs());
+
+        final File realInner = new File(realParent, "realinner");
+        assertTrue(realInner.mkdirs());
+
+        FileUtils.touch(new File(realInner, "file1"));
+        assertEquals(1, realInner.list().length);
+
+        final File randomDirectory = new File(top, "randomDir");
+        assertTrue(randomDirectory.mkdirs());
+
+        FileUtils.touch(new File(randomDirectory, "randomfile"));
+        assertEquals(1, randomDirectory.list().length);
+
+        final File symlinkDirectory = new File(realParent, "fakeinner");
+        setupSymlink(randomDirectory, symlinkDirectory);
+
+        assertEquals(1, symlinkDirectory.list().length);
+
+        final File symlinkParentDirectory = new File(top, "fakeouter");
+        setupSymlink(realParent, symlinkParentDirectory);
+
+        // assert contents of the real directory were removed including the 
symlink
+        FileUtils.cleanDirectory(symlinkParentDirectory);// should clean the 
contents of this but not recurse into other links
+        assertEquals(0, symlinkParentDirectory.list().length);
+        assertEquals(0, realParent.list().length);
+
+        // ensure that the contents of the symlink were NOT removed.
+        assertEquals("Contents of sym link should not have been removed", 1, 
randomDirectory.list().length);
+    }
+
+    public void testStillClearsIfGivenDirectoryIsASymlink() throws Exception {
+        if (System.getProperty("os.name").startsWith("Win")) {
+            // cant create symlinks in windows.
+            return;
+        }
+
+        final File randomDirectory = new File(top, "randomDir");
+        assertTrue(randomDirectory.mkdirs());
+
+        FileUtils.touch(new File(randomDirectory, "randomfile"));
+        assertEquals(1, randomDirectory.list().length);
+
+        final File symlinkDirectory = new File(top, "fakeDir");
+        setupSymlink(randomDirectory, symlinkDirectory);
+
+        FileUtils.cleanDirectory(symlinkDirectory);
+        assertEquals(0, symlinkDirectory.list().length);
+        assertEquals(0, randomDirectory.list().length);
+    }
+
+
+    public void testIdentifiesSymlinkDir() throws Exception {
+        if (System.getProperty("os.name").startsWith("Win")) {
+            // cant create symlinks in windows.
+            return;
+        }
+
+        final File randomDirectory = new File(top, "randomDir");
+        assertTrue(randomDirectory.mkdirs());
+
+        final File symlinkDirectory = new File(top, "fakeDir");
+        setupSymlink(randomDirectory, symlinkDirectory);
+
+        assertTrue(FileUtils.isSymlink(symlinkDirectory));
+        assertFalse(FileUtils.isSymlink(randomDirectory));
+    }
+
+    public void testIdentifiesSymlinkFile() throws Exception {
+        if (System.getProperty("os.name").startsWith("Win")) {
+            // cant create symlinks in windows.
+            return;
+        }
+
+        final File randomFile = new File(top, "randomfile");
+        FileUtils.touch(randomFile);
+
+        final File symlinkFile = new File(top, "fakeinner");
+        setupSymlink(randomFile, symlinkFile);
+
+        assertTrue(FileUtils.isSymlink(symlinkFile));
+        assertFalse(FileUtils.isSymlink(randomFile));
+    }
+
+    public void testCorrectlyIdentifySymlinkWithParentSymLink() throws 
Exception {
+        if (System.getProperty("os.name").startsWith("Win")) {
+            // cant create symlinks in windows.
+            return;
+        }
+
+        final File realParent = new File(top, "realparent");
+        assertTrue(realParent.mkdirs());
+
+        final File symlinkParentDirectory = new File(top, "fakeparent");
+        setupSymlink(realParent, symlinkParentDirectory);
+
+        final File realChild = new File(symlinkParentDirectory, "realChild");
+        assertTrue(realChild.mkdirs());
+
+        final File symlinkChild = new File(symlinkParentDirectory, 
"fakeChild");
+        setupSymlink(realChild, symlinkChild);
+
+        assertTrue(FileUtils.isSymlink(symlinkChild));
+        assertFalse(FileUtils.isSymlink(realChild));
+    }
+
+    private void setupSymlink(File res, File link) throws Exception {
+        // create symlink
+        List args = new ArrayList();
+        args.add("ln");
+        args.add("-s");
+
+        args.add(res.getAbsolutePath());
+        args.add(link.getAbsolutePath());
+
+        Process proc;
+
+        proc = Runtime.getRuntime().exec((String[]) args.toArray(new 
String[args.size()]));
+        proc.waitFor();
+    }
+
+}

Propchange: 
commons/proper/io/trunk/src/test/org/apache/commons/io/FileUtilsCleanSymlinksTestCase.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
commons/proper/io/trunk/src/test/org/apache/commons/io/FileUtilsCleanSymlinksTestCase.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL


Reply via email to