8016579: (process) IOException thrown by ProcessBuilder.start() method is incorrectly encoded
Reviewed-by: martin, dxu
--- 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;
}