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