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