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.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  
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      @Test
56      public void testCleanDirWithASymlinkDir() throws Exception {
57          if (System.getProperty("os.name").startsWith("Win")) {
58              // Can't use "ln" for symlinks on the command line in Windows.
59              return;
60          }
61  
62          final File realOuter = new File(top, "realouter");
63          assertTrue(realOuter.mkdirs());
64  
65          final File realInner = new File(realOuter, "realinner");
66          assertTrue(realInner.mkdirs());
67  
68          FileUtils.touch(new File(realInner, "file1"));
69          assertEquals(1, realInner.list().length);
70  
71          final File randomDirectory = new File(top, "randomDir");
72          assertTrue(randomDirectory.mkdirs());
73  
74          FileUtils.touch(new File(randomDirectory, "randomfile"));
75          assertEquals(1, randomDirectory.list().length);
76  
77          final File symlinkDirectory = new File(realOuter, "fakeinner");
78          assertTrue(setupSymlink(randomDirectory, symlinkDirectory));
79  
80          assertEquals(1, symlinkDirectory.list().length);
81  
82          // assert contents of the real directory were removed including the symlink
83          FileUtils.cleanDirectory(realOuter);
84          assertEquals(0, realOuter.list().length);
85  
86          // ensure that the contents of the symlink were NOT removed.
87          assertEquals(1, randomDirectory.list().length, "Contents of sym link should not have been removed");
88      }
89  
90      @Test
91      public void testCleanDirWithParentSymlinks() throws Exception {
92          if (System.getProperty("os.name").startsWith("Win")) {
93              // Can't use "ln" for symlinks on the command line in Windows.
94              return;
95          }
96  
97          final File realParent = new File(top, "realparent");
98          assertTrue(realParent.mkdirs());
99  
100         final File realInner = new File(realParent, "realinner");
101         assertTrue(realInner.mkdirs());
102 
103         FileUtils.touch(new File(realInner, "file1"));
104         assertEquals(1, realInner.list().length);
105 
106         final File randomDirectory = new File(top, "randomDir");
107         assertTrue(randomDirectory.mkdirs());
108 
109         FileUtils.touch(new File(randomDirectory, "randomfile"));
110         assertEquals(1, randomDirectory.list().length);
111 
112         final File symlinkDirectory = new File(realParent, "fakeinner");
113         assertTrue(setupSymlink(randomDirectory, symlinkDirectory));
114 
115         assertEquals(1, symlinkDirectory.list().length);
116 
117         final File symlinkParentDirectory = new File(top, "fakeouter");
118         assertTrue(setupSymlink(realParent, symlinkParentDirectory));
119 
120         // assert contents of the real directory were removed including the symlink
121         // should clean the contents of this but not recurse into other links
122         FileUtils.cleanDirectory(symlinkParentDirectory);
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
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     @Test
165     public void testCorrectlyIdentifySymlinkWithParentSymLink() throws Exception {
166         if (System.getProperty("os.name").startsWith("Win")) {
167             // Can't use "ln" for symlinks on the command line in Windows.
168             return;
169         }
170 
171         final File realParent = new File(top, "realparent");
172         assertTrue(realParent.mkdirs());
173 
174         final File symlinkParentDirectory = new File(top, "fakeparent");
175         assertTrue(setupSymlink(realParent, symlinkParentDirectory));
176 
177         final File realChild = new File(symlinkParentDirectory, "realChild");
178         assertTrue(realChild.mkdirs());
179 
180         final File symlinkChild = new File(symlinkParentDirectory, "fakeChild");
181         assertTrue(setupSymlink(realChild, symlinkChild));
182 
183         assertTrue(FileUtils.isSymlink(symlinkChild));
184         assertFalse(FileUtils.isSymlink(realChild));
185     }
186 
187     @Test
188     public void testIdentifiesBrokenSymlinkFile() throws Exception {
189         if (System.getProperty("os.name").startsWith("Win")) {
190             // Can't use "ln" for symlinks on the command line in Windows.
191             return;
192         }
193 
194         final File nonExistentFile = new File(top, "non-existent");
195         final File symlinkFile = new File(top, "fakeinner");
196         final File badSymlinkInPathFile = new File(symlinkFile, "fakeinner");
197         final File nonExistentParentFile = new File("non-existent", "file");
198 
199         assertTrue(setupSymlink(nonExistentFile, symlinkFile));
200 
201         assertTrue(FileUtils.isSymlink(symlinkFile));
202         assertFalse(FileUtils.isSymlink(nonExistentFile));
203         assertFalse(FileUtils.isSymlink(nonExistentParentFile));
204         assertFalse(FileUtils.isSymlink(badSymlinkInPathFile));
205     }
206 
207     @Test
208     public void testIdentifiesSymlinkDir() throws Exception {
209         if (System.getProperty("os.name").startsWith("Win")) {
210             // Can't use "ln" for symlinks on the command line in Windows.
211             return;
212         }
213 
214         final File randomDirectory = new File(top, "randomDir");
215         assertTrue(randomDirectory.mkdirs());
216 
217         final File symlinkDirectory = new File(top, "fakeDir");
218         assertTrue(setupSymlink(randomDirectory, symlinkDirectory));
219 
220         assertTrue(FileUtils.isSymlink(symlinkDirectory));
221         assertFalse(FileUtils.isSymlink(randomDirectory));
222     }
223 
224     @Test
225     public void testIdentifiesSymlinkFile() throws Exception {
226         if (System.getProperty("os.name").startsWith("Win")) {
227             // Can't use "ln" for symlinks on the command line in Windows.
228             return;
229         }
230 
231         final File randomFile = new File(top, "randomfile");
232         FileUtils.touch(randomFile);
233 
234         final File symlinkFile = new File(top, "fakeinner");
235         assertTrue(setupSymlink(randomFile, symlinkFile));
236 
237         assertTrue(FileUtils.isSymlink(symlinkFile));
238         assertFalse(FileUtils.isSymlink(randomFile));
239     }
240 
241     @Test
242     public void testStillClearsIfGivenDirectoryIsASymlink() throws Exception {
243         if (System.getProperty("os.name").startsWith("Win")) {
244             // Can't use "ln" for symlinks on the command line in Windows.
245             return;
246         }
247 
248         final File randomDirectory = new File(top, "randomDir");
249         assertTrue(randomDirectory.mkdirs());
250 
251         FileUtils.touch(new File(randomDirectory, "randomfile"));
252         assertEquals(1, randomDirectory.list().length);
253 
254         final File symlinkDirectory = new File(top, "fakeDir");
255         assertTrue(setupSymlink(randomDirectory, symlinkDirectory));
256 
257         FileUtils.cleanDirectory(symlinkDirectory);
258         assertEquals(0, symlinkDirectory.list().length);
259         assertEquals(0, randomDirectory.list().length);
260     }
261 
262 }