tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ntmisc.c (32394B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 /*
      7 * ntmisc.c
      8 *
      9 */
     10 
     11 #include "primpl.h"
     12 #include <math.h> /* for fabs() */
     13 #include <windows.h>
     14 
     15 char* _PR_MD_GET_ENV(const char* name) { return getenv(name); }
     16 
     17 /*
     18 ** _PR_MD_PUT_ENV() -- add or change environment variable
     19 **
     20 **
     21 */
     22 PRIntn _PR_MD_PUT_ENV(const char* name) { return (putenv(name)); }
     23 
     24 /*
     25 **************************************************************************
     26 **************************************************************************
     27 **
     28 **     Date and time routines
     29 **
     30 **************************************************************************
     31 **************************************************************************
     32 */
     33 
     34 /*
     35 * The NSPR epoch (00:00:00 1 Jan 1970 UTC) in FILETIME.
     36 * We store the value in a PRTime variable for convenience.
     37 */
     38 #ifdef __GNUC__
     39 const PRTime _pr_filetime_offset = 116444736000000000LL;
     40 const PRTime _pr_filetime_divisor = 10LL;
     41 #else
     42 const PRTime _pr_filetime_offset = 116444736000000000i64;
     43 const PRTime _pr_filetime_divisor = 10i64;
     44 #endif
     45 
     46 #ifdef WINCE
     47 
     48 #  define FILETIME_TO_INT64(ft) \
     49    (((PRInt64)ft.dwHighDateTime) << 32 | (PRInt64)ft.dwLowDateTime)
     50 
     51 static void LowResTime(LPFILETIME lpft) { GetCurrentFT(lpft); }
     52 
     53 typedef struct CalibrationData {
     54  long double freq;         /* The performance counter frequency */
     55  long double offset;       /* The low res 'epoch' */
     56  long double timer_offset; /* The high res 'epoch' */
     57 
     58  /* The last high res time that we returned since recalibrating */
     59  PRInt64 last;
     60 
     61  PRBool calibrated;
     62 
     63  CRITICAL_SECTION data_lock;
     64  CRITICAL_SECTION calibration_lock;
     65  PRInt64 granularity;
     66 } CalibrationData;
     67 
     68 static CalibrationData calibration;
     69 
     70 typedef void (*GetSystemTimeAsFileTimeFcn)(LPFILETIME);
     71 static GetSystemTimeAsFileTimeFcn ce6_GetSystemTimeAsFileTime = NULL;
     72 
     73 static void NowCalibrate(void) {
     74  FILETIME ft, ftStart;
     75  LARGE_INTEGER liFreq, now;
     76 
     77  if (calibration.freq == 0.0) {
     78    if (!QueryPerformanceFrequency(&liFreq)) {
     79      /* High-performance timer is unavailable */
     80      calibration.freq = -1.0;
     81    } else {
     82      calibration.freq = (long double)liFreq.QuadPart;
     83    }
     84  }
     85  if (calibration.freq > 0.0) {
     86    PRInt64 calibrationDelta = 0;
     87    /*
     88     * By wrapping a timeBegin/EndPeriod pair of calls around this loop,
     89     * the loop seems to take much less time (1 ms vs 15ms) on Vista.
     90     */
     91    timeBeginPeriod(1);
     92    LowResTime(&ftStart);
     93    do {
     94      LowResTime(&ft);
     95    } while (memcmp(&ftStart, &ft, sizeof(ft)) == 0);
     96    timeEndPeriod(1);
     97 
     98    calibration.granularity =
     99        (FILETIME_TO_INT64(ft) - FILETIME_TO_INT64(ftStart)) / 10;
    100 
    101    QueryPerformanceCounter(&now);
    102 
    103    calibration.offset = (long double)FILETIME_TO_INT64(ft);
    104    calibration.timer_offset = (long double)now.QuadPart;
    105    /*
    106     * The windows epoch is around 1600. The unix epoch is around 1970.
    107     * _pr_filetime_offset is the difference (in windows time units which
    108     * are 10 times more highres than the JS time unit)
    109     */
    110    calibration.offset -= _pr_filetime_offset;
    111    calibration.offset *= 0.1;
    112    calibration.last = 0;
    113 
    114    calibration.calibrated = PR_TRUE;
    115  }
    116 }
    117 
    118 #  define CALIBRATIONLOCK_SPINCOUNT 0
    119 #  define DATALOCK_SPINCOUNT 4096
    120 #  define LASTLOCK_SPINCOUNT 4096
    121 
    122 void _MD_InitTime(void) {
    123  /* try for CE6 GetSystemTimeAsFileTime first */
    124  HANDLE h = GetModuleHandleW(L"coredll.dll");
    125  ce6_GetSystemTimeAsFileTime =
    126      (GetSystemTimeAsFileTimeFcn)GetProcAddressA(h, "GetSystemTimeAsFileTime");
    127 
    128  /* otherwise go the slow route */
    129  if (ce6_GetSystemTimeAsFileTime == NULL) {
    130    memset(&calibration, 0, sizeof(calibration));
    131    NowCalibrate();
    132    InitializeCriticalSection(&calibration.calibration_lock);
    133    InitializeCriticalSection(&calibration.data_lock);
    134  }
    135 }
    136 
    137 void _MD_CleanupTime(void) {
    138  if (ce6_GetSystemTimeAsFileTime == NULL) {
    139    DeleteCriticalSection(&calibration.calibration_lock);
    140    DeleteCriticalSection(&calibration.data_lock);
    141  }
    142 }
    143 
    144 #  define MUTEX_SETSPINCOUNT(m, c)
    145 
    146 /*
    147 *-----------------------------------------------------------------------
    148 *
    149 * PR_Now --
    150 *
    151 *     Returns the current time in microseconds since the epoch.
    152 *     The epoch is midnight January 1, 1970 GMT.
    153 *     The implementation is machine dependent.  This is the
    154 *     implementation for Windows.
    155 *     Cf. time_t time(time_t *tp)
    156 *
    157 *-----------------------------------------------------------------------
    158 */
    159 
    160 PR_IMPLEMENT(PRTime)
    161 PR_Now(void) {
    162  long double lowresTime, highresTimerValue;
    163  FILETIME ft;
    164  LARGE_INTEGER now;
    165  PRBool calibrated = PR_FALSE;
    166  PRBool needsCalibration = PR_FALSE;
    167  PRInt64 returnedTime;
    168  long double cachedOffset = 0.0;
    169 
    170  if (ce6_GetSystemTimeAsFileTime) {
    171    union {
    172      FILETIME ft;
    173      PRTime prt;
    174    } currentTime;
    175 
    176    PR_ASSERT(sizeof(FILETIME) == sizeof(PRTime));
    177 
    178    ce6_GetSystemTimeAsFileTime(&currentTime.ft);
    179 
    180    /* written this way on purpose, since the second term becomes
    181     * a constant, and the entire expression is faster to execute.
    182     */
    183    return currentTime.prt / _pr_filetime_divisor -
    184           _pr_filetime_offset / _pr_filetime_divisor;
    185  }
    186 
    187  do {
    188    if (!calibration.calibrated || needsCalibration) {
    189      EnterCriticalSection(&calibration.calibration_lock);
    190      EnterCriticalSection(&calibration.data_lock);
    191 
    192      /* Recalibrate only if no one else did before us */
    193      if (calibration.offset == cachedOffset) {
    194        /*
    195         * Since calibration can take a while, make any other
    196         * threads immediately wait
    197         */
    198        MUTEX_SETSPINCOUNT(&calibration.data_lock, 0);
    199 
    200        NowCalibrate();
    201 
    202        calibrated = PR_TRUE;
    203 
    204        /* Restore spin count */
    205        MUTEX_SETSPINCOUNT(&calibration.data_lock, DATALOCK_SPINCOUNT);
    206      }
    207      LeaveCriticalSection(&calibration.data_lock);
    208      LeaveCriticalSection(&calibration.calibration_lock);
    209    }
    210 
    211    /* Calculate a low resolution time */
    212    LowResTime(&ft);
    213    lowresTime =
    214        ((long double)(FILETIME_TO_INT64(ft) - _pr_filetime_offset)) * 0.1;
    215 
    216    if (calibration.freq > 0.0) {
    217      long double highresTime, diff;
    218      DWORD timeAdjustment, timeIncrement;
    219      BOOL timeAdjustmentDisabled;
    220 
    221      /* Default to 15.625 ms if the syscall fails */
    222      long double skewThreshold = 15625.25;
    223 
    224      /* Grab high resolution time */
    225      QueryPerformanceCounter(&now);
    226      highresTimerValue = (long double)now.QuadPart;
    227 
    228      EnterCriticalSection(&calibration.data_lock);
    229      highresTime = calibration.offset +
    230                    1000000L * (highresTimerValue - calibration.timer_offset) /
    231                        calibration.freq;
    232      cachedOffset = calibration.offset;
    233 
    234      /*
    235       * On some dual processor/core systems, we might get an earlier
    236       * time so we cache the last time that we returned.
    237       */
    238      calibration.last = PR_MAX(calibration.last, (PRInt64)highresTime);
    239      returnedTime = calibration.last;
    240      LeaveCriticalSection(&calibration.data_lock);
    241 
    242      /* Get an estimate of clock ticks per second from our own test */
    243      skewThreshold = calibration.granularity;
    244      /* Check for clock skew */
    245      diff = lowresTime - highresTime;
    246 
    247      /*
    248       * For some reason that I have not determined, the skew can be
    249       * up to twice a kernel tick. This does not seem to happen by
    250       * itself, but I have only seen it triggered by another program
    251       * doing some kind of file I/O. The symptoms are a negative diff
    252       * followed by an equally large positive diff.
    253       */
    254      if (fabs(diff) > 2 * skewThreshold) {
    255        if (calibrated) {
    256          /*
    257           * If we already calibrated once this instance, and the
    258           * clock is still skewed, then either the processor(s) are
    259           * wildly changing clockspeed or the system is so busy that
    260           * we get switched out for long periods of time. In either
    261           * case, it would be infeasible to make use of high
    262           * resolution results for anything, so let's resort to old
    263           * behavior for this call. It's possible that in the
    264           * future, the user will want the high resolution timer, so
    265           * we don't disable it entirely.
    266           */
    267          returnedTime = (PRInt64)lowresTime;
    268          needsCalibration = PR_FALSE;
    269        } else {
    270          /*
    271           * It is possible that when we recalibrate, we will return
    272           * a value less than what we have returned before; this is
    273           * unavoidable. We cannot tell the different between a
    274           * faulty QueryPerformanceCounter implementation and user
    275           * changes to the operating system time. Since we must
    276           * respect user changes to the operating system time, we
    277           * cannot maintain the invariant that Date.now() never
    278           * decreases; the old implementation has this behavior as
    279           * well.
    280           */
    281          needsCalibration = PR_TRUE;
    282        }
    283      } else {
    284        /* No detectable clock skew */
    285        returnedTime = (PRInt64)highresTime;
    286        needsCalibration = PR_FALSE;
    287      }
    288    } else {
    289      /* No high resolution timer is available, so fall back */
    290      returnedTime = (PRInt64)lowresTime;
    291    }
    292  } while (needsCalibration);
    293 
    294  return returnedTime;
    295 }
    296 
    297 #else
    298 
    299 PR_IMPLEMENT(PRTime)
    300 PR_Now(void) {
    301  PRTime prt;
    302  FILETIME ft;
    303  SYSTEMTIME st;
    304 
    305  GetSystemTime(&st);
    306  SystemTimeToFileTime(&st, &ft);
    307  _PR_FileTimeToPRTime(&ft, &prt);
    308  return prt;
    309 }
    310 
    311 #endif
    312 
    313 /*
    314 ***********************************************************************
    315 ***********************************************************************
    316 *
    317 * Process creation routines
    318 *
    319 ***********************************************************************
    320 ***********************************************************************
    321 */
    322 
    323 /*
    324 * Assemble the command line by concatenating the argv array.
    325 * On success, this function returns 0 and the resulting command
    326 * line is returned in *cmdLine.  On failure, it returns -1.
    327 */
    328 static int assembleCmdLine(char* const* argv, char** cmdLine) {
    329  char* const* arg;
    330  char *p, *q;
    331  size_t cmdLineSize;
    332  int numBackslashes;
    333  int i;
    334  int argNeedQuotes;
    335 
    336  /*
    337   * Find out how large the command line buffer should be.
    338   */
    339  cmdLineSize = 0;
    340  for (arg = argv; *arg; arg++) {
    341    /*
    342     * \ and " need to be escaped by a \.  In the worst case,
    343     * every character is a \ or ", so the string of length
    344     * may double.  If we quote an argument, that needs two ".
    345     * Finally, we need a space between arguments, and
    346     * a null byte at the end of command line.
    347     */
    348    cmdLineSize += 2 * strlen(*arg) /* \ and " need to be escaped */
    349                   + 2              /* we quote every argument */
    350                   + 1;             /* space in between, or final null */
    351  }
    352  p = *cmdLine = PR_MALLOC((PRUint32)cmdLineSize);
    353  if (p == NULL) {
    354    return -1;
    355  }
    356 
    357  for (arg = argv; *arg; arg++) {
    358    /* Add a space to separates the arguments */
    359    if (arg != argv) {
    360      *p++ = ' ';
    361    }
    362    q = *arg;
    363    numBackslashes = 0;
    364    argNeedQuotes = 0;
    365 
    366    /*
    367     * If the argument is empty or contains white space, it needs to
    368     * be quoted.
    369     */
    370    if (**arg == '\0' || strpbrk(*arg, " \f\n\r\t\v")) {
    371      argNeedQuotes = 1;
    372    }
    373 
    374    if (argNeedQuotes) {
    375      *p++ = '"';
    376    }
    377    while (*q) {
    378      if (*q == '\\') {
    379        numBackslashes++;
    380        q++;
    381      } else if (*q == '"') {
    382        if (numBackslashes) {
    383          /*
    384           * Double the backslashes since they are followed
    385           * by a quote
    386           */
    387          for (i = 0; i < 2 * numBackslashes; i++) {
    388            *p++ = '\\';
    389          }
    390          numBackslashes = 0;
    391        }
    392        /* To escape the quote */
    393        *p++ = '\\';
    394        *p++ = *q++;
    395      } else {
    396        if (numBackslashes) {
    397          /*
    398           * Backslashes are not followed by a quote, so
    399           * don't need to double the backslashes.
    400           */
    401          for (i = 0; i < numBackslashes; i++) {
    402            *p++ = '\\';
    403          }
    404          numBackslashes = 0;
    405        }
    406        *p++ = *q++;
    407      }
    408    }
    409 
    410    /* Now we are at the end of this argument */
    411    if (numBackslashes) {
    412      /*
    413       * Double the backslashes if we have a quote string
    414       * delimiter at the end.
    415       */
    416      if (argNeedQuotes) {
    417        numBackslashes *= 2;
    418      }
    419      for (i = 0; i < numBackslashes; i++) {
    420        *p++ = '\\';
    421      }
    422    }
    423    if (argNeedQuotes) {
    424      *p++ = '"';
    425    }
    426  }
    427 
    428  *p = '\0';
    429  return 0;
    430 }
    431 
    432 /*
    433 * Assemble the environment block by concatenating the envp array
    434 * (preserving the terminating null byte in each array element)
    435 * and adding a null byte at the end.
    436 *
    437 * Returns 0 on success.  The resulting environment block is returned
    438 * in *envBlock.  Note that if envp is NULL, a NULL pointer is returned
    439 * in *envBlock.  Returns -1 on failure.
    440 */
    441 static int assembleEnvBlock(char** envp, char** envBlock) {
    442  char* p;
    443  char* q;
    444  char** env;
    445  char* curEnv;
    446  char *cwdStart, *cwdEnd;
    447  size_t envBlockSize;
    448 
    449  if (envp == NULL) {
    450    *envBlock = NULL;
    451    return 0;
    452  }
    453 
    454 #ifdef WINCE
    455  {
    456    PRUnichar* wideCurEnv = mozce_GetEnvString();
    457    int len =
    458        WideCharToMultiByte(CP_ACP, 0, wideCurEnv, -1, NULL, 0, NULL, NULL);
    459    curEnv = (char*)PR_MALLOC(len * sizeof(char));
    460    WideCharToMultiByte(CP_ACP, 0, wideCurEnv, -1, curEnv, len, NULL, NULL);
    461    free(wideCurEnv);
    462  }
    463 #else
    464  curEnv = GetEnvironmentStrings();
    465 #endif
    466 
    467  cwdStart = curEnv;
    468  while (*cwdStart) {
    469    if (cwdStart[0] == '=' && cwdStart[1] != '\0' && cwdStart[2] == ':' &&
    470        cwdStart[3] == '=') {
    471      break;
    472    }
    473    cwdStart += strlen(cwdStart) + 1;
    474  }
    475  cwdEnd = cwdStart;
    476  if (*cwdEnd) {
    477    cwdEnd += strlen(cwdEnd) + 1;
    478    while (*cwdEnd) {
    479      if (cwdEnd[0] != '=' || cwdEnd[1] == '\0' || cwdEnd[2] != ':' ||
    480          cwdEnd[3] != '=') {
    481        break;
    482      }
    483      cwdEnd += strlen(cwdEnd) + 1;
    484    }
    485  }
    486  envBlockSize = cwdEnd - cwdStart;
    487 
    488  for (env = envp; *env; env++) {
    489    envBlockSize += strlen(*env) + 1;
    490  }
    491  envBlockSize++;
    492 
    493  p = *envBlock = PR_MALLOC((PRUint32)envBlockSize);
    494  if (p == NULL) {
    495 #ifdef WINCE
    496    PR_Free(curEnv);
    497 #else
    498    FreeEnvironmentStrings(curEnv);
    499 #endif
    500    return -1;
    501  }
    502 
    503  q = cwdStart;
    504  while (q < cwdEnd) {
    505    *p++ = *q++;
    506  }
    507 #ifdef WINCE
    508  PR_Free(curEnv);
    509 #else
    510  FreeEnvironmentStrings(curEnv);
    511 #endif
    512 
    513  for (env = envp; *env; env++) {
    514    q = *env;
    515    while (*q) {
    516      *p++ = *q++;
    517    }
    518    *p++ = '\0';
    519  }
    520  *p = '\0';
    521  return 0;
    522 }
    523 
    524 /*
    525 * For qsort.  We sort (case-insensitive) the environment strings
    526 * before generating the environment block.
    527 */
    528 static int compare(const void* arg1, const void* arg2) {
    529  return _stricmp(*(char**)arg1, *(char**)arg2);
    530 }
    531 
    532 PRProcess* _PR_CreateWindowsProcess(const char* path, char* const* argv,
    533                                    char* const* envp,
    534                                    const PRProcessAttr* attr) {
    535 #ifdef WINCE
    536  STARTUPINFOW startupInfo;
    537  PRUnichar* wideCmdLine;
    538  PRUnichar* wideCwd;
    539  int len = 0;
    540 #else
    541  STARTUPINFO startupInfo;
    542 #endif
    543  DWORD creationFlags = 0;
    544  PROCESS_INFORMATION procInfo;
    545  BOOL retVal;
    546  char* cmdLine = NULL;
    547  char* envBlock = NULL;
    548  char** newEnvp = NULL;
    549  const char* cwd = NULL; /* current working directory */
    550  PRProcess* proc = NULL;
    551  PRBool hasFdInheritBuffer;
    552 
    553  proc = PR_NEW(PRProcess);
    554  if (!proc) {
    555    PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
    556    goto errorExit;
    557  }
    558 
    559  if (assembleCmdLine(argv, &cmdLine) == -1) {
    560    PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
    561    goto errorExit;
    562  }
    563 
    564 #ifndef WINCE
    565  /*
    566   * If attr->fdInheritBuffer is not NULL, we need to insert
    567   * it into the envp array, so envp cannot be NULL.
    568   */
    569  hasFdInheritBuffer = (attr && attr->fdInheritBuffer);
    570  if ((envp == NULL) && hasFdInheritBuffer) {
    571    envp = environ;
    572  }
    573 
    574  if (envp != NULL) {
    575    int idx;
    576    int numEnv;
    577    PRBool found = PR_FALSE;
    578 
    579    numEnv = 0;
    580    while (envp[numEnv]) {
    581      numEnv++;
    582    }
    583    newEnvp = (char**)PR_MALLOC((numEnv + 2) * sizeof(char*));
    584    for (idx = 0; idx < numEnv; idx++) {
    585      newEnvp[idx] = envp[idx];
    586      if (hasFdInheritBuffer && !found &&
    587          !strncmp(newEnvp[idx], "NSPR_INHERIT_FDS=", 17)) {
    588        newEnvp[idx] = attr->fdInheritBuffer;
    589        found = PR_TRUE;
    590      }
    591    }
    592    if (hasFdInheritBuffer && !found) {
    593      newEnvp[idx++] = attr->fdInheritBuffer;
    594    }
    595    newEnvp[idx] = NULL;
    596    qsort((void*)newEnvp, (size_t)idx, sizeof(char*), compare);
    597  }
    598  if (assembleEnvBlock(newEnvp, &envBlock) == -1) {
    599    PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
    600    goto errorExit;
    601  }
    602 
    603  ZeroMemory(&startupInfo, sizeof(startupInfo));
    604  startupInfo.cb = sizeof(startupInfo);
    605 
    606  if (attr) {
    607    PRBool redirected = PR_FALSE;
    608 
    609    /*
    610     * XXX the default value for stdin, stdout, and stderr
    611     * should probably be the console input and output, not
    612     * those of the parent process.
    613     */
    614    startupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
    615    startupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
    616    startupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
    617    if (attr->stdinFd) {
    618      startupInfo.hStdInput = (HANDLE)attr->stdinFd->secret->md.osfd;
    619      redirected = PR_TRUE;
    620    }
    621    if (attr->stdoutFd) {
    622      startupInfo.hStdOutput = (HANDLE)attr->stdoutFd->secret->md.osfd;
    623      redirected = PR_TRUE;
    624      /*
    625       * If stdout is redirected, we can assume that the process will
    626       * not write anything useful to the console windows, and therefore
    627       * automatically set the CREATE_NO_WINDOW flag.
    628       */
    629      creationFlags |= CREATE_NO_WINDOW;
    630    }
    631    if (attr->stderrFd) {
    632      startupInfo.hStdError = (HANDLE)attr->stderrFd->secret->md.osfd;
    633      redirected = PR_TRUE;
    634    }
    635    if (redirected) {
    636      startupInfo.dwFlags |= STARTF_USESTDHANDLES;
    637    }
    638    cwd = attr->currentDirectory;
    639  }
    640 #endif
    641 
    642 #ifdef WINCE
    643  len = MultiByteToWideChar(CP_ACP, 0, cmdLine, -1, NULL, 0);
    644  wideCmdLine = (PRUnichar*)PR_MALLOC(len * sizeof(PRUnichar));
    645  MultiByteToWideChar(CP_ACP, 0, cmdLine, -1, wideCmdLine, len);
    646  len = MultiByteToWideChar(CP_ACP, 0, cwd, -1, NULL, 0);
    647  wideCwd = PR_MALLOC(len * sizeof(PRUnichar));
    648  MultiByteToWideChar(CP_ACP, 0, cwd, -1, wideCwd, len);
    649  retVal =
    650      CreateProcessW(NULL, wideCmdLine, NULL, /* security attributes for the new
    651                                               * process */
    652                     NULL, /* security attributes for the primary
    653                            * thread in the new process */
    654                     TRUE, /* inherit handles */
    655                     creationFlags, envBlock, /* an environment block,
    656                                               * consisting of a null-terminated
    657                                               * block of null-terminated
    658                                               * strings.  Each string is in the
    659                                               * form: name=value
    660                                               * XXX: usually NULL */
    661                     wideCwd,                 /* current drive and directory */
    662                     &startupInfo, &procInfo);
    663  PR_Free(wideCmdLine);
    664  PR_Free(wideCwd);
    665 #else
    666  retVal =
    667      CreateProcess(NULL, cmdLine, NULL, /* security attributes for the new
    668                                          * process */
    669                    NULL,                /* security attributes for the primary
    670                                          * thread in the new process */
    671                    TRUE,                /* inherit handles */
    672                    creationFlags, envBlock, /* an environment block, consisting
    673                                              * of a null-terminated block of
    674                                              * null-terminated strings.  Each
    675                                              * string is in the form:
    676                                              *     name=value
    677                                              * XXX: usually NULL */
    678                    cwd,                     /* current drive and directory */
    679                    &startupInfo, &procInfo);
    680 #endif
    681 
    682  if (retVal == FALSE) {
    683    /* XXX what error code? */
    684    PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
    685    goto errorExit;
    686  }
    687 
    688  CloseHandle(procInfo.hThread);
    689  proc->md.handle = procInfo.hProcess;
    690  proc->md.id = procInfo.dwProcessId;
    691 
    692  PR_DELETE(cmdLine);
    693  if (newEnvp) {
    694    PR_DELETE(newEnvp);
    695  }
    696  if (envBlock) {
    697    PR_DELETE(envBlock);
    698  }
    699  return proc;
    700 
    701 errorExit:
    702  if (cmdLine) {
    703    PR_DELETE(cmdLine);
    704  }
    705  if (newEnvp) {
    706    PR_DELETE(newEnvp);
    707  }
    708  if (envBlock) {
    709    PR_DELETE(envBlock);
    710  }
    711  if (proc) {
    712    PR_DELETE(proc);
    713  }
    714  return NULL;
    715 } /* _PR_CreateWindowsProcess */
    716 
    717 PRStatus _PR_DetachWindowsProcess(PRProcess* process) {
    718  CloseHandle(process->md.handle);
    719  PR_DELETE(process);
    720  return PR_SUCCESS;
    721 }
    722 
    723 /*
    724 * XXX: This implementation is a temporary quick solution.
    725 * It can be called by native threads only (not by fibers).
    726 */
    727 PRStatus _PR_WaitWindowsProcess(PRProcess* process, PRInt32* exitCode) {
    728  DWORD dwRetVal;
    729 
    730  dwRetVal = WaitForSingleObject(process->md.handle, INFINITE);
    731  if (dwRetVal == WAIT_FAILED) {
    732    PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
    733    return PR_FAILURE;
    734  }
    735  PR_ASSERT(dwRetVal == WAIT_OBJECT_0);
    736  if (exitCode != NULL &&
    737      GetExitCodeProcess(process->md.handle, exitCode) == FALSE) {
    738    PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
    739    return PR_FAILURE;
    740  }
    741  CloseHandle(process->md.handle);
    742  PR_DELETE(process);
    743  return PR_SUCCESS;
    744 }
    745 
    746 PRStatus _PR_KillWindowsProcess(PRProcess* process) {
    747  /*
    748   * On Unix, if a process terminates normally, its exit code is
    749   * between 0 and 255.  So here on Windows, we use the exit code
    750   * 256 to indicate that the process is killed.
    751   */
    752  if (TerminateProcess(process->md.handle, 256)) {
    753    return PR_SUCCESS;
    754  }
    755  PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
    756  return PR_FAILURE;
    757 }
    758 
    759 PRStatus _MD_WindowsGetHostName(char* name, PRUint32 namelen) {
    760  PRIntn rv;
    761  PRInt32 syserror;
    762 
    763  rv = gethostname(name, (PRInt32)namelen);
    764  if (0 == rv) {
    765    return PR_SUCCESS;
    766  }
    767  syserror = WSAGetLastError();
    768  PR_ASSERT(WSANOTINITIALISED != syserror);
    769  _PR_MD_MAP_GETHOSTNAME_ERROR(syserror);
    770  return PR_FAILURE;
    771 }
    772 
    773 PRStatus _MD_WindowsGetSysInfo(PRSysInfo cmd, char* name, PRUint32 namelen) {
    774  OSVERSIONINFO osvi;
    775 
    776  PR_ASSERT((cmd == PR_SI_SYSNAME) || (cmd == PR_SI_RELEASE) ||
    777            (cmd == PR_SI_RELEASE_BUILD));
    778 
    779  ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
    780  osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    781 
    782  if (!GetVersionEx(&osvi)) {
    783    _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
    784    return PR_FAILURE;
    785  }
    786 
    787  switch (osvi.dwPlatformId) {
    788    case VER_PLATFORM_WIN32_NT:
    789      if (PR_SI_SYSNAME == cmd) {
    790        (void)PR_snprintf(name, namelen, "Windows_NT");
    791      } else if (PR_SI_RELEASE == cmd) {
    792        (void)PR_snprintf(name, namelen, "%d.%d", osvi.dwMajorVersion,
    793                          osvi.dwMinorVersion);
    794      } else if (PR_SI_RELEASE_BUILD == cmd) {
    795        (void)PR_snprintf(name, namelen, "%d", osvi.dwBuildNumber);
    796      }
    797      break;
    798    case VER_PLATFORM_WIN32_WINDOWS:
    799      if (PR_SI_SYSNAME == cmd) {
    800        if ((osvi.dwMajorVersion > 4) ||
    801            ((osvi.dwMajorVersion == 4) && (osvi.dwMinorVersion > 0))) {
    802          (void)PR_snprintf(name, namelen, "Windows_98");
    803        } else {
    804          (void)PR_snprintf(name, namelen, "Windows_95");
    805        }
    806      } else if (PR_SI_RELEASE == cmd) {
    807        (void)PR_snprintf(name, namelen, "%d.%d", osvi.dwMajorVersion,
    808                          osvi.dwMinorVersion);
    809      } else if (PR_SI_RELEASE_BUILD == cmd) {
    810        (void)PR_snprintf(name, namelen, "%d", osvi.dwBuildNumber);
    811      }
    812      break;
    813 #ifdef VER_PLATFORM_WIN32_CE
    814    case VER_PLATFORM_WIN32_CE:
    815      if (PR_SI_SYSNAME == cmd) {
    816        (void)PR_snprintf(name, namelen, "Windows_CE");
    817      } else if (PR_SI_RELEASE == cmd) {
    818        (void)PR_snprintf(name, namelen, "%d.%d", osvi.dwMajorVersion,
    819                          osvi.dwMinorVersion);
    820      } else if (PR_SI_RELEASE_BUILD == cmd) {
    821        if (namelen) {
    822          *name = 0;
    823        }
    824      }
    825      break;
    826 #endif
    827    default:
    828      if (PR_SI_SYSNAME == cmd) {
    829        (void)PR_snprintf(name, namelen, "Windows_Unknown");
    830      } else if (PR_SI_RELEASE == cmd) {
    831        (void)PR_snprintf(name, namelen, "%d.%d", 0, 0);
    832      } else if (PR_SI_RELEASE_BUILD == cmd) {
    833        if (namelen) {
    834          *name = 0;
    835        }
    836      }
    837      break;
    838  }
    839  return PR_SUCCESS;
    840 }
    841 
    842 PRStatus _MD_WindowsGetReleaseName(char* name, PRUint32 namelen) {
    843  OSVERSIONINFO osvi;
    844 
    845  ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
    846  osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    847 
    848  if (!GetVersionEx(&osvi)) {
    849    _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
    850    return PR_FAILURE;
    851  }
    852 
    853  switch (osvi.dwPlatformId) {
    854    case VER_PLATFORM_WIN32_NT:
    855    case VER_PLATFORM_WIN32_WINDOWS:
    856      (void)PR_snprintf(name, namelen, "%d.%d", osvi.dwMajorVersion,
    857                        osvi.dwMinorVersion);
    858      break;
    859    default:
    860      (void)PR_snprintf(name, namelen, "%d.%d", 0, 0);
    861      break;
    862  }
    863  return PR_SUCCESS;
    864 }
    865 
    866 /*
    867 **********************************************************************
    868 *
    869 * Memory-mapped files
    870 *
    871 **********************************************************************
    872 */
    873 
    874 PRStatus _MD_CreateFileMap(PRFileMap* fmap, PRInt64 size) {
    875  DWORD dwHi, dwLo;
    876  DWORD flProtect;
    877  PROsfd osfd;
    878 
    879  osfd = (fmap->fd == (PRFileDesc*)-1) ? -1 : fmap->fd->secret->md.osfd;
    880 
    881  dwLo = (DWORD)(size & 0xffffffff);
    882  dwHi = (DWORD)(((PRUint64)size >> 32) & 0xffffffff);
    883 
    884  if (fmap->prot == PR_PROT_READONLY) {
    885    flProtect = PAGE_READONLY;
    886    fmap->md.dwAccess = FILE_MAP_READ;
    887  } else if (fmap->prot == PR_PROT_READWRITE) {
    888    flProtect = PAGE_READWRITE;
    889    fmap->md.dwAccess = FILE_MAP_WRITE;
    890  } else {
    891    PR_ASSERT(fmap->prot == PR_PROT_WRITECOPY);
    892 #ifdef WINCE
    893    /* WINCE does not have FILE_MAP_COPY. */
    894    PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
    895    return PR_FAILURE;
    896 #else
    897    flProtect = PAGE_WRITECOPY;
    898    fmap->md.dwAccess = FILE_MAP_COPY;
    899 #endif
    900  }
    901 
    902  fmap->md.hFileMap =
    903      CreateFileMapping((HANDLE)osfd, NULL, flProtect, dwHi, dwLo, NULL);
    904 
    905  if (fmap->md.hFileMap == NULL) {
    906    PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
    907    return PR_FAILURE;
    908  }
    909  return PR_SUCCESS;
    910 }
    911 
    912 PRInt32 _MD_GetMemMapAlignment(void) {
    913  SYSTEM_INFO info;
    914  GetSystemInfo(&info);
    915  return info.dwAllocationGranularity;
    916 }
    917 
    918 extern PRLogModuleInfo* _pr_shma_lm;
    919 
    920 void* _MD_MemMap(PRFileMap* fmap, PROffset64 offset, PRUint32 len) {
    921  DWORD dwHi, dwLo;
    922  void* addr;
    923 
    924  dwLo = (DWORD)(offset & 0xffffffff);
    925  dwHi = (DWORD)(((PRUint64)offset >> 32) & 0xffffffff);
    926  if ((addr = MapViewOfFile(fmap->md.hFileMap, fmap->md.dwAccess, dwHi, dwLo,
    927                            len)) == NULL) {
    928    {
    929      LPVOID lpMsgBuf;
    930 
    931      FormatMessage(
    932          FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
    933          GetLastError(),
    934          MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),  // Default language
    935          (LPTSTR)&lpMsgBuf, 0, NULL);
    936      PR_LOG(_pr_shma_lm, PR_LOG_DEBUG, ("md_memmap(): %s", lpMsgBuf));
    937    }
    938    PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
    939  }
    940  return addr;
    941 }
    942 
    943 PRStatus _MD_MemUnmap(void* addr, PRUint32 len) {
    944  if (UnmapViewOfFile(addr)) {
    945    return PR_SUCCESS;
    946  }
    947  _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
    948  return PR_FAILURE;
    949 }
    950 
    951 PRStatus _MD_CloseFileMap(PRFileMap* fmap) {
    952  CloseHandle(fmap->md.hFileMap);
    953  PR_DELETE(fmap);
    954  return PR_SUCCESS;
    955 }
    956 
    957 PRStatus _MD_SyncMemMap(PRFileDesc* fd, void* addr, PRUint32 len) {
    958  PROsfd osfd = fd->secret->md.osfd;
    959 
    960  /* The FlushViewOfFile page on MSDN says:
    961   *  To flush all the dirty pages plus the metadata for the file and
    962   *  ensure that they are physically written to disk, call
    963   *  FlushViewOfFile and then call the FlushFileBuffers function.
    964   */
    965  if (FlushViewOfFile(addr, len) && FlushFileBuffers((HANDLE)osfd)) {
    966    return PR_SUCCESS;
    967  }
    968  _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
    969  return PR_FAILURE;
    970 }
    971 
    972 /*
    973 ***********************************************************************
    974 *
    975 * Atomic increment and decrement operations for x86 processors
    976 *
    977 * We don't use InterlockedIncrement and InterlockedDecrement
    978 * because on NT 3.51 and Win95, they return a number with
    979 * the same sign as the incremented/decremented result, rather
    980 * than the result itself.  On NT 4.0 these functions do return
    981 * the incremented/decremented result.
    982 *
    983 * The result is returned in the eax register by the inline
    984 * assembly code.  We disable the harmless "no return value"
    985 * warning (4035) for these two functions.
    986 *
    987 ***********************************************************************
    988 */
    989 
    990 #if defined(_M_IX86) || defined(_X86_)
    991 
    992 #  pragma warning(disable : 4035)
    993 PRInt32 _PR_MD_ATOMIC_INCREMENT(PRInt32* val) {
    994 #  if defined(__GNUC__)
    995  PRInt32 result;
    996  asm volatile("lock ; xadd %0, %1"
    997               : "=r"(result), "=m"(*val)
    998               : "0"(1), "m"(*val));
    999  return result + 1;
   1000 #  else
   1001  __asm
   1002  {
   1003        mov ecx, val
   1004        mov eax, 1
   1005        lock xadd dword ptr [ecx], eax
   1006        inc eax
   1007  }
   1008 #  endif /* __GNUC__ */
   1009 }
   1010 #  pragma warning(default : 4035)
   1011 
   1012 #  pragma warning(disable : 4035)
   1013 PRInt32 _PR_MD_ATOMIC_DECREMENT(PRInt32* val) {
   1014 #  if defined(__GNUC__)
   1015  PRInt32 result;
   1016  asm volatile("lock ; xadd %0, %1"
   1017               : "=r"(result), "=m"(*val)
   1018               : "0"(-1), "m"(*val));
   1019  // asm volatile("lock ; xadd %0, %1" : "=m" (val), "=a" (result) : "-1" (1));
   1020  return result - 1;
   1021 #  else
   1022  __asm
   1023  {
   1024        mov ecx, val
   1025        mov eax, 0ffffffffh
   1026        lock xadd dword ptr [ecx], eax
   1027        dec eax
   1028  }
   1029 #  endif /* __GNUC__ */
   1030 }
   1031 #  pragma warning(default : 4035)
   1032 
   1033 #  pragma warning(disable : 4035)
   1034 PRInt32 _PR_MD_ATOMIC_ADD(PRInt32* intp, PRInt32 val) {
   1035 #  if defined(__GNUC__)
   1036  PRInt32 result;
   1037  // asm volatile("lock ; xadd %1, %0" : "=m" (intp), "=a" (result) : "1"
   1038  // (val));
   1039  asm volatile("lock ; xadd %0, %1"
   1040               : "=r"(result), "=m"(*intp)
   1041               : "0"(val), "m"(*intp));
   1042  return result + val;
   1043 #  else
   1044  __asm
   1045  {
   1046        mov ecx, intp
   1047        mov eax, val
   1048        mov edx, eax
   1049        lock xadd dword ptr [ecx], eax
   1050        add eax, edx
   1051  }
   1052 #  endif /* __GNUC__ */
   1053 }
   1054 #  pragma warning(default : 4035)
   1055 
   1056 #  ifdef _PR_HAVE_ATOMIC_CAS
   1057 
   1058 #    pragma warning(disable : 4035)
   1059 void PR_StackPush(PRStack* stack, PRStackElem* stack_elem) {
   1060 #    if defined(__GNUC__)
   1061  void** tos = (void**)stack;
   1062  void* tmp;
   1063 
   1064 retry:
   1065  if (*tos == (void*)-1) {
   1066    goto retry;
   1067  }
   1068 
   1069  __asm__("xchg %0,%1" : "=r"(tmp), "=m"(*tos) : "0"(-1), "m"(*tos));
   1070 
   1071  if (tmp == (void*)-1) {
   1072    goto retry;
   1073  }
   1074 
   1075  *(void**)stack_elem = tmp;
   1076  __asm__("" : : : "memory");
   1077  *tos = stack_elem;
   1078 #    else
   1079  __asm
   1080  {
   1081        mov ebx, stack
   1082        mov ecx, stack_elem
   1083        retry:  mov eax,[ebx]
   1084        cmp eax,-1
   1085        je retry
   1086        mov eax,-1
   1087        xchg dword ptr [ebx], eax
   1088        cmp eax,-1
   1089        je  retry
   1090        mov [ecx],eax
   1091        mov [ebx],ecx
   1092  }
   1093 #    endif /* __GNUC__ */
   1094 }
   1095 #    pragma warning(default : 4035)
   1096 
   1097 #    pragma warning(disable : 4035)
   1098 PRStackElem* PR_StackPop(PRStack* stack) {
   1099 #    if defined(__GNUC__)
   1100  void** tos = (void**)stack;
   1101  void* tmp;
   1102 
   1103 retry:
   1104  if (*tos == (void*)-1) {
   1105    goto retry;
   1106  }
   1107 
   1108  __asm__("xchg %0,%1" : "=r"(tmp), "=m"(*tos) : "0"(-1), "m"(*tos));
   1109 
   1110  if (tmp == (void*)-1) {
   1111    goto retry;
   1112  }
   1113 
   1114  if (tmp != (void*)0) {
   1115    void* next = *(void**)tmp;
   1116    *tos = next;
   1117    *(void**)tmp = 0;
   1118  } else {
   1119    *tos = tmp;
   1120  }
   1121 
   1122  return tmp;
   1123 #    else
   1124  __asm
   1125  {
   1126        mov ebx, stack
   1127        retry:  mov eax,[ebx]
   1128        cmp eax,-1
   1129        je retry
   1130        mov eax,-1
   1131        xchg dword ptr [ebx], eax
   1132        cmp eax,-1
   1133        je  retry
   1134        cmp eax,0
   1135        je  empty
   1136        mov ecx,[eax]
   1137        mov [ebx],ecx
   1138        mov [eax],0
   1139        jmp done
   1140        empty:
   1141        mov [ebx],eax
   1142        done:
   1143  }
   1144 #    endif /* __GNUC__ */
   1145 }
   1146 #    pragma warning(default : 4035)
   1147 
   1148 #  endif /* _PR_HAVE_ATOMIC_CAS */
   1149 
   1150 #endif /* x86 processors */