# HG changeset patch # User igerasim # Date 1413871460 -14400 # Node ID df14347d022cba98881b444ee2ed4a23b8f6ee4f # Parent 3265941b1b6c36e8a7714b57ca60e04da80e3382 8016579: (process) IOException thrown by ProcessBuilder.start() method is incorrectly encoded diff --git a/make/java/nio/Makefile b/make/java/nio/Makefile --- a/make/java/nio/Makefile +++ b/make/java/nio/Makefile @@ -380,6 +380,8 @@ -libpath:$(LIBDIR) java.lib \ $(OBJDIR)/../../../../sun/java.net/net/$(OBJDIRNAME)/net.lib \ $(OBJDIR)/../../../java.lang/java/$(OBJDIRNAME)/io_util.obj \ + $(OBJDIR)/../../../java.lang/java/$(OBJDIRNAME)/io_util_md.obj \ + $(OBJDIR)/../../../java.lang/java/$(OBJDIRNAME)/canonicalize_md.obj \ $(OBJDIR)/../../../java.lang/java/$(OBJDIRNAME)/FileDescriptor_md.obj endif ifeq ($(PLATFORM), linux) diff --git a/src/share/native/java/io/io_util.c b/src/share/native/java/io/io_util.c --- a/src/share/native/java/io/io_util.c +++ b/src/share/native/java/io/io_util.c @@ -214,13 +214,17 @@ throwFileNotFoundException(JNIEnv *env, jstring path) { char buf[256]; - jint n; + size_t n; jobject x; jstring why = NULL; - n = JVM_GetLastErrorString(buf, sizeof(buf)); + 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", diff --git a/src/share/native/java/io/io_util.h b/src/share/native/java/io/io_util.h --- a/src/share/native/java/io/io_util.h +++ b/src/share/native/java/io/io_util.h @@ -54,7 +54,7 @@ jint len, jboolean append, jfieldID fid); void fileOpen(JNIEnv *env, jobject this, jstring path, jfieldID fid, int flags); void throwFileNotFoundException(JNIEnv *env, jstring path); - +size_t getLastErrorString(char *buf, size_t len); /* * Macros for managing platform strings. The typical usage pattern is: diff --git a/src/solaris/native/java/io/io_util_md.c b/src/solaris/native/java/io/io_util_md.c --- a/src/solaris/native/java/io/io_util_md.c +++ b/src/solaris/native/java/io/io_util_md.c @@ -118,3 +118,18 @@ JNU_ThrowIOExceptionWithLastError(env, "close failed"); } } + +size_t +getLastErrorString(char *buf, size_t len) +{ + if (errno == 0 || len < 1) return 0; + + 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; +} diff --git a/src/windows/native/java/io/io_util_md.c b/src/windows/native/java/io/io_util_md.c --- a/src/windows/native/java/io/io_util_md.c +++ b/src/windows/native/java/io/io_util_md.c @@ -29,6 +29,7 @@ #include "io_util.h" #include "io_util_md.h" #include +#include #include #include @@ -42,6 +43,7 @@ extern jboolean onNT = JNI_FALSE; + static DWORD MAX_INPUT_EVENTS = 2000; void @@ -589,3 +591,77 @@ } return long_to_jlong(pos.QuadPart); } + +size_t +getLastErrorString(char *utf8_jvmErrorMsg, size_t cbErrorMsg) +{ + 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); + } + } + free(utf16_osErrorMsg); + } + } + return n; +} diff --git a/src/windows/native/java/lang/ProcessImpl_md.c b/src/windows/native/java/lang/ProcessImpl_md.c --- a/src/windows/native/java/lang/ProcessImpl_md.c +++ b/src/windows/native/java/lang/ProcessImpl_md.c @@ -112,20 +112,70 @@ return newFlag; } +/* 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 @@ -193,7 +243,7 @@ instead of 'Everybody' access */ PIPE_SIZE)) { - win32Error(env, "CreatePipe"); + win32Error(env, L"CreatePipe"); return FALSE; } else { /* [thisProcessEnd] has no the inherit flag because @@ -349,7 +399,7 @@ &si, /* (in) startup information */ &pi)) /* (out) process information */ { - win32Error(env, "CreateProcess"); + win32Error(env, L"CreateProcess"); } else { closeSafely(pi.hThread); ret = (jlong)pi.hProcess; @@ -410,7 +460,7 @@ { DWORD exit_code; if (GetExitCodeProcess((HANDLE) handle, &exit_code) == 0) - win32Error(env, "GetExitCodeProcess"); + win32Error(env, L"GetExitCodeProcess"); return exit_code; } @@ -431,7 +481,7 @@ FALSE, /* Wait for ANY event */ INFINITE) /* Wait forever */ == WAIT_FAILED) - win32Error(env, "WaitForMultipleObjects"); + win32Error(env, L"WaitForMultipleObjects"); } JNIEXPORT void JNICALL