8016579: (process) IOException thrown by ProcessBuilder.start() method is incorrectly encoded
authoruta
Fri, 19 Jul 2013 12:53:36 +0400
changeset 19032 e31afe87fb92
parent 19031 6c9afe6dad33
child 19033 13013b301bd0
8016579: (process) IOException thrown by ProcessBuilder.start() method is incorrectly encoded Reviewed-by: martin, dxu
jdk/src/share/native/java/io/io_util.c
jdk/src/windows/native/java/io/io_util_md.c
jdk/src/windows/native/java/lang/ProcessImpl_md.c
--- a/jdk/src/share/native/java/io/io_util.c	Thu Jul 18 23:16:52 2013 -0700
+++ b/jdk/src/share/native/java/io/io_util.c	Fri Jul 19 12:53:36 2013 +0400
@@ -211,7 +211,11 @@
 
     n = getLastErrorString(buf, sizeof(buf));
     if (n > 0) {
+#ifdef WIN32
+        why = (*env)->NewStringUTF(env, buf);
+#else
         why = JNU_NewStringPlatform(env, buf);
+#endif
     }
     x = JNU_NewObjectByName(env,
                             "java/io/FileNotFoundException",
--- a/jdk/src/windows/native/java/io/io_util_md.c	Thu Jul 18 23:16:52 2013 -0700
+++ b/jdk/src/windows/native/java/io/io_util_md.c	Fri Jul 19 12:53:36 2013 +0400
@@ -29,6 +29,7 @@
 #include "io_util.h"
 #include "io_util_md.h"
 #include <stdio.h>
+#include <windows.h>
 
 #include <wchar.h>
 #include <io.h>
@@ -40,6 +41,7 @@
 #include <limits.h>
 #include <wincon.h>
 
+
 static DWORD MAX_INPUT_EVENTS = 2000;
 
 /* If this returns NULL then an exception is pending */
@@ -569,42 +571,75 @@
 }
 
 size_t
