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