8148023: File.createTempFile is not adhering to the contract regarding file name lengths
Summary: Truncate the prefix, suffix, random characters per the specification
Reviewed-by: rriggs
--- a/jdk/make/mapfiles/libjava/mapfile-vers Tue Dec 20 10:49:50 2016 +0100
+++ b/jdk/make/mapfiles/libjava/mapfile-vers Tue Dec 20 11:46:09 2016 -0800
@@ -114,6 +114,7 @@
Java_java_io_UnixFileSystem_getBooleanAttributes0;
Java_java_io_UnixFileSystem_getLastModifiedTime;
Java_java_io_UnixFileSystem_getLength;
+ Java_java_io_UnixFileSystem_getNameMax0;
Java_java_io_UnixFileSystem_getSpace;
Java_java_io_UnixFileSystem_initIDs;
Java_java_io_UnixFileSystem_list;
--- a/jdk/src/java.base/share/classes/java/io/File.java Tue Dec 20 10:49:50 2016 +0100
+++ b/jdk/src/java.base/share/classes/java/io/File.java Tue Dec 20 11:46:09 2016 -0800
@@ -1903,20 +1903,72 @@
// file name generation
private static final SecureRandom random = new SecureRandom();
+ private static int shortenSubName(int subNameLength, int excess,
+ int nameMin) {
+ int newLength = Math.max(nameMin, subNameLength - excess);
+ if (newLength < subNameLength) {
+ return newLength;
+ }
+ return subNameLength;
+ }
static File generateFile(String prefix, String suffix, File dir)
throws IOException
{
long n = random.nextLong();
+ String nus = Long.toUnsignedString(n);
// Use only the file name from the supplied prefix
prefix = (new File(prefix)).getName();
- String name = prefix + Long.toUnsignedString(n) + suffix;
+
+ int prefixLength = prefix.length();
+ int nusLength = nus.length();
+ int suffixLength = suffix.length();;
+
+ String name;
+ int nameMax = fs.getNameMax(dir.getPath());
+ int excess = prefixLength + nusLength + suffixLength - nameMax;
+ if (excess <= 0) {
+ name = prefix + nus + suffix;
+ } else {
+ // Name exceeds the maximum path component length: shorten it
+
+ // Attempt to shorten the prefix length to no less then 3
+ prefixLength = shortenSubName(prefixLength, excess, 3);
+ excess = prefixLength + nusLength + suffixLength - nameMax;
+
+ if (excess > 0) {
+ // Attempt to shorten the suffix length to no less than
+ // 0 or 4 depending on whether it begins with a dot ('.')
+ suffixLength = shortenSubName(suffixLength, excess,
+ suffix.indexOf(".") == 0 ? 4 : 0);
+ suffixLength = shortenSubName(suffixLength, excess, 3);
+ excess = prefixLength + nusLength + suffixLength - nameMax;
+ }
+
+ if (excess > 0 && excess <= nusLength - 5) {
+ // Attempt to shorten the random character string length
+ // to no less than 5
+ nusLength = shortenSubName(nusLength, excess, 5);
+ }
+
+ StringBuilder sb =
+ new StringBuilder(prefixLength + nusLength + suffixLength);
+ sb.append(prefixLength < prefix.length() ?
+ prefix.substring(0, prefixLength) : prefix);
+ sb.append(nusLength < nus.length() ?
+ nus.substring(0, nusLength) : nus);
+ sb.append(suffixLength < suffix.length() ?
+ suffix.substring(0, suffixLength) : suffix);
+ name = sb.toString();
+ }
+
File f = new File(dir, name);
if (!name.equals(f.getName()) || f.isInvalid()) {
if (System.getSecurityManager() != null)
throw new IOException("Unable to create temporary file");
else
- throw new IOException("Unable to create temporary file, " + f);
+ throw new IOException("Unable to create temporary file, "
+ + name);
}
return f;
}
--- a/jdk/src/java.base/share/classes/java/io/FileSystem.java Tue Dec 20 10:49:50 2016 +0100
+++ b/jdk/src/java.base/share/classes/java/io/FileSystem.java Tue Dec 20 11:46:09 2016 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2016, 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
@@ -213,6 +213,13 @@
/* -- Basic infrastructure -- */
/**
+ * Retrieve the maximum length of a component of a file path.
+ *
+ * @return The maximum length of a file path component.
+ */
+ public abstract int getNameMax(String path);
+
+ /**
* Compare two abstract pathnames lexicographically.
*/
public abstract int compare(File f1, File f2);
--- a/jdk/src/java.base/unix/classes/java/io/UnixFileSystem.java Tue Dec 20 10:49:50 2016 +0100
+++ b/jdk/src/java.base/unix/classes/java/io/UnixFileSystem.java Tue Dec 20 11:46:09 2016 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2016, 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
@@ -299,6 +299,16 @@
/* -- Basic infrastructure -- */
+ private native long getNameMax0(String path);
+
+ public int getNameMax(String path) {
+ long nameMax = getNameMax0(path);
+ if (nameMax > Integer.MAX_VALUE) {
+ nameMax = Integer.MAX_VALUE;
+ }
+ return (int)nameMax;
+ }
+
public int compare(File f1, File f2) {
return f1.getPath().compareTo(f2.getPath());
}
--- a/jdk/src/java.base/unix/native/libjava/UnixFileSystem_md.c Tue Dec 20 10:49:50 2016 +0100
+++ b/jdk/src/java.base/unix/native/libjava/UnixFileSystem_md.c Tue Dec 20 11:46:09 2016 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2016, 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
@@ -23,6 +23,7 @@
* questions.
*/
+#include <unistd.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/time.h>
@@ -38,6 +39,10 @@
#include <dlfcn.h>
#include <limits.h>
+#if defined(__solaris__) && !defined(NAME_MAX)
+#define NAME_MAX MAXNAMLEN
+#endif
+
#include "jni.h"
#include "jni_util.h"
#include "jlong.h"
@@ -487,3 +492,14 @@
} END_PLATFORM_STRING(env, path);
return rv;
}
+
+JNIEXPORT jlong JNICALL
+Java_java_io_UnixFileSystem_getNameMax0(JNIEnv *env, jobject this,
+ jstring pathname)
+{
+ jlong length = -1;
+ WITH_PLATFORM_STRING(env, pathname, path) {
+ length = (jlong)pathconf(path, _PC_NAME_MAX);
+ } END_PLATFORM_STRING(env, path);
+ return length != -1 ? length : (jlong)NAME_MAX;
+}
--- a/jdk/src/java.base/windows/classes/java/io/WinNTFileSystem.java Tue Dec 20 10:49:50 2016 +0100
+++ b/jdk/src/java.base/windows/classes/java/io/WinNTFileSystem.java Tue Dec 20 11:46:09 2016 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2016, 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
@@ -25,6 +25,8 @@
package java.io;
+import java.io.File;
+import java.nio.file.Path;
import java.util.Locale;
import java.util.Properties;
import sun.security.action.GetPropertyAction;
@@ -627,6 +629,27 @@
/* -- Basic infrastructure -- */
+ // Obtain maximum file component length from GetVolumeInformation which
+ // expects the path to be null or a root component ending in a backslash
+ private native int getNameMax0(String path);
+
+ public int getNameMax(String path) {
+ String s = null;
+ if (path != null) {
+ File f = new File(path);
+ if (f.isAbsolute()) {
+ Path root = f.toPath().getRoot();
+ if (root != null) {
+ s = root.toString();
+ if (!s.endsWith("\\")) {
+ s = s + "\\";
+ }
+ }
+ }
+ }
+ return getNameMax0(s);
+ }
+
@Override
public int compare(File f1, File f2) {
return f1.getPath().compareToIgnoreCase(f2.getPath());
--- a/jdk/src/java.base/windows/native/libjava/WinNTFileSystem_md.c Tue Dec 20 10:49:50 2016 +0100
+++ b/jdk/src/java.base/windows/native/libjava/WinNTFileSystem_md.c Tue Dec 20 11:46:09 2016 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2016, 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
@@ -887,3 +887,42 @@
free(pathbuf);
return rv;
}
+
+// pathname is expected to be either null or to contain the root
+// of the path terminated by a backslash
+JNIEXPORT jint JNICALL
+Java_java_io_WinNTFileSystem_getNameMax0(JNIEnv *env, jobject this,
+ jstring pathname)
+{
+ BOOL res = 0;
+ DWORD maxComponentLength;
+
+ if (pathname == NULL) {
+ res = GetVolumeInformationW(NULL,
+ NULL,
+ 0,
+ NULL,
+ &maxComponentLength,
+ NULL,
+ NULL,
+ 0);
+ } else {
+ WITH_UNICODE_STRING(env, pathname, path) {
+ res = GetVolumeInformationW(path,
+ NULL,
+ 0,
+ NULL,
+ &maxComponentLength,
+ NULL,
+ NULL,
+ 0);
+ } END_UNICODE_STRING(env, path);
+ }
+
+ if (res == 0) {
+ JNU_ThrowIOExceptionWithLastError(env,
+ "Could not get maximum component length");
+ }
+
+ return (jint)maxComponentLength;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/io/File/createTempFile/NameTooLong.java Tue Dec 20 11:46:09 2016 -0800
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2016, 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 8148023
+ * @summary Verify that createTempFile() will not fail for long component names.
+ */
+
+import java.io.File;
+import java.io.IOException;
+
+public class NameTooLong {
+ public static void main(String[] args) {
+ String[][] prefixSuffix = new String[][] {
+ new String[] {"1234567890123456789012345678901234567xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx89012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890","txt"},
+ new String[] {"prefix","1234567890123456789012345678901234567xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx89012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890.txt"},
+ new String[] {"prefix",".txt1234567890123456789012345678901234567xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx89012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"}
+ };
+
+ int failures = 0;
+ int index = 0;
+ for (String[] ps : prefixSuffix) {
+ File f;
+ try {
+ f = File.createTempFile(ps[0], ps[1]);
+ String s = f.toPath().getFileName().toString();
+ if (!s.startsWith(ps[0].substring(0, 3))) {
+ System.err.printf("%s did not start with %s%n", s,
+ ps[0].substring(0, 3));
+ failures++;
+ }
+ if (ps[1].startsWith(".")
+ && !s.contains(ps[1].substring(0, 4))) {
+ System.err.printf("%s did not contain %s%n", s,
+ ps[1].substring(0, 4));;
+ failures++;
+ }
+ } catch (IOException e) {
+ failures++;
+ System.err.println();
+ e.printStackTrace();
+ System.err.println();
+ }
+ index++;
+ }
+
+ if (failures != 0) {
+ throw new RuntimeException("Test failed!");
+ }
+ }
+}