tor-browser

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

w95io.c (33019B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      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 /* Windows 95 IO module
      7 *
      8 * Assumes synchronous I/O.
      9 *
     10 */
     11 
     12 #include "primpl.h"
     13 #include <direct.h>
     14 #include <mbstring.h>
     15 #ifdef MOZ_UNICODE
     16 #  include <wchar.h>
     17 #endif /* MOZ_UNICODE */
     18 
     19 struct _MDLock _pr_ioq_lock;
     20 
     21 /*
     22 * NSPR-to-NT access right mapping table for files.
     23 */
     24 static DWORD fileAccessTable[] = {FILE_GENERIC_READ, FILE_GENERIC_WRITE,
     25                                  FILE_GENERIC_EXECUTE};
     26 
     27 /*
     28 * NSPR-to-NT access right mapping table for directories.
     29 */
     30 static DWORD dirAccessTable[] = {FILE_GENERIC_READ,
     31                                 FILE_GENERIC_WRITE | FILE_DELETE_CHILD,
     32                                 FILE_GENERIC_EXECUTE};
     33 
     34 static PRBool IsPrevCharSlash(const char* str, const char* current);
     35 
     36 void _PR_MD_INIT_IO() {
     37  WORD WSAVersion = 0x0101;
     38  WSADATA WSAData;
     39  int err;
     40 
     41  err = WSAStartup(WSAVersion, &WSAData);
     42  PR_ASSERT(0 == err);
     43 
     44 #ifdef DEBUG
     45  /* Doublecheck _pr_filetime_offset's hard-coded value is correct. */
     46  {
     47    SYSTEMTIME systime;
     48    union {
     49      PRTime prt;
     50      FILETIME ft;
     51    } filetime;
     52    BOOL rv;
     53 
     54    systime.wYear = 1970;
     55    systime.wMonth = 1;
     56    /* wDayOfWeek is ignored */
     57    systime.wDay = 1;
     58    systime.wHour = 0;
     59    systime.wMinute = 0;
     60    systime.wSecond = 0;
     61    systime.wMilliseconds = 0;
     62 
     63    rv = SystemTimeToFileTime(&systime, &filetime.ft);
     64    PR_ASSERT(0 != rv);
     65    PR_ASSERT(filetime.prt == _pr_filetime_offset);
     66  }
     67 #endif /* DEBUG */
     68 
     69  _PR_NT_InitSids();
     70 
     71  _PR_MD_InitSockets();
     72 }
     73 
     74 PRStatus _PR_MD_WAIT(PRThread* thread, PRIntervalTime ticks) {
     75  DWORD rv;
     76 
     77  PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT)
     78                       ? INFINITE
     79                       : PR_IntervalToMilliseconds(ticks);
     80  rv = WaitForSingleObject(thread->md.blocked_sema, msecs);
     81  switch (rv) {
     82    case WAIT_OBJECT_0:
     83      return PR_SUCCESS;
     84    case WAIT_TIMEOUT:
     85      _PR_THREAD_LOCK(thread);
     86      if (thread->state == _PR_IO_WAIT) {
     87        ;
     88      } else {
     89        if (thread->wait.cvar != NULL) {
     90          thread->wait.cvar = NULL;
     91          _PR_THREAD_UNLOCK(thread);
     92        } else {
     93          /* The CVAR was notified just as the timeout
     94           * occurred.  This led to us being notified twice.
     95           * call WaitForSingleObject() to clear the semaphore.
     96           */
     97          _PR_THREAD_UNLOCK(thread);
     98          rv = WaitForSingleObject(thread->md.blocked_sema, 0);
     99          PR_ASSERT(rv == WAIT_OBJECT_0);
    100        }
    101      }
    102      return PR_SUCCESS;
    103    default:
    104      return PR_FAILURE;
    105  }
    106 }
    107 PRStatus _PR_MD_WAKEUP_WAITER(PRThread* thread) {
    108  if (_PR_IS_NATIVE_THREAD(thread)) {
    109    if (ReleaseSemaphore(thread->md.blocked_sema, 1, NULL) == FALSE) {
    110      return PR_FAILURE;
    111    } else {
    112      return PR_SUCCESS;
    113    }
    114  }
    115 }
    116 
    117 /* --- FILE IO ----------------------------------------------------------- */
    118 /*
    119 *  _PR_MD_OPEN() -- Open a file
    120 *
    121 *  returns: a fileHandle
    122 *
    123 *  The NSPR open flags (osflags) are translated into flags for Win95
    124 *
    125 *  Mode seems to be passed in as a unix style file permissions argument
    126 *  as in 0666, in the case of opening the logFile.
    127 *
    128 */
    129 PROsfd _PR_MD_OPEN(const char* name, PRIntn osflags, int mode) {
    130  HANDLE file;
    131  PRInt32 access = 0;
    132  PRInt32 flags = 0;
    133  PRInt32 flag6 = 0;
    134 
    135  if (osflags & PR_SYNC) {
    136    flag6 = FILE_FLAG_WRITE_THROUGH;
    137  }
    138 
    139  if (osflags & PR_RDONLY || osflags & PR_RDWR) {
    140    access |= GENERIC_READ;
    141  }
    142  if (osflags & PR_WRONLY || osflags & PR_RDWR) {
    143    access |= GENERIC_WRITE;
    144  }
    145 
    146  if (osflags & PR_CREATE_FILE && osflags & PR_EXCL) {
    147    flags = CREATE_NEW;
    148  } else if (osflags & PR_CREATE_FILE) {
    149    if (osflags & PR_TRUNCATE) {
    150      flags = CREATE_ALWAYS;
    151    } else {
    152      flags = OPEN_ALWAYS;
    153    }
    154  } else {
    155    if (osflags & PR_TRUNCATE) {
    156      flags = TRUNCATE_EXISTING;
    157    } else {
    158      flags = OPEN_EXISTING;
    159    }
    160  }
    161 
    162  file = CreateFileA(name, access, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
    163                     flags, flag6, NULL);
    164  if (file == INVALID_HANDLE_VALUE) {
    165    _PR_MD_MAP_OPEN_ERROR(GetLastError());
    166    return -1;
    167  }
    168 
    169  return (PROsfd)file;
    170 }
    171 
    172 PROsfd _PR_MD_OPEN_FILE(const char* name, PRIntn osflags, int mode) {
    173  HANDLE file;
    174  PRInt32 access = 0;
    175  PRInt32 flags = 0;
    176  PRInt32 flag6 = 0;
    177  SECURITY_ATTRIBUTES sa;
    178  LPSECURITY_ATTRIBUTES lpSA = NULL;
    179  PSECURITY_DESCRIPTOR pSD = NULL;
    180  PACL pACL = NULL;
    181 
    182  if (osflags & PR_CREATE_FILE) {
    183    if (_PR_NT_MakeSecurityDescriptorACL(mode, fileAccessTable, &pSD, &pACL) ==
    184        PR_SUCCESS) {
    185      sa.nLength = sizeof(sa);
    186      sa.lpSecurityDescriptor = pSD;
    187      sa.bInheritHandle = FALSE;
    188      lpSA = &sa;
    189    }
    190  }
    191 
    192  if (osflags & PR_SYNC) {
    193    flag6 = FILE_FLAG_WRITE_THROUGH;
    194  }
    195 
    196  if (osflags & PR_RDONLY || osflags & PR_RDWR) {
    197    access |= GENERIC_READ;
    198  }
    199  if (osflags & PR_WRONLY || osflags & PR_RDWR) {
    200    access |= GENERIC_WRITE;
    201  }
    202 
    203  if (osflags & PR_CREATE_FILE && osflags & PR_EXCL) {
    204    flags = CREATE_NEW;
    205  } else if (osflags & PR_CREATE_FILE) {
    206    if (osflags & PR_TRUNCATE) {
    207      flags = CREATE_ALWAYS;
    208    } else {
    209      flags = OPEN_ALWAYS;
    210    }
    211  } else {
    212    if (osflags & PR_TRUNCATE) {
    213      flags = TRUNCATE_EXISTING;
    214    } else {
    215      flags = OPEN_EXISTING;
    216    }
    217  }
    218 
    219  file = CreateFileA(name, access, FILE_SHARE_READ | FILE_SHARE_WRITE, lpSA,
    220                     flags, flag6, NULL);
    221  if (lpSA != NULL) {
    222    _PR_NT_FreeSecurityDescriptorACL(pSD, pACL);
    223  }
    224  if (file == INVALID_HANDLE_VALUE) {
    225    _PR_MD_MAP_OPEN_ERROR(GetLastError());
    226    return -1;
    227  }
    228 
    229  return (PROsfd)file;
    230 }
    231 
    232 PRInt32 _PR_MD_READ(PRFileDesc* fd, void* buf, PRInt32 len) {
    233  PRUint32 bytes;
    234  int rv, err;
    235 
    236  rv = ReadFile((HANDLE)fd->secret->md.osfd, (LPVOID)buf, len, &bytes, NULL);
    237 
    238  if (rv == 0) {
    239    err = GetLastError();
    240    /* ERROR_HANDLE_EOF can only be returned by async io */
    241    PR_ASSERT(err != ERROR_HANDLE_EOF);
    242    if (err == ERROR_BROKEN_PIPE) {
    243      return 0;
    244    } else {
    245      _PR_MD_MAP_READ_ERROR(err);
    246      return -1;
    247    }
    248  }
    249  return bytes;
    250 }
    251 
    252 PRInt32 _PR_MD_WRITE(PRFileDesc* fd, const void* buf, PRInt32 len) {
    253  PROsfd f = fd->secret->md.osfd;
    254  PRInt32 bytes;
    255  int rv;
    256 
    257  rv = WriteFile((HANDLE)f, buf, len, &bytes, NULL);
    258 
    259  if (rv == 0) {
    260    _PR_MD_MAP_WRITE_ERROR(GetLastError());
    261    return -1;
    262  }
    263  return bytes;
    264 } /* --- end _PR_MD_WRITE() --- */
    265 
    266 PROffset32 _PR_MD_LSEEK(PRFileDesc* fd, PROffset32 offset,
    267                        PRSeekWhence whence) {
    268  DWORD moveMethod;
    269  PROffset32 rv;
    270 
    271  switch (whence) {
    272    case PR_SEEK_SET:
    273      moveMethod = FILE_BEGIN;
    274      break;
    275    case PR_SEEK_CUR:
    276      moveMethod = FILE_CURRENT;
    277      break;
    278    case PR_SEEK_END:
    279      moveMethod = FILE_END;
    280      break;
    281    default:
    282      PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
    283      return -1;
    284  }
    285 
    286  rv = SetFilePointer((HANDLE)fd->secret->md.osfd, offset, NULL, moveMethod);
    287 
    288  /*
    289   * If the lpDistanceToMoveHigh argument (third argument) is
    290   * NULL, SetFilePointer returns 0xffffffff on failure.
    291   */
    292  if (-1 == rv) {
    293    _PR_MD_MAP_LSEEK_ERROR(GetLastError());
    294  }
    295  return rv;
    296 }
    297 
    298 PROffset64 _PR_MD_LSEEK64(PRFileDesc* fd, PROffset64 offset,
    299                          PRSeekWhence whence) {
    300  DWORD moveMethod;
    301  LARGE_INTEGER li;
    302  DWORD err;
    303 
    304  switch (whence) {
    305    case PR_SEEK_SET:
    306      moveMethod = FILE_BEGIN;
    307      break;
    308    case PR_SEEK_CUR:
    309      moveMethod = FILE_CURRENT;
    310      break;
    311    case PR_SEEK_END:
    312      moveMethod = FILE_END;
    313      break;
    314    default:
    315      PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
    316      return -1;
    317  }
    318 
    319  li.QuadPart = offset;
    320  li.LowPart = SetFilePointer((HANDLE)fd->secret->md.osfd, li.LowPart,
    321                              &li.HighPart, moveMethod);
    322 
    323  if (0xffffffff == li.LowPart && (err = GetLastError()) != NO_ERROR) {
    324    _PR_MD_MAP_LSEEK_ERROR(err);
    325    li.QuadPart = -1;
    326  }
    327  return li.QuadPart;
    328 }
    329 
    330 /*
    331 * This is documented to succeed on read-only files, but Win32's
    332 * FlushFileBuffers functions fails with "access denied" in such a
    333 * case.  So we only signal an error if the error is *not* "access
    334 * denied".
    335 */
    336 PRInt32 _PR_MD_FSYNC(PRFileDesc* fd) {
    337  /*
    338   * From the documentation:
    339   *
    340   *     On Windows NT, the function FlushFileBuffers fails if hFile
    341   *     is a handle to console output. That is because console
    342   *     output is not buffered. The function returns FALSE, and
    343   *     GetLastError returns ERROR_INVALID_HANDLE.
    344   *
    345   * On the other hand, on Win95, it returns without error.  I cannot
    346   * assume that 0, 1, and 2 are console, because if someone closes
    347   * System.out and then opens a file, they might get file descriptor
    348   * 1.  An error on *that* version of 1 should be reported, whereas
    349   * an error on System.out (which was the original 1) should be
    350   * ignored.  So I use isatty() to ensure that such an error was due
    351   * to this bogosity, and if it was, I ignore the error.
    352   */
    353 
    354  BOOL ok = FlushFileBuffers((HANDLE)fd->secret->md.osfd);
    355 
    356  if (!ok) {
    357    DWORD err = GetLastError();
    358    if (err != ERROR_ACCESS_DENIED) {  // from winerror.h
    359      _PR_MD_MAP_FSYNC_ERROR(err);
    360      return -1;
    361    }
    362  }
    363  return 0;
    364 }
    365 
    366 PRInt32 _MD_CloseFile(PROsfd osfd) {
    367  PRInt32 rv;
    368 
    369  rv = (CloseHandle((HANDLE)osfd)) ? 0 : -1;
    370  if (rv == -1) {
    371    _PR_MD_MAP_CLOSE_ERROR(GetLastError());
    372  }
    373  return rv;
    374 }
    375 
    376 /* --- DIR IO ------------------------------------------------------------ */
    377 #define GetFileFromDIR(d) (d)->d_entry.cFileName
    378 #define FileIsHidden(d) ((d)->d_entry.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
    379 
    380 static void FlipSlashes(char* cp, size_t len) {
    381  while (len-- > 0) {
    382    if (cp[0] == '/') {
    383      cp[0] = PR_DIRECTORY_SEPARATOR;
    384    }
    385    cp = _mbsinc(cp);
    386  }
    387 } /* end FlipSlashes() */
    388 
    389 /*
    390 **
    391 ** Local implementations of standard Unix RTL functions which are not provided
    392 ** by the VC RTL.
    393 **
    394 */
    395 
    396 PRInt32 _PR_MD_CLOSE_DIR(_MDDir* d) {
    397  if (d) {
    398    if (FindClose(d->d_hdl)) {
    399      d->magic = (PRUint32)-1;
    400      return 0;
    401    } else {
    402      _PR_MD_MAP_CLOSEDIR_ERROR(GetLastError());
    403      return -1;
    404    }
    405  }
    406  PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
    407  return -1;
    408 }
    409 
    410 PRStatus _PR_MD_OPEN_DIR(_MDDir* d, const char* name) {
    411  char filename[MAX_PATH];
    412  size_t len;
    413 
    414  len = strlen(name);
    415  /* Need 5 bytes for \*.* and the trailing null byte. */
    416  if (len + 5 > MAX_PATH) {
    417    PR_SetError(PR_NAME_TOO_LONG_ERROR, 0);
    418    return PR_FAILURE;
    419  }
    420  strcpy(filename, name);
    421 
    422  /*
    423   * If 'name' ends in a slash or backslash, do not append
    424   * another backslash.
    425   */
    426  if (IsPrevCharSlash(filename, filename + len)) {
    427    len--;
    428  }
    429  strcpy(&filename[len], "\\*.*");
    430  FlipSlashes(filename, strlen(filename));
    431 
    432  d->d_hdl = FindFirstFileA(filename, &(d->d_entry));
    433  if (d->d_hdl == INVALID_HANDLE_VALUE) {
    434    _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
    435    return PR_FAILURE;
    436  }
    437  d->firstEntry = PR_TRUE;
    438  d->magic = _MD_MAGIC_DIR;
    439  return PR_SUCCESS;
    440 }
    441 
    442 char* _PR_MD_READ_DIR(_MDDir* d, PRIntn flags) {
    443  PRInt32 err;
    444  BOOL rv;
    445  char* fileName;
    446 
    447  if (d) {
    448    while (1) {
    449      if (d->firstEntry) {
    450        d->firstEntry = PR_FALSE;
    451        rv = 1;
    452      } else {
    453        rv = FindNextFileA(d->d_hdl, &(d->d_entry));
    454      }
    455      if (rv == 0) {
    456        break;
    457      }
    458      fileName = GetFileFromDIR(d);
    459      if ((flags & PR_SKIP_DOT) && (fileName[0] == '.') &&
    460          (fileName[1] == '\0')) {
    461        continue;
    462      }
    463      if ((flags & PR_SKIP_DOT_DOT) && (fileName[0] == '.') &&
    464          (fileName[1] == '.') && (fileName[2] == '\0')) {
    465        continue;
    466      }
    467      if ((flags & PR_SKIP_HIDDEN) && FileIsHidden(d)) {
    468        continue;
    469      }
    470      return fileName;
    471    }
    472    err = GetLastError();
    473    PR_ASSERT(NO_ERROR != err);
    474    _PR_MD_MAP_READDIR_ERROR(err);
    475    return NULL;
    476  }
    477  PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
    478  return NULL;
    479 }
    480 
    481 PRInt32 _PR_MD_DELETE(const char* name) {
    482  if (DeleteFileA(name)) {
    483    return 0;
    484  } else {
    485    _PR_MD_MAP_DELETE_ERROR(GetLastError());
    486    return -1;
    487  }
    488 }
    489 
    490 void _PR_FileTimeToPRTime(const FILETIME* filetime, PRTime* prtm) {
    491  PR_ASSERT(sizeof(FILETIME) == sizeof(PRTime));
    492  CopyMemory(prtm, filetime, sizeof(PRTime));
    493 #if defined(__MINGW32__)
    494  *prtm = (*prtm - _pr_filetime_offset) / 10LL;
    495 #else
    496  *prtm = (*prtm - _pr_filetime_offset) / 10i64;
    497 #endif
    498 
    499 #ifdef DEBUG
    500  /* Doublecheck our calculation. */
    501  {
    502    SYSTEMTIME systime;
    503    PRExplodedTime etm;
    504    PRTime cmp; /* for comparison */
    505    BOOL rv;
    506 
    507    rv = FileTimeToSystemTime(filetime, &systime);
    508    PR_ASSERT(0 != rv);
    509 
    510    /*
    511     * PR_ImplodeTime ignores wday and yday.
    512     */
    513    etm.tm_usec = systime.wMilliseconds * PR_USEC_PER_MSEC;
    514    etm.tm_sec = systime.wSecond;
    515    etm.tm_min = systime.wMinute;
    516    etm.tm_hour = systime.wHour;
    517    etm.tm_mday = systime.wDay;
    518    etm.tm_month = systime.wMonth - 1;
    519    etm.tm_year = systime.wYear;
    520    /*
    521     * It is not well-documented what time zone the FILETIME's
    522     * are in.  WIN32_FIND_DATA is documented to be in UTC (GMT).
    523     * But BY_HANDLE_FILE_INFORMATION is unclear about this.
    524     * By our best judgement, we assume that FILETIME is in UTC.
    525     */
    526    etm.tm_params.tp_gmt_offset = 0;
    527    etm.tm_params.tp_dst_offset = 0;
    528    cmp = PR_ImplodeTime(&etm);
    529 
    530    /*
    531     * SYSTEMTIME is in milliseconds precision, so we convert PRTime's
    532     * microseconds to milliseconds before doing the comparison.
    533     */
    534    PR_ASSERT((cmp / PR_USEC_PER_MSEC) == (*prtm / PR_USEC_PER_MSEC));
    535  }
    536 #endif /* DEBUG */
    537 }
    538 
    539 PRInt32 _PR_MD_STAT(const char* fn, struct stat* info) {
    540  PRInt32 rv;
    541 
    542  rv = _stat(fn, (struct _stat*)info);
    543  if (-1 == rv) {
    544    /*
    545     * Check for MSVC runtime library _stat() bug.
    546     * (It's really a bug in FindFirstFile().)
    547     * If a pathname ends in a backslash or slash,
    548     * e.g., c:\temp\ or c:/temp/, _stat() will fail.
    549     * Note: a pathname ending in a slash (e.g., c:/temp/)
    550     * can be handled by _stat() on NT but not on Win95.
    551     *
    552     * We remove the backslash or slash at the end and
    553     * try again.
    554     */
    555 
    556    size_t len = strlen(fn);
    557    if (len > 0 && len <= _MAX_PATH && IsPrevCharSlash(fn, fn + len)) {
    558      char newfn[_MAX_PATH + 1];
    559 
    560      strcpy(newfn, fn);
    561      newfn[len - 1] = '\0';
    562      rv = _stat(newfn, (struct _stat*)info);
    563    }
    564  }
    565 
    566  if (-1 == rv) {
    567    _PR_MD_MAP_STAT_ERROR(errno);
    568  }
    569  return rv;
    570 }
    571 
    572 #define _PR_IS_SLASH(ch) ((ch) == '/' || (ch) == '\\')
    573 
    574 static PRBool IsPrevCharSlash(const char* str, const char* current) {
    575  const char* prev;
    576 
    577  if (str >= current) {
    578    return PR_FALSE;
    579  }
    580  prev = _mbsdec(str, current);
    581  return (prev == current - 1) && _PR_IS_SLASH(*prev);
    582 }
    583 
    584 /*
    585 * IsRootDirectory --
    586 *
    587 * Return PR_TRUE if the pathname 'fn' is a valid root directory,
    588 * else return PR_FALSE.  The char buffer pointed to by 'fn' must
    589 * be writable.  During the execution of this function, the contents
    590 * of the buffer pointed to by 'fn' may be modified, but on return
    591 * the original contents will be restored.  'buflen' is the size of
    592 * the buffer pointed to by 'fn'.
    593 *
    594 * Root directories come in three formats:
    595 * 1. / or \, meaning the root directory of the current drive.
    596 * 2. C:/ or C:\, where C is a drive letter.
    597 * 3. \\<server name>\<share point name>\ or
    598 *    \\<server name>\<share point name>, meaning the root directory
    599 *    of a UNC (Universal Naming Convention) name.
    600 */
    601 
    602 static PRBool IsRootDirectory(char* fn, size_t buflen) {
    603  char* p;
    604  PRBool slashAdded = PR_FALSE;
    605  PRBool rv = PR_FALSE;
    606 
    607  if (_PR_IS_SLASH(fn[0]) && fn[1] == '\0') {
    608    return PR_TRUE;
    609  }
    610 
    611  if (isalpha((unsigned char)fn[0]) && fn[1] == ':' && _PR_IS_SLASH(fn[2]) && fn[3] == '\0') {
    612    rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE;
    613    return rv;
    614  }
    615 
    616  /* The UNC root directory */
    617 
    618  if (_PR_IS_SLASH(fn[0]) && _PR_IS_SLASH(fn[1])) {
    619    /* The 'server' part should have at least one character. */
    620    p = &fn[2];
    621    if (*p == '\0' || _PR_IS_SLASH(*p)) {
    622      return PR_FALSE;
    623    }
    624 
    625    /* look for the next slash */
    626    do {
    627      p = _mbsinc(p);
    628    } while (*p != '\0' && !_PR_IS_SLASH(*p));
    629    if (*p == '\0') {
    630      return PR_FALSE;
    631    }
    632 
    633    /* The 'share' part should have at least one character. */
    634    p++;
    635    if (*p == '\0' || _PR_IS_SLASH(*p)) {
    636      return PR_FALSE;
    637    }
    638 
    639    /* look for the final slash */
    640    do {
    641      p = _mbsinc(p);
    642    } while (*p != '\0' && !_PR_IS_SLASH(*p));
    643    if (_PR_IS_SLASH(*p) && p[1] != '\0') {
    644      return PR_FALSE;
    645    }
    646    if (*p == '\0') {
    647      /*
    648       * GetDriveType() doesn't work correctly if the
    649       * path is of the form \\server\share, so we add
    650       * a final slash temporarily.
    651       */
    652      if ((p + 1) < (fn + buflen)) {
    653        *p++ = '\\';
    654        *p = '\0';
    655        slashAdded = PR_TRUE;
    656      } else {
    657        return PR_FALSE; /* name too long */
    658      }
    659    }
    660    rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE;
    661    /* restore the 'fn' buffer */
    662    if (slashAdded) {
    663      *--p = '\0';
    664    }
    665  }
    666  return rv;
    667 }
    668 
    669 PRInt32 _PR_MD_GETFILEINFO64(const char* fn, PRFileInfo64* info) {
    670  WIN32_FILE_ATTRIBUTE_DATA findFileData;
    671  BOOL rv;
    672 
    673  if (NULL == fn || '\0' == *fn) {
    674    PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
    675    return -1;
    676  }
    677 
    678  rv = GetFileAttributesEx(fn, GetFileExInfoStandard, &findFileData);
    679  if (!rv) {
    680    _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
    681    return -1;
    682  }
    683 
    684  if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
    685    info->type = PR_FILE_DIRECTORY;
    686  } else {
    687    info->type = PR_FILE_FILE;
    688  }
    689 
    690  info->size = findFileData.nFileSizeHigh;
    691  info->size = (info->size << 32) + findFileData.nFileSizeLow;
    692 
    693  _PR_FileTimeToPRTime(&findFileData.ftLastWriteTime, &info->modifyTime);
    694 
    695  if (0 == findFileData.ftCreationTime.dwLowDateTime &&
    696      0 == findFileData.ftCreationTime.dwHighDateTime) {
    697    info->creationTime = info->modifyTime;
    698  } else {
    699    _PR_FileTimeToPRTime(&findFileData.ftCreationTime, &info->creationTime);
    700  }
    701 
    702  return 0;
    703 }
    704 
    705 PRInt32 _PR_MD_GETFILEINFO(const char* fn, PRFileInfo* info) {
    706  PRFileInfo64 info64;
    707  PRInt32 rv = _PR_MD_GETFILEINFO64(fn, &info64);
    708  if (0 == rv) {
    709    info->type = info64.type;
    710    info->size = (PRUint32)info64.size;
    711    info->modifyTime = info64.modifyTime;
    712    info->creationTime = info64.creationTime;
    713  }
    714  return rv;
    715 }
    716 
    717 PRInt32 _PR_MD_GETOPENFILEINFO64(const PRFileDesc* fd, PRFileInfo64* info) {
    718  int rv;
    719 
    720  BY_HANDLE_FILE_INFORMATION hinfo;
    721 
    722  rv = GetFileInformationByHandle((HANDLE)fd->secret->md.osfd, &hinfo);
    723  if (rv == FALSE) {
    724    _PR_MD_MAP_FSTAT_ERROR(GetLastError());
    725    return -1;
    726  }
    727 
    728  if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
    729    info->type = PR_FILE_DIRECTORY;
    730  } else {
    731    info->type = PR_FILE_FILE;
    732  }
    733 
    734  info->size = hinfo.nFileSizeHigh;
    735  info->size = (info->size << 32) + hinfo.nFileSizeLow;
    736 
    737  _PR_FileTimeToPRTime(&hinfo.ftLastWriteTime, &(info->modifyTime));
    738  _PR_FileTimeToPRTime(&hinfo.ftCreationTime, &(info->creationTime));
    739 
    740  return 0;
    741 }
    742 
    743 PRInt32 _PR_MD_GETOPENFILEINFO(const PRFileDesc* fd, PRFileInfo* info) {
    744  PRFileInfo64 info64;
    745  int rv = _PR_MD_GETOPENFILEINFO64(fd, &info64);
    746  if (0 == rv) {
    747    info->type = info64.type;
    748    info->modifyTime = info64.modifyTime;
    749    info->creationTime = info64.creationTime;
    750    LL_L2I(info->size, info64.size);
    751  }
    752  return rv;
    753 }
    754 
    755 PRStatus _PR_MD_SET_FD_INHERITABLE(PRFileDesc* fd, PRBool inheritable) {
    756  BOOL rv;
    757 
    758  /*
    759   * The SetHandleInformation function fails with the
    760   * ERROR_CALL_NOT_IMPLEMENTED error on Win95.
    761   */
    762  rv = SetHandleInformation((HANDLE)fd->secret->md.osfd, HANDLE_FLAG_INHERIT,
    763                            inheritable ? HANDLE_FLAG_INHERIT : 0);
    764  if (0 == rv) {
    765    _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
    766    return PR_FAILURE;
    767  }
    768  return PR_SUCCESS;
    769 }
    770 
    771 void _PR_MD_INIT_FD_INHERITABLE(PRFileDesc* fd, PRBool imported) {
    772  if (imported) {
    773    fd->secret->inheritable = _PR_TRI_UNKNOWN;
    774  } else {
    775    fd->secret->inheritable = _PR_TRI_FALSE;
    776  }
    777 }
    778 
    779 void _PR_MD_QUERY_FD_INHERITABLE(PRFileDesc* fd) {
    780  DWORD flags;
    781 
    782  PR_ASSERT(_PR_TRI_UNKNOWN == fd->secret->inheritable);
    783  if (GetHandleInformation((HANDLE)fd->secret->md.osfd, &flags)) {
    784    if (flags & HANDLE_FLAG_INHERIT) {
    785      fd->secret->inheritable = _PR_TRI_TRUE;
    786    } else {
    787      fd->secret->inheritable = _PR_TRI_FALSE;
    788    }
    789  }
    790 }
    791 
    792 PRInt32 _PR_MD_RENAME(const char* from, const char* to) {
    793  /* Does this work with dot-relative pathnames? */
    794  if (MoveFileA(from, to)) {
    795    return 0;
    796  } else {
    797    _PR_MD_MAP_RENAME_ERROR(GetLastError());
    798    return -1;
    799  }
    800 }
    801 
    802 PRInt32 _PR_MD_ACCESS(const char* name, PRAccessHow how) {
    803  PRInt32 rv;
    804  switch (how) {
    805    case PR_ACCESS_WRITE_OK:
    806      rv = _access(name, 02);
    807      break;
    808    case PR_ACCESS_READ_OK:
    809      rv = _access(name, 04);
    810      break;
    811    case PR_ACCESS_EXISTS:
    812      return _access(name, 00);
    813      break;
    814    default:
    815      PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
    816      return -1;
    817  }
    818  if (rv < 0) {
    819    _PR_MD_MAP_ACCESS_ERROR(errno);
    820  }
    821  return rv;
    822 }
    823 
    824 PRInt32 _PR_MD_MKDIR(const char* name, PRIntn mode) {
    825  /* XXXMB - how to translate the "mode"??? */
    826  if (CreateDirectoryA(name, NULL)) {
    827    return 0;
    828  } else {
    829    _PR_MD_MAP_MKDIR_ERROR(GetLastError());
    830    return -1;
    831  }
    832 }
    833 
    834 PRInt32 _PR_MD_MAKE_DIR(const char* name, PRIntn mode) {
    835  BOOL rv;
    836  SECURITY_ATTRIBUTES sa;
    837  LPSECURITY_ATTRIBUTES lpSA = NULL;
    838  PSECURITY_DESCRIPTOR pSD = NULL;
    839  PACL pACL = NULL;
    840 
    841  if (_PR_NT_MakeSecurityDescriptorACL(mode, dirAccessTable, &pSD, &pACL) ==
    842      PR_SUCCESS) {
    843    sa.nLength = sizeof(sa);
    844    sa.lpSecurityDescriptor = pSD;
    845    sa.bInheritHandle = FALSE;
    846    lpSA = &sa;
    847  }
    848  rv = CreateDirectoryA(name, lpSA);
    849  if (lpSA != NULL) {
    850    _PR_NT_FreeSecurityDescriptorACL(pSD, pACL);
    851  }
    852  if (rv) {
    853    return 0;
    854  } else {
    855    _PR_MD_MAP_MKDIR_ERROR(GetLastError());
    856    return -1;
    857  }
    858 }
    859 
    860 PRInt32 _PR_MD_RMDIR(const char* name) {
    861  if (RemoveDirectoryA(name)) {
    862    return 0;
    863  } else {
    864    _PR_MD_MAP_RMDIR_ERROR(GetLastError());
    865    return -1;
    866  }
    867 }
    868 
    869 PRStatus _PR_MD_LOCKFILE(PROsfd f) {
    870  PRStatus rc = PR_SUCCESS;
    871  DWORD rv;
    872 
    873  rv = LockFile((HANDLE)f, 0l, 0l, 0x0l, 0xffffffffl);
    874  if (rv == 0) {
    875    DWORD err = GetLastError();
    876    _PR_MD_MAP_DEFAULT_ERROR(err);
    877    PR_LOG(_pr_io_lm, PR_LOG_ERROR,
    878           ("_PR_MD_LOCKFILE() failed. Error: %d", err));
    879    rc = PR_FAILURE;
    880  }
    881 
    882  return rc;
    883 } /* end _PR_MD_LOCKFILE() */
    884 
    885 PRStatus _PR_MD_TLOCKFILE(PROsfd f) {
    886  PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
    887  return PR_FAILURE;
    888 } /* end _PR_MD_TLOCKFILE() */
    889 
    890 PRStatus _PR_MD_UNLOCKFILE(PROsfd f) {
    891  PRInt32 rv;
    892 
    893  rv = UnlockFile((HANDLE)f, 0l, 0l, 0x0l, 0xffffffffl);
    894 
    895  if (rv) {
    896    return PR_SUCCESS;
    897  } else {
    898    _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
    899    return PR_FAILURE;
    900  }
    901 } /* end _PR_MD_UNLOCKFILE() */
    902 
    903 PRInt32 _PR_MD_PIPEAVAILABLE(PRFileDesc* fd) {
    904  if (NULL == fd) {
    905    PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
    906  } else {
    907    PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
    908  }
    909  return -1;
    910 }
    911 
    912 #ifdef MOZ_UNICODE
    913 
    914 typedef HANDLE(WINAPI* CreateFileWFn)(LPCWSTR, DWORD, DWORD,
    915                                      LPSECURITY_ATTRIBUTES, DWORD, DWORD,
    916                                      HANDLE);
    917 static CreateFileWFn createFileW = CreateFileW;
    918 typedef HANDLE(WINAPI* FindFirstFileWFn)(LPCWSTR, LPWIN32_FIND_DATAW);
    919 static FindFirstFileWFn findFirstFileW = FindFirstFileW;
    920 typedef BOOL(WINAPI* FindNextFileWFn)(HANDLE, LPWIN32_FIND_DATAW);
    921 static FindNextFileWFn findNextFileW = FindNextFileW;
    922 typedef DWORD(WINAPI* GetFullPathNameWFn)(LPCWSTR, DWORD, LPWSTR, LPWSTR*);
    923 static GetFullPathNameWFn getFullPathNameW = GetFullPathNameW;
    924 typedef UINT(WINAPI* GetDriveTypeWFn)(LPCWSTR);
    925 static GetDriveTypeWFn getDriveTypeW = GetDriveTypeW;
    926 
    927 #endif /* MOZ_UNICODE */
    928 
    929 #ifdef MOZ_UNICODE
    930 
    931 /* ================ UTF16 Interfaces ================================ */
    932 static void FlipSlashesW(PRUnichar* cp, size_t len) {
    933  while (len-- > 0) {
    934    if (cp[0] == L'/') {
    935      cp[0] = L'\\';
    936    }
    937    cp++;
    938  }
    939 } /* end FlipSlashesW() */
    940 
    941 PROsfd _PR_MD_OPEN_FILE_UTF16(const PRUnichar* name, PRIntn osflags, int mode) {
    942  HANDLE file;
    943  PRInt32 access = 0;
    944  PRInt32 flags = 0;
    945  PRInt32 flag6 = 0;
    946  SECURITY_ATTRIBUTES sa;
    947  LPSECURITY_ATTRIBUTES lpSA = NULL;
    948  PSECURITY_DESCRIPTOR pSD = NULL;
    949  PACL pACL = NULL;
    950 
    951  if (osflags & PR_CREATE_FILE) {
    952    if (_PR_NT_MakeSecurityDescriptorACL(mode, fileAccessTable, &pSD, &pACL) ==
    953        PR_SUCCESS) {
    954      sa.nLength = sizeof(sa);
    955      sa.lpSecurityDescriptor = pSD;
    956      sa.bInheritHandle = FALSE;
    957      lpSA = &sa;
    958    }
    959  }
    960 
    961  if (osflags & PR_SYNC) {
    962    flag6 = FILE_FLAG_WRITE_THROUGH;
    963  }
    964 
    965  if (osflags & PR_RDONLY || osflags & PR_RDWR) {
    966    access |= GENERIC_READ;
    967  }
    968  if (osflags & PR_WRONLY || osflags & PR_RDWR) {
    969    access |= GENERIC_WRITE;
    970  }
    971 
    972  if (osflags & PR_CREATE_FILE && osflags & PR_EXCL) {
    973    flags = CREATE_NEW;
    974  } else if (osflags & PR_CREATE_FILE) {
    975    if (osflags & PR_TRUNCATE) {
    976      flags = CREATE_ALWAYS;
    977    } else {
    978      flags = OPEN_ALWAYS;
    979    }
    980  } else {
    981    if (osflags & PR_TRUNCATE) {
    982      flags = TRUNCATE_EXISTING;
    983    } else {
    984      flags = OPEN_EXISTING;
    985    }
    986  }
    987 
    988  file = createFileW(name, access, FILE_SHARE_READ | FILE_SHARE_WRITE, lpSA,
    989                     flags, flag6, NULL);
    990  if (lpSA != NULL) {
    991    _PR_NT_FreeSecurityDescriptorACL(pSD, pACL);
    992  }
    993  if (file == INVALID_HANDLE_VALUE) {
    994    _PR_MD_MAP_OPEN_ERROR(GetLastError());
    995    return -1;
    996  }
    997 
    998  return (PROsfd)file;
    999 }
   1000 
   1001 PRStatus _PR_MD_OPEN_DIR_UTF16(_MDDirUTF16* d, const PRUnichar* name) {
   1002  PRUnichar filename[MAX_PATH];
   1003  int len;
   1004 
   1005  len = wcslen(name);
   1006  /* Need 5 bytes for \*.* and the trailing null byte. */
   1007  if (len + 5 > MAX_PATH) {
   1008    PR_SetError(PR_NAME_TOO_LONG_ERROR, 0);
   1009    return PR_FAILURE;
   1010  }
   1011  wcscpy(filename, name);
   1012 
   1013  /*
   1014   * If 'name' ends in a slash or backslash, do not append
   1015   * another backslash.
   1016   */
   1017  if (filename[len - 1] == L'/' || filename[len - 1] == L'\\') {
   1018    len--;
   1019  }
   1020  wcscpy(&filename[len], L"\\*.*");
   1021  FlipSlashesW(filename, wcslen(filename));
   1022 
   1023  d->d_hdl = findFirstFileW(filename, &(d->d_entry));
   1024  if (d->d_hdl == INVALID_HANDLE_VALUE) {
   1025    _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
   1026    return PR_FAILURE;
   1027  }
   1028  d->firstEntry = PR_TRUE;
   1029  d->magic = _MD_MAGIC_DIR;
   1030  return PR_SUCCESS;
   1031 }
   1032 
   1033 PRUnichar* _PR_MD_READ_DIR_UTF16(_MDDirUTF16* d, PRIntn flags) {
   1034  PRInt32 err;
   1035  BOOL rv;
   1036  PRUnichar* fileName;
   1037 
   1038  if (d) {
   1039    while (1) {
   1040      if (d->firstEntry) {
   1041        d->firstEntry = PR_FALSE;
   1042        rv = 1;
   1043      } else {
   1044        rv = findNextFileW(d->d_hdl, &(d->d_entry));
   1045      }
   1046      if (rv == 0) {
   1047        break;
   1048      }
   1049      fileName = GetFileFromDIR(d);
   1050      if ((flags & PR_SKIP_DOT) && (fileName[0] == L'.') &&
   1051          (fileName[1] == L'\0')) {
   1052        continue;
   1053      }
   1054      if ((flags & PR_SKIP_DOT_DOT) && (fileName[0] == L'.') &&
   1055          (fileName[1] == L'.') && (fileName[2] == L'\0')) {
   1056        continue;
   1057      }
   1058      if ((flags & PR_SKIP_HIDDEN) && FileIsHidden(d)) {
   1059        continue;
   1060      }
   1061      return fileName;
   1062    }
   1063    err = GetLastError();
   1064    PR_ASSERT(NO_ERROR != err);
   1065    _PR_MD_MAP_READDIR_ERROR(err);
   1066    return NULL;
   1067  }
   1068  PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
   1069  return NULL;
   1070 }
   1071 
   1072 PRInt32 _PR_MD_CLOSE_DIR_UTF16(_MDDirUTF16* d) {
   1073  if (d) {
   1074    if (FindClose(d->d_hdl)) {
   1075      d->magic = (PRUint32)-1;
   1076      return 0;
   1077    } else {
   1078      _PR_MD_MAP_CLOSEDIR_ERROR(GetLastError());
   1079      return -1;
   1080    }
   1081  }
   1082  PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
   1083  return -1;
   1084 }
   1085 
   1086 #  define _PR_IS_W_SLASH(ch) ((ch) == L'/' || (ch) == L'\\')
   1087 
   1088 /*
   1089 * IsRootDirectoryW --
   1090 *
   1091 * Return PR_TRUE if the pathname 'fn' is a valid root directory,
   1092 * else return PR_FALSE.  The PRUnichar buffer pointed to by 'fn' must
   1093 * be writable.  During the execution of this function, the contents
   1094 * of the buffer pointed to by 'fn' may be modified, but on return
   1095 * the original contents will be restored.  'buflen' is the size of
   1096 * the buffer pointed to by 'fn', in PRUnichars.
   1097 *
   1098 * Root directories come in three formats:
   1099 * 1. / or \, meaning the root directory of the current drive.
   1100 * 2. C:/ or C:\, where C is a drive letter.
   1101 * 3. \\<server name>\<share point name>\ or
   1102 *    \\<server name>\<share point name>, meaning the root directory
   1103 *    of a UNC (Universal Naming Convention) name.
   1104 */
   1105 
   1106 static PRBool IsRootDirectoryW(PRUnichar* fn, size_t buflen) {
   1107  PRUnichar* p;
   1108  PRBool slashAdded = PR_FALSE;
   1109  PRBool rv = PR_FALSE;
   1110 
   1111  if (_PR_IS_W_SLASH(fn[0]) && fn[1] == L'\0') {
   1112    return PR_TRUE;
   1113  }
   1114 
   1115  if (iswalpha(fn[0]) && fn[1] == L':' && _PR_IS_W_SLASH(fn[2]) &&
   1116      fn[3] == L'\0') {
   1117    rv = getDriveTypeW(fn) > 1 ? PR_TRUE : PR_FALSE;
   1118    return rv;
   1119  }
   1120 
   1121  /* The UNC root directory */
   1122 
   1123  if (_PR_IS_W_SLASH(fn[0]) && _PR_IS_W_SLASH(fn[1])) {
   1124    /* The 'server' part should have at least one character. */
   1125    p = &fn[2];
   1126    if (*p == L'\0' || _PR_IS_W_SLASH(*p)) {
   1127      return PR_FALSE;
   1128    }
   1129 
   1130    /* look for the next slash */
   1131    do {
   1132      p++;
   1133    } while (*p != L'\0' && !_PR_IS_W_SLASH(*p));
   1134    if (*p == L'\0') {
   1135      return PR_FALSE;
   1136    }
   1137 
   1138    /* The 'share' part should have at least one character. */
   1139    p++;
   1140    if (*p == L'\0' || _PR_IS_W_SLASH(*p)) {
   1141      return PR_FALSE;
   1142    }
   1143 
   1144    /* look for the final slash */
   1145    do {
   1146      p++;
   1147    } while (*p != L'\0' && !_PR_IS_W_SLASH(*p));
   1148    if (_PR_IS_W_SLASH(*p) && p[1] != L'\0') {
   1149      return PR_FALSE;
   1150    }
   1151    if (*p == L'\0') {
   1152      /*
   1153       * GetDriveType() doesn't work correctly if the
   1154       * path is of the form \\server\share, so we add
   1155       * a final slash temporarily.
   1156       */
   1157      if ((p + 1) < (fn + buflen)) {
   1158        *p++ = L'\\';
   1159        *p = L'\0';
   1160        slashAdded = PR_TRUE;
   1161      } else {
   1162        return PR_FALSE; /* name too long */
   1163      }
   1164    }
   1165    rv = getDriveTypeW(fn) > 1 ? PR_TRUE : PR_FALSE;
   1166    /* restore the 'fn' buffer */
   1167    if (slashAdded) {
   1168      *--p = L'\0';
   1169    }
   1170  }
   1171  return rv;
   1172 }
   1173 
   1174 PRInt32 _PR_MD_GETFILEINFO64_UTF16(const PRUnichar* fn, PRFileInfo64* info) {
   1175  HANDLE hFindFile;
   1176  WIN32_FIND_DATAW findFileData;
   1177  PRUnichar pathbuf[MAX_PATH + 1];
   1178 
   1179  if (NULL == fn || L'\0' == *fn) {
   1180    PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
   1181    return -1;
   1182  }
   1183 
   1184  /*
   1185   * FindFirstFile() expands wildcard characters.  So
   1186   * we make sure the pathname contains no wildcard.
   1187   */
   1188  if (NULL != wcspbrk(fn, L"?*")) {
   1189    PR_SetError(PR_FILE_NOT_FOUND_ERROR, 0);
   1190    return -1;
   1191  }
   1192 
   1193  hFindFile = findFirstFileW(fn, &findFileData);
   1194  if (INVALID_HANDLE_VALUE == hFindFile) {
   1195    DWORD len;
   1196    PRUnichar* filePart;
   1197 
   1198    /*
   1199     * FindFirstFile() does not work correctly on root directories.
   1200     * It also doesn't work correctly on a pathname that ends in a
   1201     * slash.  So we first check to see if the pathname specifies a
   1202     * root directory.  If not, and if the pathname ends in a slash,
   1203     * we remove the final slash and try again.
   1204     */
   1205 
   1206    /*
   1207     * If the pathname does not contain ., \, and /, it cannot be
   1208     * a root directory or a pathname that ends in a slash.
   1209     */
   1210    if (NULL == wcspbrk(fn, L".\\/")) {
   1211      _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
   1212      return -1;
   1213    }
   1214    len = getFullPathNameW(fn, sizeof(pathbuf) / sizeof(pathbuf[0]), pathbuf,
   1215                           &filePart);
   1216    if (0 == len) {
   1217      _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
   1218      return -1;
   1219    }
   1220    if (len > sizeof(pathbuf) / sizeof(pathbuf[0])) {
   1221      PR_SetError(PR_NAME_TOO_LONG_ERROR, 0);
   1222      return -1;
   1223    }
   1224    if (IsRootDirectoryW(pathbuf, sizeof(pathbuf) / sizeof(pathbuf[0]))) {
   1225      info->type = PR_FILE_DIRECTORY;
   1226      info->size = 0;
   1227      /*
   1228       * These timestamps don't make sense for root directories.
   1229       */
   1230      info->modifyTime = 0;
   1231      info->creationTime = 0;
   1232      return 0;
   1233    }
   1234    if (!_PR_IS_W_SLASH(pathbuf[len - 1])) {
   1235      _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
   1236      return -1;
   1237    } else {
   1238      pathbuf[len - 1] = L'\0';
   1239      hFindFile = findFirstFileW(pathbuf, &findFileData);
   1240      if (INVALID_HANDLE_VALUE == hFindFile) {
   1241        _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
   1242        return -1;
   1243      }
   1244    }
   1245  }
   1246 
   1247  FindClose(hFindFile);
   1248 
   1249  if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
   1250    info->type = PR_FILE_DIRECTORY;
   1251  } else {
   1252    info->type = PR_FILE_FILE;
   1253  }
   1254 
   1255  info->size = findFileData.nFileSizeHigh;
   1256  info->size = (info->size << 32) + findFileData.nFileSizeLow;
   1257 
   1258  _PR_FileTimeToPRTime(&findFileData.ftLastWriteTime, &info->modifyTime);
   1259 
   1260  if (0 == findFileData.ftCreationTime.dwLowDateTime &&
   1261      0 == findFileData.ftCreationTime.dwHighDateTime) {
   1262    info->creationTime = info->modifyTime;
   1263  } else {
   1264    _PR_FileTimeToPRTime(&findFileData.ftCreationTime, &info->creationTime);
   1265  }
   1266 
   1267  return 0;
   1268 }
   1269 /* ================ end of UTF16 Interfaces ================================ */
   1270 #endif /* MOZ_UNICODE */