| /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
| /* ***** BEGIN LICENSE BLOCK ***** |
| * Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
| * |
| * The contents of this file are subject to the Mozilla Public License Version |
| * 1.1 (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * http://www.mozilla.org/MPL/ |
| * |
| * Software distributed under the License is distributed on an "AS IS" basis, |
| * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
| * for the specific language governing rights and limitations under the |
| * License. |
| * |
| * The Original Code is the Netscape Portable Runtime (NSPR). |
| * |
| * The Initial Developer of the Original Code is |
| * Netscape Communications Corporation. |
| * Portions created by the Initial Developer are Copyright (C) 1998-2000 |
| * the Initial Developer. All Rights Reserved. |
| * |
| * Contributor(s): |
| * Masayuki Nakano <masayuki@d-toybox.com> |
| * |
| * Alternatively, the contents of this file may be used under the terms of |
| * either the GNU General Public License Version 2 or later (the "GPL"), or |
| * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), |
| * in which case the provisions of the GPL or the LGPL are applicable instead |
| * of those above. If you wish to allow use of your version of this file only |
| * under the terms of either the GPL or the LGPL, and not to allow others to |
| * use your version of this file under the terms of the MPL, indicate your |
| * decision by deleting the provisions above and replace them with the notice |
| * and other provisions required by the GPL or the LGPL. If you do not delete |
| * the provisions above, a recipient may use your version of this file under |
| * the terms of any one of the MPL, the GPL or the LGPL. |
| * |
| * ***** END LICENSE BLOCK ***** */ |
| |
| /* Windows 95 IO module |
| * |
| * Assumes synchronous I/O. |
| * |
| */ |
| |
| #include "primpl.h" |
| #include <direct.h> |
| #include <mbstring.h> |
| #ifdef MOZ_UNICODE |
| #include <wchar.h> |
| #endif /* MOZ_UNICODE */ |
| |
| #ifdef WINCE |
| |
| static HANDLE CreateFileA(LPCSTR lpFileName, |
| DWORD dwDesiredAccess, |
| DWORD dwShareMode, |
| LPSECURITY_ATTRIBUTES lpSecurityAttributes, |
| DWORD dwCreationDisposition, |
| DWORD dwFlagsAndAttributes, |
| HANDLE hTemplateFile) |
| { |
| PRUnichar wFileName[MAX_PATH]; |
| MultiByteToWideChar(CP_ACP, 0, lpFileName, -1, wFileName, MAX_PATH); |
| return CreateFileW(wFileName, dwDesiredAccess, dwShareMode, |
| lpSecurityAttributes, dwCreationDisposition, |
| dwFlagsAndAttributes, hTemplateFile); |
| } |
| |
| /* |
| * We seem to call FindFirstFileA and FindNextFileA just to get |
| * the file names in a directory listing. If so, we could define |
| * a custom WIN32_FIND_DATAA structure with just the cFileName |
| * member, and the CopyFindFileDataW2A function could just |
| * copy/convert the cFileName member. |
| */ |
| static void CopyFindFileDataW2A(LPWIN32_FIND_DATAW from, |
| LPWIN32_FIND_DATAA to) |
| { |
| /* |
| * WIN32_FIND_DATAA and WIN32_FIND_DATAW are slightly different. |
| * The dwReserved0, dwReserved1, and cAlternateFileName members |
| * exist only in WIN32_FIND_DATAA. The dwOID member exists only |
| * in WIN32_FIND_DATAW. |
| */ |
| to->dwFileAttributes = from->dwFileAttributes; |
| to->ftCreationTime = from->ftCreationTime; |
| to->ftLastAccessTime = from->ftLastAccessTime; |
| to->ftLastWriteTime = from->ftLastWriteTime; |
| to->nFileSizeHigh = from->nFileSizeHigh; |
| to->nFileSizeLow = from->nFileSizeLow; |
| to->dwReserved0 = 0; |
| to->dwReserved1 = 0; |
| WideCharToMultiByte(CP_ACP, 0, from->cFileName, -1, |
| to->cFileName, MAX_PATH, NULL, NULL); |
| to->cAlternateFileName[0] = '\0'; |
| } |
| |
| static HANDLE FindFirstFileA(LPCSTR lpFileName, |
| LPWIN32_FIND_DATAA lpFindFileData) |
| { |
| PRUnichar wFileName[MAX_PATH]; |
| HANDLE hFindFile; |
| WIN32_FIND_DATAW wFindFileData; |
| |
| MultiByteToWideChar(CP_ACP, 0, lpFileName, -1, wFileName, MAX_PATH); |
| hFindFile = FindFirstFileW(wFileName, &wFindFileData); |
| if (hFindFile != INVALID_HANDLE_VALUE) { |
| CopyFindFileDataW2A(&wFindFileData, lpFindFileData); |
| } |
| return hFindFile; |
| } |
| |
| static BOOL FindNextFileA(HANDLE hFindFile, |
| LPWIN32_FIND_DATAA lpFindFileData) |
| { |
| WIN32_FIND_DATAW wFindFileData; |
| BOOL rv; |
| |
| rv = FindNextFileW(hFindFile, &wFindFileData); |
| if (rv) { |
| CopyFindFileDataW2A(&wFindFileData, lpFindFileData); |
| } |
| return rv; |
| } |
| |
| static BOOL GetFileAttributesExA(LPCSTR lpFileName, |
| GET_FILEEX_INFO_LEVELS fInfoLevelId, |
| LPVOID lpFileInformation) |
| { |
| PRUnichar wFileName[MAX_PATH]; |
| MultiByteToWideChar(CP_ACP, 0, lpFileName, -1, wFileName, MAX_PATH); |
| return GetFileAttributesExW(wFileName, fInfoLevelId, lpFileInformation); |
| } |
| |
| static BOOL DeleteFileA(LPCSTR lpFileName) |
| { |
| PRUnichar wFileName[MAX_PATH]; |
| MultiByteToWideChar(CP_ACP, 0, lpFileName, -1, wFileName, MAX_PATH); |
| return DeleteFileW(wFileName); |
| } |
| |
| static BOOL MoveFileA(LPCSTR from, LPCSTR to) |
| { |
| PRUnichar wFrom[MAX_PATH]; |
| PRUnichar wTo[MAX_PATH]; |
| MultiByteToWideChar(CP_ACP, 0, from, -1, wFrom, MAX_PATH); |
| MultiByteToWideChar(CP_ACP, 0, to, -1, wTo, MAX_PATH); |
| return MoveFileW(wFrom, wTo); |
| } |
| |
| static BOOL CreateDirectoryA(LPCSTR lpPathName, |
| LPSECURITY_ATTRIBUTES lpSecurityAttributes) |
| { |
| PRUnichar wPathName[MAX_PATH]; |
| MultiByteToWideChar(CP_ACP, 0, lpPathName, -1, wPathName, MAX_PATH); |
| return CreateDirectoryW(wPathName, lpSecurityAttributes); |
| } |
| |
| static BOOL RemoveDirectoryA(LPCSTR lpPathName) |
| { |
| PRUnichar wPathName[MAX_PATH]; |
| MultiByteToWideChar(CP_ACP, 0, lpPathName, -1, wPathName, MAX_PATH); |
| return RemoveDirectoryW(wPathName); |
| } |
| |
| static long GetDriveType(const char *lpRootPathName) |
| { |
| PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); |
| return 0; // The drive type cannot be determined. |
| } |
| |
| static DWORD GetFullPathName(const char *lpFileName, |
| DWORD nBufferLength, |
| const char *lpBuffer, |
| const char **lpFilePart) |
| { |
| // needs work dft |
| DWORD len = strlen(lpFileName); |
| if (len > nBufferLength) |
| return len; |
| |
| strncpy((char *)lpBuffer, lpFileName, len); |
| ((char *)lpBuffer)[len] = '\0'; |
| |
| if (lpFilePart) { |
| char *sep = strrchr(lpBuffer, '\\'); |
| if (sep) { |
| sep++; // pass the seperator |
| *lpFilePart = sep; |
| } else { |
| *lpFilePart = lpBuffer; |
| } |
| } |
| return len; |
| } |
| |
| static BOOL LockFile(HANDLE hFile, |
| DWORD dwFileOffsetLow, |
| DWORD dwFileOffsetHigh, |
| DWORD nNumberOfBytesToLockLow, |
| DWORD nNumberOfBytesToLockHigh) |
| { |
| OVERLAPPED overlapped = {0}; |
| overlapped.Offset = dwFileOffsetLow; |
| overlapped.OffsetHigh = dwFileOffsetHigh; |
| return LockFileEx(hFile, |
| LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, |
| 0, // reserved |
| nNumberOfBytesToLockLow, |
| nNumberOfBytesToLockHigh, &overlapped); |
| } |
| |
| static BOOL UnlockFile(HANDLE hFile, |
| DWORD dwFileOffsetLow, |
| DWORD dwFileOffsetHigh, |
| DWORD nNumberOfBytesToUnlockLow, |
| DWORD nNumberOfBytesToUnlockHigh) |
| { |
| OVERLAPPED overlapped = {0}; |
| overlapped.Offset = dwFileOffsetLow; |
| overlapped.OffsetHigh = dwFileOffsetHigh; |
| return UnlockFileEx(hFile, |
| 0, // reserved |
| nNumberOfBytesToUnlockLow, |
| nNumberOfBytesToUnlockHigh, &overlapped); |
| } |
| |
| static unsigned char *_mbsdec(const unsigned char *string1, |
| const unsigned char *string2) |
| { |
| // needs work dft |
| return NULL; |
| } |
| |
| static unsigned char *_mbsinc(const unsigned char *inCurrent) |
| { |
| // needs work dft |
| return (unsigned char *)(inCurrent + 1); |
| } |
| |
| #endif |
| |
| 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 |
| }; |
| |
| /* Windows CE has GetFileAttributesEx. */ |
| #ifndef WINCE |
| typedef BOOL (WINAPI *GetFileAttributesExFn)(LPCTSTR, |
| GET_FILEEX_INFO_LEVELS, |
| LPVOID); |
| static GetFileAttributesExFn getFileAttributesEx; |
| static void InitGetFileInfo(void); |
| #endif |
| |
| static void InitUnicodeSupport(void); |
| |
| 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(); |
| |
| #ifndef WINCE |
| InitGetFileInfo(); |
| #endif |
| |
| InitUnicodeSupport(); |
| |
| _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; |
| break; |
| 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; |
| break; |
| default: |
| return PR_FAILURE; |
| break; |
| } |
| } |
| 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) |
| { |
| #ifdef WINCE |
| // needs work. dft |
| PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); |
| return -1; |
| #else |
| 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; |
| #endif |
| } |
| |
| #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. \\<server name>\<share point name>\ or |
| * \\<server name>\<share point name>, 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; |
| } |
| |
| #ifndef WINCE |
| /* |
| * InitGetFileInfo -- |
| * |
| * Called during IO init. Checks for the existence of the system function |
| * GetFileAttributeEx, which when available is used in GETFILEINFO calls. |
| * If the routine exists, then the address of the routine is stored in the |
| * variable getFileAttributesEx, which will be used to call the routine. |
| */ |
| static void InitGetFileInfo(void) |
| { |
| HMODULE module; |
| module = GetModuleHandle("Kernel32.dll"); |
| if (!module) { |
| PR_LOG(_pr_io_lm, PR_LOG_DEBUG, |
| ("InitGetFileInfo: GetModuleHandle() failed: %d", |
| GetLastError())); |
| return; |
| } |
| |
| getFileAttributesEx = (GetFileAttributesExFn) |
| GetProcAddress(module, "GetFileAttributesExA"); |
| } |
| |
| /* |
| * If GetFileAttributeEx doesn't exist, we call FindFirstFile as a |
| * fallback. |
| */ |
| static BOOL |
| GetFileAttributesExFB(const char *fn, WIN32_FIND_DATA *findFileData) |
| { |
| HANDLE hFindFile; |
| |
| /* |
| * FindFirstFile() expands wildcard characters. So |
| * we make sure the pathname contains no wildcard. |
| */ |
| if (NULL != _mbspbrk(fn, "?*")) { |
| SetLastError(ERROR_INVALID_NAME); |
| return FALSE; |
| } |
| |
| hFindFile = FindFirstFile(fn, findFileData); |
| if (INVALID_HANDLE_VALUE == hFindFile) { |
| DWORD len; |
| char *filePart; |
| char pathbuf[MAX_PATH + 1]; |
| |
| /* |
| * 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 == _mbspbrk(fn, ".\\/")) { |
| return FALSE; |
| } |
| len = GetFullPathName(fn, sizeof(pathbuf), pathbuf, |
| &filePart); |
| if (0 == len) { |
| return FALSE; |
| } |
| if (len > sizeof(pathbuf)) { |
| SetLastError(ERROR_FILENAME_EXCED_RANGE); |
| return FALSE; |
| } |
| if (IsRootDirectory(pathbuf, sizeof(pathbuf))) { |
| findFileData->dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY; |
| /* The file size doesn't have a meaning for directories. */ |
| findFileData->nFileSizeHigh = 0; |
| findFileData->nFileSizeLow = 0; |
| /* |
| * For a directory, these timestamps all specify when the |
| * directory is created. The creation time doesn't make |
| * sense for root directories, so we set it to (NSPR) time 0. |
| */ |
| memcpy(&findFileData->ftCreationTime, &_pr_filetime_offset, 8); |
| findFileData->ftLastAccessTime = findFileData->ftCreationTime; |
| findFileData->ftLastWriteTime = findFileData->ftCreationTime; |
| return TRUE; |
| } |
| if (!IsPrevCharSlash(pathbuf, pathbuf + len)) { |
| return FALSE; |
| } else { |
| pathbuf[len - 1] = '\0'; |
| hFindFile = FindFirstFile(pathbuf, findFileData); |
| if (INVALID_HANDLE_VALUE == hFindFile) { |
| return FALSE; |
| } |
| } |
| } |
| |
| FindClose(hFindFile); |
| return TRUE; |
| } |
| #endif |
| |
| PRInt32 |
| _PR_MD_GETFILEINFO64(const char *fn, PRFileInfo64 *info) |
| { |
| #ifdef WINCE |
| WIN32_FILE_ATTRIBUTE_DATA findFileData; |
| #else |
| WIN32_FIND_DATA findFileData; |
| #endif |
| BOOL rv; |
| |
| if (NULL == fn || '\0' == *fn) { |
| PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); |
| return -1; |
| } |
| |
| #ifdef WINCE |
| rv = GetFileAttributesExA(fn, GetFileExInfoStandard, &findFileData); |
| #else |
| /* GetFileAttributesEx is supported on Win 2K and up. */ |
| if (getFileAttributesEx) { |
| rv = getFileAttributesEx(fn, GetFileExInfoStandard, &findFileData); |
| } else { |
| rv = GetFileAttributesExFB(fn, &findFileData); |
| } |
| #endif |
| 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) |
| { |
| #ifdef WINCE |
| PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); |
| return PR_FAILURE; |
| #else |
| 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; |
| #endif |
| } |
| |
| 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) |
| { |
| #ifdef WINCE |
| fd->secret->inheritable = _PR_TRI_FALSE; |
| #else |
| 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; |
| } |
| } |
| #endif |
| } |
| |
| 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) |
| { |
| #ifdef WINCE |
| PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); |
| return -1; |
| #else |
| 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; |
| #endif |
| } |
| |
| 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 rc = GetLastError(); |
| PR_LOG( _pr_io_lm, PR_LOG_ERROR, |
| ("_PR_MD_LOCKFILE() failed. Error: %d", rc )); |
| 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 */ |
| |
| PRBool _pr_useUnicode = PR_FALSE; |
| |
| static void InitUnicodeSupport(void) |
| { |
| #ifdef WINCE |
| /* The A functions don't even exist in Windows Mobile. */ |
| _pr_useUnicode = PR_TRUE; |
| #else |
| /* |
| * The W functions exist on Win9x as stubs that fail with the |
| * ERROR_CALL_NOT_IMPLEMENTED error. We plan to emulate the |
| * MSLU W functions on Win9x in the future. |
| */ |
| |
| /* Find out if we are running on a Unicode enabled version of Windows */ |
| OSVERSIONINFOA osvi = {0}; |
| |
| osvi.dwOSVersionInfoSize = sizeof(osvi); |
| if (GetVersionExA(&osvi)) { |
| _pr_useUnicode = (osvi.dwPlatformId >= VER_PLATFORM_WIN32_NT); |
| } else { |
| _pr_useUnicode = PR_FALSE; |
| } |
| #ifdef DEBUG |
| /* |
| * In debug builds, allow explicit use of ANSI methods to simulate |
| * a Win9x environment for testing purposes. |
| */ |
| if (getenv("WINAPI_USE_ANSI")) |
| _pr_useUnicode = PR_FALSE; |
| #endif |
| #endif |
| } |
| |
| #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. \\<server name>\<share point name>\ or |
| * \\<server name>\<share point name>, 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 */ |