8148023: File.createTempFile is not adhering to the contract regarding file name lengths

Truncate the prefix, suffix, random characters per the specification

Reviewed-by: rriggs
This commit is contained in:
Brian Burkhalter 2016-12-20 11:46:09 -08:00
parent a717e4a14b
commit 2cae845615
8 changed files with 227 additions and 7 deletions

View File

@ -114,6 +114,7 @@ SUNWprivate_1.1 {
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;

View File

@ -1903,20 +1903,72 @@ public class File
// 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;
}

View File

@ -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
@ -212,6 +212,13 @@ abstract class FileSystem {
/* -- 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.
*/

View File

@ -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 @@ class UnixFileSystem extends FileSystem {
/* -- 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());
}

View File

@ -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 @@ Java_java_io_UnixFileSystem_getSpace(JNIEnv *env, jobject this,
} 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;
}

View File

@ -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 @@ class WinNTFileSystem extends FileSystem {
/* -- 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());

View File

@ -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 @@ Java_java_io_WinNTFileSystem_getSpace0(JNIEnv *env, jobject this,
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;
}

View File

@ -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!");
}
}
}