8190737: use unicode version of the canonicalize() function to handle long path on windows
authorccheung
Fri, 14 Sep 2018 11:17:25 -0700
changeset 51750 9151fde080e6
parent 51749 a0f0da2c2719
child 51751 b5921376ff2c
8190737: use unicode version of the canonicalize() function to handle long path on windows Summary: also calling CreateFileW in zip_util.c to handle long path Reviewed-by: sherman, iklam
src/hotspot/os/windows/include/jvm_md.h
src/java.base/share/native/libzip/zip_util.c
src/java.base/windows/native/libjava/canonicalize_md.c
src/java.base/windows/native/libjava/io_util_md.h
test/hotspot/jtreg/runtime/LoadClass/LongBCP.java
--- a/src/hotspot/os/windows/include/jvm_md.h	Tue Sep 11 22:16:47 2018 -0700
+++ b/src/hotspot/os/windows/include/jvm_md.h	Fri Sep 14 11:17:25 2018 -0700
@@ -63,7 +63,7 @@
 #include <stddef.h>  /* For uintptr_t */
 #include <stdlib.h>
 
-#define JVM_MAXPATHLEN _MAX_PATH
+#define JVM_MAXPATHLEN 1024
 
 #define JVM_R_OK    4
 #define JVM_W_OK    2
--- a/src/java.base/share/native/libzip/zip_util.c	Tue Sep 11 22:16:47 2018 -0700
+++ b/src/java.base/share/native/libzip/zip_util.c	Fri Sep 14 11:17:25 2018 -0700
@@ -100,6 +100,9 @@
 static ZFILE
 ZFILE_Open(const char *fname, int flags) {
 #ifdef WIN32
+    WCHAR *wfname, *wprefixed_fname;
+    size_t converted_chars, fname_length;
+    jlong fhandle;
     const DWORD access =
         (flags & O_RDWR)   ? (GENERIC_WRITE | GENERIC_READ) :
         (flags & O_WRONLY) ?  GENERIC_WRITE :
@@ -121,14 +124,37 @@
         FILE_ATTRIBUTE_NORMAL;
     const DWORD flagsAndAttributes = maybeWriteThrough | maybeDeleteOnClose;
 
-    return (jlong) CreateFile(
-        fname,          /* Wide char path name */
-        access,         /* Read and/or write permission */
-        sharing,        /* File sharing flags */
-        NULL,           /* Security attributes */
-        disposition,        /* creation disposition */
-        flagsAndAttributes, /* flags and attributes */
-        NULL);
+    fname_length = strlen(fname);
+    if (fname_length < MAX_PATH) {
+        return (jlong)CreateFile(
+            fname,              /* path name in multibyte char */
+            access,             /* Read and/or write permission */
+            sharing,            /* File sharing flags */
+            NULL,               /* Security attributes */
+            disposition,        /* creation disposition */
+            flagsAndAttributes, /* flags and attributes */
+            NULL);
+    } else {
+        if ((wfname = (WCHAR*)malloc((fname_length + 1) * sizeof(WCHAR))) == NULL)
+            return (jlong)INVALID_HANDLE_VALUE;
+
+        if (mbstowcs_s(&converted_chars, wfname, fname_length + 1, fname, fname_length) != 0) {
+            free(wfname);
+            return (jlong)INVALID_HANDLE_VALUE;
+        }
+        wprefixed_fname = getPrefixed(wfname, (int)fname_length);
+        fhandle = (jlong)CreateFileW(
+            wprefixed_fname,    /* Wide char path name */
+            access,             /* Read and/or write permission */
+            sharing,            /* File sharing flags */
+            NULL,               /* Security attributes */
+            disposition,        /* creation disposition */
+            flagsAndAttributes, /* flags and attributes */
+            NULL);
+        free(wfname);
+        free(wprefixed_fname);
+        return fhandle;
+    }
 #else
     return open(fname, flags, 0);
 #endif
--- a/src/java.base/windows/native/libjava/canonicalize_md.c	Tue Sep 11 22:16:47 2018 -0700
+++ b/src/java.base/windows/native/libjava/canonicalize_md.c	Fri Sep 14 11:17:25 2018 -0700
@@ -225,6 +225,8 @@
     return 1;
 }
 
+int wcanonicalize(WCHAR *orig_path, WCHAR *result, int size);
+
 /* Convert a pathname to canonical form.  The input orig_path is assumed to
    have been converted to native form already, via JVM_NativePath().  This is
    necessary because _fullpath() rejects duplicate separator characters on
@@ -237,6 +239,38 @@
     HANDLE h;
     char path[1024];    /* Working copy of path */
     char *src, *dst, *dend;
