# HG changeset patch # User bpb # Date 1481924417 28800 # Fri Dec 16 13:40:17 2016 -0800 # Node ID ce6e83b35749e3d8b39420a630c1b76a6b96c47e # Parent 164b346d89b2fb2733681e6d2f359f4508a6d35a 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: XXX diff --git a/make/mapfiles/libjava/mapfile-vers b/make/mapfiles/libjava/mapfile-vers --- a/make/mapfiles/libjava/mapfile-vers +++ b/make/mapfiles/libjava/mapfile-vers @@ -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; diff --git a/src/java.base/share/classes/java/io/File.java b/src/java.base/share/classes/java/io/File.java --- a/src/java.base/share/classes/java/io/File.java +++ b/src/java.base/share/classes/java/io/File.java @@ -1903,20 +1903,54 @@ // file name generation private static final SecureRandom random = new SecureRandom(); + private static String shortenSubName(String subName, int excess, + int nameMin) { + long newLength = Math.max(nameMin, subName.length() - excess); + if (newLength < subName.length()) { + subName = subName.substring(0, (int)newLength); + } + return subName; + } 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; + String name = prefix + nus + suffix; + + // Shorten the name if it exceeds the maximum path component length + long nameMax = fs.getNameMax(dir.getPath()); + int excess = name.length() - (int)nameMax; + if (excess > 0) { + // Attempt to shorten the prefix length to no less then 3 + prefix = shortenSubName(prefix, excess, 3); + name = prefix + nus + suffix; + excess = name.length() - (int)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 ('.') + suffix = shortenSubName(suffix, excess, + suffix.indexOf(".") == 0 ? 4 : 0); + name = prefix + nus + suffix; + excess = name.length() - (int)nameMax; + } + if (excess > 0 && excess <= nus.length() - 5) { + // Attempt to shorten the random string length to no less than 5 + nus = shortenSubName(nus, excess, 5); + name = prefix + nus + suffix; + } + 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, " + + f); } return f; } diff --git a/src/java.base/share/classes/java/io/FileSystem.java b/src/java.base/share/classes/java/io/FileSystem.java --- a/src/java.base/share/classes/java/io/FileSystem.java +++ b/src/java.base/share/classes/java/io/FileSystem.java @@ -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 long getNameMax(String path); + + /** * Compare two abstract pathnames lexicographically. */ public abstract int compare(File f1, File f2); diff --git a/src/java.base/unix/classes/java/io/UnixFileSystem.java b/src/java.base/unix/classes/java/io/UnixFileSystem.java --- a/src/java.base/unix/classes/java/io/UnixFileSystem.java +++ b/src/java.base/unix/classes/java/io/UnixFileSystem.java @@ -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,12 @@ /* -- Basic infrastructure -- */ + private native long getNameMax0(String path); + + public long getNameMax(String path) { + return getNameMax0(path); + } + public int compare(File f1, File f2) { return f1.getPath().compareTo(f2.getPath()); } diff --git a/src/java.base/unix/native/libjava/UnixFileSystem_md.c b/src/java.base/unix/native/libjava/UnixFileSystem_md.c --- a/src/java.base/unix/native/libjava/UnixFileSystem_md.c +++ b/src/java.base/unix/native/libjava/UnixFileSystem_md.c @@ -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 #include #include #include @@ -38,6 +39,10 @@ #include #include +#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; +} diff --git a/src/java.base/windows/classes/java/io/WinNTFileSystem.java b/src/java.base/windows/classes/java/io/WinNTFileSystem.java --- a/src/java.base/windows/classes/java/io/WinNTFileSystem.java +++ b/src/java.base/windows/classes/java/io/WinNTFileSystem.java @@ -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 long getNameMax0(String path); + + public long 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()); diff --git a/src/java.base/windows/native/libjava/WinNTFileSystem_md.c b/src/java.base/windows/native/libjava/WinNTFileSystem_md.c --- a/src/java.base/windows/native/libjava/WinNTFileSystem_md.c +++ b/src/java.base/windows/native/libjava/WinNTFileSystem_md.c @@ -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 jlong 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 compnent length"); + } + + return (jlong)maxComponentLength; +} diff --git a/test/java/io/File/createTempFile/NameTooLong.java b/test/java/io/File/createTempFile/NameTooLong.java new file mode 100644 --- /dev/null +++ b/test/java/io/File/createTempFile/NameTooLong.java @@ -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!"); + } + } +}