andre@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ andre@0: /* This Source Code Form is subject to the terms of the Mozilla Public andre@0: * License, v. 2.0. If a copy of the MPL was not distributed with this andre@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ andre@0: andre@0: /* Windows 95 IO module andre@0: * andre@0: * Assumes synchronous I/O. andre@0: * andre@0: */ andre@0: andre@0: #include "primpl.h" andre@0: #include andre@0: #include andre@0: #ifdef MOZ_UNICODE andre@0: #include andre@0: #endif /* MOZ_UNICODE */ andre@0: andre@0: struct _MDLock _pr_ioq_lock; andre@0: andre@0: /* andre@0: * NSPR-to-NT access right mapping table for files. andre@0: */ andre@0: static DWORD fileAccessTable[] = { andre@0: FILE_GENERIC_READ, andre@0: FILE_GENERIC_WRITE, andre@0: FILE_GENERIC_EXECUTE andre@0: }; andre@0: andre@0: /* andre@0: * NSPR-to-NT access right mapping table for directories. andre@0: */ andre@0: static DWORD dirAccessTable[] = { andre@0: FILE_GENERIC_READ, andre@0: FILE_GENERIC_WRITE|FILE_DELETE_CHILD, andre@0: FILE_GENERIC_EXECUTE andre@0: }; andre@0: andre@0: static PRBool IsPrevCharSlash(const char *str, const char *current); andre@0: andre@0: void andre@0: _PR_MD_INIT_IO() andre@0: { andre@0: WORD WSAVersion = 0x0101; andre@0: WSADATA WSAData; andre@0: int err; andre@0: andre@0: err = WSAStartup( WSAVersion, &WSAData ); andre@0: PR_ASSERT(0 == err); andre@0: andre@0: #ifdef DEBUG andre@0: /* Doublecheck _pr_filetime_offset's hard-coded value is correct. */ andre@0: { andre@0: SYSTEMTIME systime; andre@0: union { andre@0: PRTime prt; andre@0: FILETIME ft; andre@0: } filetime; andre@0: BOOL rv; andre@0: andre@0: systime.wYear = 1970; andre@0: systime.wMonth = 1; andre@0: /* wDayOfWeek is ignored */ andre@0: systime.wDay = 1; andre@0: systime.wHour = 0; andre@0: systime.wMinute = 0; andre@0: systime.wSecond = 0; andre@0: systime.wMilliseconds = 0; andre@0: andre@0: rv = SystemTimeToFileTime(&systime, &filetime.ft); andre@0: PR_ASSERT(0 != rv); andre@0: PR_ASSERT(filetime.prt == _pr_filetime_offset); andre@0: } andre@0: #endif /* DEBUG */ andre@0: andre@0: _PR_NT_InitSids(); andre@0: andre@0: _PR_MD_InitSockets(); andre@0: } andre@0: andre@0: PRStatus andre@0: _PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks) andre@0: { andre@0: DWORD rv; andre@0: andre@0: PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) ? andre@0: INFINITE : PR_IntervalToMilliseconds(ticks); andre@0: rv = WaitForSingleObject(thread->md.blocked_sema, msecs); andre@0: switch(rv) andre@0: { andre@0: case WAIT_OBJECT_0: andre@0: return PR_SUCCESS; andre@0: break; andre@0: case WAIT_TIMEOUT: andre@0: _PR_THREAD_LOCK(thread); andre@0: if (thread->state == _PR_IO_WAIT) { andre@0: ; andre@0: } else { andre@0: if (thread->wait.cvar != NULL) { andre@0: thread->wait.cvar = NULL; andre@0: _PR_THREAD_UNLOCK(thread); andre@0: } else { andre@0: /* The CVAR was notified just as the timeout andre@0: * occurred. This led to us being notified twice. andre@0: * call WaitForSingleObject() to clear the semaphore. andre@0: */ andre@0: _PR_THREAD_UNLOCK(thread); andre@0: rv = WaitForSingleObject(thread->md.blocked_sema, 0); andre@0: PR_ASSERT(rv == WAIT_OBJECT_0); andre@0: } andre@0: } andre@0: return PR_SUCCESS; andre@0: break; andre@0: default: andre@0: return PR_FAILURE; andre@0: break; andre@0: } andre@0: } andre@0: PRStatus andre@0: _PR_MD_WAKEUP_WAITER(PRThread *thread) andre@0: { andre@0: if ( _PR_IS_NATIVE_THREAD(thread) ) andre@0: { andre@0: if (ReleaseSemaphore(thread->md.blocked_sema, 1, NULL) == FALSE) andre@0: return PR_FAILURE; andre@0: else andre@0: return PR_SUCCESS; andre@0: } andre@0: } andre@0: andre@0: andre@0: /* --- FILE IO ----------------------------------------------------------- */ andre@0: /* andre@0: * _PR_MD_OPEN() -- Open a file andre@0: * andre@0: * returns: a fileHandle andre@0: * andre@0: * The NSPR open flags (osflags) are translated into flags for Win95 andre@0: * andre@0: * Mode seems to be passed in as a unix style file permissions argument andre@0: * as in 0666, in the case of opening the logFile. andre@0: * andre@0: */ andre@0: PROsfd andre@0: _PR_MD_OPEN(const char *name, PRIntn osflags, int mode) andre@0: { andre@0: HANDLE file; andre@0: PRInt32 access = 0; andre@0: PRInt32 flags = 0; andre@0: PRInt32 flag6 = 0; andre@0: andre@0: if (osflags & PR_SYNC) flag6 = FILE_FLAG_WRITE_THROUGH; andre@0: andre@0: if (osflags & PR_RDONLY || osflags & PR_RDWR) andre@0: access |= GENERIC_READ; andre@0: if (osflags & PR_WRONLY || osflags & PR_RDWR) andre@0: access |= GENERIC_WRITE; andre@0: andre@0: if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL ) andre@0: flags = CREATE_NEW; andre@0: else if (osflags & PR_CREATE_FILE) { andre@0: if (osflags & PR_TRUNCATE) andre@0: flags = CREATE_ALWAYS; andre@0: else andre@0: flags = OPEN_ALWAYS; andre@0: } else { andre@0: if (osflags & PR_TRUNCATE) andre@0: flags = TRUNCATE_EXISTING; andre@0: else andre@0: flags = OPEN_EXISTING; andre@0: } andre@0: andre@0: file = CreateFileA(name, andre@0: access, andre@0: FILE_SHARE_READ|FILE_SHARE_WRITE, andre@0: NULL, andre@0: flags, andre@0: flag6, andre@0: NULL); andre@0: if (file == INVALID_HANDLE_VALUE) { andre@0: _PR_MD_MAP_OPEN_ERROR(GetLastError()); andre@0: return -1; andre@0: } andre@0: andre@0: return (PROsfd)file; andre@0: } andre@0: andre@0: PROsfd andre@0: _PR_MD_OPEN_FILE(const char *name, PRIntn osflags, int mode) andre@0: { andre@0: HANDLE file; andre@0: PRInt32 access = 0; andre@0: PRInt32 flags = 0; andre@0: PRInt32 flag6 = 0; andre@0: SECURITY_ATTRIBUTES sa; andre@0: LPSECURITY_ATTRIBUTES lpSA = NULL; andre@0: PSECURITY_DESCRIPTOR pSD = NULL; andre@0: PACL pACL = NULL; andre@0: andre@0: if (osflags & PR_CREATE_FILE) { andre@0: if (_PR_NT_MakeSecurityDescriptorACL(mode, fileAccessTable, andre@0: &pSD, &pACL) == PR_SUCCESS) { andre@0: sa.nLength = sizeof(sa); andre@0: sa.lpSecurityDescriptor = pSD; andre@0: sa.bInheritHandle = FALSE; andre@0: lpSA = &sa; andre@0: } andre@0: } andre@0: andre@0: if (osflags & PR_SYNC) flag6 = FILE_FLAG_WRITE_THROUGH; andre@0: andre@0: if (osflags & PR_RDONLY || osflags & PR_RDWR) andre@0: access |= GENERIC_READ; andre@0: if (osflags & PR_WRONLY || osflags & PR_RDWR) andre@0: access |= GENERIC_WRITE; andre@0: andre@0: if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL ) andre@0: flags = CREATE_NEW; andre@0: else if (osflags & PR_CREATE_FILE) { andre@0: if (osflags & PR_TRUNCATE) andre@0: flags = CREATE_ALWAYS; andre@0: else andre@0: flags = OPEN_ALWAYS; andre@0: } else { andre@0: if (osflags & PR_TRUNCATE) andre@0: flags = TRUNCATE_EXISTING; andre@0: else andre@0: flags = OPEN_EXISTING; andre@0: } andre@0: andre@0: file = CreateFileA(name, andre@0: access, andre@0: FILE_SHARE_READ|FILE_SHARE_WRITE, andre@0: lpSA, andre@0: flags, andre@0: flag6, andre@0: NULL); andre@0: if (lpSA != NULL) { andre@0: _PR_NT_FreeSecurityDescriptorACL(pSD, pACL); andre@0: } andre@0: if (file == INVALID_HANDLE_VALUE) { andre@0: _PR_MD_MAP_OPEN_ERROR(GetLastError()); andre@0: return -1; andre@0: } andre@0: andre@0: return (PROsfd)file; andre@0: } andre@0: andre@0: PRInt32 andre@0: _PR_MD_READ(PRFileDesc *fd, void *buf, PRInt32 len) andre@0: { andre@0: PRUint32 bytes; andre@0: int rv, err; andre@0: andre@0: rv = ReadFile((HANDLE)fd->secret->md.osfd, andre@0: (LPVOID)buf, andre@0: len, andre@0: &bytes, andre@0: NULL); andre@0: andre@0: if (rv == 0) andre@0: { andre@0: err = GetLastError(); andre@0: /* ERROR_HANDLE_EOF can only be returned by async io */ andre@0: PR_ASSERT(err != ERROR_HANDLE_EOF); andre@0: if (err == ERROR_BROKEN_PIPE) andre@0: return 0; andre@0: else { andre@0: _PR_MD_MAP_READ_ERROR(err); andre@0: return -1; andre@0: } andre@0: } andre@0: return bytes; andre@0: } andre@0: andre@0: PRInt32 andre@0: _PR_MD_WRITE(PRFileDesc *fd, const void *buf, PRInt32 len) andre@0: { andre@0: PROsfd f = fd->secret->md.osfd; andre@0: PRInt32 bytes; andre@0: int rv; andre@0: andre@0: rv = WriteFile((HANDLE)f, andre@0: buf, andre@0: len, andre@0: &bytes, andre@0: NULL ); andre@0: andre@0: if (rv == 0) andre@0: { andre@0: _PR_MD_MAP_WRITE_ERROR(GetLastError()); andre@0: return -1; andre@0: } andre@0: return bytes; andre@0: } /* --- end _PR_MD_WRITE() --- */ andre@0: andre@0: PROffset32 andre@0: _PR_MD_LSEEK(PRFileDesc *fd, PROffset32 offset, PRSeekWhence whence) andre@0: { andre@0: DWORD moveMethod; andre@0: PROffset32 rv; andre@0: andre@0: switch (whence) { andre@0: case PR_SEEK_SET: andre@0: moveMethod = FILE_BEGIN; andre@0: break; andre@0: case PR_SEEK_CUR: andre@0: moveMethod = FILE_CURRENT; andre@0: break; andre@0: case PR_SEEK_END: andre@0: moveMethod = FILE_END; andre@0: break; andre@0: default: andre@0: PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); andre@0: return -1; andre@0: } andre@0: andre@0: rv = SetFilePointer((HANDLE)fd->secret->md.osfd, offset, NULL, moveMethod); andre@0: andre@0: /* andre@0: * If the lpDistanceToMoveHigh argument (third argument) is andre@0: * NULL, SetFilePointer returns 0xffffffff on failure. andre@0: */ andre@0: if (-1 == rv) { andre@0: _PR_MD_MAP_LSEEK_ERROR(GetLastError()); andre@0: } andre@0: return rv; andre@0: } andre@0: andre@0: PROffset64 andre@0: _PR_MD_LSEEK64(PRFileDesc *fd, PROffset64 offset, PRSeekWhence whence) andre@0: { andre@0: DWORD moveMethod; andre@0: LARGE_INTEGER li; andre@0: DWORD err; andre@0: andre@0: switch (whence) { andre@0: case PR_SEEK_SET: andre@0: moveMethod = FILE_BEGIN; andre@0: break; andre@0: case PR_SEEK_CUR: andre@0: moveMethod = FILE_CURRENT; andre@0: break; andre@0: case PR_SEEK_END: andre@0: moveMethod = FILE_END; andre@0: break; andre@0: default: andre@0: PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); andre@0: return -1; andre@0: } andre@0: andre@0: li.QuadPart = offset; andre@0: li.LowPart = SetFilePointer((HANDLE)fd->secret->md.osfd, andre@0: li.LowPart, &li.HighPart, moveMethod); andre@0: andre@0: if (0xffffffff == li.LowPart && (err = GetLastError()) != NO_ERROR) { andre@0: _PR_MD_MAP_LSEEK_ERROR(err); andre@0: li.QuadPart = -1; andre@0: } andre@0: return li.QuadPart; andre@0: } andre@0: andre@0: /* andre@0: * This is documented to succeed on read-only files, but Win32's andre@0: * FlushFileBuffers functions fails with "access denied" in such a andre@0: * case. So we only signal an error if the error is *not* "access andre@0: * denied". andre@0: */ andre@0: PRInt32 andre@0: _PR_MD_FSYNC(PRFileDesc *fd) andre@0: { andre@0: /* andre@0: * From the documentation: andre@0: * andre@0: * On Windows NT, the function FlushFileBuffers fails if hFile andre@0: * is a handle to console output. That is because console andre@0: * output is not buffered. The function returns FALSE, and andre@0: * GetLastError returns ERROR_INVALID_HANDLE. andre@0: * andre@0: * On the other hand, on Win95, it returns without error. I cannot andre@0: * assume that 0, 1, and 2 are console, because if someone closes andre@0: * System.out and then opens a file, they might get file descriptor andre@0: * 1. An error on *that* version of 1 should be reported, whereas andre@0: * an error on System.out (which was the original 1) should be andre@0: * ignored. So I use isatty() to ensure that such an error was due andre@0: * to this bogosity, and if it was, I ignore the error. andre@0: */ andre@0: andre@0: BOOL ok = FlushFileBuffers((HANDLE)fd->secret->md.osfd); andre@0: andre@0: if (!ok) { andre@0: DWORD err = GetLastError(); andre@0: if (err != ERROR_ACCESS_DENIED) { // from winerror.h andre@0: _PR_MD_MAP_FSYNC_ERROR(err); andre@0: return -1; andre@0: } andre@0: } andre@0: return 0; andre@0: } andre@0: andre@0: PRInt32 andre@0: _MD_CloseFile(PROsfd osfd) andre@0: { andre@0: PRInt32 rv; andre@0: andre@0: rv = (CloseHandle((HANDLE)osfd))?0:-1; andre@0: if (rv == -1) andre@0: _PR_MD_MAP_CLOSE_ERROR(GetLastError()); andre@0: return rv; andre@0: } andre@0: andre@0: andre@0: /* --- DIR IO ------------------------------------------------------------ */ andre@0: #define GetFileFromDIR(d) (d)->d_entry.cFileName andre@0: #define FileIsHidden(d) ((d)->d_entry.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) andre@0: andre@0: static void FlipSlashes(char *cp, size_t len) andre@0: { andre@0: while (len-- > 0) { andre@0: if (cp[0] == '/') { andre@0: cp[0] = PR_DIRECTORY_SEPARATOR; andre@0: } andre@0: cp = _mbsinc(cp); andre@0: } andre@0: } /* end FlipSlashes() */ andre@0: andre@0: andre@0: /* andre@0: ** andre@0: ** Local implementations of standard Unix RTL functions which are not provided andre@0: ** by the VC RTL. andre@0: ** andre@0: */ andre@0: andre@0: PRInt32 andre@0: _PR_MD_CLOSE_DIR(_MDDir *d) andre@0: { andre@0: if ( d ) { andre@0: if (FindClose(d->d_hdl)) { andre@0: d->magic = (PRUint32)-1; andre@0: return 0; andre@0: } else { andre@0: _PR_MD_MAP_CLOSEDIR_ERROR(GetLastError()); andre@0: return -1; andre@0: } andre@0: } andre@0: PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); andre@0: return -1; andre@0: } andre@0: andre@0: andre@0: PRStatus andre@0: _PR_MD_OPEN_DIR(_MDDir *d, const char *name) andre@0: { andre@0: char filename[ MAX_PATH ]; andre@0: size_t len; andre@0: andre@0: len = strlen(name); andre@0: /* Need 5 bytes for \*.* and the trailing null byte. */ andre@0: if (len + 5 > MAX_PATH) { andre@0: PR_SetError(PR_NAME_TOO_LONG_ERROR, 0); andre@0: return PR_FAILURE; andre@0: } andre@0: strcpy(filename, name); andre@0: andre@0: /* andre@0: * If 'name' ends in a slash or backslash, do not append andre@0: * another backslash. andre@0: */ andre@0: if (IsPrevCharSlash(filename, filename + len)) { andre@0: len--; andre@0: } andre@0: strcpy(&filename[len], "\\*.*"); andre@0: FlipSlashes( filename, strlen(filename) ); andre@0: andre@0: d->d_hdl = FindFirstFileA( filename, &(d->d_entry) ); andre@0: if ( d->d_hdl == INVALID_HANDLE_VALUE ) { andre@0: _PR_MD_MAP_OPENDIR_ERROR(GetLastError()); andre@0: return PR_FAILURE; andre@0: } andre@0: d->firstEntry = PR_TRUE; andre@0: d->magic = _MD_MAGIC_DIR; andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: char * andre@0: _PR_MD_READ_DIR(_MDDir *d, PRIntn flags) andre@0: { andre@0: PRInt32 err; andre@0: BOOL rv; andre@0: char *fileName; andre@0: andre@0: if ( d ) { andre@0: while (1) { andre@0: if (d->firstEntry) { andre@0: d->firstEntry = PR_FALSE; andre@0: rv = 1; andre@0: } else { andre@0: rv = FindNextFileA(d->d_hdl, &(d->d_entry)); andre@0: } andre@0: if (rv == 0) { andre@0: break; andre@0: } andre@0: fileName = GetFileFromDIR(d); andre@0: if ( (flags & PR_SKIP_DOT) && andre@0: (fileName[0] == '.') && (fileName[1] == '\0')) andre@0: continue; andre@0: if ( (flags & PR_SKIP_DOT_DOT) && andre@0: (fileName[0] == '.') && (fileName[1] == '.') && andre@0: (fileName[2] == '\0')) andre@0: continue; andre@0: if ( (flags & PR_SKIP_HIDDEN) && FileIsHidden(d)) andre@0: continue; andre@0: return fileName; andre@0: } andre@0: err = GetLastError(); andre@0: PR_ASSERT(NO_ERROR != err); andre@0: _PR_MD_MAP_READDIR_ERROR(err); andre@0: return NULL; andre@0: } andre@0: PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); andre@0: return NULL; andre@0: } andre@0: andre@0: PRInt32 andre@0: _PR_MD_DELETE(const char *name) andre@0: { andre@0: if (DeleteFileA(name)) { andre@0: return 0; andre@0: } else { andre@0: _PR_MD_MAP_DELETE_ERROR(GetLastError()); andre@0: return -1; andre@0: } andre@0: } andre@0: andre@0: void andre@0: _PR_FileTimeToPRTime(const FILETIME *filetime, PRTime *prtm) andre@0: { andre@0: PR_ASSERT(sizeof(FILETIME) == sizeof(PRTime)); andre@0: CopyMemory(prtm, filetime, sizeof(PRTime)); andre@0: #if defined(__MINGW32__) andre@0: *prtm = (*prtm - _pr_filetime_offset) / 10LL; andre@0: #else andre@0: *prtm = (*prtm - _pr_filetime_offset) / 10i64; andre@0: #endif andre@0: andre@0: #ifdef DEBUG andre@0: /* Doublecheck our calculation. */ andre@0: { andre@0: SYSTEMTIME systime; andre@0: PRExplodedTime etm; andre@0: PRTime cmp; /* for comparison */ andre@0: BOOL rv; andre@0: andre@0: rv = FileTimeToSystemTime(filetime, &systime); andre@0: PR_ASSERT(0 != rv); andre@0: andre@0: /* andre@0: * PR_ImplodeTime ignores wday and yday. andre@0: */ andre@0: etm.tm_usec = systime.wMilliseconds * PR_USEC_PER_MSEC; andre@0: etm.tm_sec = systime.wSecond; andre@0: etm.tm_min = systime.wMinute; andre@0: etm.tm_hour = systime.wHour; andre@0: etm.tm_mday = systime.wDay; andre@0: etm.tm_month = systime.wMonth - 1; andre@0: etm.tm_year = systime.wYear; andre@0: /* andre@0: * It is not well-documented what time zone the FILETIME's andre@0: * are in. WIN32_FIND_DATA is documented to be in UTC (GMT). andre@0: * But BY_HANDLE_FILE_INFORMATION is unclear about this. andre@0: * By our best judgement, we assume that FILETIME is in UTC. andre@0: */ andre@0: etm.tm_params.tp_gmt_offset = 0; andre@0: etm.tm_params.tp_dst_offset = 0; andre@0: cmp = PR_ImplodeTime(&etm); andre@0: andre@0: /* andre@0: * SYSTEMTIME is in milliseconds precision, so we convert PRTime's andre@0: * microseconds to milliseconds before doing the comparison. andre@0: */ andre@0: PR_ASSERT((cmp / PR_USEC_PER_MSEC) == (*prtm / PR_USEC_PER_MSEC)); andre@0: } andre@0: #endif /* DEBUG */ andre@0: } andre@0: andre@0: PRInt32 andre@0: _PR_MD_STAT(const char *fn, struct stat *info) andre@0: { andre@0: PRInt32 rv; andre@0: andre@0: rv = _stat(fn, (struct _stat *)info); andre@0: if (-1 == rv) { andre@0: /* andre@0: * Check for MSVC runtime library _stat() bug. andre@0: * (It's really a bug in FindFirstFile().) andre@0: * If a pathname ends in a backslash or slash, andre@0: * e.g., c:\temp\ or c:/temp/, _stat() will fail. andre@0: * Note: a pathname ending in a slash (e.g., c:/temp/) andre@0: * can be handled by _stat() on NT but not on Win95. andre@0: * andre@0: * We remove the backslash or slash at the end and andre@0: * try again. andre@0: */ andre@0: andre@0: size_t len = strlen(fn); andre@0: if (len > 0 && len <= _MAX_PATH andre@0: && IsPrevCharSlash(fn, fn + len)) { andre@0: char newfn[_MAX_PATH + 1]; andre@0: andre@0: strcpy(newfn, fn); andre@0: newfn[len - 1] = '\0'; andre@0: rv = _stat(newfn, (struct _stat *)info); andre@0: } andre@0: } andre@0: andre@0: if (-1 == rv) { andre@0: _PR_MD_MAP_STAT_ERROR(errno); andre@0: } andre@0: return rv; andre@0: } andre@0: andre@0: #define _PR_IS_SLASH(ch) ((ch) == '/' || (ch) == '\\') andre@0: andre@0: static PRBool andre@0: IsPrevCharSlash(const char *str, const char *current) andre@0: { andre@0: const char *prev; andre@0: andre@0: if (str >= current) andre@0: return PR_FALSE; andre@0: prev = _mbsdec(str, current); andre@0: return (prev == current - 1) && _PR_IS_SLASH(*prev); andre@0: } andre@0: andre@0: /* andre@0: * IsRootDirectory -- andre@0: * andre@0: * Return PR_TRUE if the pathname 'fn' is a valid root directory, andre@0: * else return PR_FALSE. The char buffer pointed to by 'fn' must andre@0: * be writable. During the execution of this function, the contents andre@0: * of the buffer pointed to by 'fn' may be modified, but on return andre@0: * the original contents will be restored. 'buflen' is the size of andre@0: * the buffer pointed to by 'fn'. andre@0: * andre@0: * Root directories come in three formats: andre@0: * 1. / or \, meaning the root directory of the current drive. andre@0: * 2. C:/ or C:\, where C is a drive letter. andre@0: * 3. \\\\ or andre@0: * \\\, meaning the root directory andre@0: * of a UNC (Universal Naming Convention) name. andre@0: */ andre@0: andre@0: static PRBool andre@0: IsRootDirectory(char *fn, size_t buflen) andre@0: { andre@0: char *p; andre@0: PRBool slashAdded = PR_FALSE; andre@0: PRBool rv = PR_FALSE; andre@0: andre@0: if (_PR_IS_SLASH(fn[0]) && fn[1] == '\0') { andre@0: return PR_TRUE; andre@0: } andre@0: andre@0: if (isalpha(fn[0]) && fn[1] == ':' && _PR_IS_SLASH(fn[2]) andre@0: && fn[3] == '\0') { andre@0: rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE; andre@0: return rv; andre@0: } andre@0: andre@0: /* The UNC root directory */ andre@0: andre@0: if (_PR_IS_SLASH(fn[0]) && _PR_IS_SLASH(fn[1])) { andre@0: /* The 'server' part should have at least one character. */ andre@0: p = &fn[2]; andre@0: if (*p == '\0' || _PR_IS_SLASH(*p)) { andre@0: return PR_FALSE; andre@0: } andre@0: andre@0: /* look for the next slash */ andre@0: do { andre@0: p = _mbsinc(p); andre@0: } while (*p != '\0' && !_PR_IS_SLASH(*p)); andre@0: if (*p == '\0') { andre@0: return PR_FALSE; andre@0: } andre@0: andre@0: /* The 'share' part should have at least one character. */ andre@0: p++; andre@0: if (*p == '\0' || _PR_IS_SLASH(*p)) { andre@0: return PR_FALSE; andre@0: } andre@0: andre@0: /* look for the final slash */ andre@0: do { andre@0: p = _mbsinc(p); andre@0: } while (*p != '\0' && !_PR_IS_SLASH(*p)); andre@0: if (_PR_IS_SLASH(*p) && p[1] != '\0') { andre@0: return PR_FALSE; andre@0: } andre@0: if (*p == '\0') { andre@0: /* andre@0: * GetDriveType() doesn't work correctly if the andre@0: * path is of the form \\server\share, so we add andre@0: * a final slash temporarily. andre@0: */ andre@0: if ((p + 1) < (fn + buflen)) { andre@0: *p++ = '\\'; andre@0: *p = '\0'; andre@0: slashAdded = PR_TRUE; andre@0: } else { andre@0: return PR_FALSE; /* name too long */ andre@0: } andre@0: } andre@0: rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE; andre@0: /* restore the 'fn' buffer */ andre@0: if (slashAdded) { andre@0: *--p = '\0'; andre@0: } andre@0: } andre@0: return rv; andre@0: } andre@0: andre@0: PRInt32 andre@0: _PR_MD_GETFILEINFO64(const char *fn, PRFileInfo64 *info) andre@0: { andre@0: WIN32_FILE_ATTRIBUTE_DATA findFileData; andre@0: BOOL rv; andre@0: andre@0: if (NULL == fn || '\0' == *fn) { andre@0: PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); andre@0: return -1; andre@0: } andre@0: andre@0: rv = GetFileAttributesEx(fn, GetFileExInfoStandard, &findFileData); andre@0: if (!rv) { andre@0: _PR_MD_MAP_OPENDIR_ERROR(GetLastError()); andre@0: return -1; andre@0: } andre@0: andre@0: if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { andre@0: info->type = PR_FILE_DIRECTORY; andre@0: } else { andre@0: info->type = PR_FILE_FILE; andre@0: } andre@0: andre@0: info->size = findFileData.nFileSizeHigh; andre@0: info->size = (info->size << 32) + findFileData.nFileSizeLow; andre@0: andre@0: _PR_FileTimeToPRTime(&findFileData.ftLastWriteTime, &info->modifyTime); andre@0: andre@0: if (0 == findFileData.ftCreationTime.dwLowDateTime && andre@0: 0 == findFileData.ftCreationTime.dwHighDateTime) { andre@0: info->creationTime = info->modifyTime; andre@0: } else { andre@0: _PR_FileTimeToPRTime(&findFileData.ftCreationTime, andre@0: &info->creationTime); andre@0: } andre@0: andre@0: return 0; andre@0: } andre@0: andre@0: PRInt32 andre@0: _PR_MD_GETFILEINFO(const char *fn, PRFileInfo *info) andre@0: { andre@0: PRFileInfo64 info64; andre@0: PRInt32 rv = _PR_MD_GETFILEINFO64(fn, &info64); andre@0: if (0 == rv) andre@0: { andre@0: info->type = info64.type; andre@0: info->size = (PRUint32) info64.size; andre@0: info->modifyTime = info64.modifyTime; andre@0: info->creationTime = info64.creationTime; andre@0: } andre@0: return rv; andre@0: } andre@0: andre@0: PRInt32 andre@0: _PR_MD_GETOPENFILEINFO64(const PRFileDesc *fd, PRFileInfo64 *info) andre@0: { andre@0: int rv; andre@0: andre@0: BY_HANDLE_FILE_INFORMATION hinfo; andre@0: andre@0: rv = GetFileInformationByHandle((HANDLE)fd->secret->md.osfd, &hinfo); andre@0: if (rv == FALSE) { andre@0: _PR_MD_MAP_FSTAT_ERROR(GetLastError()); andre@0: return -1; andre@0: } andre@0: andre@0: if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) andre@0: info->type = PR_FILE_DIRECTORY; andre@0: else andre@0: info->type = PR_FILE_FILE; andre@0: andre@0: info->size = hinfo.nFileSizeHigh; andre@0: info->size = (info->size << 32) + hinfo.nFileSizeLow; andre@0: andre@0: _PR_FileTimeToPRTime(&hinfo.ftLastWriteTime, &(info->modifyTime) ); andre@0: _PR_FileTimeToPRTime(&hinfo.ftCreationTime, &(info->creationTime) ); andre@0: andre@0: return 0; andre@0: } andre@0: andre@0: PRInt32 andre@0: _PR_MD_GETOPENFILEINFO(const PRFileDesc *fd, PRFileInfo *info) andre@0: { andre@0: PRFileInfo64 info64; andre@0: int rv = _PR_MD_GETOPENFILEINFO64(fd, &info64); andre@0: if (0 == rv) andre@0: { andre@0: info->type = info64.type; andre@0: info->modifyTime = info64.modifyTime; andre@0: info->creationTime = info64.creationTime; andre@0: LL_L2I(info->size, info64.size); andre@0: } andre@0: return rv; andre@0: } andre@0: andre@0: PRStatus andre@0: _PR_MD_SET_FD_INHERITABLE(PRFileDesc *fd, PRBool inheritable) andre@0: { andre@0: BOOL rv; andre@0: andre@0: /* andre@0: * The SetHandleInformation function fails with the andre@0: * ERROR_CALL_NOT_IMPLEMENTED error on Win95. andre@0: */ andre@0: rv = SetHandleInformation( andre@0: (HANDLE)fd->secret->md.osfd, andre@0: HANDLE_FLAG_INHERIT, andre@0: inheritable ? HANDLE_FLAG_INHERIT : 0); andre@0: if (0 == rv) { andre@0: _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); andre@0: return PR_FAILURE; andre@0: } andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: void andre@0: _PR_MD_INIT_FD_INHERITABLE(PRFileDesc *fd, PRBool imported) andre@0: { andre@0: if (imported) { andre@0: fd->secret->inheritable = _PR_TRI_UNKNOWN; andre@0: } else { andre@0: fd->secret->inheritable = _PR_TRI_FALSE; andre@0: } andre@0: } andre@0: andre@0: void andre@0: _PR_MD_QUERY_FD_INHERITABLE(PRFileDesc *fd) andre@0: { andre@0: DWORD flags; andre@0: andre@0: PR_ASSERT(_PR_TRI_UNKNOWN == fd->secret->inheritable); andre@0: if (GetHandleInformation((HANDLE)fd->secret->md.osfd, &flags)) { andre@0: if (flags & HANDLE_FLAG_INHERIT) { andre@0: fd->secret->inheritable = _PR_TRI_TRUE; andre@0: } else { andre@0: fd->secret->inheritable = _PR_TRI_FALSE; andre@0: } andre@0: } andre@0: } andre@0: andre@0: PRInt32 andre@0: _PR_MD_RENAME(const char *from, const char *to) andre@0: { andre@0: /* Does this work with dot-relative pathnames? */ andre@0: if (MoveFileA(from, to)) { andre@0: return 0; andre@0: } else { andre@0: _PR_MD_MAP_RENAME_ERROR(GetLastError()); andre@0: return -1; andre@0: } andre@0: } andre@0: andre@0: PRInt32 andre@0: _PR_MD_ACCESS(const char *name, PRAccessHow how) andre@0: { andre@0: PRInt32 rv; andre@0: switch (how) { andre@0: case PR_ACCESS_WRITE_OK: andre@0: rv = _access(name, 02); andre@0: break; andre@0: case PR_ACCESS_READ_OK: andre@0: rv = _access(name, 04); andre@0: break; andre@0: case PR_ACCESS_EXISTS: andre@0: return _access(name, 00); andre@0: break; andre@0: default: andre@0: PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); andre@0: return -1; andre@0: } andre@0: if (rv < 0) andre@0: _PR_MD_MAP_ACCESS_ERROR(errno); andre@0: return rv; andre@0: } andre@0: andre@0: PRInt32 andre@0: _PR_MD_MKDIR(const char *name, PRIntn mode) andre@0: { andre@0: /* XXXMB - how to translate the "mode"??? */ andre@0: if (CreateDirectoryA(name, NULL)) { andre@0: return 0; andre@0: } else { andre@0: _PR_MD_MAP_MKDIR_ERROR(GetLastError()); andre@0: return -1; andre@0: } andre@0: } andre@0: andre@0: PRInt32 andre@0: _PR_MD_MAKE_DIR(const char *name, PRIntn mode) andre@0: { andre@0: BOOL rv; andre@0: SECURITY_ATTRIBUTES sa; andre@0: LPSECURITY_ATTRIBUTES lpSA = NULL; andre@0: PSECURITY_DESCRIPTOR pSD = NULL; andre@0: PACL pACL = NULL; andre@0: andre@0: if (_PR_NT_MakeSecurityDescriptorACL(mode, dirAccessTable, andre@0: &pSD, &pACL) == PR_SUCCESS) { andre@0: sa.nLength = sizeof(sa); andre@0: sa.lpSecurityDescriptor = pSD; andre@0: sa.bInheritHandle = FALSE; andre@0: lpSA = &sa; andre@0: } andre@0: rv = CreateDirectoryA(name, lpSA); andre@0: if (lpSA != NULL) { andre@0: _PR_NT_FreeSecurityDescriptorACL(pSD, pACL); andre@0: } andre@0: if (rv) { andre@0: return 0; andre@0: } else { andre@0: _PR_MD_MAP_MKDIR_ERROR(GetLastError()); andre@0: return -1; andre@0: } andre@0: } andre@0: andre@0: PRInt32 andre@0: _PR_MD_RMDIR(const char *name) andre@0: { andre@0: if (RemoveDirectoryA(name)) { andre@0: return 0; andre@0: } else { andre@0: _PR_MD_MAP_RMDIR_ERROR(GetLastError()); andre@0: return -1; andre@0: } andre@0: } andre@0: andre@0: PRStatus andre@0: _PR_MD_LOCKFILE(PROsfd f) andre@0: { andre@0: PRStatus rc = PR_SUCCESS; andre@0: DWORD rv; andre@0: andre@0: rv = LockFile( (HANDLE)f, andre@0: 0l, 0l, andre@0: 0x0l, 0xffffffffl ); andre@0: if ( rv == 0 ) { andre@0: DWORD rc = GetLastError(); andre@0: PR_LOG( _pr_io_lm, PR_LOG_ERROR, andre@0: ("_PR_MD_LOCKFILE() failed. Error: %d", rc )); andre@0: rc = PR_FAILURE; andre@0: } andre@0: andre@0: return rc; andre@0: } /* end _PR_MD_LOCKFILE() */ andre@0: andre@0: PRStatus andre@0: _PR_MD_TLOCKFILE(PROsfd f) andre@0: { andre@0: PR_SetError( PR_NOT_IMPLEMENTED_ERROR, 0 ); andre@0: return PR_FAILURE; andre@0: } /* end _PR_MD_TLOCKFILE() */ andre@0: andre@0: andre@0: PRStatus andre@0: _PR_MD_UNLOCKFILE(PROsfd f) andre@0: { andre@0: PRInt32 rv; andre@0: andre@0: rv = UnlockFile( (HANDLE) f, andre@0: 0l, 0l, andre@0: 0x0l, 0xffffffffl ); andre@0: andre@0: if ( rv ) andre@0: { andre@0: return PR_SUCCESS; andre@0: } andre@0: else andre@0: { andre@0: _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); andre@0: return PR_FAILURE; andre@0: } andre@0: } /* end _PR_MD_UNLOCKFILE() */ andre@0: andre@0: PRInt32 andre@0: _PR_MD_PIPEAVAILABLE(PRFileDesc *fd) andre@0: { andre@0: if (NULL == fd) andre@0: PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); andre@0: else andre@0: PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); andre@0: return -1; andre@0: } andre@0: andre@0: #ifdef MOZ_UNICODE andre@0: andre@0: typedef HANDLE (WINAPI *CreateFileWFn) (LPCWSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE); andre@0: static CreateFileWFn createFileW = CreateFileW; andre@0: typedef HANDLE (WINAPI *FindFirstFileWFn) (LPCWSTR, LPWIN32_FIND_DATAW); andre@0: static FindFirstFileWFn findFirstFileW = FindFirstFileW; andre@0: typedef BOOL (WINAPI *FindNextFileWFn) (HANDLE, LPWIN32_FIND_DATAW); andre@0: static FindNextFileWFn findNextFileW = FindNextFileW; andre@0: typedef DWORD (WINAPI *GetFullPathNameWFn) (LPCWSTR, DWORD, LPWSTR, LPWSTR *); andre@0: static GetFullPathNameWFn getFullPathNameW = GetFullPathNameW; andre@0: typedef UINT (WINAPI *GetDriveTypeWFn) (LPCWSTR); andre@0: static GetDriveTypeWFn getDriveTypeW = GetDriveTypeW; andre@0: andre@0: #endif /* MOZ_UNICODE */ andre@0: andre@0: #ifdef MOZ_UNICODE andre@0: andre@0: /* ================ UTF16 Interfaces ================================ */ andre@0: static void FlipSlashesW(PRUnichar *cp, size_t len) andre@0: { andre@0: while (len-- > 0) { andre@0: if (cp[0] == L'/') { andre@0: cp[0] = L'\\'; andre@0: } andre@0: cp++; andre@0: } andre@0: } /* end FlipSlashesW() */ andre@0: andre@0: PROsfd andre@0: _PR_MD_OPEN_FILE_UTF16(const PRUnichar *name, PRIntn osflags, int mode) andre@0: { andre@0: HANDLE file; andre@0: PRInt32 access = 0; andre@0: PRInt32 flags = 0; andre@0: PRInt32 flag6 = 0; andre@0: SECURITY_ATTRIBUTES sa; andre@0: LPSECURITY_ATTRIBUTES lpSA = NULL; andre@0: PSECURITY_DESCRIPTOR pSD = NULL; andre@0: PACL pACL = NULL; andre@0: andre@0: if (osflags & PR_CREATE_FILE) { andre@0: if (_PR_NT_MakeSecurityDescriptorACL(mode, fileAccessTable, andre@0: &pSD, &pACL) == PR_SUCCESS) { andre@0: sa.nLength = sizeof(sa); andre@0: sa.lpSecurityDescriptor = pSD; andre@0: sa.bInheritHandle = FALSE; andre@0: lpSA = &sa; andre@0: } andre@0: } andre@0: andre@0: if (osflags & PR_SYNC) flag6 = FILE_FLAG_WRITE_THROUGH; andre@0: andre@0: if (osflags & PR_RDONLY || osflags & PR_RDWR) andre@0: access |= GENERIC_READ; andre@0: if (osflags & PR_WRONLY || osflags & PR_RDWR) andre@0: access |= GENERIC_WRITE; andre@0: andre@0: if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL ) andre@0: flags = CREATE_NEW; andre@0: else if (osflags & PR_CREATE_FILE) { andre@0: if (osflags & PR_TRUNCATE) andre@0: flags = CREATE_ALWAYS; andre@0: else andre@0: flags = OPEN_ALWAYS; andre@0: } else { andre@0: if (osflags & PR_TRUNCATE) andre@0: flags = TRUNCATE_EXISTING; andre@0: else andre@0: flags = OPEN_EXISTING; andre@0: } andre@0: andre@0: file = createFileW(name, andre@0: access, andre@0: FILE_SHARE_READ|FILE_SHARE_WRITE, andre@0: lpSA, andre@0: flags, andre@0: flag6, andre@0: NULL); andre@0: if (lpSA != NULL) { andre@0: _PR_NT_FreeSecurityDescriptorACL(pSD, pACL); andre@0: } andre@0: if (file == INVALID_HANDLE_VALUE) { andre@0: _PR_MD_MAP_OPEN_ERROR(GetLastError()); andre@0: return -1; andre@0: } andre@0: andre@0: return (PROsfd)file; andre@0: } andre@0: andre@0: PRStatus andre@0: _PR_MD_OPEN_DIR_UTF16(_MDDirUTF16 *d, const PRUnichar *name) andre@0: { andre@0: PRUnichar filename[ MAX_PATH ]; andre@0: int len; andre@0: andre@0: len = wcslen(name); andre@0: /* Need 5 bytes for \*.* and the trailing null byte. */ andre@0: if (len + 5 > MAX_PATH) { andre@0: PR_SetError(PR_NAME_TOO_LONG_ERROR, 0); andre@0: return PR_FAILURE; andre@0: } andre@0: wcscpy(filename, name); andre@0: andre@0: /* andre@0: * If 'name' ends in a slash or backslash, do not append andre@0: * another backslash. andre@0: */ andre@0: if (filename[len - 1] == L'/' || filename[len - 1] == L'\\') { andre@0: len--; andre@0: } andre@0: wcscpy(&filename[len], L"\\*.*"); andre@0: FlipSlashesW( filename, wcslen(filename) ); andre@0: andre@0: d->d_hdl = findFirstFileW( filename, &(d->d_entry) ); andre@0: if ( d->d_hdl == INVALID_HANDLE_VALUE ) { andre@0: _PR_MD_MAP_OPENDIR_ERROR(GetLastError()); andre@0: return PR_FAILURE; andre@0: } andre@0: d->firstEntry = PR_TRUE; andre@0: d->magic = _MD_MAGIC_DIR; andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: PRUnichar * andre@0: _PR_MD_READ_DIR_UTF16(_MDDirUTF16 *d, PRIntn flags) andre@0: { andre@0: PRInt32 err; andre@0: BOOL rv; andre@0: PRUnichar *fileName; andre@0: andre@0: if ( d ) { andre@0: while (1) { andre@0: if (d->firstEntry) { andre@0: d->firstEntry = PR_FALSE; andre@0: rv = 1; andre@0: } else { andre@0: rv = findNextFileW(d->d_hdl, &(d->d_entry)); andre@0: } andre@0: if (rv == 0) { andre@0: break; andre@0: } andre@0: fileName = GetFileFromDIR(d); andre@0: if ( (flags & PR_SKIP_DOT) && andre@0: (fileName[0] == L'.') && (fileName[1] == L'\0')) andre@0: continue; andre@0: if ( (flags & PR_SKIP_DOT_DOT) && andre@0: (fileName[0] == L'.') && (fileName[1] == L'.') && andre@0: (fileName[2] == L'\0')) andre@0: continue; andre@0: if ( (flags & PR_SKIP_HIDDEN) && FileIsHidden(d)) andre@0: continue; andre@0: return fileName; andre@0: } andre@0: err = GetLastError(); andre@0: PR_ASSERT(NO_ERROR != err); andre@0: _PR_MD_MAP_READDIR_ERROR(err); andre@0: return NULL; andre@0: } andre@0: PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); andre@0: return NULL; andre@0: } andre@0: andre@0: PRInt32 andre@0: _PR_MD_CLOSE_DIR_UTF16(_MDDirUTF16 *d) andre@0: { andre@0: if ( d ) { andre@0: if (FindClose(d->d_hdl)) { andre@0: d->magic = (PRUint32)-1; andre@0: return 0; andre@0: } else { andre@0: _PR_MD_MAP_CLOSEDIR_ERROR(GetLastError()); andre@0: return -1; andre@0: } andre@0: } andre@0: PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); andre@0: return -1; andre@0: } andre@0: andre@0: #define _PR_IS_W_SLASH(ch) ((ch) == L'/' || (ch) == L'\\') andre@0: andre@0: /* andre@0: * IsRootDirectoryW -- andre@0: * andre@0: * Return PR_TRUE if the pathname 'fn' is a valid root directory, andre@0: * else return PR_FALSE. The PRUnichar buffer pointed to by 'fn' must andre@0: * be writable. During the execution of this function, the contents andre@0: * of the buffer pointed to by 'fn' may be modified, but on return andre@0: * the original contents will be restored. 'buflen' is the size of andre@0: * the buffer pointed to by 'fn', in PRUnichars. andre@0: * andre@0: * Root directories come in three formats: andre@0: * 1. / or \, meaning the root directory of the current drive. andre@0: * 2. C:/ or C:\, where C is a drive letter. andre@0: * 3. \\\\ or andre@0: * \\\, meaning the root directory andre@0: * of a UNC (Universal Naming Convention) name. andre@0: */ andre@0: andre@0: static PRBool andre@0: IsRootDirectoryW(PRUnichar *fn, size_t buflen) andre@0: { andre@0: PRUnichar *p; andre@0: PRBool slashAdded = PR_FALSE; andre@0: PRBool rv = PR_FALSE; andre@0: andre@0: if (_PR_IS_W_SLASH(fn[0]) && fn[1] == L'\0') { andre@0: return PR_TRUE; andre@0: } andre@0: andre@0: if (iswalpha(fn[0]) && fn[1] == L':' && _PR_IS_W_SLASH(fn[2]) andre@0: && fn[3] == L'\0') { andre@0: rv = getDriveTypeW(fn) > 1 ? PR_TRUE : PR_FALSE; andre@0: return rv; andre@0: } andre@0: andre@0: /* The UNC root directory */ andre@0: andre@0: if (_PR_IS_W_SLASH(fn[0]) && _PR_IS_W_SLASH(fn[1])) { andre@0: /* The 'server' part should have at least one character. */ andre@0: p = &fn[2]; andre@0: if (*p == L'\0' || _PR_IS_W_SLASH(*p)) { andre@0: return PR_FALSE; andre@0: } andre@0: andre@0: /* look for the next slash */ andre@0: do { andre@0: p++; andre@0: } while (*p != L'\0' && !_PR_IS_W_SLASH(*p)); andre@0: if (*p == L'\0') { andre@0: return PR_FALSE; andre@0: } andre@0: andre@0: /* The 'share' part should have at least one character. */ andre@0: p++; andre@0: if (*p == L'\0' || _PR_IS_W_SLASH(*p)) { andre@0: return PR_FALSE; andre@0: } andre@0: andre@0: /* look for the final slash */ andre@0: do { andre@0: p++; andre@0: } while (*p != L'\0' && !_PR_IS_W_SLASH(*p)); andre@0: if (_PR_IS_W_SLASH(*p) && p[1] != L'\0') { andre@0: return PR_FALSE; andre@0: } andre@0: if (*p == L'\0') { andre@0: /* andre@0: * GetDriveType() doesn't work correctly if the andre@0: * path is of the form \\server\share, so we add andre@0: * a final slash temporarily. andre@0: */ andre@0: if ((p + 1) < (fn + buflen)) { andre@0: *p++ = L'\\'; andre@0: *p = L'\0'; andre@0: slashAdded = PR_TRUE; andre@0: } else { andre@0: return PR_FALSE; /* name too long */ andre@0: } andre@0: } andre@0: rv = getDriveTypeW(fn) > 1 ? PR_TRUE : PR_FALSE; andre@0: /* restore the 'fn' buffer */ andre@0: if (slashAdded) { andre@0: *--p = L'\0'; andre@0: } andre@0: } andre@0: return rv; andre@0: } andre@0: andre@0: PRInt32 andre@0: _PR_MD_GETFILEINFO64_UTF16(const PRUnichar *fn, PRFileInfo64 *info) andre@0: { andre@0: HANDLE hFindFile; andre@0: WIN32_FIND_DATAW findFileData; andre@0: PRUnichar pathbuf[MAX_PATH + 1]; andre@0: andre@0: if (NULL == fn || L'\0' == *fn) { andre@0: PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); andre@0: return -1; andre@0: } andre@0: andre@0: /* andre@0: * FindFirstFile() expands wildcard characters. So andre@0: * we make sure the pathname contains no wildcard. andre@0: */ andre@0: if (NULL != wcspbrk(fn, L"?*")) { andre@0: PR_SetError(PR_FILE_NOT_FOUND_ERROR, 0); andre@0: return -1; andre@0: } andre@0: andre@0: hFindFile = findFirstFileW(fn, &findFileData); andre@0: if (INVALID_HANDLE_VALUE == hFindFile) { andre@0: DWORD len; andre@0: PRUnichar *filePart; andre@0: andre@0: /* andre@0: * FindFirstFile() does not work correctly on root directories. andre@0: * It also doesn't work correctly on a pathname that ends in a andre@0: * slash. So we first check to see if the pathname specifies a andre@0: * root directory. If not, and if the pathname ends in a slash, andre@0: * we remove the final slash and try again. andre@0: */ andre@0: andre@0: /* andre@0: * If the pathname does not contain ., \, and /, it cannot be andre@0: * a root directory or a pathname that ends in a slash. andre@0: */ andre@0: if (NULL == wcspbrk(fn, L".\\/")) { andre@0: _PR_MD_MAP_OPENDIR_ERROR(GetLastError()); andre@0: return -1; andre@0: } andre@0: len = getFullPathNameW(fn, sizeof(pathbuf)/sizeof(pathbuf[0]), pathbuf, andre@0: &filePart); andre@0: if (0 == len) { andre@0: _PR_MD_MAP_OPENDIR_ERROR(GetLastError()); andre@0: return -1; andre@0: } andre@0: if (len > sizeof(pathbuf)/sizeof(pathbuf[0])) { andre@0: PR_SetError(PR_NAME_TOO_LONG_ERROR, 0); andre@0: return -1; andre@0: } andre@0: if (IsRootDirectoryW(pathbuf, sizeof(pathbuf)/sizeof(pathbuf[0]))) { andre@0: info->type = PR_FILE_DIRECTORY; andre@0: info->size = 0; andre@0: /* andre@0: * These timestamps don't make sense for root directories. andre@0: */ andre@0: info->modifyTime = 0; andre@0: info->creationTime = 0; andre@0: return 0; andre@0: } andre@0: if (!_PR_IS_W_SLASH(pathbuf[len - 1])) { andre@0: _PR_MD_MAP_OPENDIR_ERROR(GetLastError()); andre@0: return -1; andre@0: } else { andre@0: pathbuf[len - 1] = L'\0'; andre@0: hFindFile = findFirstFileW(pathbuf, &findFileData); andre@0: if (INVALID_HANDLE_VALUE == hFindFile) { andre@0: _PR_MD_MAP_OPENDIR_ERROR(GetLastError()); andre@0: return -1; andre@0: } andre@0: } andre@0: } andre@0: andre@0: FindClose(hFindFile); andre@0: andre@0: if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { andre@0: info->type = PR_FILE_DIRECTORY; andre@0: } else { andre@0: info->type = PR_FILE_FILE; andre@0: } andre@0: andre@0: info->size = findFileData.nFileSizeHigh; andre@0: info->size = (info->size << 32) + findFileData.nFileSizeLow; andre@0: andre@0: _PR_FileTimeToPRTime(&findFileData.ftLastWriteTime, &info->modifyTime); andre@0: andre@0: if (0 == findFileData.ftCreationTime.dwLowDateTime && andre@0: 0 == findFileData.ftCreationTime.dwHighDateTime) { andre@0: info->creationTime = info->modifyTime; andre@0: } else { andre@0: _PR_FileTimeToPRTime(&findFileData.ftCreationTime, andre@0: &info->creationTime); andre@0: } andre@0: andre@0: return 0; andre@0: } andre@0: /* ================ end of UTF16 Interfaces ================================ */ andre@0: #endif /* MOZ_UNICODE */