View Javadoc
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.apache.commons.lang3.SystemProperties;
29  import org.junit.jupiter.api.Test;
30  import org.junit.jupiter.api.io.TempDir;
31  
32  /**
33   * Test cases for FileUtils.cleanDirectory() method that involve symlinks.
34   * & FileUtils.isSymlink(File file)
35   */
36  public class FileUtilsCleanSymlinksTest {
37  
38      @TempDir
39      public File top;
40  
41      private boolean setupSymlink(final File res, final File link) throws Exception {
42          // create symlink
43          final List<String> args = new ArrayList<>();
44          args.add("ln");
45          args.add("-s");
46  
47          args.add(res.getAbsolutePath());
48          args.add(link.getAbsolutePath());
49  
50          final Process proc;
51  
52          proc = Runtime.getRuntime().exec(args.toArray(ArrayUtils.EMPTY_STRING_ARRAY));
53          return proc.waitFor() == 0;
54      }
55  
56      @Test
57      public void testCleanDirWithASymlinkDir() throws Exception {
58          if (SystemProperties.getOsName().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 symbolic link should not have been removed");
89      }
90  
91      @Test
92      public void testCleanDirWithParentSymlinks() throws Exception {
93          if (SystemProperties.getOsName().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         // should clean the contents of this but not recurse into other links
123         FileUtils.cleanDirectory(symlinkParentDirectory);
124         assertEquals(0, symlinkParentDirectory.list().length);
125         assertEquals(0, realParent.list().length);
126 
127         // ensure that the contents of the symlink were NOT removed.
128         assertEquals(1, randomDirectory.list().length, "Contents of symbolic link should not have been removed");
129     }
130 
131     @Test
132     public void testCleanDirWithSymlinkFile() throws Exception {
133         if (SystemProperties.getOsName().startsWith("Win")) {
134             // Can't use "ln" for symlinks on the command line in Windows.
135             return;
136         }
137 
138         final File realOuter = new File(top, "realouter");
139         assertTrue(realOuter.mkdirs());
140 
141         final File realInner = new File(realOuter, "realinner");
142         assertTrue(realInner.mkdirs());
143 
144         final File realFile = new File(realInner, "file1");
145         FileUtils.touch(realFile);
146         assertEquals(1, realInner.list().length);
147 
148         final File randomFile = new File(top, "randomfile");
149         FileUtils.touch(randomFile);
150 
151         final File symlinkFile = new File(realInner, "fakeinner");
152         assertTrue(setupSymlink(randomFile, symlinkFile));
153 
154         assertEquals(2, realInner.list().length);
155 
156         // assert contents of the real directory were removed including the symlink
157         FileUtils.cleanDirectory(realOuter);
158         assertEquals(0, realOuter.list().length);
159 
160         // ensure that the contents of the symlink were NOT removed.
161         assertTrue(randomFile.exists());
162         assertFalse(symlinkFile.exists());
163     }
164 
165     @Test
166     public void testCorrectlyIdentifySymlinkWithParentSymLink() throws Exception {
167         if (SystemProperties.getOsName().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
189     public void testIdentifiesBrokenSymlinkFile() throws Exception {
190         if (SystemProperties.getOsName().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
209     public void testIdentifiesSymlinkDir() throws Exception {
210         if (SystemProperties.getOsName().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
226     public void testIdentifiesSymlinkFile() throws Exception {
227         if (SystemProperties.getOsName().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
243     public void testStillClearsIfGivenDirectoryIsASymlink() throws Exception {
244         if (SystemProperties.getOsName().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 }