1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package libcore.java.io;
18 
19 import android.system.ErrnoException;
20 import android.system.OsConstants;
21 
22 import java.io.File;
23 import java.io.FileFilter;
24 import java.io.FilenameFilter;
25 import java.io.IOException;
26 import java.nio.file.InvalidPathException;
27 import java.nio.file.Path;
28 import java.nio.file.Paths;
29 import java.util.UUID;
30 import libcore.io.Libcore;
31 
32 import static android.system.Os.stat;
33 
34 public class FileTest extends junit.framework.TestCase {
35 
36     static {
37         System.loadLibrary("javacoretests");
38     }
39 
createTemporaryDirectory()40     private static File createTemporaryDirectory() throws Exception {
41         String base = System.getProperty("java.io.tmpdir");
42         File directory = new File(base, UUID.randomUUID().toString());
43         assertTrue(directory.mkdirs());
44         return directory;
45     }
46 
longString(int n)47     private static String longString(int n) {
48         StringBuilder result = new StringBuilder();
49         for (int i = 0; i < n; ++i) {
50             result.append('x');
51         }
52         return result.toString();
53     }
54 
createDeepStructure(File base)55     private static File createDeepStructure(File base) throws Exception {
56         // ext has a limit of around 256 characters for each path entry.
57         // 128 characters should be safe for everything but FAT.
58         String longString = longString(128);
59         // Keep creating subdirectories until the path length is greater than 1KiB.
60         // Ubuntu 8.04's kernel is happy up to about 4KiB.
61         File f = base;
62         for (int i = 0; f.toString().length() <= 1024; ++i) {
63             f = new File(f, longString);
64             assertTrue(f.mkdir());
65         }
66         return f;
67     }
68 
69     // Rather than test all methods, assume that if createTempFile creates a long path and
70     // exists can see it, the code for coping with long paths (shared by all methods) works.
test_longPath()71     public void test_longPath() throws Exception {
72         File base = createTemporaryDirectory();
73         assertTrue(createDeepStructure(base).exists());
74     }
75 
76     /*
77      * readlink(2) is a special case,.
78      *
79      * This test assumes you can create symbolic links in the temporary directory. This
80      * isn't true on Android if you're using /sdcard (which is used if this test is
81      * run using vogar). It will work in /data/data/ though.
82      */
test_longReadlink()83     public void test_longReadlink() throws Exception {
84         File base = createTemporaryDirectory();
85         File target = createDeepStructure(base);
86         File source = new File(base, "source");
87         assertFalse(source.exists());
88         assertTrue(target.exists());
89         assertTrue(target.getCanonicalPath().length() > 1024);
90         ln_s(target, source);
91         assertTrue(source.exists());
92         assertEquals(target.getCanonicalPath(), source.getCanonicalPath());
93     }
94 
95     // TODO: File.list is a special case too, but I haven't fixed it yet, and the new code,
96     // like the old code, will die of a native buffer overrun if we exercise it.
97 
test_emptyFilename()98     public void test_emptyFilename() throws Exception {
99         // The behavior of the empty filename is an odd mixture.
100         File f = new File("");
101         // Mostly it behaves like an invalid path...
102         assertFalse(f.canExecute());
103         assertFalse(f.canRead());
104         assertFalse(f.canWrite());
105         try {
106             f.createNewFile();
107             fail("expected IOException");
108         } catch (IOException expected) {
109         }
110         assertFalse(f.delete());
111         f.deleteOnExit();
112         assertFalse(f.exists());
113         assertEquals("", f.getName());
114         assertEquals(null, f.getParent());
115         assertEquals(null, f.getParentFile());
116         assertEquals("", f.getPath());
117         assertFalse(f.isAbsolute());
118         assertFalse(f.isDirectory());
119         assertFalse(f.isFile());
120         assertFalse(f.isHidden());
121         assertEquals(0, f.lastModified());
122         assertEquals(0, f.length());
123         assertEquals(null, f.list());
124         assertEquals(null, f.list(null));
125         assertEquals(null, f.listFiles());
126         assertEquals(null, f.listFiles((FileFilter) null));
127         assertEquals(null, f.listFiles((FilenameFilter) null));
128         assertFalse(f.mkdir());
129         assertFalse(f.mkdirs());
130         assertFalse(f.renameTo(f));
131         assertFalse(f.setLastModified(123));
132         assertFalse(f.setExecutable(true));
133         assertFalse(f.setReadOnly());
134         assertFalse(f.setReadable(true));
135         assertFalse(f.setWritable(true));
136         // ...but sometimes it behaves like "user.dir".
137         String cwd = System.getProperty("user.dir");
138         assertEquals(new File(cwd), f.getAbsoluteFile());
139         assertEquals(cwd, f.getAbsolutePath());
140         // TODO: how do we test these without hard-coding assumptions about where our temporary
141         // directory is? (In practice, on Android, our temporary directory is accessed through
142         // a symbolic link, so the canonical file/path will be different.)
143         //assertEquals(new File(cwd), f.getCanonicalFile());
144         //assertEquals(cwd, f.getCanonicalPath());
145     }
146 
147     // http://b/2486943 - between eclair and froyo, we added a call to
148     // isAbsolute from the File constructor, potentially breaking subclasses.
test_subclassing()149     public void test_subclassing() throws Exception {
150         class MyFile extends File {
151             private String field;
152             MyFile(String s) {
153                 super(s);
154                 field = "";
155             }
156             @Override public boolean isAbsolute() {
157                 field.length();
158                 return super.isAbsolute();
159             }
160         }
161         new MyFile("");
162     }
163 
164     /*
165      * http://b/3047893 - getCanonicalPath wasn't actually resolving symbolic links.
166      *
167      * This test assumes you can create symbolic links in the temporary directory. This
168      * isn't true on Android if you're using /sdcard (which is used if this test is
169      * run using vogar). It will work in /data/data/ though.
170      */
test_getCanonicalPath()171     public void test_getCanonicalPath() throws Exception {
172         File base = createTemporaryDirectory();
173         File target = new File(base, "target");
174         target.createNewFile(); // The RI won't follow a dangling symlink, which seems like a bug!
175         File linkName = new File(base, "link");
176         ln_s(target, linkName);
177         assertEquals(target.getCanonicalPath(), linkName.getCanonicalPath());
178 
179         // .../subdir/shorter -> .../target (using a link to ../target).
180         File subdir = new File(base, "subdir");
181         assertTrue(subdir.mkdir());
182         linkName = new File(subdir, "shorter");
183         ln_s("../target", linkName.toString());
184         assertEquals(target.getCanonicalPath(), linkName.getCanonicalPath());
185 
186         // .../l -> .../subdir/longer (using a relative link to subdir/longer).
187         linkName = new File(base, "l");
188         ln_s("subdir/longer", linkName.toString());
189         File longer = new File(base, "subdir/longer");
190         longer.createNewFile(); // The RI won't follow a dangling symlink, which seems like a bug!
191         assertEquals(longer.getCanonicalPath(), linkName.getCanonicalPath());
192 
193         // .../double -> .../target (via a link into subdir and a link back out).
194         linkName = new File(base, "double");
195         ln_s("subdir/shorter", linkName.toString());
196         assertEquals(target.getCanonicalPath(), linkName.getCanonicalPath());
197     }
198 
ln_s(File target, File linkName)199     private static void ln_s(File target, File linkName) throws Exception {
200         ln_s(target.toString(), linkName.toString());
201     }
202 
ln_s(String target, String linkName)203     private static void ln_s(String target, String linkName) throws Exception {
204         Libcore.os.symlink(target, linkName);
205     }
206 
test_createNewFile()207     public void test_createNewFile() throws Exception {
208         File f = File.createTempFile("FileTest", "tmp");
209         assertFalse(f.createNewFile()); // EEXIST -> false
210         assertFalse(f.getParentFile().createNewFile()); // EEXIST -> false, even if S_ISDIR
211         try {
212             new File(f, "poop").createNewFile(); // ENOTDIR -> throw
213             fail();
214         } catch (IOException expected) {
215         }
216         try {
217             new File("").createNewFile(); // ENOENT -> throw
218             fail();
219         } catch (IOException expected) {
220         }
221     }
222 
test_rename()223     public void test_rename() throws Exception {
224         File f = File.createTempFile("FileTest", "tmp");
225         assertFalse(f.renameTo(new File("")));
226         assertFalse(new File("").renameTo(f));
227         assertFalse(f.renameTo(new File(".")));
228         assertTrue(f.renameTo(f));
229     }
230 
test_getAbsolutePath()231     public void test_getAbsolutePath() throws Exception {
232         String userDir = System.getProperty("user.dir");
233         if (!userDir.endsWith(File.separator)) {
234             userDir = userDir + File.separator;
235         }
236 
237         File f = new File("poop");
238         assertEquals(userDir + "poop", f.getAbsolutePath());
239     }
240 
test_getSpace()241     public void test_getSpace() throws Exception {
242         assertTrue(new File("/").getFreeSpace() >= 0);
243         assertTrue(new File("/").getTotalSpace() >= 0);
244         assertTrue(new File("/").getUsableSpace() >= 0);
245     }
246 
test_mkdirs()247     public void test_mkdirs() throws Exception {
248         // Set up a directory to test in.
249         File base = createTemporaryDirectory();
250 
251         // mkdirs returns true only if it _creates_ a directory.
252         // So we get false for a directory that already exists...
253         assertTrue(base.exists());
254         assertFalse(base.mkdirs());
255         // But true if we had to create something.
256         File a = new File(base, "a");
257         assertFalse(a.exists());
258         assertTrue(a.mkdirs());
259         assertTrue(a.exists());
260 
261         // Test the recursive case where we need to create multiple parents.
262         File b = new File(a, "b");
263         File c = new File(b, "c");
264         File d = new File(c, "d");
265         assertTrue(a.exists());
266         assertFalse(b.exists());
267         assertFalse(c.exists());
268         assertFalse(d.exists());
269         assertTrue(d.mkdirs());
270         assertTrue(a.exists());
271         assertTrue(b.exists());
272         assertTrue(c.exists());
273         assertTrue(d.exists());
274 
275         // Test the case where the 'directory' exists as a file.
276         File existsAsFile = new File(base, "existsAsFile");
277         existsAsFile.createNewFile();
278         assertTrue(existsAsFile.exists());
279         assertFalse(existsAsFile.mkdirs());
280 
281         // Test the case where the parent exists as a file.
282         File badParent = new File(existsAsFile, "sub");
283         assertTrue(existsAsFile.exists());
284         assertFalse(badParent.exists());
285         assertFalse(badParent.mkdirs());
286     }
287 
288     // http://b/23523042 : Test that creating a directory and file with
289     // a surrogate pair in the name works as expected and roundtrips correctly.
testFilesWithSurrogatePairs()290     public void testFilesWithSurrogatePairs() throws Exception {
291         File base = createTemporaryDirectory();
292         // U+13000 encodes to the surrogate pair D80C + DC00 in UTF16
293         // and the 4 byte UTF-8 sequence [ 0xf0, 0x93, 0x80, 0x80 ]. The
294         // file name must be encoded using the 4 byte UTF-8 sequence before
295         // it reaches the file system.
296         File subDir = new File(base, "dir_\uD80C\uDC00");
297         assertTrue(subDir.mkdir());
298         File subFile = new File(subDir, "file_\uD80C\uDC00");
299         assertTrue(subFile.createNewFile());
300 
301         File[] files = base.listFiles();
302         assertEquals(1, files.length);
303         assertEquals("dir_\uD80C\uDC00", files[0].getName());
304 
305         files = subDir.listFiles();
306         assertEquals(1, files.length);
307         assertEquals("file_\uD80C\uDC00", files[0].getName());
308 
309         // Throws on error.
310         nativeTestFilesWithSurrogatePairs(base.getAbsolutePath());
311     }
312 
313     // http://b/25878034
314     //
315     // The test makes sure that #exists doesn't use stat. To implement the same, it installs
316     // SECCOMP filter. The SECCOMP filter is designed to not allow stat(fstatat64/newfstatat) calls
317     // and whenever a thread makes the system call, android.system.ErrnoException
318     // (EPERM - Operation not permitted) will be raised.
testExistsOnSystem()319     public void testExistsOnSystem() throws ErrnoException, IOException {
320         File tmpFile = File.createTempFile("testExistsOnSystem", ".tmp");
321         try {
322             assertEquals("SECCOMP filter is not installed.", 0, installSeccompFilter());
323             try {
324                 // Verify that SECCOMP filter obstructs stat.
325                 stat(tmpFile.getAbsolutePath());
326                 fail();
327             } catch (ErrnoException expected) {
328                 assertEquals(OsConstants.EPERM, expected.errno);
329             }
330             assertTrue(tmpFile.exists());
331         } finally {
332             tmpFile.delete();
333         }
334     }
335 
installSeccompFilter()336     private static native int installSeccompFilter();
337 
338     // http://b/25859957
339     //
340     // OpenJdk is treating empty parent string as a special case,
341     // it substitutes parent string with the filesystem default parent value "/"
342     // This wasn't the case before the switch to openJdk.
testEmptyParentString()343     public void testEmptyParentString() {
344         File f1 = new File("", "foo.bar");
345         File f2 = new File((String)null, "foo.bar");
346         assertEquals("foo.bar", f1.toString());
347         assertEquals("foo.bar", f2.toString());
348     }
349 
350     // http://b/27273930
testJavaIoTmpdirMutable()351     public void testJavaIoTmpdirMutable() throws Exception {
352         final String oldTmpDir = System.getProperty("java.io.tmpdir");
353         final String directoryName = "/newTemp" + Integer.toString(Math.randomIntInternal());
354         final String newTmpDir = oldTmpDir + directoryName;
355         File subDir = new File(oldTmpDir, directoryName);
356         assertTrue(subDir.mkdir());
357         try {
358             System.setProperty("java.io.tmpdir", newTmpDir);
359             File tempFile = File.createTempFile("foo", ".bar");
360             assertTrue(tempFile.getAbsolutePath().contains(directoryName));
361         } finally {
362             System.setProperty("java.io.tmpdir", oldTmpDir);
363         }
364     }
365 
nativeTestFilesWithSurrogatePairs(String base)366     private static native void nativeTestFilesWithSurrogatePairs(String base);
367 
368     // http://b/27731686
testBug27731686()369     public void testBug27731686() {
370         File file = new File("/test1", "/");
371         assertEquals("/test1", file.getPath());
372         assertEquals("test1", file.getName());
373     }
374 
testFileNameNormalization()375     public void testFileNameNormalization() {
376         assertEquals("/", new File("/", "/").getPath());
377         assertEquals("/", new File("/", "").getPath());
378         assertEquals("/", new File("", "/").getPath());
379         assertEquals("", new File("", "").getPath());
380 
381         assertEquals("/foo/bar", new File("/foo/", "/bar/").getPath());
382         assertEquals("/foo/bar", new File("/foo", "/bar//").getPath());
383     }
384 
test_toPath()385     public void test_toPath() {
386         File file = new File("testPath");
387         Path filePath = file.toPath();
388         assertEquals(Paths.get("testPath"), filePath);
389 
390         File file1 = new File("'\u0000'");
391         try {
392             file1.toPath();
393             fail();
394         } catch (InvalidPathException expected) {}
395     }
396 }
397