+    wchar_t *worig_path, *wresult;
+    size_t converted_chars = 0;
+
+    /* handle long path with length >= MAX_PATH */
+    if (strlen(orig_path) >= MAX_PATH) {
+        if ((worig_path = (WCHAR*)malloc(size * sizeof(WCHAR))) == NULL)
+            return -1;
+
+        if (mbstowcs_s(&converted_chars, worig_path, (size_t)size, orig_path, (size_t)(size - 1)) != 0) {
+            free(worig_path);
+            return -1;
+        }
+
+        if ((wresult = (WCHAR*)malloc(size * sizeof(WCHAR))) == NULL)
+            return -1;
+
+        if (wcanonicalize(worig_path, wresult, size) != 0) {
+            free(worig_path);
+            free(wresult);
+            return -1;
+        }
+
+        if (wcstombs_s(&converted_chars, result, (size_t)size, wresult, (size_t)(size - 1)) != 0) {
+            free(worig_path);
+            free(wresult);
+            return -1;
+        }
+
+        free(worig_path);
+        free(wresult);
+        return 0;
+    }
 
     /* Reject paths that contain wildcards */
     if (wild(orig_path)) {
@@ -245,15 +279,15 @@
     }
 
     /* Collapse instances of "foo\.." and ensure absoluteness.  Note that
-       contrary to the documentation, the _fullpath procedure does not require
-       the drive to be available.  It also does not reliably change all
-       occurrences of '/' to '\\' on Win95, so now JVM_NativePath does that. */
-    if(!_fullpath(path, orig_path, sizeof(path))) {
+      contrary to the documentation, the _fullpath procedure does not require
+      the drive to be available.  It also does not reliably change all
+      occurrences of '/' to '\\' on Win95, so now JVM_NativePath does that. */
+    if (!_fullpath(path, orig_path, sizeof(path))) {
         return -1;
     }
 
     /* Correction for Win95: _fullpath may leave a trailing "\\"
-       on a UNC pathname */
+      on a UNC pathname */
     if ((path[0] == '\\') && (path[1] == '\\')) {
         char *p = path + strlen(path);
         if ((p[-1] == '\\') && !islb(p[-2])) {
@@ -281,16 +315,16 @@
         char *p;
         p = nextsep(src + 2);    /* Skip past host name */
         if (!*p) {
-        /* A UNC pathname must begin with "\\\\host\\share",
-           so reject this path as invalid if there is no share name */
+            /* A UNC pathname must begin with "\\\\host\\share",
+            so reject this path as invalid if there is no share name */
             errno = EINVAL;
             return -1;
-    }
-    p = nextsep(p + 1);    /* Skip past share name */
-    if (!(dst = cp(dst, dend, '\0', src, p))) {
-        return -1;
-    }
-    src = p;
+        }
+        p = nextsep(p + 1);    /* Skip past share name */
+        if (!(dst = cp(dst, dend, '\0', src, p))) {
+            return -1;
+        }
+        src = p;
     } else {
         /* Invalid path */
         errno = EINVAL;
@@ -309,11 +343,11 @@
     }
 
     /* At this point we have copied either a drive specifier ("z:") or a UNC
-       prefix ("\\\\host\\share") to the result buffer, and src points to the
-       first byte of the remainder of the path.  We now scan through the rest
-       of the path, looking up each prefix in order to find the true name of
-       the last element of each prefix, thereby computing the full true name of
-       the original path. */
+    prefix ("\\\\host\\share") to the result buffer, and src points to the
+    first byte of the remainder of the path.  We now scan through the rest
+    of the path, looking up each prefix in order to find the true name of
+    the last element of each prefix, thereby computing the full true name of
+    the original path. */
     while (*src) {
         char *p = nextsep(src + 1);    /* Find next separator */
         char c = *p;
@@ -325,8 +359,8 @@
             /* Lookup succeeded; append true name to result and continue */
             FindClose(h);
             if (!(dst = cp(dst, dend, '\\',
-                           fd.cFileName,
-                           fd.cFileName + strlen(fd.cFileName)))) {
+                fd.cFileName,
+                fd.cFileName + strlen(fd.cFileName)))) {
                 return -1;
             }
             src = p;
@@ -344,8 +378,8 @@
     }
 
     if (dst >= dend) {
-    errno = ENAMETOOLONG;
-    return -1;
+        errno = ENAMETOOLONG;
+        return -1;
     }
     *dst = '\0';
     return 0;
@@ -587,7 +621,7 @@
  */
 
 /* copy \\?\ or \\?\UNC\ to the front of path*/
-WCHAR*
+__declspec(dllexport) WCHAR*
 getPrefixed(const WCHAR* path, int pathlen) {
     WCHAR* pathbuf = (WCHAR*)malloc((pathlen + 10) * sizeof (WCHAR));
     if (pathbuf != 0) {
--- a/src/java.base/windows/native/libjava/io_util_md.h	Tue Sep 11 22:16:47 2018 -0700
+++ b/src/java.base/windows/native/libjava/io_util_md.h	Fri Sep 14 11:17:25 2018 -0700
@@ -38,7 +38,7 @@
  */
 WCHAR* pathToNTPath(JNIEnv *env, jstring path, jboolean throwFNFE);
 WCHAR* fileToNTPath(JNIEnv *env, jobject file, jfieldID id);
-WCHAR* getPrefixed(const WCHAR* path, int pathlen);
+__declspec(dllexport) WCHAR* getPrefixed(const WCHAR* path, int pathlen);
 WCHAR* currentDir(int di);
 int currentDirLength(const WCHAR* path, int pathlen);
 int handleAvailable(FD fd, jlong *pbytes);
--- a/test/hotspot/jtreg/runtime/LoadClass/LongBCP.java	Tue Sep 11 22:16:47 2018 -0700
+++ b/test/hotspot/jtreg/runtime/LoadClass/LongBCP.java	Fri Sep 14 11:17:25 2018 -0700
@@ -29,6 +29,7 @@
  * @library /test/lib
  * @modules java.base/jdk.internal.misc
  *          java.management
+ *          jdk.jartool/sun.tools.jar
  * @run main LongBCP
  */
 
@@ -84,6 +85,23 @@
         output.shouldContain("Hello World")
               .shouldHaveExitValue(0);
 
+        // create a hello.jar
+        sun.tools.jar.Main jarTool = new sun.tools.jar.Main(System.out, System.err, "jar");
+        String helloJar = destDir.toString() + File.separator + "hello.jar";
+        if (!jarTool.run(new String[]
+            {"-cf", helloJar, "-C", destDir.toString(), "Hello.class"})) {
+            throw new RuntimeException("Could not write the Hello jar file");
+        }
+
+        // run with long bootclasspath to hello.jar
+        bootCP = "-Xbootclasspath/a:" + helloJar;
+        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.