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    *      https://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  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      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          assertTrue(realOuter.exists());
86          assertEquals(0, realOuter.list().length);
87  
88          // ensure that the contents of the symlink were NOT removed.
89          assertEquals(1, randomDirectory.list().length, "Contents of symbolic link should not have been removed");
90      }
91  
92      @Test
93      void testCleanDirWithParentSymlinks() throws Exception {
94          if (SystemProperties.getOsName().startsWith("Win")) {
95              // Can't use "ln" for symlinks on the command line in Windows.
96              return;
97          }
98  
99          final File realParent = new File(top, "realparent");
100         assertTrue(realParent.mkdirs());
101 
102         final File realInner = new File(realParent, "realinner");
103         assertTrue(realInner.mkdirs());
104 
105         FileUtils.touch(new File(realInner, "file1"));
106         assertEquals(1, realInner.list().length);
107 
108         final File randomDirectory = new File(top, "randomDir");
109         assertTrue(randomDirectory.mkdirs());
110 
111         FileUtils.touch(new File(randomDirectory, "randomfile"));
112         assertEquals(1, randomDirectory.list().length);
113 
114         final File symlinkDirectory = new File(realParent, "fakeinner");
115         assertTrue(setupSymlink(randomDirectory, symlinkDirectory));
116 
117         assertEquals(1, symlinkDirectory.list().length);
118 
119         final File symlinkParentDirectory = new File(top, "fakeouter");
120         assertTrue(setupSymlink(realParent, symlinkParentDirectory));
121 
122         // assert contents of the real directory were removed including the symlink
123         // should clean the contents of this but not recurse into other links
124         FileUtils.cleanDirectory(symlinkParentDirectory);
125         assertTrue(symlinkParentDirectory.exists());
126         assertEquals(0, symlinkParentDirectory.list().length);
127         assertEquals(0, realParent.list().length);
128 
129         // ensure that the contents of the symlink were NOT removed.
130         assertEquals(1, randomDirectory.list().length, "Contents of symbolic link should not have been removed");
131     }
132 
133     @Test
134     void testCleanDirWithSymlinkFile() throws Exception {
135         if (SystemProperties.getOsName().startsWith("Win")) {
136             // Can't use "ln" for symlinks on the command line in Windows.
137             return;
138         }
139 
140         final File realOuter = new File(top, "realouter");
141         assertTrue(realOuter.mkdirs());
142 
143         final File realInner = new File(realOuter, "realinner");
144         assertTrue(realInner.mkdirs());
145 
146         final File realFile = new File(realInner, "file1");
147         FileUtils.touch(realFile);
148         assertEquals(1, realInner.list().length);
149 
150         final File randomFile = new File(top, "randomfile");
151         FileUtils.touch(randomFile);
152 
153         final File symlinkFile = new File(realInner, "fakeinner");
154         assertTrue(setupSymlink(randomFile, symlinkFile));
155 
156         assertEquals(2, realInner.list().length);
157 
158         // assert contents of the real directory were removed including the symlink
159         FileUtils.cleanDirectory(realOuter);
160         assertTrue(realOuter.exists());
161         assertEquals(0, realOuter.list().length);
162 
163         // ensure that the contents of the symlink were NOT removed.
164         assertTrue(randomFile.exists());
165         assertFalse(symlinkFile.exists());
166     }
167 
168     @Test
169     void testCorrectlyIdentifySymlinkWithParentSymLink() throws Exception {
170         if (SystemProperties.getOsName().startsWith("Win")) {
171             // Can't use "ln" for symlinks on the command line in Windows.
172             return;
173         }
174 
175         final File realParent = new File(top, "realparent");
176         assertTrue(realParent.mkdirs());
177 
178         final File symlinkParentDirectory = new File(top, "fakeparent");
179         assertTrue(setupSymlink(realParent, symlinkParentDirectory));
180 
181         final File realChild = new File(symlinkParentDirectory, "realChild");
182         assertTrue(realChild.mkdirs());
183 
184         final File symlinkChild = new File(symlinkParentDirectory, "fakeChild");
185         assertTrue(setupSymlink(realChild, symlinkChild));
186 
187         assertTrue(FileUtils.isSymlink(symlinkChild));
188         assertFalse(FileUtils.isSymlink(realChild));
189     }
190 
191     @Test
192     void testIdentifiesBrokenSymlinkFile() throws Exception {
193         if (SystemProperties.getOsName().startsWith("Win")) {
194             // Can't use "ln" for symlinks on the command line in Windows.
195             return;
196         }
197 
198         final File nonExistentFile = new File(top, "non-existent");
199         final File symlinkFile = new File(top, "fakeinner");
200         final File badSymlinkInPathFile = new File(symlinkFile, "fakeinner");
201         final File nonExistentParentFile = new File("non-existent", "file");
202 
203         assertTrue(setupSymlink(nonExistentFile, symlinkFile));
204 
205         assertTrue(FileUtils.isSymlink(symlinkFile));
206         assertFalse(FileUtils.isSymlink(nonExistentFile));
207         assertFalse(FileUtils.isSymlink(nonExistentParentFile));
208         assertFalse(FileUtils.isSymlink(badSymlinkInPathFile));
209     }
210 
211     @Test
212     void testIdentifiesSymlinkDir() throws Exception {
213         if (SystemProperties.getOsName().startsWith("Win")) {
214             // Can't use "ln" for symlinks on the command line in Windows.
215             return;
216         }
217 
218         final File randomDirectory = new File(top, "randomDir");
219         assertTrue(randomDirectory.mkdirs());
220 
221         final File symlinkDirectory = new File(top, "fakeDir");
222         assertTrue(setupSymlink(randomDirectory, symlinkDirectory));
223 
224         assertTrue(FileUtils.isSymlink(symlinkDirectory));
225         assertFalse(FileUtils.isSymlink(randomDirectory));
226     }
227 
228     @Test
229     void testIdentifiesSymlinkFile() throws Exception {
230         if (SystemProperties.getOsName().startsWith("Win")) {
231             // Can't use "ln" for symlinks on the command line in Windows.
232             return;
233         }
234 
235         final File randomFile = new File(top, "randomfile");
236         FileUtils.touch(randomFile);
237 
238         final File symlinkFile = new File(top, "fakeinner");
239         assertTrue(setupSymlink(randomFile, symlinkFile));
240 
241         assertTrue(FileUtils.isSymlink(symlinkFile));
242         assertFalse(FileUtils.isSymlink(randomFile));
243     }
244 
245     @Test
246     void testStillClearsIfGivenDirectoryIsASymlink() throws Exception {
247         if (SystemProperties.getOsName().startsWith("Win")) {
248             // Can't use "ln" for symlinks on the command line in Windows.
249             return;
250         }
251 
252         final File randomDirectory = new File(top, "randomDir");
253         assertTrue(randomDirectory.mkdirs());
254 
255         FileUtils.touch(new File(randomDirectory, "randomfile"));
256         assertEquals(1, randomDirectory.list().length);
257 
258         final File symlinkDirectory = new File(top, "fakeDir");
259         assertTrue(setupSymlink(randomDirectory, symlinkDirectory));
260 
261         FileUtils.cleanDirectory(symlinkDirectory);
262         assertTrue(symlinkDirectory.exists());
263         assertEquals(0, symlinkDirectory.list().length);
264         assertEquals(0, randomDirectory.list().length);
265     }
266 
267 }