--- old/src/hotspot/os/windows/os_windows.cpp 2017-11-20 11:53:19.878785355 -0800 +++ new/src/hotspot/os/windows/os_windows.cpp 2017-11-20 11:53:19.473747267 -0800 @@ -4061,41 +4061,116 @@ } } +// combine the high and low DWORD into a ULONGLONG +static ULONGLONG make_double_word(DWORD high_word, DWORD low_word) { + ULONGLONG value = high_word; + value <<= sizeof(high_word) * 8; + value |= low_word; + return value; +} + +// Transfers data from WIN32_FILE_ATTRIBUTE_DATA structure to struct stat +static void file_attribute_data_to_stat(struct stat* sbuf, WIN32_FILE_ATTRIBUTE_DATA file_data) { + ::memset((void*)sbuf, 0, sizeof(struct stat)); + sbuf->st_size = (_off_t)make_double_word(file_data.nFileSizeHigh, file_data.nFileSizeLow); + sbuf->st_mtime = make_double_word(file_data.ftLastWriteTime.dwHighDateTime, + file_data.ftLastWriteTime.dwLowDateTime); + sbuf->st_ctime = make_double_word(file_data.ftCreationTime.dwHighDateTime, + file_data.ftCreationTime.dwLowDateTime); + sbuf->st_atime = make_double_word(file_data.ftLastAccessTime.dwHighDateTime, + file_data.ftLastAccessTime.dwLowDateTime); + if ((file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { + sbuf->st_mode |= S_IFDIR; + } else { + sbuf->st_mode |= S_IFREG; + } +} + +// The following function is adapted from java.base/windows/native/libjava/canonicalize_md.c +// Creates an UNC path from a single byte path. Return buffer is +// allocated in C heap and needs to be freed by the caller. +// Returns NULL on error. +static wchar_t* create_unc_path(const char* path, errno_t &err) { + wchar_t* wpath = NULL; + size_t converted_chars = 0; + size_t path_len = strlen(path) + 1; // includes the terminating NULL + if (path[0] == '\\' && path[1] == '\\') { + if (path[2] == '?' && path[3] == '\\'){ + // if it already has a \\?\ don't do the prefix + wpath = (wchar_t*)os::malloc(path_len * sizeof(wchar_t), mtInternal); + if (wpath != NULL) { + err = ::mbstowcs_s(&converted_chars, wpath, path_len, path, path_len); + } else { + err = ENOMEM; + } + } else { + // only UNC pathname includes double slashes here + wpath = (wchar_t*)os::malloc((path_len + 7) * sizeof(wchar_t), mtInternal); + if (wpath != NULL) { + ::wcscpy(wpath, L"\\\\?\\UNC\0"); + err = ::mbstowcs_s(&converted_chars, &wpath[7], path_len, path, path_len); + } else { + err = ENOMEM; + } + } + } else { + wpath = (wchar_t*)os::malloc((path_len + 4) * sizeof(wchar_t), mtInternal); + if (wpath != NULL) { + ::wcscpy(wpath, L"\\\\?\\\0"); + err = ::mbstowcs_s(&converted_chars, &wpath[4], path_len, path, path_len); + } else { + err = ENOMEM; + } + } + return wpath; +} + +static void destroy_unc_path(wchar_t* wpath) { + os::free(wpath); +} int os::stat(const char *path, struct stat *sbuf) { - char pathbuf[MAX_PATH]; - if (strlen(path) > MAX_PATH - 1) { - errno = ENAMETOOLONG; + char* pathbuf = (char*)os::strdup(path, mtInternal); + if (pathbuf == NULL) { + errno = ENOMEM; return -1; } - os::native_path(strcpy(pathbuf, path)); - int ret = ::stat(pathbuf, sbuf); - if (sbuf != NULL && UseUTCFileTimestamp) { - // Fix for 6539723. st_mtime returned from stat() is dependent on - // the system timezone and so can return different values for the - // same file if/when daylight savings time changes. This adjustment - // makes sure the same timestamp is returned regardless of the TZ. - // - // See: - // http://msdn.microsoft.com/library/ - // default.asp?url=/library/en-us/sysinfo/base/ - // time_zone_information_str.asp - // and - // http://msdn.microsoft.com/library/default.asp?url= - // /library/en-us/sysinfo/base/settimezoneinformation.asp - // - // NOTE: there is a insidious bug here: If the timezone is changed - // after the call to stat() but before 'GetTimeZoneInformation()', then - // the adjustment we do here will be wrong and we'll return the wrong - // value (which will likely end up creating an invalid class data - // archive). Absent a better API for this, or some time zone locking - // mechanism, we'll have to live with this risk. - TIME_ZONE_INFORMATION tz; - DWORD tzid = GetTimeZoneInformation(&tz); - int daylightBias = - (tzid == TIME_ZONE_ID_DAYLIGHT) ? tz.DaylightBias : tz.StandardBias; - sbuf->st_mtime += (tz.Bias + daylightBias) * 60; + os::native_path(pathbuf); + int ret; + WIN32_FILE_ATTRIBUTE_DATA file_data; + // Not using stat() to avoid the problem described in JDK-6539723 + if (strlen(path) < MAX_PATH) { + BOOL bret = ::GetFileAttributesExA(pathbuf, GetFileExInfoStandard, &file_data); + if (!bret) { + errno = ::GetLastError(); + ret = -1; + } + else { + file_attribute_data_to_stat(sbuf, file_data); + ret = 0; + } + } else { + errno_t err = ERROR_SUCCESS; + wchar_t* wpath = create_unc_path(pathbuf, err); + if (err != ERROR_SUCCESS) { + if (wpath != NULL) { + destroy_unc_path(wpath); + } + os::free(pathbuf); + errno = err; + return -1; + } + BOOL bret = ::GetFileAttributesExW(wpath, GetFileExInfoStandard, &file_data); + if (!bret) { + errno = ::GetLastError(); + ret = -1; + } else { + file_attribute_data_to_stat(sbuf, file_data); + ret = 0; + } + destroy_unc_path(wpath); } + os::free(pathbuf); return ret; } @@ -4208,14 +4283,34 @@ // from src/windows/hpi/src/sys_api_md.c int os::open(const char *path, int oflag, int mode) { - char pathbuf[MAX_PATH]; - - if (strlen(path) > MAX_PATH - 1) { - errno = ENAMETOOLONG; + char* pathbuf = (char*)os::strdup(path, mtInternal); + if (pathbuf == NULL) { + errno = ENOMEM; return -1; } - os::native_path(strcpy(pathbuf, path)); - return ::open(pathbuf, oflag | O_BINARY | O_NOINHERIT, mode); + os::native_path(pathbuf); + int ret; + if (strlen(path) < MAX_PATH) { + ret = ::open(pathbuf, oflag | O_BINARY | O_NOINHERIT, mode); + } else { + errno_t err = ERROR_SUCCESS; + wchar_t* wpath = create_unc_path(pathbuf, err); + if (err != ERROR_SUCCESS) { + if (wpath != NULL) { + destroy_unc_path(wpath); + } + os::free(pathbuf); + errno = err; + return -1; + } + ret = ::_wopen(wpath, oflag | O_BINARY | O_NOINHERIT, mode); + if (ret == -1) { + errno = ::GetLastError(); + } + destroy_unc_path(wpath); + } + os::free(pathbuf); + return ret; } FILE* os::open(int fd, const char* mode) { --- old/src/hotspot/share/classfile/classLoader.cpp 2017-11-20 11:53:21.228912315 -0800 +++ new/src/hotspot/share/classfile/classLoader.cpp 2017-11-20 11:53:20.842876014 -0800 @@ -262,11 +262,11 @@ ClassFileStream* ClassPathDirEntry::open_stream(const char* name, TRAPS) { // construct full path name - char* path = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, char, JVM_MAXPATHLEN); - if (jio_snprintf(path, JVM_MAXPATHLEN, "%s%s%s", _dir, os::file_separator(), name) == -1) { - FREE_RESOURCE_ARRAY(char, path, JVM_MAXPATHLEN); - return NULL; - } + assert((_dir != NULL) && (name != NULL), "sanity"); + size_t path_len = strlen(_dir) + strlen(name) + strlen(os::file_separator()) + 1; + char* path = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, char, path_len); + int len = jio_snprintf(path, path_len, "%s%s%s", _dir, os::file_separator(), name); + assert(len == (int)(path_len - 1), "sanity"); // check if file exists struct stat st; if (os::stat(path, &st) == 0) { @@ -291,7 +291,7 @@ if (UsePerfData) { ClassLoader::perf_sys_classfile_bytes_read()->inc(num_read); } - FREE_RESOURCE_ARRAY(char, path, JVM_MAXPATHLEN); + FREE_RESOURCE_ARRAY(char, path, path_len); // Resource allocated return new ClassFileStream(buffer, st.st_size, @@ -300,7 +300,7 @@ } } } - FREE_RESOURCE_ARRAY(char, path, JVM_MAXPATHLEN); + FREE_RESOURCE_ARRAY(char, path, path_len); return NULL; } @@ -381,9 +381,13 @@ if (is_multi_ver) { int n; - char* entry_name = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, char, JVM_MAXPATHLEN); + const char* version_entry = "META-INF/versions/"; + // 10 is the max length of a decimal 32-bit non-negative number + // 2 includes the '/' and trailing zero + size_t entry_name_len = strlen(version_entry) + 10 + strlen(name) + 2; + char* entry_name = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, char, entry_name_len); if (version > 0) { - n = jio_snprintf(entry_name, JVM_MAXPATHLEN, "META-INF/versions/%d/%s", version, name); + n = jio_snprintf(entry_name, entry_name_len, "%s%d/%s", version_entry, version, name); entry_name[n] = '\0'; buffer = open_entry((const char*)entry_name, filesize, false, CHECK_NULL); if (buffer == NULL) { @@ -392,7 +396,7 @@ } if (buffer == NULL) { for (int i = cur_ver; i >= base_version; i--) { - n = jio_snprintf(entry_name, JVM_MAXPATHLEN, "META-INF/versions/%d/%s", i, name); + n = jio_snprintf(entry_name, entry_name_len, "%s%d/%s", version_entry, i, name); entry_name[n] = '\0'; buffer = open_entry((const char*)entry_name, filesize, false, CHECK_NULL); if (buffer != NULL) { @@ -400,7 +404,7 @@ } } } - FREE_RESOURCE_ARRAY(char, entry_name, JVM_MAXPATHLEN); + FREE_RESOURCE_ARRAY(char, entry_name, entry_name_len); } } return buffer; --- /dev/null 2017-09-20 01:43:30.052953571 -0700 +++ new/test/hotspot/jtreg/runtime/LoadClass/LongBCP.java 2017-11-20 11:53:21.930978334 -0800 @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2017, 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 + * @summary JVM should be able to handle full path (directory path plus + * class name) or directory path longer than MAX_PATH specified + * in -Xbootclasspath/a on windows. + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * @run main LongBCP + */ + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import jdk.test.lib.Platform; +import jdk.test.lib.compiler.CompilerUtils; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; + +public class LongBCP { + + private static final int MAX_PATH = 260; + + public static void main(String args[]) throws Exception { + Path sourceDir = Paths.get(System.getProperty("test.src"), "test-classes"); + Path classDir = Paths.get(System.getProperty("test.classes")); + Path destDir = classDir; + + // create a sub-path so that the destDir length is almost MAX_PATH + // so that the full path (with the class name) will exceed MAX_PATH + int subDirLen = MAX_PATH - classDir.toString().length() - 2; + if (subDirLen > 0) { + char[] chars = new char[subDirLen]; + Arrays.fill(chars, 'x'); + String subPath = new String(chars); + destDir = Paths.get(System.getProperty("test.classes"), subPath); + } + + CompilerUtils.compile(sourceDir, destDir); + + String bootCP = "-Xbootclasspath/a:" + destDir.toString(); + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + bootCP, "Hello"); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldContain("Hello World") + .shouldHaveExitValue(0); + + // increase the length of destDir to slightly over MAX_PATH + destDir = Paths.get(destDir.toString(), "xxxxx"); + CompilerUtils.compile(sourceDir, destDir); + + bootCP = "-Xbootclasspath/a:" + destDir.toString(); + pb = ProcessTools.createJavaProcessBuilder( + bootCP, "Hello"); + + output = new OutputAnalyzer(pb.start()); + output.shouldContain("Hello World") + .shouldHaveExitValue(0); + + // relative path tests + // We currently cannot handle relative path specified in the + // -Xbootclasspath/a on windows. + // + // relative path length within the 256 limit + char[] chars = new char[255]; + Arrays.fill(chars, 'y'); + String subPath = new String(chars); + destDir = Paths.get(".", subPath); + + CompilerUtils.compile(sourceDir, destDir); + + bootCP = "-Xbootclasspath/a:" + destDir.toString(); + pb = ProcessTools.createJavaProcessBuilder( + bootCP, "Hello"); + + output = new OutputAnalyzer(pb.start()); + if (!Platform.isWindows()) { + output.shouldContain("Hello World") + .shouldHaveExitValue(0); + } else { + output.shouldContain("Could not find or load main class Hello") + .shouldHaveExitValue(1); + } + + // total relative path length exceeds MAX_PATH + destDir = Paths.get(destDir.toString(), "yyyyyyyy"); + + CompilerUtils.compile(sourceDir, destDir); + + bootCP = "-Xbootclasspath/a:" + destDir.toString(); + pb = ProcessTools.createJavaProcessBuilder( + bootCP, "Hello"); + + output = new OutputAnalyzer(pb.start()); + if (!Platform.isWindows()) { + output.shouldContain("Hello World") + .shouldHaveExitValue(0); + } else { + output.shouldContain("Could not find or load main class Hello") + .shouldHaveExitValue(1); + } + } +} --- /dev/null 2017-09-20 01:43:30.052953571 -0700 +++ new/test/hotspot/jtreg/runtime/LoadClass/test-classes/Hello.java 2017-11-20 11:53:22.726053099 -0800 @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + */ + +public class Hello { + public static void main(String args[]) { + System.out.println("Hello World"); + } +}