1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.io; 18 19 import static org.junit.jupiter.api.Assertions.assertEquals; 20 import static org.junit.jupiter.api.Assertions.assertFalse; 21 import static org.junit.jupiter.api.Assertions.assertTrue; 22 23 import java.io.File; 24 import java.util.ArrayList; 25 import java.util.List; 26 27 import org.apache.commons.lang3.ArrayUtils; 28 import org.junit.jupiter.api.Test; 29 import org.junit.jupiter.api.io.TempDir; 30 31 /** 32 * Test cases for FileUtils.cleanDirectory() method that involve symlinks. 33 * & FileUtils.isSymlink(File file) 34 */ 35 public class FileUtilsCleanSymlinksTest { 36 37 @TempDir 38 public File top; 39 setupSymlink(final File res, final File link)40 private boolean setupSymlink(final File res, final File link) throws Exception { 41 // create symlink 42 final List<String> args = new ArrayList<>(); 43 args.add("ln"); 44 args.add("-s"); 45 46 args.add(res.getAbsolutePath()); 47 args.add(link.getAbsolutePath()); 48 49 final Process proc; 50 51 proc = Runtime.getRuntime().exec(args.toArray(ArrayUtils.EMPTY_STRING_ARRAY)); 52 return proc.waitFor() == 0; 53 } 54 55 56 @Test testCleanDirWithASymlinkDir()57 public void testCleanDirWithASymlinkDir() throws Exception { 58 if (System.getProperty("os.name").startsWith("Win")) { 59 // Can't use "ln" for symlinks on the command line in Windows. 60 return; 61 } 62 63 final File realOuter = new File(top, "realouter"); 64 assertTrue(realOuter.mkdirs()); 65 66 final File realInner = new File(realOuter, "realinner"); 67 assertTrue(realInner.mkdirs()); 68 69 FileUtils.touch(new File(realInner, "file1")); 70 assertEquals(1, realInner.list().length); 71 72 final File randomDirectory = new File(top, "randomDir"); 73 assertTrue(randomDirectory.mkdirs()); 74 75 FileUtils.touch(new File(randomDirectory, "randomfile")); 76 assertEquals(1, randomDirectory.list().length); 77 78 final File symlinkDirectory = new File(realOuter, "fakeinner"); 79 assertTrue(setupSymlink(randomDirectory, symlinkDirectory)); 80 81 assertEquals(1, symlinkDirectory.list().length); 82 83 // assert contents of the real directory were removed including the symlink 84 FileUtils.cleanDirectory(realOuter); 85 assertEquals(0, realOuter.list().length); 86 87 // ensure that the contents of the symlink were NOT removed. 88 assertEquals(1, randomDirectory.list().length, "Contents of sym link should not have been removed"); 89 } 90 91 @Test testCleanDirWithParentSymlinks()92 public void testCleanDirWithParentSymlinks() throws Exception { 93 if (System.getProperty("os.name").startsWith("Win")) { 94 // Can't use "ln" for symlinks on the command line in Windows. 95 return; 96 } 97 98 final File realParent = new File(top, "realparent"); 99 assertTrue(realParent.mkdirs()); 100 101 final File realInner = new File(realParent, "realinner"); 102 assertTrue(realInner.mkdirs()); 103 104 FileUtils.touch(new File(realInner, "file1")); 105 assertEquals(1, realInner.list().length); 106 107 final File randomDirectory = new File(top, "randomDir"); 108 assertTrue(randomDirectory.mkdirs()); 109 110 FileUtils.touch(new File(randomDirectory, "randomfile")); 111 assertEquals(1, randomDirectory.list().length); 112 113 final File symlinkDirectory = new File(realParent, "fakeinner"); 114 assertTrue(setupSymlink(randomDirectory, symlinkDirectory)); 115 116 assertEquals(1, symlinkDirectory.list().length); 117 118 final File symlinkParentDirectory = new File(top, "fakeouter"); 119 assertTrue(setupSymlink(realParent, symlinkParentDirectory)); 120 121 // assert contents of the real directory were removed including the symlink 122 FileUtils.cleanDirectory(symlinkParentDirectory);// should clean the contents of this but not recurse into other links 123 assertEquals(0, symlinkParentDirectory.list().length); 124 assertEquals(0, realParent.list().length); 125 126 // ensure that the contents of the symlink were NOT removed. 127 assertEquals(1, randomDirectory.list().length, "Contents of sym link should not have been removed"); 128 } 129 130 @Test testCleanDirWithSymlinkFile()131 public void testCleanDirWithSymlinkFile() throws Exception { 132 if (System.getProperty("os.name").startsWith("Win")) { 133 // Can't use "ln" for symlinks on the command line in Windows. 134 return; 135 } 136 137 final File realOuter = new File(top, "realouter"); 138 assertTrue(realOuter.mkdirs()); 139 140 final File realInner = new File(realOuter, "realinner"); 141 assertTrue(realInner.mkdirs()); 142 143 final File realFile = new File(realInner, "file1"); 144 FileUtils.touch(realFile); 145 assertEquals(1, realInner.list().length); 146 147 final File randomFile = new File(top, "randomfile"); 148 FileUtils.touch(randomFile); 149 150 final File symlinkFile = new File(realInner, "fakeinner"); 151 assertTrue(setupSymlink(randomFile, symlinkFile)); 152 153 assertEquals(2, realInner.list().length); 154 155 // assert contents of the real directory were removed including the symlink 156 FileUtils.cleanDirectory(realOuter); 157 assertEquals(0, realOuter.list().length); 158 159 // ensure that the contents of the symlink were NOT removed. 160 assertTrue(randomFile.exists()); 161 assertFalse(symlinkFile.exists()); 162 } 163 164 165 @Test testCorrectlyIdentifySymlinkWithParentSymLink()166 public void testCorrectlyIdentifySymlinkWithParentSymLink() throws Exception { 167 if (System.getProperty("os.name").startsWith("Win")) { 168 // Can't use "ln" for symlinks on the command line in Windows. 169 return; 170 } 171 172 final File realParent = new File(top, "realparent"); 173 assertTrue(realParent.mkdirs()); 174 175 final File symlinkParentDirectory = new File(top, "fakeparent"); 176 assertTrue(setupSymlink(realParent, symlinkParentDirectory)); 177 178 final File realChild = new File(symlinkParentDirectory, "realChild"); 179 assertTrue(realChild.mkdirs()); 180 181 final File symlinkChild = new File(symlinkParentDirectory, "fakeChild"); 182 assertTrue(setupSymlink(realChild, symlinkChild)); 183 184 assertTrue(FileUtils.isSymlink(symlinkChild)); 185 assertFalse(FileUtils.isSymlink(realChild)); 186 } 187 188 @Test testIdentifiesBrokenSymlinkFile()189 public void testIdentifiesBrokenSymlinkFile() throws Exception { 190 if (System.getProperty("os.name").startsWith("Win")) { 191 // Can't use "ln" for symlinks on the command line in Windows. 192 return; 193 } 194 195 final File nonExistentFile = new File(top, "non-existent"); 196 final File symlinkFile = new File(top, "fakeinner"); 197 final File badSymlinkInPathFile = new File(symlinkFile, "fakeinner"); 198 final File nonExistentParentFile = new File("non-existent", "file"); 199 200 assertTrue(setupSymlink(nonExistentFile, symlinkFile)); 201 202 assertTrue(FileUtils.isSymlink(symlinkFile)); 203 assertFalse(FileUtils.isSymlink(nonExistentFile)); 204 assertFalse(FileUtils.isSymlink(nonExistentParentFile)); 205 assertFalse(FileUtils.isSymlink(badSymlinkInPathFile)); 206 } 207 208 @Test testIdentifiesSymlinkDir()209 public void testIdentifiesSymlinkDir() throws Exception { 210 if (System.getProperty("os.name").startsWith("Win")) { 211 // Can't use "ln" for symlinks on the command line in Windows. 212 return; 213 } 214 215 final File randomDirectory = new File(top, "randomDir"); 216 assertTrue(randomDirectory.mkdirs()); 217 218 final File symlinkDirectory = new File(top, "fakeDir"); 219 assertTrue(setupSymlink(randomDirectory, symlinkDirectory)); 220 221 assertTrue(FileUtils.isSymlink(symlinkDirectory)); 222 assertFalse(FileUtils.isSymlink(randomDirectory)); 223 } 224 225 @Test testIdentifiesSymlinkFile()226 public void testIdentifiesSymlinkFile() throws Exception { 227 if (System.getProperty("os.name").startsWith("Win")) { 228 // Can't use "ln" for symlinks on the command line in Windows. 229 return; 230 } 231 232 final File randomFile = new File(top, "randomfile"); 233 FileUtils.touch(randomFile); 234 235 final File symlinkFile = new File(top, "fakeinner"); 236 assertTrue(setupSymlink(randomFile, symlinkFile)); 237 238 assertTrue(FileUtils.isSymlink(symlinkFile)); 239 assertFalse(FileUtils.isSymlink(randomFile)); 240 } 241 242 @Test testStillClearsIfGivenDirectoryIsASymlink()243 public void testStillClearsIfGivenDirectoryIsASymlink() throws Exception { 244 if (System.getProperty("os.name").startsWith("Win")) { 245 // Can't use "ln" for symlinks on the command line in Windows. 246 return; 247 } 248 249 final File randomDirectory = new File(top, "randomDir"); 250 assertTrue(randomDirectory.mkdirs()); 251 252 FileUtils.touch(new File(randomDirectory, "randomfile")); 253 assertEquals(1, randomDirectory.list().length); 254 255 final File symlinkDirectory = new File(top, "fakeDir"); 256 assertTrue(setupSymlink(randomDirectory, symlinkDirectory)); 257 258 FileUtils.cleanDirectory(symlinkDirectory); 259 assertEquals(0, symlinkDirectory.list().length); 260 assertEquals(0, randomDirectory.list().length); 261 } 262 263 } 264