8003992: File and other classes in java.io do not handle embedded nulls properly
Summary: Have every file operation done with File, FileInputStream, FileOutputStream, or RandomAccessFile that involves a file path containing NUL fail. Also reviewed by fweimer@redhat.com
Reviewed-by: alanb, sherman, ahgross, mduigou, dholmes, aph, plevart, martin
--- a/jdk/src/share/classes/java/io/File.java Mon May 06 06:05:06 2013 +0200
+++ b/jdk/src/share/classes/java/io/File.java Mon May 06 14:17:59 2013 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1994, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -156,7 +156,7 @@
private static final FileSystem fs = DefaultFileSystem.getFileSystem();
/**
- * This abstract pathname's normalized pathname string. A normalized
+ * This abstract pathname's normalized pathname string. A normalized
* pathname string uses the default name-separator character and does not
* contain any duplicate or redundant separators.
*
@@ -165,6 +165,32 @@
private final String path;
/**
+ * Enum type that indicates the status of a file path.
+ */
+ private static enum PathStatus { INVALID, CHECKED };
+
+ /**
+ * The flag indicating whether the file path is invalid.
+ */
+ private transient PathStatus status = null;
+
+ /**
+ * Check if the file has an invalid path. Currently, the inspection of
+ * a file path is very limited, and it only covers Nul character check.
+ * Returning true means the path is definitely invalid/garbage. But
+ * returning false does not guarantee that the path is valid.
+ *
+ * @return true if the file path is invalid.
+ */
+ final boolean isInvalid() {
+ if (status == null) {
+ status = (this.path.indexOf('\u0000') < 0) ? PathStatus.CHECKED
+ : PathStatus.INVALID;
+ }
+ return status == PathStatus.INVALID;
+ }
+
+ /**
* The length of this abstract pathname's prefix, or zero if it has no
* prefix.
*/
@@ -586,6 +612,9 @@
* @see Path#toRealPath
*/
public String getCanonicalPath() throws IOException {
+ if (isInvalid()) {
+ throw new IOException("Invalid file path");
+ }
return fs.canonicalize(fs.resolve(this));
}
@@ -651,6 +680,9 @@
*/
@Deprecated
public URL toURL() throws MalformedURLException {
+ if (isInvalid()) {
+ throw new MalformedURLException("Invalid file path");
+ }
return new URL("file", "", slashify(getAbsolutePath(), isDirectory()));
}
@@ -730,6 +762,9 @@
if (security != null) {
security.checkRead(path);
}
+ if (isInvalid()) {
+ return false;
+ }
return fs.checkAccess(this, FileSystem.ACCESS_READ);
}
@@ -755,6 +790,9 @@
if (security != null) {
security.checkWrite(path);
}
+ if (isInvalid()) {
+ return false;
+ }
return fs.checkAccess(this, FileSystem.ACCESS_WRITE);
}
@@ -775,6 +813,9 @@
if (security != null) {
security.checkRead(path);
}
+ if (isInvalid()) {
+ return false;
+ }
return ((fs.getBooleanAttributes(this) & FileSystem.BA_EXISTS) != 0);
}
@@ -802,6 +843,9 @@
if (security != null) {
security.checkRead(path);
}
+ if (isInvalid()) {
+ return false;
+ }
return ((fs.getBooleanAttributes(this) & FileSystem.BA_DIRECTORY)
!= 0);
}
@@ -832,6 +876,9 @@
if (security != null) {
security.checkRead(path);
}
+ if (isInvalid()) {
+ return false;
+ }
return ((fs.getBooleanAttributes(this) & FileSystem.BA_REGULAR) != 0);
}
@@ -858,6 +905,9 @@
if (security != null) {
security.checkRead(path);
}
+ if (isInvalid()) {
+ return false;
+ }
return ((fs.getBooleanAttributes(this) & FileSystem.BA_HIDDEN) != 0);
}
@@ -887,6 +937,9 @@
if (security != null) {
security.checkRead(path);
}
+ if (isInvalid()) {
+ return 0L;
+ }
return fs.getLastModifiedTime(this);
}
@@ -915,6 +968,9 @@
if (security != null) {
security.checkRead(path);
}
+ if (isInvalid()) {
+ return 0L;
+ }
return fs.getLength(this);
}
@@ -950,6 +1006,9 @@
public boolean createNewFile() throws IOException {
SecurityManager security = System.getSecurityManager();
if (security != null) security.checkWrite(path);
+ if (isInvalid()) {
+ throw new IOException("Invalid file path");
+ }
return fs.createFileExclusively(path);
}
@@ -976,6 +1035,9 @@
if (security != null) {
security.checkDelete(path);
}
+ if (isInvalid()) {
+ return false;
+ }
return fs.delete(this);
}
@@ -1011,6 +1073,9 @@
if (security != null) {
security.checkDelete(path);
}
+ if (isInvalid()) {
+ return;
+ }
DeleteOnExitHook.add(path);
}
@@ -1051,6 +1116,9 @@
if (security != null) {
security.checkRead(path);
}
+ if (isInvalid()) {
+ return null;
+ }
return fs.list(this);
}
@@ -1242,6 +1310,9 @@
if (security != null) {
security.checkWrite(path);
}
+ if (isInvalid()) {
+ return false;
+ }
return fs.createDirectory(this);
}
@@ -1317,6 +1388,12 @@
security.checkWrite(path);
security.checkWrite(dest.path);
}
+ if (dest == null) {
+ throw new NullPointerException();
+ }
+ if (this.isInvalid() || dest.isInvalid()) {
+ return false;
+ }
return fs.rename(this, dest);
}
@@ -1352,6 +1429,9 @@
if (security != null) {
security.checkWrite(path);
}
+ if (isInvalid()) {
+ return false;
+ }
return fs.setLastModifiedTime(this, time);
}
@@ -1379,6 +1459,9 @@
if (security != null) {
security.checkWrite(path);
}
+ if (isInvalid()) {
+ return false;
+ }
return fs.setReadOnly(this);
}
@@ -1419,6 +1502,9 @@
if (security != null) {
security.checkWrite(path);
}
+ if (isInvalid()) {
+ return false;
+ }
return fs.setPermission(this, FileSystem.ACCESS_WRITE, writable, ownerOnly);
}
@@ -1493,6 +1579,9 @@
if (security != null) {
security.checkWrite(path);
}
+ if (isInvalid()) {
+ return false;
+ }
return fs.setPermission(this, FileSystem.ACCESS_READ, readable, ownerOnly);
}
@@ -1570,6 +1659,9 @@
if (security != null) {
security.checkWrite(path);
}
+ if (isInvalid()) {
+ return false;
+ }
return fs.setPermission(this, FileSystem.ACCESS_EXECUTE, executable, ownerOnly);
}
@@ -1629,6 +1721,9 @@
if (security != null) {
security.checkExec(path);
}
+ if (isInvalid()) {
+ return false;
+ }
return fs.checkAccess(this, FileSystem.ACCESS_EXECUTE);
}
@@ -1705,6 +1800,9 @@
sm.checkPermission(new RuntimePermission("getFileSystemAttributes"));
sm.checkRead(path);
}
+ if (isInvalid()) {
+ return 0L;
+ }
return fs.getSpace(this, FileSystem.SPACE_TOTAL);
}
@@ -1721,7 +1819,7 @@
* makes no guarantee that write operations to this file system
* will succeed.
*
- * @return The number of unallocated bytes on the partition <tt>0L</tt>
+ * @return The number of unallocated bytes on the partition or <tt>0L</tt>
* if the abstract pathname does not name a partition. This
* value will be less than or equal to the total file system size
* returned by {@link #getTotalSpace}.
@@ -1740,6 +1838,9 @@
sm.checkPermission(new RuntimePermission("getFileSystemAttributes"));
sm.checkRead(path);
}
+ if (isInvalid()) {
+ return 0L;
+ }
return fs.getSpace(this, FileSystem.SPACE_FREE);
}
@@ -1778,6 +1879,9 @@
sm.checkPermission(new RuntimePermission("getFileSystemAttributes"));
sm.checkRead(path);
}
+ if (isInvalid()) {
+ return 0L;
+ }
return fs.getSpace(this, FileSystem.SPACE_USABLE);
}
@@ -1787,8 +1891,8 @@
private TempDirectory() { }
// temporary directory location
- private static final File tmpdir = new File(fs.normalize(AccessController
- .doPrivileged(new GetPropertyAction("java.io.tmpdir"))));
+ private static final File tmpdir = new File(AccessController
+ .doPrivileged(new GetPropertyAction("java.io.tmpdir")));
static File location() {
return tmpdir;
}
@@ -1899,6 +2003,9 @@
throw se;
}
}
+ if (f.isInvalid()) {
+ throw new IOException("Unable to create temporary file");
+ }
} while (!fs.createFileExclusively(f.getPath()));
return f;
}
--- a/jdk/src/share/classes/java/io/FileInputStream.java Mon May 06 06:05:06 2013 +0200
+++ b/jdk/src/share/classes/java/io/FileInputStream.java Mon May 06 14:17:59 2013 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1994, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -123,6 +123,9 @@
if (name == null) {
throw new NullPointerException();
}
+ if (file.isInvalid()) {
+ throw new FileNotFoundException("Invalid file path");
+ }
fd = new FileDescriptor();
fd.attach(this);
open(name);
--- a/jdk/src/share/classes/java/io/FileOutputStream.java Mon May 06 06:05:06 2013 +0200
+++ b/jdk/src/share/classes/java/io/FileOutputStream.java Mon May 06 14:17:59 2013 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1994, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -196,6 +196,9 @@
if (name == null) {
throw new NullPointerException();
}
+ if (file.isInvalid()) {
+ throw new FileNotFoundException("Invalid file path");
+ }
this.fd = new FileDescriptor();
fd.attach(this);
this.append = append;
--- a/jdk/src/share/classes/java/io/RandomAccessFile.java Mon May 06 06:05:06 2013 +0200
+++ b/jdk/src/share/classes/java/io/RandomAccessFile.java Mon May 06 14:17:59 2013 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1994, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -228,6 +228,9 @@
if (name == null) {
throw new NullPointerException();
}
+ if (file.isInvalid()) {
+ throw new FileNotFoundException("Invalid file path");
+ }
fd = new FileDescriptor();
fd.attach(this);
open(name, imode);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/io/File/NulFile.java Mon May 06 14:17:59 2013 -0700
@@ -0,0 +1,625 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 8003992
+ * @summary Test a file whose path name is embedded with NUL character, and
+ * ensure it is handled correctly.
+ * @author Dan Xu
+ */
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.RandomAccessFile;
+import java.io.FileNotFoundException;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.nio.file.InvalidPathException;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectInputStream;
+
+public class NulFile {
+
+ private static final char CHAR_NUL = '\u0000';
+
+ private static final String ExceptionMsg = "Invalid file path";
+
+ public static void main(String[] args) {
+ testFile();
+ testFileInUnix();
+ testFileInWindows();
+ testTempFile();
+ }
+
+ private static void testFile() {
+ test(new File(new StringBuilder().append(CHAR_NUL).toString()));
+ test(new File(
+ new StringBuilder().append("").append(CHAR_NUL).toString()));
+ test(new File(
+ new StringBuilder().append(CHAR_NUL).append("").toString()));
+ }
+
+ private static void testFileInUnix() {
+ String osName = System.getProperty("os.name");
+ if (osName.startsWith("Windows"))
+ return;
+
+ String unixFile = "/";
+ test(unixFile);
+
+ unixFile = "//";
+ test(unixFile);
+
+ unixFile = "data/info";
+ test(unixFile);
+
+ unixFile = "/data/info";
+ test(unixFile);
+
+ unixFile = "//data//info";
+ test(unixFile);
+ }
+
+ private static void testFileInWindows() {
+ String osName = System.getProperty("os.name");
+ if (!osName.startsWith("Windows"))
+ return;
+
+ String windowsFile = "\\";
+ test(windowsFile);
+
+ windowsFile = "\\\\";
+ test(windowsFile);
+
+ windowsFile = "/";
+ test(windowsFile);
+
+ windowsFile = "//";
+ test(windowsFile);
+
+ windowsFile = "/\\";
+ test(windowsFile);
+
+ windowsFile = "\\/";
+ test(windowsFile);
+
+ windowsFile = "data\\info";
+ test(windowsFile);
+
+ windowsFile = "\\data\\info";
+ test(windowsFile);
+
+ windowsFile = "\\\\server\\data\\info";
+ test(windowsFile);
+
+ windowsFile = "z:data\\info";
+ test(windowsFile);
+
+ windowsFile = "z:\\data\\info";
+ test(windowsFile);
+ }
+
+ private static void test(final String name) {
+ int length = name.length();
+
+ for (int i = 0; i <= length; i++) {
+ StringBuilder sbName = new StringBuilder(name);
+ sbName.insert(i, CHAR_NUL);
+ String curName = sbName.toString();
+
+ // test File(String parent, String child)
+ File testFile = new File(curName, "child");
+ test(testFile);
+ testFile = new File("parent", curName);
+ test(testFile);
+
+ // test File(String pathname)
+ testFile = new File(curName);
+ test(testFile);
+
+ // test File(File parent, String child)
+ testFile = new File(new File(curName), "child");
+ test(testFile);
+ testFile = new File(new File("parent"), curName);
+ test(testFile);
+
+ // test FileInputStream
+ testFileInputStream(curName);
+
+ // test FileOutputStream
+ testFileOutputStream(curName);
+
+ // test RandomAccessFile
+ testRandomAccessFile(curName);
+ }
+ }
+
+ private static void testFileInputStream(final String str) {
+ boolean exceptionThrown = false;
+ FileInputStream is = null;
+ try {
+ is = new FileInputStream(str);
+ } catch (FileNotFoundException ex) {
+ if (ExceptionMsg.equals(ex.getMessage()))
+ exceptionThrown = true;
+ }
+ if (!exceptionThrown) {
+ throw new RuntimeException("FileInputStream constructor"
+ + " should throw FileNotFoundException");
+ }
+ if (is != null) {
+ throw new RuntimeException("FileInputStream constructor"
+ + " should fail");
+ }
+
+ exceptionThrown = false;
+ is = null;
+ try {
+ is = new FileInputStream(new File(str));
+ } catch (FileNotFoundException ex) {
+ if (ExceptionMsg.equals(ex.getMessage()))
+ exceptionThrown = true;
+ }
+ if (!exceptionThrown) {
+ throw new RuntimeException("FileInputStream constructor"
+ + " should throw FileNotFoundException");
+ }
+ if (is != null) {
+ throw new RuntimeException("FileInputStream constructor"
+ + " should fail");
+ }
+ }
+
+ private static void testFileOutputStream(final String str) {
+ boolean exceptionThrown = false;
+ FileOutputStream os = null;
+ try {
+ os = new FileOutputStream(str);
+ } catch (FileNotFoundException ex) {
+ if (ExceptionMsg.equals(ex.getMessage()))
+ exceptionThrown = true;
+ }
+ if (!exceptionThrown) {
+ throw new RuntimeException("FileOutputStream constructor"
+ + " should throw FileNotFoundException");
+ }
+ if (os != null) {
+ throw new RuntimeException("FileOutputStream constructor"
+ + " should fail");
+ }
+
+ exceptionThrown = false;
+ os = null;
+ try {
+ os = new FileOutputStream(new File(str));
+ } catch (FileNotFoundException ex) {
+ if (ExceptionMsg.equals(ex.getMessage()))
+ exceptionThrown = true;
+ }
+ if (!exceptionThrown) {
+ throw new RuntimeException("FileOutputStream constructor"
+ + " should throw FileNotFoundException");
+ }
+ if (os != null) {
+ throw new RuntimeException("FileOutputStream constructor"
+ + " should fail");
+ }
+ }
+
+ private static void testRandomAccessFile(final String str) {
+ boolean exceptionThrown = false;
+ RandomAccessFile raf = null;
+ String[] modes = {"r", "rw", "rws", "rwd"};
+
+ for (String mode : modes) {
+ try {
+ raf = new RandomAccessFile(str, mode);
+ } catch (FileNotFoundException ex) {
+ if (ExceptionMsg.equals(ex.getMessage()))
+ exceptionThrown = true;
+ }
+ if (!exceptionThrown) {
+ throw new RuntimeException("RandomAccessFile constructor"
+ + " should throw FileNotFoundException");
+ }
+ if (raf != null) {
+ throw new RuntimeException("RandomAccessFile constructor"
+ + " should fail");
+ }
+
+ exceptionThrown = false;
+ raf = null;
+ try {
+ raf = new RandomAccessFile(new File(str), mode);
+ } catch (FileNotFoundException ex) {
+ if (ExceptionMsg.equals(ex.getMessage()))
+ exceptionThrown = true;
+ }
+ if (!exceptionThrown) {
+ throw new RuntimeException("RandomAccessFile constructor"
+ + " should throw FileNotFoundException");
+ }
+ if (raf != null) {
+ throw new RuntimeException("RandomAccessFile constructor"
+ + " should fail");
+ }
+ }
+ }
+
+ private static void test(File testFile) {
+ test(testFile, false);
+ // test serialization
+ testSerialization(testFile);
+ }
+
+ @SuppressWarnings("deprecation")
+ private static void test(File testFile, boolean derived) {
+ boolean exceptionThrown = false;
+
+ if (testFile == null) {
+ throw new RuntimeException("test file should not be null.");
+ }
+
+ // getPath()
+ if (testFile.getPath().indexOf(CHAR_NUL) < 0) {
+ throw new RuntimeException(
+ "File path should contain Nul character");
+ }
+ // getAbsolutePath()
+ if (testFile.getAbsolutePath().indexOf(CHAR_NUL) < 0) {
+ throw new RuntimeException(
+ "File absolute path should contain Nul character");
+ }
+ // getAbsoluteFile()
+ File derivedAbsFile = testFile.getAbsoluteFile();
+ if (derived) {
+ if (derivedAbsFile.getPath().indexOf(CHAR_NUL) < 0) {
+ throw new RuntimeException(
+ "Derived file path should also contain Nul character");
+ }
+ } else {
+ test(derivedAbsFile, true);
+ }
+ // getCanonicalPath()
+ try {
+ exceptionThrown = false;
+ testFile.getCanonicalPath();
+ } catch (IOException ex) {
+ if (ExceptionMsg.equals(ex.getMessage()))
+ exceptionThrown = true;
+ }
+ if (!exceptionThrown) {
+ throw new RuntimeException(
+ "getCanonicalPath() should throw IOException with"
+ + " message \"" + ExceptionMsg + "\"");
+ }
+ // getCanonicalFile()
+ try {
+ exceptionThrown = false;
+ testFile.getCanonicalFile();
+ } catch (IOException ex) {
+ if (ExceptionMsg.equals(ex.getMessage()))
+ exceptionThrown = true;
+ }
+ if (!exceptionThrown) {
+ throw new RuntimeException(
+ "getCanonicalFile() should throw IOException with"
+ + " message \"" + ExceptionMsg + "\"");
+ }
+ // toURL()
+ try {
+ exceptionThrown = false;
+ testFile.toURL();
+ } catch (MalformedURLException ex) {
+ if (ExceptionMsg.equals(ex.getMessage()))
+ exceptionThrown = true;
+ }
+ if (!exceptionThrown) {
+ throw new RuntimeException("toURL() should throw IOException with"
+ + " message \"" + ExceptionMsg + "\"");
+ }
+ // canRead()
+ if (testFile.canRead())
+ throw new RuntimeException("File should not be readable");
+ // canWrite()
+ if (testFile.canWrite())
+ throw new RuntimeException("File should not be writable");
+ // exists()
+ if (testFile.exists())
+ throw new RuntimeException("File should not be existed");
+ // isDirectory()
+ if (testFile.isDirectory())
+ throw new RuntimeException("File should not be a directory");
+ // isFile()
+ if (testFile.isFile())
+ throw new RuntimeException("File should not be a file");
+ // isHidden()
+ if (testFile.isHidden())
+ throw new RuntimeException("File should not be hidden");
+ // lastModified()
+ if (testFile.lastModified() != 0L)
+ throw new RuntimeException("File last modified time should be 0L");
+ // length()
+ if (testFile.length() != 0L)
+ throw new RuntimeException("File length should be 0L");
+ // createNewFile()
+ try {
+ exceptionThrown = false;
+ testFile.createNewFile();
+ } catch (IOException ex) {
+ if (ExceptionMsg.equals(ex.getMessage()))
+ exceptionThrown = true;
+ }
+ if (!exceptionThrown) {
+ throw new RuntimeException(
+ "createNewFile() should throw IOException with"
+ + " message \"" + ExceptionMsg + "\"");
+ }
+ // delete()
+ if (testFile.delete())
+ throw new RuntimeException("Delete operation should fail");
+ // list()
+ if (testFile.list() != null)
+ throw new RuntimeException("File list() should return null");
+ // list(FilenameFilter)
+ FilenameFilter fnFilter = new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String name) {
+ return false;
+ }
+ };
+ if (testFile.list(fnFilter) != null) {
+ throw new RuntimeException("File list(FilenameFilter) should"
+ + " return null");
+ }
+ // listFiles()
+ if (testFile.listFiles() != null)
+ throw new RuntimeException("File listFiles() should return null");
+ // listFiles(FilenameFilter)
+ if (testFile.listFiles(fnFilter) != null) {
+ throw new RuntimeException("File listFiles(FilenameFilter)"
+ + " should return null");
+ }
+ // listFiles(FileFilter)
+ FileFilter fFilter = new FileFilter() {
+ @Override
+ public boolean accept(File file) {
+ return false;
+ }
+ };
+ if (testFile.listFiles(fFilter) != null) {
+ throw new RuntimeException("File listFiles(FileFilter)"
+ + " should return null");
+ }
+ // mkdir()
+ if (testFile.mkdir()) {
+ throw new RuntimeException("File should not be able to"
+ + " create directory");
+ }
+ // mkdirs()
+ if (testFile.mkdirs()) {
+ throw new RuntimeException("File should not be able to"
+ + " create directories");
+ }
+ // renameTo(File)
+ if (testFile.renameTo(new File("dest")))
+ throw new RuntimeException("File rename should fail");
+ if (new File("dest").renameTo(testFile))
+ throw new RuntimeException("File rename should fail");
+ try {
+ exceptionThrown = false;
+ testFile.renameTo(null);
+ } catch (NullPointerException ex) {
+ exceptionThrown = true;
+ }
+ if (!exceptionThrown) {
+ throw new RuntimeException("File rename should thrown NPE");
+ }
+ // setLastModified(long)
+ if (testFile.setLastModified(0L)) {
+ throw new RuntimeException("File should fail to set"
+ + " last modified time");
+ }
+ try {
+ exceptionThrown = false;
+ testFile.setLastModified(-1);
+ } catch (IllegalArgumentException ex) {
+ if ("Negative time".equals(ex.getMessage()))
+ exceptionThrown = true;
+ }
+ if (!exceptionThrown) {
+ throw new RuntimeException("File should fail to set"
+ + " last modified time with message \"Negative time\"");
+ }
+ // setReadOnly()
+ if (testFile.setReadOnly())
+ throw new RuntimeException("File should fail to set read-only");
+ // setWritable(boolean writable, boolean ownerOnly)
+ if (testFile.setWritable(true, true))
+ throw new RuntimeException("File should fail to set writable");
+ if (testFile.setWritable(true, false))
+ throw new RuntimeException("File should fail to set writable");
+ if (testFile.setWritable(false, true))
+ throw new RuntimeException("File should fail to set writable");
+ if (testFile.setWritable(false, false))
+ throw new RuntimeException("File should fail to set writable");
+ // setWritable(boolean writable)
+ if (testFile.setWritable(false))
+ throw new RuntimeException("File should fail to set writable");
+ if (testFile.setWritable(true))
+ throw new RuntimeException("File should fail to set writable");
+ // setReadable(boolean readable, boolean ownerOnly)
+ if (testFile.setReadable(true, true))
+ throw new RuntimeException("File should fail to set readable");
+ if (testFile.setReadable(true, false))
+ throw new RuntimeException("File should fail to set readable");
+ if (testFile.setReadable(false, true))
+ throw new RuntimeException("File should fail to set readable");
+ if (testFile.setReadable(false, false))
+ throw new RuntimeException("File should fail to set readable");
+ // setReadable(boolean readable)
+ if (testFile.setReadable(false))
+ throw new RuntimeException("File should fail to set readable");
+ if (testFile.setReadable(true))
+ throw new RuntimeException("File should fail to set readable");
+ // setExecutable(boolean executable, boolean ownerOnly)
+ if (testFile.setExecutable(true, true))
+ throw new RuntimeException("File should fail to set executable");
+ if (testFile.setExecutable(true, false))
+ throw new RuntimeException("File should fail to set executable");
+ if (testFile.setExecutable(false, true))
+ throw new RuntimeException("File should fail to set executable");
+ if (testFile.setExecutable(false, false))
+ throw new RuntimeException("File should fail to set executable");
+ // setExecutable(boolean executable)
+ if (testFile.setExecutable(false))
+ throw new RuntimeException("File should fail to set executable");
+ if (testFile.setExecutable(true))
+ throw new RuntimeException("File should fail to set executable");
+ // canExecute()
+ if (testFile.canExecute())
+ throw new RuntimeException("File should not be executable");
+ // getTotalSpace()
+ if (testFile.getTotalSpace() != 0L)
+ throw new RuntimeException("The total space should be 0L");
+ // getFreeSpace()
+ if (testFile.getFreeSpace() != 0L)
+ throw new RuntimeException("The free space should be 0L");
+ // getUsableSpace()
+ if (testFile.getUsableSpace() != 0L)
+ throw new RuntimeException("The usable space should be 0L");
+ // compareTo(File null)
+ try {
+ exceptionThrown = false;
+ testFile.compareTo(null);
+ } catch (NullPointerException ex) {
+ exceptionThrown = true;
+ }
+ if (!exceptionThrown) {
+ throw new RuntimeException("compareTo(null) should throw NPE");
+ }
+ // toString()
+ if (testFile.toString().indexOf(CHAR_NUL) < 0) {
+ throw new RuntimeException(
+ "File path should contain Nul character");
+ }
+ // toPath()
+ try {
+ exceptionThrown = false;
+ testFile.toPath();
+ } catch (InvalidPathException ex) {
+ exceptionThrown = true;
+ }
+ if (!exceptionThrown) {
+ throw new RuntimeException("toPath() should throw"
+ + " InvalidPathException");
+ }
+ }
+
+ private static void testSerialization(File testFile) {
+ String path = testFile.getPath();
+ try {
+ // serialize test file
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(baos);
+ oos.writeObject(testFile);
+ oos.close();
+ // deserialize test file
+ byte[] bytes = baos.toByteArray();
+ ByteArrayInputStream is = new ByteArrayInputStream(bytes);
+ ObjectInputStream ois = new ObjectInputStream(is);
+ File newFile = (File) ois.readObject();
+ // test
+ String newPath = newFile.getPath();
+ if (!path.equals(newPath)) {
+ throw new RuntimeException(
+ "Serialization should not change file path");
+ }
+ test(newFile, false);
+ } catch (IOException | ClassNotFoundException ex) {
+ System.err.println("Exception happens in testSerialization");
+ System.err.println(ex.getMessage());
+ }
+ }
+
+ private static void testTempFile() {
+ final String[] names = {"x", "xx", "xxx", "xxxx"};
+ final String shortPrefix = "sp";
+ final String prefix = "prefix";
+ final String suffix = "suffix";
+ File tmpDir = new File("tmpDir");
+
+ for (String name : names) {
+ int length = name.length();
+ for (int i = 0; i <= length; i++) {
+ StringBuilder sbName = new StringBuilder(name);
+ sbName.insert(i, CHAR_NUL);
+ String curName = sbName.toString();
+
+ // test prefix
+ testCreateTempFile(curName, suffix, tmpDir);
+ // test suffix
+ testCreateTempFile(shortPrefix, curName, tmpDir);
+ testCreateTempFile(prefix, curName, tmpDir);
+ // test directory
+ testCreateTempFile(shortPrefix, suffix, new File(curName));
+ testCreateTempFile(prefix, suffix, new File(curName));
+ }
+ }
+ }
+
+ private static void testCreateTempFile(String prefix, String suffix,
+ File directory) {
+ // createTempFile(String prefix, String suffix, File directory)
+ boolean exceptionThrown = false;
+ boolean shortPrefix = (prefix.length() < 3);
+ if (shortPrefix) {
+ try {
+ File.createTempFile(prefix, suffix, directory);
+ } catch (IllegalArgumentException ex) {
+ if ("Prefix string too short".equals(ex.getMessage()))
+ exceptionThrown = true;
+ } catch (IOException ioe) {
+ System.err.println("IOException happens in testCreateTempFile");
+ System.err.println(ioe.getMessage());
+ }
+ } else {
+ try {
+ File.createTempFile(prefix, suffix, directory);
+ } catch (IOException ex) {
+ if ("Unable to create temporary file".equals(ex.getMessage()))
+ exceptionThrown = true;
+ }
+ }
+ if (!exceptionThrown) {
+ throw new RuntimeException("createTempFile() should throw"
+ + (shortPrefix ? " IllegalArgumentException"
+ : " IOException"));
+ }
+ }
+}