1 /*
   2  * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #include "jni.h"
  27 #include "jni_util.h"
  28 #include "jvm.h"
  29 #include "io_util.h"
  30 #include "io_util_md.h"
  31 #include <stdio.h>
  32 #include <windows.h>
  33 
  34 #include <wchar.h>
  35 #include <io.h>
  36 #include <fcntl.h>
  37 #include <errno.h>
  38 #include <string.h>
  39 #include <sys/types.h>
  40 #include <sys/stat.h>
  41 #include <limits.h>
  42 #include <wincon.h>
  43 
  44 extern jboolean onNT = JNI_FALSE;
  45 
  46 static DWORD MAX_INPUT_EVENTS = 2000;
  47 
  48 void
  49 initializeWindowsVersion() {
  50     OSVERSIONINFO ver;
  51     ver.dwOSVersionInfoSize = sizeof(ver);
  52     GetVersionEx(&ver);
  53     if (ver.dwPlatformId == VER_PLATFORM_WIN32_NT) {
  54         onNT = JNI_TRUE;
  55     } else {
  56         onNT = JNI_FALSE;
  57     }
  58 }
  59 
  60 /* If this returns NULL then an exception is pending */
  61 WCHAR*
  62 fileToNTPath(JNIEnv *env, jobject file, jfieldID id) {
  63     jstring path = NULL;
  64     if (file != NULL) {
  65         path = (*env)->GetObjectField(env, file, id);
  66     }
  67     return pathToNTPath(env, path, JNI_FALSE);
  68 }
  69 
  70 /* Returns the working directory for the given drive, or NULL */
  71 WCHAR*
  72 currentDir(int di) {
  73     UINT dt;
  74     WCHAR root[4];
  75     // verify drive is valid as _wgetdcwd in the VC++ 2010 runtime
  76     // library does not handle invalid drives.
  77     root[0] = L'A' + (WCHAR)(di - 1);
  78     root[1] = L':';
  79     root[2] = L'\\';
  80     root[3] = L'\0';
  81     dt = GetDriveTypeW(root);
  82     if (dt == DRIVE_UNKNOWN || dt == DRIVE_NO_ROOT_DIR) {
  83         return NULL;
  84     } else {
  85         return _wgetdcwd(di, NULL, MAX_PATH);
  86     }
  87 }
  88 
  89 /* We cache the length of current working dir here to avoid
  90    calling _wgetcwd() every time we need to resolve a relative
  91    path. This piece of code needs to be revisited if chdir
  92    makes its way into java runtime.
  93 */
  94 
  95 int
  96 currentDirLength(const WCHAR* ps, int pathlen) {
  97     WCHAR *dir;
  98     if (pathlen > 2 && ps[1] == L':' && ps[2] != L'\\') {
  99         //drive-relative
 100         WCHAR d = ps[0];
 101         int dirlen = 0;
 102         int di = 0;
 103         if ((d >= L'a') && (d <= L'z')) di = d - L'a' + 1;
 104         else if ((d >= L'A') && (d <= L'Z')) di = d - L'A' + 1;
 105         else return 0; /* invalid drive name. */
 106         dir = currentDir(di);
 107         if (dir != NULL){
 108             dirlen = (int)wcslen(dir);
 109             free(dir);
 110         }
 111         return dirlen;
 112     } else {
 113         static int curDirLenCached = -1;
 114         //relative to both drive and directory
 115         if (curDirLenCached == -1) {
 116             int dirlen = -1;
 117             dir = _wgetcwd(NULL, MAX_PATH);
 118             if (dir != NULL) {
 119                 curDirLenCached = (int)wcslen(dir);
 120                 free(dir);
 121             }
 122         }
 123         return curDirLenCached;
 124     }
 125 }
 126 
 127 /*
 128   The "abpathlen" is the size of the buffer needed by _wfullpath. If the
 129   "path" is a relative path, it is "the length of the current dir" + "the
 130   length of the path", if it's "absolute" already, it's the same as
 131   pathlen which is the length of "path".
 132  */
 133 WCHAR* prefixAbpath(const WCHAR* path, int pathlen, int abpathlen) {
 134     WCHAR* pathbuf = NULL;
 135     WCHAR* abpath = NULL;
 136 
 137     abpathlen += 10;  //padding
 138     abpath = (WCHAR*)malloc(abpathlen * sizeof(WCHAR));
 139     if (abpath) {
 140         /* Collapse instances of "foo\.." and ensure absoluteness before
 141            going down to prefixing.
 142         */
 143         if (_wfullpath(abpath, path, abpathlen)) {
 144             pathbuf = getPrefixed(abpath, abpathlen);
 145         } else {
 146             /* _wfullpath fails if the pathlength exceeds 32k wchar.
 147                Instead of doing more fancy things we simply copy the
 148                ps into the return buffer, the subsequent win32 API will
 149                probably fail with FileNotFoundException, which is expected
 150             */
 151             pathbuf = (WCHAR*)malloc((pathlen + 6) * sizeof(WCHAR));
 152             if (pathbuf != 0) {
 153                 wcscpy(pathbuf, path);
 154             }
 155         }
 156         free(abpath);
 157     }
 158     return pathbuf;
 159 }
 160 
 161 /* If this returns NULL then an exception is pending */
 162 WCHAR*
 163 pathToNTPath(JNIEnv *env, jstring path, jboolean throwFNFE) {
 164     int pathlen = 0;
 165     WCHAR *pathbuf = NULL;
 166     int max_path = 248; /* CreateDirectoryW() has the limit of 248 */
 167 
 168     WITH_UNICODE_STRING(env, path, ps) {
 169         pathlen = (int)wcslen(ps);
 170         if (pathlen != 0) {
 171             if (pathlen > 2 &&
 172                 (ps[0] == L'\\' && ps[1] == L'\\' ||   //UNC
 173                  ps[1] == L':' && ps[2] == L'\\'))     //absolute
 174             {
 175                  if (pathlen > max_path - 1) {
 176                      pathbuf = prefixAbpath(ps, pathlen, pathlen);
 177                  } else {
 178                      pathbuf = (WCHAR*)malloc((pathlen + 6) * sizeof(WCHAR));
 179                      if (pathbuf != 0) {
 180                          wcscpy(pathbuf, ps);
 181                      }
 182                  }
 183             } else {
 184                 /* If the path came in as a relative path, need to verify if
 185                    its absolute form is bigger than max_path or not, if yes
 186                    need to (1)convert it to absolute and (2)prefix. This is
 187                    obviously a burden to all relative paths (The current dir/len
 188                    for "drive & directory" relative path is cached, so we only
 189                    calculate it once but for "drive-relative path we call
 190                    _wgetdcwd() and wcslen() everytime), but a hit we have
 191                    to take if we want to support relative path beyond max_path.
 192                    There is no way to predict how long the absolute path will be
 193                    (therefor allocate the sufficient memory block) before calling
 194                    _wfullpath(), we have to get the length of "current" dir first.
 195                 */
 196                 WCHAR *abpath = NULL;
 197                 int dirlen = currentDirLength(ps, pathlen);
 198                 if (dirlen + pathlen + 1 > max_path - 1) {
 199                     pathbuf = prefixAbpath(ps, pathlen, dirlen + pathlen);
 200                 } else {
 201                     pathbuf = (WCHAR*)malloc((pathlen + 6) * sizeof(WCHAR));
 202                     if (pathbuf != 0) {
 203                         wcscpy(pathbuf, ps);
 204                     }
 205                 }
 206             }
 207         }
 208     } END_UNICODE_STRING(env, ps);
 209 
 210     if (pathlen == 0) {
 211         if (throwFNFE == JNI_TRUE) {
 212             throwFileNotFoundException(env, path);
 213             return NULL;
 214         } else {
 215             pathbuf = (WCHAR*)malloc(sizeof(WCHAR));
 216             pathbuf[0] = L'\0';
 217         }
 218     }
 219     if (pathbuf == 0) {
 220         JNU_ThrowOutOfMemoryError(env, 0);
 221         return NULL;
 222     }
 223     return pathbuf;
 224 }
 225 
 226 jlong
 227 winFileHandleOpen(JNIEnv *env, jstring path, int flags)
 228 {
 229     const DWORD access =
 230         (flags & O_WRONLY) ?  GENERIC_WRITE :
 231         (flags & O_RDWR)   ? (GENERIC_READ | GENERIC_WRITE) :
 232         GENERIC_READ;
 233     const DWORD sharing =
 234         FILE_SHARE_READ | FILE_SHARE_WRITE;
 235     const DWORD disposition =
 236         /* Note: O_TRUNC overrides O_CREAT */
 237         (flags & O_TRUNC) ? CREATE_ALWAYS :
 238         (flags & O_CREAT) ? OPEN_ALWAYS   :
 239         OPEN_EXISTING;
 240     const DWORD  maybeWriteThrough =
 241         (flags & (O_SYNC | O_DSYNC)) ?
 242         FILE_FLAG_WRITE_THROUGH :
 243         FILE_ATTRIBUTE_NORMAL;
 244     const DWORD maybeDeleteOnClose =
 245         (flags & O_TEMPORARY) ?
 246         FILE_FLAG_DELETE_ON_CLOSE :
 247         FILE_ATTRIBUTE_NORMAL;
 248     const DWORD flagsAndAttributes = maybeWriteThrough | maybeDeleteOnClose;
 249     HANDLE h = NULL;
 250 
 251     if (onNT) {
 252         WCHAR *pathbuf = pathToNTPath(env, path, JNI_TRUE);
 253         if (pathbuf == NULL) {
 254             /* Exception already pending */
 255             return -1;
 256         }
 257         h = CreateFileW(
 258             pathbuf,            /* Wide char path name */
 259             access,             /* Read and/or write permission */
 260             sharing,            /* File sharing flags */
 261             NULL,               /* Security attributes */
 262             disposition,        /* creation disposition */
 263             flagsAndAttributes, /* flags and attributes */
 264             NULL);
 265         free(pathbuf);
 266     } else {
 267         WITH_PLATFORM_STRING(env, path, _ps) {
 268             h = CreateFile(_ps, access, sharing, NULL, disposition,
 269                            flagsAndAttributes, NULL);
 270         } END_PLATFORM_STRING(env, _ps);
 271     }
 272     if (h == INVALID_HANDLE_VALUE) {
 273         int error = GetLastError();
 274         if (error == ERROR_TOO_MANY_OPEN_FILES) {
 275             JNU_ThrowByName(env, JNU_JAVAIOPKG "IOException",
 276                             "Too many open files");
 277             return -1;
 278         }
 279         throwFileNotFoundException(env, path);
 280         return -1;
 281     }
 282     return (jlong) h;
 283 }
 284 
 285 void
 286 fileOpen(JNIEnv *env, jobject this, jstring path, jfieldID fid, int flags)
 287 {
 288     jlong h = winFileHandleOpen(env, path, flags);
 289     if (h >= 0) {
 290         SET_FD(this, h, fid);
 291     }
 292 }
 293 
 294 /* These are functions that use a handle fd instead of the
 295    old C style int fd as is used in HPI layer */
 296 
 297 static int
 298 handleNonSeekAvailable(jlong, long *);
 299 static int
 300 handleStdinAvailable(jlong, long *);
 301 
 302 int
 303 handleAvailable(jlong fd, jlong *pbytes) {
 304     HANDLE h = (HANDLE)fd;
 305     DWORD type = 0;
 306 
 307     type = GetFileType(h);
 308     /* Handle is for keyboard or pipe */
 309     if (type == FILE_TYPE_CHAR || type == FILE_TYPE_PIPE) {
 310         int ret;
 311         long lpbytes;
 312         HANDLE stdInHandle = GetStdHandle(STD_INPUT_HANDLE);
 313         if (stdInHandle == h) {
 314             ret = handleStdinAvailable(fd, &lpbytes); /* keyboard */
 315         } else {
 316             ret = handleNonSeekAvailable(fd, &lpbytes); /* pipe */
 317         }
 318         (*pbytes) = (jlong)(lpbytes);
 319         return ret;
 320     }
 321     /* Handle is for regular file */
 322     if (type == FILE_TYPE_DISK) {
 323         jlong current, end;
 324 
 325         LARGE_INTEGER filesize;
 326         current = handleLseek(fd, 0, SEEK_CUR);
 327         if (current < 0) {
 328             return FALSE;
 329         }
 330         if (GetFileSizeEx(h, &filesize) == 0) {
 331             return FALSE;
 332         }
 333         end = long_to_jlong(filesize.QuadPart);
 334         *pbytes = end - current;
 335         return TRUE;
 336     }
 337     return FALSE;
 338 }
 339 
 340 static int
 341 handleNonSeekAvailable(jlong fd, long *pbytes) {
 342     /* This is used for available on non-seekable devices
 343      * (like both named and anonymous pipes, such as pipes
 344      *  connected to an exec'd process).
 345      * Standard Input is a special case.
 346      *
 347      */
 348     HANDLE han;
 349 
 350     if ((han = (HANDLE) fd) == INVALID_HANDLE_VALUE) {
 351         return FALSE;
 352     }
 353 
 354     if (! PeekNamedPipe(han, NULL, 0, NULL, pbytes, NULL)) {
 355         /* PeekNamedPipe fails when at EOF.  In that case we
 356          * simply make *pbytes = 0 which is consistent with the
 357          * behavior we get on Solaris when an fd is at EOF.
 358          * The only alternative is to raise and Exception,
 359          * which isn't really warranted.
 360          */
 361         if (GetLastError() != ERROR_BROKEN_PIPE) {
 362             return FALSE;
 363         }
 364         *pbytes = 0;
 365     }
 366     return TRUE;
 367 }
 368 
 369 static int
 370 handleStdinAvailable(jlong fd, long *pbytes) {
 371     HANDLE han;
 372     DWORD numEventsRead = 0;    /* Number of events read from buffer */
 373     DWORD numEvents = 0;        /* Number of events in buffer */
 374     DWORD i = 0;                /* Loop index */
 375     DWORD curLength = 0;        /* Position marker */
 376     DWORD actualLength = 0;     /* Number of bytes readable */
 377     BOOL error = FALSE;         /* Error holder */
 378     INPUT_RECORD *lpBuffer;     /* Pointer to records of input events */
 379     DWORD bufferSize = 0;
 380 
 381     if ((han = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE) {
 382         return FALSE;
 383     }
 384 
 385     /* Construct an array of input records in the console buffer */
 386     error = GetNumberOfConsoleInputEvents(han, &numEvents);
 387     if (error == 0) {
 388         return handleNonSeekAvailable(fd, pbytes);
 389     }
 390 
 391     /* lpBuffer must fit into 64K or else PeekConsoleInput fails */
 392     if (numEvents > MAX_INPUT_EVENTS) {
 393         numEvents = MAX_INPUT_EVENTS;
 394     }
 395 
 396     bufferSize = numEvents * sizeof(INPUT_RECORD);
 397     if (bufferSize == 0)
 398         bufferSize = 1;
 399     lpBuffer = malloc(bufferSize);
 400     if (lpBuffer == NULL) {
 401         return FALSE;
 402     }
 403 
 404     error = PeekConsoleInput(han, lpBuffer, numEvents, &numEventsRead);
 405     if (error == 0) {
 406         free(lpBuffer);
 407         return FALSE;
 408     }
 409 
 410     /* Examine input records for the number of bytes available */
 411     for(i=0; i<numEvents; i++) {
 412         if (lpBuffer[i].EventType == KEY_EVENT) {
 413             KEY_EVENT_RECORD *keyRecord = (KEY_EVENT_RECORD *)
 414                                           &(lpBuffer[i].Event);
 415             if (keyRecord->bKeyDown == TRUE) {
 416                 CHAR *keyPressed = (CHAR *) &(keyRecord->uChar);
 417                 curLength++;
 418                 if (*keyPressed == '\r')
 419                     actualLength = curLength;
 420             }
 421         }
 422     }
 423     if(lpBuffer != NULL)
 424         free(lpBuffer);
 425     *pbytes = (long) actualLength;
 426     return TRUE;
 427 }
 428 
 429 /*
 430  * This is documented to succeed on read-only files, but Win32's
 431  * FlushFileBuffers functions fails with "access denied" in such a
 432  * case.  So we only signal an error if the error is *not* "access
 433  * denied".
 434  */
 435 
 436 JNIEXPORT int
 437 handleSync(jlong fd) {
 438     /*
 439      * From the documentation:
 440      *
 441      *     On Windows NT, the function FlushFileBuffers fails if hFile
 442      *     is a handle to console output. That is because console
 443      *     output is not buffered. The function returns FALSE, and
 444      *     GetLastError returns ERROR_INVALID_HANDLE.
 445      *
 446      * On the other hand, on Win95, it returns without error.  I cannot
 447      * assume that 0, 1, and 2 are console, because if someone closes
 448      * System.out and then opens a file, they might get file descriptor
 449      * 1.  An error on *that* version of 1 should be reported, whereas
 450      * an error on System.out (which was the original 1) should be
 451      * ignored.  So I use isatty() to ensure that such an error was due
 452      * to this bogosity, and if it was, I ignore the error.
 453      */
 454 
 455     HANDLE handle = (HANDLE)fd;
 456 
 457     if (!FlushFileBuffers(handle)) {
 458         if (GetLastError() != ERROR_ACCESS_DENIED) {    /* from winerror.h */
 459             return -1;
 460         }
 461     }
 462     return 0;
 463 }
 464 
 465 
 466 int
 467 handleSetLength(jlong fd, jlong length) {
 468     HANDLE h = (HANDLE)fd;
 469     long high = (long)(length >> 32);
 470     DWORD ret;
 471 
 472     if (h == (HANDLE)(-1)) return -1;
 473     ret = SetFilePointer(h, (long)(length), &high, FILE_BEGIN);
 474     if (ret == 0xFFFFFFFF && GetLastError() != NO_ERROR) {
 475         return -1;
 476     }
 477     if (SetEndOfFile(h) == FALSE) return -1;
 478     return 0;
 479 }
 480 
 481 JNIEXPORT
 482 size_t
 483 handleRead(jlong fd, void *buf, jint len)
 484 {
 485     DWORD read = 0;
 486     BOOL result = 0;
 487     HANDLE h = (HANDLE)fd;
 488     if (h == INVALID_HANDLE_VALUE) {
 489         return -1;
 490     }
 491     result = ReadFile(h,          /* File handle to read */
 492                       buf,        /* address to put data */
 493                       len,        /* number of bytes to read */
 494                       &read,      /* number of bytes read */
 495                       NULL);      /* no overlapped struct */
 496     if (result == 0) {
 497         int error = GetLastError();
 498         if (error == ERROR_BROKEN_PIPE) {
 499             return 0; /* EOF */
 500         }
 501         return -1;
 502     }
 503     return read;
 504 }
 505 
 506 static size_t writeInternal(jlong fd, const void *buf, jint len, jboolean append)
 507 {
 508     BOOL result = 0;
 509     DWORD written = 0;
 510     HANDLE h = (HANDLE)fd;
 511     if (h != INVALID_HANDLE_VALUE) {
 512         OVERLAPPED ov;
 513         LPOVERLAPPED lpOv;
 514         if (append == JNI_TRUE) {
 515             ov.Offset = (DWORD)0xFFFFFFFF;
 516             ov.OffsetHigh = (DWORD)0xFFFFFFFF;
 517             ov.hEvent = NULL;
 518             lpOv = &ov;
 519         } else {
 520             lpOv = NULL;
 521         }
 522         result = WriteFile(h,                /* File handle to write */
 523                            buf,              /* pointers to the buffers */
 524                            len,              /* number of bytes to write */
 525                            &written,         /* receives number of bytes written */
 526                            lpOv);            /* overlapped struct */
 527     }
 528     if ((h == INVALID_HANDLE_VALUE) || (result == 0)) {
 529         return -1;
 530     }
 531     return (size_t)written;
 532 }
 533 
 534 JNIEXPORT
 535 size_t handleWrite(jlong fd, const void *buf, jint len) {
 536     return writeInternal(fd, buf, len, JNI_FALSE);
 537 }
 538 
 539 JNIEXPORT
 540 size_t handleAppend(jlong fd, const void *buf, jint len) {
 541     return writeInternal(fd, buf, len, JNI_TRUE);
 542 }
 543 
 544 jint
 545 handleClose(JNIEnv *env, jobject this, jfieldID fid)
 546 {
 547     FD fd = GET_FD(this, fid);
 548     HANDLE h = (HANDLE)fd;
 549 
 550     if (h == INVALID_HANDLE_VALUE) {
 551         return 0;
 552     }
 553 
 554     /* Set the fd to -1 before closing it so that the timing window
 555      * of other threads using the wrong fd (closed but recycled fd,
 556      * that gets re-opened with some other filename) is reduced.
 557      * Practically the chance of its occurance is low, however, we are
 558      * taking extra precaution over here.
 559      */
 560     SET_FD(this, -1, fid);
 561 
 562     if (CloseHandle(h) == 0) { /* Returns zero on failure */
 563         JNU_ThrowIOExceptionWithLastError(env, "close failed");
 564     }
 565     return 0;
 566 }
 567 
 568 jlong
 569 handleLseek(jlong fd, jlong offset, jint whence)
 570 {
 571     LARGE_INTEGER pos, distance;
 572     DWORD lowPos = 0;
 573     long highPos = 0;
 574     DWORD op = FILE_CURRENT;
 575     HANDLE h = (HANDLE)fd;
 576 
 577     if (whence == SEEK_END) {
 578         op = FILE_END;
 579     }
 580     if (whence == SEEK_CUR) {
 581         op = FILE_CURRENT;
 582     }
 583     if (whence == SEEK_SET) {
 584         op = FILE_BEGIN;
 585     }
 586 
 587     distance.QuadPart = offset;
 588     if (SetFilePointerEx(h, distance, &pos, op) == 0) {
 589         return -1;
 590     }
 591     return long_to_jlong(pos.QuadPart);
 592 }
 593 
 594 size_t
 595 getLastErrorString(char *utf8_jvmErrorMsg, size_t cbErrorMsg)
 596 {
 597     size_t n = 0;
 598     if (cbErrorMsg > 0) {
 599         BOOLEAN noError = FALSE;
 600         WCHAR *utf16_osErrorMsg = (WCHAR *)malloc(cbErrorMsg*sizeof(WCHAR));
 601         if (utf16_osErrorMsg == NULL) {
 602             // OOM accident
 603             strncpy(utf8_jvmErrorMsg, "Out of memory", cbErrorMsg);
 604             // truncate if too long
 605             utf8_jvmErrorMsg[cbErrorMsg - 1] = '\0';
 606             n = strlen(utf8_jvmErrorMsg);
 607         } else {
 608             DWORD errval = GetLastError();
 609             if (errval != 0) {
 610                 // WIN32 error
 611                 n = (size_t)FormatMessageW(
 612                     FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
 613                     NULL,
 614                     errval,
 615                     0,
 616                     utf16_osErrorMsg,
 617                     (DWORD)cbErrorMsg,
 618                     NULL);
 619                 if (n > 3) {
 620                     // Drop final '.', CR, LF
 621                     if (utf16_osErrorMsg[n - 1] == L'\n') --n;
 622                     if (utf16_osErrorMsg[n - 1] == L'\r') --n;
 623                     if (utf16_osErrorMsg[n - 1] == L'.') --n;
 624                     utf16_osErrorMsg[n] = L'\0';
 625                 }
 626             } else if (errno != 0) {
 627                 // C runtime error that has no corresponding WIN32 error code
 628                 const WCHAR *rtError = _wcserror(errno);
 629                 if (rtError != NULL) {
 630                     wcsncpy(utf16_osErrorMsg, rtError, cbErrorMsg);
 631                     // truncate if too long
 632                     utf16_osErrorMsg[cbErrorMsg - 1] = L'\0';
 633                     n = wcslen(utf16_osErrorMsg);
 634                 }
 635             } else
 636                 noError = TRUE; //OS has no error to report
 637 
 638             if (!noError) {
 639                 if (n > 0) {
 640                     n = WideCharToMultiByte(
 641                         CP_UTF8,
 642                         0,
 643                         utf16_osErrorMsg,
 644                         n,
 645                         utf8_jvmErrorMsg,
 646                         cbErrorMsg,
 647                         NULL,
 648                         NULL);
 649 
 650                     // no way to die
 651                     if (n > 0)
 652                         utf8_jvmErrorMsg[min(cbErrorMsg - 1, n)] = '\0';
 653                 }
 654 
 655                 if (n <= 0) {
 656                     strncpy(utf8_jvmErrorMsg, "Secondary error while OS message extraction", cbErrorMsg);
 657                     // truncate if too long
 658                     utf8_jvmErrorMsg[cbErrorMsg - 1] = '\0';
 659                     n = strlen(utf8_jvmErrorMsg);
 660                 }
 661             }
 662             free(utf16_osErrorMsg);
 663         }
 664     }
 665     return n;
 666 }