-getLastErrorString(char *buf, size_t len)
+getLastErrorString(char *utf8_jvmErrorMsg, size_t cbErrorMsg)
 {
-    DWORD errval;
-    if (len > 0) {
-        if ((errval = GetLastError()) != 0) {
-            // DOS error
-            size_t n = (size_t)FormatMessage(
-                FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
-                NULL,
-                errval,
-                0,
-                buf,
-                (DWORD)len,
-                NULL);
-            if (n > 3) {
-                // Drop final '.', CR, LF
-                if (buf[n - 1] == '\n') n--;
-                if (buf[n - 1] == '\r') n--;
-                if (buf[n - 1] == '.') n--;
-                buf[n] = '\0';
+    size_t n = 0;
+    if (cbErrorMsg > 0) {
+        BOOLEAN noError = FALSE;
+        WCHAR *utf16_osErrorMsg = (WCHAR *)malloc(cbErrorMsg*sizeof(WCHAR));
+        if (utf16_osErrorMsg == NULL) {
+            // OOM accident
+            strncpy(utf8_jvmErrorMsg, "Out of memory", cbErrorMsg);
+            // truncate if too long
+            utf8_jvmErrorMsg[cbErrorMsg - 1] = '\0';
+            n = strlen(utf8_jvmErrorMsg);
+        } else {
+            DWORD errval = GetLastError();
+            if (errval != 0) {
+                // WIN32 error
+                n = (size_t)FormatMessageW(
+                    FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
+                    NULL,
+                    errval,
+                    0,
+                    utf16_osErrorMsg,
+                    (DWORD)cbErrorMsg,
+                    NULL);
+                if (n > 3) {
+                    // Drop final '.', CR, LF
+                    if (utf16_osErrorMsg[n - 1] == L'\n') --n;
+                    if (utf16_osErrorMsg[n - 1] == L'\r') --n;
+                    if (utf16_osErrorMsg[n - 1] == L'.') --n;
+                    utf16_osErrorMsg[n] = L'\0';
+                }
+            } else if (errno != 0) {
+                // C runtime error that has no corresponding WIN32 error code
+                const WCHAR *rtError = _wcserror(errno);
+                if (rtError != NULL) {
+                    wcsncpy(utf16_osErrorMsg, rtError, cbErrorMsg);
+                    // truncate if too long
+                    utf16_osErrorMsg[cbErrorMsg - 1] = L'\0';
+                    n = wcslen(utf16_osErrorMsg);
+                }
+            } else
+                noError = TRUE; //OS has no error to report
+
+            if (!noError) {
+                if (n > 0) {
+                    n = WideCharToMultiByte(
+                        CP_UTF8,
+                        0,
+                        utf16_osErrorMsg,
+                        n,
+                        utf8_jvmErrorMsg,
+                        cbErrorMsg,
+                        NULL,
+                        NULL);
+
+                    // no way to die
+                    if (n > 0)
+                        utf8_jvmErrorMsg[min(cbErrorMsg - 1, n)] = '\0';
+                }
+
+                if (n <= 0) {
+                    strncpy(utf8_jvmErrorMsg, "Secondary error while OS message extraction", cbErrorMsg);
+                    // truncate if too long
+                    utf8_jvmErrorMsg[cbErrorMsg - 1] = '\0';
+                    n = strlen(utf8_jvmErrorMsg);
+                }
             }
-            return n;
-        }
-
-        if (errno != 0) {
-            // C runtime error that has no corresponding DOS error code
-            const char *err = strerror(errno);
-            size_t n = strlen(err);
-            if (n >= len)
-                n = len - 1;
-
-            strncpy(buf, err, n);
-            buf[n] = '\0';
-            return n;
+            free(utf16_osErrorMsg);
         }
     }
-
-    return 0;
+    return n;
 }
--- a/jdk/src/windows/native/java/lang/ProcessImpl_md.c	Thu Jul 18 23:16:52 2013 -0700
+++ b/jdk/src/windows/native/java/lang/ProcessImpl_md.c	Fri Jul 19 12:53:36 2013 +0400
@@ -40,20 +40,70 @@
  */
 #define PIPE_SIZE (4096+24)
 
+/* We have THREE locales in action:
+ * 1. Thread default locale - dictates UNICODE-to-8bit conversion
+ * 2. System locale that defines the message localization
+ * 3. The file name locale
+ * Each locale could be an extended locale, that means that text cannot be
+ * mapped to 8bit sequence without multibyte encoding.
+ * VM is ready for that, if text is UTF-8.
+ * Here we make the work right from the beginning.
+ */
+size_t os_error_message(int errnum, WCHAR* utf16_OSErrorMsg, size_t maxMsgLength) {
+    size_t n = (size_t)FormatMessageW(
+            FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
+            NULL,
+            (DWORD)errnum,
+            0,
+            utf16_OSErrorMsg,
+            (DWORD)maxMsgLength,
+            NULL);
+    if (n > 3) {
+        // Drop final '.', CR, LF
+        if (utf16_OSErrorMsg[n - 1] == L'\n') --n;
+        if (utf16_OSErrorMsg[n - 1] == L'\r') --n;
+        if (utf16_OSErrorMsg[n - 1] == L'.') --n;
+        utf16_OSErrorMsg[n] = L'\0';
+    }
+    return n;
+}
+
+#define MESSAGE_LENGTH (256 + 100)
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(*x))
+
 static void
-win32Error(JNIEnv *env, const char *functionName)
+win32Error(JNIEnv *env, const WCHAR *functionName)
 {
-    static const char * const format = "%s error=%d, %s";
-    static const char * const fallbackFormat = "%s failed, error=%d";
-    char buf[256];
-    char errmsg[sizeof(buf) + 100];
-    const int errnum = GetLastError();
-    const int n = JVM_GetLastErrorString(buf, sizeof(buf));
-    if (n > 0)
-        sprintf(errmsg, format, functionName, errnum, buf);
-    else
-        sprintf(errmsg, fallbackFormat, functionName, errnum);
-    JNU_ThrowIOException(env, errmsg);
+    WCHAR utf16_OSErrorMsg[MESSAGE_LENGTH - 100];
+    WCHAR utf16_javaMessage[MESSAGE_LENGTH];
+    /*Good suggestion about 2-bytes-per-symbol in localized error reports*/
+    char  utf8_javaMessage[MESSAGE_LENGTH*2];
+    const int errnum = (int)GetLastError();
+    int n = os_error_message(errnum, utf16_OSErrorMsg, ARRAY_SIZE(utf16_OSErrorMsg));
+    n = (n > 0)
+        ? swprintf(utf16_javaMessage, MESSAGE_LENGTH, L"%s error=%d, %s", functionName, errnum, utf16_OSErrorMsg)
+        : swprintf(utf16_javaMessage, MESSAGE_LENGTH, L"%s failed, error=%d", functionName, errnum);
+
+    if (n > 0) /*terminate '\0' is not a part of conversion procedure*/
+        n = WideCharToMultiByte(
+            CP_UTF8,
+            0,
+            utf16_javaMessage,
+            n, /*by creation n <= MESSAGE_LENGTH*/
+            utf8_javaMessage,
+            MESSAGE_LENGTH*2,
+            NULL,
+            NULL);
+
+    /*no way to die*/
+    {
+        const char *errorMessage = "Secondary error while OS message extraction";
+        if (n > 0) {
+            utf8_javaMessage[min(MESSAGE_LENGTH*2 - 1, n)] = '\0';
+            errorMessage = utf8_javaMessage;
+        }
+        JNU_ThrowIOException(env, errorMessage);
+    }
 }
 
 static void
@@ -116,7 +166,7 @@
         handles[0] = (jlong) -1;
     } else {
         if (! CreatePipe(&inRead,  &inWrite,  &sa, PIPE_SIZE)) {
-            win32Error(env, "CreatePipe");
+            win32Error(env, L"CreatePipe");
             goto Catch;
         }
         si.hStdInput = inRead;
@@ -132,7 +182,7 @@
         handles[1] = (jlong) -1;
     } else {
         if (! CreatePipe(&outRead, &outWrite, &sa, PIPE_SIZE)) {
-            win32Error(env, "CreatePipe");
+            win32Error(env, L"CreatePipe");
             goto Catch;
         }
         si.hStdOutput = outWrite;
@@ -151,7 +201,7 @@
         handles[2] = (jlong) -1;
     } else {
         if (! CreatePipe(&errRead, &errWrite, &sa, PIPE_SIZE)) {
-            win32Error(env, "CreatePipe");
+            win32Error(env, L"CreatePipe");
             goto Catch;
         }
         si.hStdError = errWrite;
@@ -174,7 +224,7 @@
                          &si,              /* (in)  startup information */
                          &pi);             /* (out) process information */
     if (!ret) {
-        win32Error(env, "CreateProcess");
+        win32Error(env, L"CreateProcess");
         goto Catch;
     }
 
@@ -210,7 +260,7 @@
 {
     DWORD exit_code;
     if (GetExitCodeProcess((HANDLE) handle, &exit_code) == 0)
-        win32Error(env, "GetExitCodeProcess");
+        win32Error(env, L"GetExitCodeProcess");
     return exit_code;
 }
 
@@ -231,7 +281,7 @@
                                FALSE,    /* Wait for ANY event */
                                INFINITE)  /* Wait forever */
         == WAIT_FAILED)
-        win32Error(env, "WaitForMultipleObjects");
+        win32Error(env, L"WaitForMultipleObjects");
 }
 
 JNIEXPORT void JNICALL
@@ -250,7 +300,7 @@
                                     dwTimeout);  /* Wait for dwTimeout */
 
     if (result == WAIT_FAILED)
-        win32Error(env, "WaitForMultipleObjects");
+        win32Error(env, L"WaitForMultipleObjects");
 }
 
 JNIEXPORT void JNICALL
@@ -263,7 +313,7 @@
 Java_java_lang_ProcessImpl_isProcessAlive(JNIEnv *env, jclass ignored, jlong handle)
 {
     DWORD dwExitStatus;
-    GetExitCodeProcess(handle, &dwExitStatus);
+    GetExitCodeProcess((HANDLE) handle, &dwExitStatus);
     return dwExitStatus == STILL_ACTIVE;
 }