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                      }
 169                  }
 170             } else {
 171                 /* If the path came in as a relative path, need to verify if
 172                    its absolute form is bigger than max_path or not, if yes
 173                    need to (1)convert it to absolute and (2)prefix. This is
 174                    obviously a burden to all relative paths (The current dir/len
 175                    for "drive & directory" relative path is cached, so we only
 176                    calculate it once but for "drive-relative path we call
 177                    _wgetdcwd() and wcslen() everytime), but a hit we have
 178                    to take if we want to support relative path beyond max_path.
 179                    There is no way to predict how long the absolute path will be
 180                    (therefor allocate the sufficient memory block) before calling
 181                    _wfullpath(), we have to get the length of "current" dir first.
 182                 */
 183                 WCHAR *abpath = NULL;
 184                 int dirlen = currentDirLength(ps, pathlen);
 185                 if (dirlen + pathlen + 1 > max_path - 1) {
 186                     pathbuf = prefixAbpath(ps, pathlen, dirlen + pathlen);
 187                 } else {
 188                     pathbuf = (WCHAR*)malloc((pathlen + 6) * sizeof(WCHAR));
 189                     if (pathbuf != 0) {
 190                         wcscpy(pathbuf, ps);
 191                     }
 192                 }
 193             }
 194         }
 195     } END_UNICODE_STRING(env, ps);
 196 
 197     if (pathlen == 0) {
 198         if (throwFNFE == JNI_TRUE) {
 199             throwFileNotFoundException(env, path);
 200             return NULL;
 201         } else {
 202             pathbuf = (WCHAR*)malloc(sizeof(WCHAR));
 203             pathbuf[0] = L'\0';
 204         }
 205     }
 206     if (pathbuf == 0) {
 207         JNU_ThrowOutOfMemoryError(env, 0);
 208         return NULL;
 209     }
 210     return pathbuf;
 211 }
 212 
 213 FD
 214 winFileHandleOpen(JNIEnv *env, jstring path, int flags)
 215 {
 216     const DWORD access =
 217         (flags & O_WRONLY) ?  GENERIC_WRITE :
 218         (flags & O_RDWR)   ? (GENERIC_READ | GENERIC_WRITE) :
 219         GENERIC_READ;
 220     const DWORD sharing =
 221         FILE_SHARE_READ | FILE_SHARE_WRITE;
 222     const DWORD disposition =
 223         /* Note: O_TRUNC overrides O_CREAT */
 224         (flags & O_TRUNC) ? CREATE_ALWAYS :
 225         (flags & O_CREAT) ? OPEN_ALWAYS   :
 226         OPEN_EXISTING;
 227     const DWORD  maybeWriteThrough =
 228         (flags & (O_SYNC | O_DSYNC)) ?
 229         FILE_FLAG_WRITE_THROUGH :
 230         FILE_ATTRIBUTE_NORMAL;
 231     const DWORD maybeDeleteOnClose =
 232         (flags & O_TEMPORARY) ?
 233         FILE_FLAG_DELETE_ON_CLOSE :
 234         FILE_ATTRIBUTE_NORMAL;
 235     const DWORD flagsAndAttributes = maybeWriteThrough | maybeDeleteOnClose;
 236     HANDLE h = NULL;
 237 
 238     WCHAR *pathbuf = pathToNTPath(env, path, JNI_TRUE);
 239     if (pathbuf == NULL) {
 240         /* Exception already pending */
 241         return -1;
 242     }
 243     h = CreateFileW(
 244         pathbuf,            /* Wide char path name */
 245         access,             /* Read and/or write permission */
 246         sharing,            /* File sharing flags */
 247         NULL,               /* Security attributes */
 248         disposition,        /* creation disposition */
 249         flagsAndAttributes, /* flags and attributes */
 250         NULL);
 251     free(pathbuf);
 252 
 253     if (h == INVALID_HANDLE_VALUE) {
 254         int error = GetLastError();
 255         if (error == ERROR_TOO_MANY_OPEN_FILES) {
 256             JNU_ThrowByName(env, JNU_JAVAIOPKG "IOException",
 257                             "Too many open files");
 258             return -1;
 259         }
 260         throwFileNotFoundException(env, path);
 261         return -1;
 262     }
 263     return (jlong) h;
 264 }
 265 
 266 void
 267 fileOpen(JNIEnv *env, jobject this, jstring path, jfieldID fid, int flags)
 268 {
 269     FD h = winFileHandleOpen(env, path, flags);
 270     if (h >= 0) {
 271         SET_FD(this, h, fid);
 272     }
 273 }
 274 
 275 /* These are functions that use a handle fd instead of the
 276    old C style int fd as is used in HPI layer */
 277 
 278 static int
 279 handleNonSeekAvailable(FD, long *);
 280 static int
 281 handleStdinAvailable(FD, long *);
 282 
 283 int
 284 handleAvailable(FD fd, jlong *pbytes) {
 285     HANDLE h = (HANDLE)fd;
 286     DWORD type = 0;
 287 
 288     type = GetFileType(h);
 289     /* Handle is for keyboard or pipe */
 290     if (type == FILE_TYPE_CHAR || type == FILE_TYPE_PIPE) {
 291         int ret;
 292         long lpbytes;
 293         HANDLE stdInHandle = GetStdHandle(STD_INPUT_HANDLE);
 294         if (stdInHandle == h) {
 295             ret = handleStdinAvailable(fd, &lpbytes); /* keyboard */
 296         } else {
 297             ret = handleNonSeekAvailable(fd, &lpbytes); /* pipe */
 298         }
 299         (*pbytes) = (jlong)(lpbytes);
 300         return ret;
 301     }
 302     /* Handle is for regular file */
 303     if (type == FILE_TYPE_DISK) {
 304         jlong current, end;
 305 
 306         LARGE_INTEGER filesize;
 307         current = handleLseek(fd, 0, SEEK_CUR);
 308         if (current < 0) {
 309             return FALSE;
 310         }
 311         if (GetFileSizeEx(h, &filesize) == 0) {
 312             return FALSE;
 313         }
 314         end = long_to_jlong(filesize.QuadPart);
 315         *pbytes = end - current;
 316         return TRUE;
 317     }
 318     return FALSE;
 319 }
 320 
 321 static int
 322 handleNonSeekAvailable(FD fd, long *pbytes) {
 323     /* This is used for available on non-seekable devices
 324      * (like both named and anonymous pipes, such as pipes
 325      *  connected to an exec'd process).
 326      * Standard Input is a special case.
 327      *
 328      */
 329     HANDLE han;
 330 
 331     if ((han = (HANDLE) fd) == INVALID_HANDLE_VALUE) {
 332         return FALSE;
 333     }
 334 
 335     if (! PeekNamedPipe(han, NULL, 0, NULL, pbytes, NULL)) {
 336         /* PeekNamedPipe fails when at EOF.  In that case we
 337          * simply make *pbytes = 0 which is consistent with the
 338          * behavior we get on Solaris when an fd is at EOF.
 339          * The only alternative is to raise and Exception,
 340          * which isn't really warranted.
 341          */
 342         if (GetLastError() != ERROR_BROKEN_PIPE) {
 343             return FALSE;
 344         }
 345         *pbytes = 0;
 346     }
 347     return TRUE;
 348 }
 349 
 350 static int
 351 handleStdinAvailable(FD fd, long *pbytes) {
 352     HANDLE han;
 353     DWORD numEventsRead = 0;    /* Number of events read from buffer */
 354     DWORD numEvents = 0;        /* Number of events in buffer */
 355     DWORD i = 0;                /* Loop index */
 356     DWORD curLength = 0;        /* Position marker */
 357     DWORD actualLength = 0;     /* Number of bytes readable */
 358     BOOL error = FALSE;         /* Error holder */
 359     INPUT_RECORD *lpBuffer;     /* Pointer to records of input events */
 360     DWORD bufferSize = 0;
 361 
 362     if ((han = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE) {
 363         return FALSE;
 364     }
 365 
 366     /* Construct an array of input records in the console buffer */
 367     error = GetNumberOfConsoleInputEvents(han, &numEvents);
 368     if (error == 0) {
 369         return handleNonSeekAvailable(fd, pbytes);
 370     }
 371 
 372     /* lpBuffer must fit into 64K or else PeekConsoleInput fails */
 373     if (numEvents > MAX_INPUT_EVENTS) {
 374         numEvents = MAX_INPUT_EVENTS;
 375     }
 376 
 377     bufferSize = numEvents * sizeof(INPUT_RECORD);
 378     if (bufferSize == 0)
 379         bufferSize = 1;
 380     lpBuffer = malloc(bufferSize);
 381     if (lpBuffer == NULL) {
 382         return FALSE;
 383     }
 384 
 385     error = PeekConsoleInput(han, lpBuffer, numEvents, &numEventsRead);
 386     if (error == 0) {
 387         free(lpBuffer);
 388         return FALSE;
 389     }
 390 
 391     /* Examine input records for the number of bytes available */
 392     for(i=0; i<numEvents; i++) {
 393         if (lpBuffer[i].EventType == KEY_EVENT) {
 394             KEY_EVENT_RECORD *keyRecord = (KEY_EVENT_RECORD *)
 395                                           &(lpBuffer[i].Event);
 396             if (keyRecord->bKeyDown == TRUE) {
 397                 CHAR *keyPressed = (CHAR *) &(keyRecord->uChar);
 398                 curLength++;
 399                 if (*keyPressed == '\r')
 400                     actualLength = curLength;
 401             }
 402         }
 403     }
 404     if(lpBuffer != NULL)
 405         free(lpBuffer);
 406     *pbytes = (long) actualLength;
 407     return TRUE;
 408 }
 409 
 410 /*
 411  * This is documented to succeed on read-only files, but Win32's
 412  * FlushFileBuffers functions fails with "access denied" in such a
 413  * case.  So we only signal an error if the error is *not* "access
 414  * denied".
 415  */
 416 
 417 int
 418 handleSync(FD fd) {
 419     /*
 420      * From the documentation:
 421      *
 422      *     On Windows NT, the function FlushFileBuffers fails if hFile
 423      *     is a handle to console output. That is because console
 424      *     output is not buffered. The function returns FALSE, and
 425      *     GetLastError returns ERROR_INVALID_HANDLE.
 426      *
 427      * On the other hand, on Win95, it returns without error.  I cannot
 428      * assume that 0, 1, and 2 are console, because if someone closes
 429      * System.out and then opens a file, they might get file descriptor
 430      * 1.  An error on *that* version of 1 should be reported, whereas
 431      * an error on System.out (which was the original 1) should be
 432      * ignored.  So I use isatty() to ensure that such an error was due
 433      * to this bogosity, and if it was, I ignore the error.
 434      */
 435 
 436     HANDLE handle = (HANDLE)fd;
 437 
 438     if (!FlushFileBuffers(handle)) {
 439         if (GetLastError() != ERROR_ACCESS_DENIED) {    /* from winerror.h */
 440             return -1;
 441         }
 442     }
 443     return 0;
 444 }
 445 
 446 
 447 int
 448 handleSetLength(FD fd, jlong length) {
 449     HANDLE h = (HANDLE)fd;
 450     long high = (long)(length >> 32);
 451     DWORD ret;
 452 
 453     if (h == (HANDLE)(-1)) return -1;
 454     ret = SetFilePointer(h, (long)(length), &high, FILE_BEGIN);
 455     if (ret == 0xFFFFFFFF && GetLastError() != NO_ERROR) {
 456         return -1;
 457     }
 458     if (SetEndOfFile(h) == FALSE) return -1;
 459     return 0;
 460 }
 461 
 462 JNIEXPORT
 463 jint
 464 handleRead(FD fd, void *buf, jint len)
 465 {
 466     DWORD read = 0;
 467     BOOL result = 0;
 468     HANDLE h = (HANDLE)fd;
 469     if (h == INVALID_HANDLE_VALUE) {
 470         return -1;
 471     }
 472     result = ReadFile(h,          /* File handle to read */
 473                       buf,        /* address to put data */
 474                       len,        /* number of bytes to read */
 475                       &read,      /* number of bytes read */
 476                       NULL);      /* no overlapped struct */
 477     if (result == 0) {
 478         int error = GetLastError();
 479         if (error == ERROR_BROKEN_PIPE) {
 480             return 0; /* EOF */
 481         }
 482         return -1;
 483     }
 484     return (jint)read;
 485 }
 486 
 487 static jint writeInternal(FD fd, const void *buf, jint len, jboolean append)
 488 {
 489     BOOL result = 0;
 490     DWORD written = 0;
 491     HANDLE h = (HANDLE)fd;
 492     if (h != INVALID_HANDLE_VALUE) {
 493         OVERLAPPED ov;
 494         LPOVERLAPPED lpOv;
 495         if (append == JNI_TRUE) {
 496             ov.Offset = (DWORD)0xFFFFFFFF;
 497             ov.OffsetHigh = (DWORD)0xFFFFFFFF;
 498             ov.hEvent = NULL;
 499             lpOv = &ov;
 500         } else {
 501             lpOv = NULL;
 502         }
 503         result = WriteFile(h,                /* File handle to write */
 504                            buf,              /* pointers to the buffers */
 505                            len,              /* number of bytes to write */
 506                            &written,         /* receives number of bytes written */
 507                            lpOv);            /* overlapped struct */
 508     }
 509     if ((h == INVALID_HANDLE_VALUE) || (result == 0)) {
 510         return -1;
 511     }
 512     return (jint)written;
 513 }
 514 
 515 jint handleWrite(FD fd, const void *buf, jint len) {
 516     return writeInternal(fd, buf, len, JNI_FALSE);
 517 }
 518 
 519 jint handleAppend(FD fd, const void *buf, jint len) {
 520     return writeInternal(fd, buf, len, JNI_TRUE);
 521 }
 522 
 523 jint
 524 handleClose(JNIEnv *env, jobject this, jfieldID fid)
 525 {
 526     FD fd = GET_FD(this, fid);
 527     HANDLE h = (HANDLE)fd;
 528 
 529     if (h == INVALID_HANDLE_VALUE) {
 530         return 0;
 531     }
 532 
 533     /* Set the fd to -1 before closing it so that the timing window
 534      * of other threads using the wrong fd (closed but recycled fd,
 535      * that gets re-opened with some other filename) is reduced.
 536      * Practically the chance of its occurance is low, however, we are
 537      * taking extra precaution over here.
 538      */
 539     SET_FD(this, -1, fid);
 540 
 541     if (CloseHandle(h) == 0) { /* Returns zero on failure */
 542         JNU_ThrowIOExceptionWithLastError(env, "close failed");
 543     }
 544     return 0;
 545 }
 546 
 547 jlong
 548 handleLseek(FD fd, jlong offset, jint whence)
 549 {
 550     LARGE_INTEGER pos, distance;
 551     DWORD lowPos = 0;
 552     long highPos = 0;
 553     DWORD op = FILE_CURRENT;
 554     HANDLE h = (HANDLE)fd;
 555 
 556     if (whence == SEEK_END) {
 557         op = FILE_END;
 558     }
 559     if (whence == SEEK_CUR) {
 560         op = FILE_CURRENT;
 561     }
 562     if (whence == SEEK_SET) {
 563         op = FILE_BEGIN;
 564     }
 565 
 566     distance.QuadPart = offset;
 567     if (SetFilePointerEx(h, distance, &pos, op) == 0) {
 568         return -1;
 569     }
 570     return long_to_jlong(pos.QuadPart);
 571 }
 572 
 573 size_t
 574 getLastErrorString(char *utf8_jvmErrorMsg, size_t cbErrorMsg)
 575 {
 576     size_t n = 0;
 577     if (cbErrorMsg > 0) {
 578         BOOLEAN noError = FALSE;
 579         WCHAR *utf16_osErrorMsg = (WCHAR *)malloc(cbErrorMsg*sizeof(WCHAR));
 580         if (utf16_osErrorMsg == NULL) {
 581             // OOM accident 
 582             strncpy(utf8_jvmErrorMsg, "Out of memory", cbErrorMsg);
 583             // truncate if too long
 584             utf8_jvmErrorMsg[cbErrorMsg - 1] = '\0';
 585             n = strlen(utf8_jvmErrorMsg);
 586         } else {
 587             DWORD errval = GetLastError();
 588             if (errval != 0) {
 589                 // WIN32 error
 590                 n = (size_t)FormatMessageW(
 591                     FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
 592                     NULL,
 593                     errval,
 594                     0,
 595                     utf16_osErrorMsg,
 596                     (DWORD)cbErrorMsg,
 597                     NULL);
 598                 if (n > 3) {
 599                     // Drop final '.', CR, LF
 600                     if (utf16_osErrorMsg[n - 1] == L'\n') --n;
 601                     if (utf16_osErrorMsg[n - 1] == L'\r') --n;
 602                     if (utf16_osErrorMsg[n - 1] == L'.') --n;
 603                     utf16_osErrorMsg[n] = L'\0';
 604                 }
 605             } else if (errno != 0) {
 606                 // C runtime error that has no corresponding WIN32 error code
 607                 const WCHAR *rtError = _wcserror(errno);
 608                 if (rtError != NULL) {
 609                     wcsncpy(utf16_osErrorMsg, rtError, cbErrorMsg);
 610                     // truncate if too long
 611                     utf16_osErrorMsg[cbErrorMsg - 1] = L'\0';
 612                     n = wcslen(utf16_osErrorMsg);
 613                 }
 614             } else
 615                 noError = TRUE; //OS has no error to report
 616 
 617             if (!noError) {
 618                 if (n > 0) {
 619                     n = WideCharToMultiByte(
 620                         CP_UTF8,
 621                         0,
 622                         utf16_osErrorMsg,
 623                         n,
 624                         utf8_jvmErrorMsg,
 625                         cbErrorMsg,
 626                         NULL,
 627                         NULL);
 628 
 629                     // no way to die
 630                     if (n > 0)
 631                         utf8_jvmErrorMsg[min(cbErrorMsg - 1, n)] = '\0';
 632                 }
 633 
 634                 if (n <= 0) {
 635                     strncpy(utf8_jvmErrorMsg, "Secondary error while OS message extraction", cbErrorMsg);
 636                     // truncate if too long
 637                     utf8_jvmErrorMsg[cbErrorMsg - 1] = '\0';
 638                     n = strlen(utf8_jvmErrorMsg);
 639                 }
 640             }
 641             free(utf16_osErrorMsg);
 642         }
 643     }
 644     return n;
 645 }