| /* |
| * Copyright (C) 2007 Apple Inc. All rights reserved. |
| * Copyright (C) 2008 Collabora, Ltd. All rights reserved. |
| * Copyright (C) 2007-2009 Torch Mobile, Inc. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of |
| * its contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "FileSystem.h" |
| |
| #include "NotImplemented.h" |
| #include "PlatformString.h" |
| #include <wincrypt.h> |
| #include <windows.h> |
| #include <wtf/text/CString.h> |
| |
| namespace WebCore { |
| |
| static size_t reverseFindPathSeparator(const String& path, unsigned start = UINT_MAX) |
| { |
| size_t positionSlash = path.reverseFind('/', start); |
| size_t positionBackslash = path.reverseFind('\\', start); |
| |
| if (positionSlash == notFound) |
| return positionBackslash; |
| |
| if (positionBackslash == notFound) |
| return positionSlash; |
| |
| return std::max(positionSlash, positionBackslash); |
| } |
| |
| static bool getFileInfo(const String& path, BY_HANDLE_FILE_INFORMATION& fileInfo) |
| { |
| String filename = path; |
| HANDLE hFile = CreateFile(filename.charactersWithNullTermination(), GENERIC_READ, FILE_SHARE_READ, 0 |
| , OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, 0); |
| |
| if (hFile == INVALID_HANDLE_VALUE) |
| return false; |
| |
| bool rtn = GetFileInformationByHandle(hFile, &fileInfo) ? true : false; |
| |
| CloseHandle(hFile); |
| return rtn; |
| } |
| |
| bool getFileSize(const String& path, long long& result) |
| { |
| BY_HANDLE_FILE_INFORMATION fileInformation; |
| if (!getFileInfo(path, fileInformation)) |
| return false; |
| |
| ULARGE_INTEGER fileSize; |
| fileSize.LowPart = fileInformation.nFileSizeLow; |
| fileSize.HighPart = fileInformation.nFileSizeHigh; |
| |
| result = fileSize.QuadPart; |
| |
| return true; |
| } |
| |
| bool getFileModificationTime(const String& path, time_t& result) |
| { |
| BY_HANDLE_FILE_INFORMATION fileInformation; |
| if (!getFileInfo(path, fileInformation)) |
| return false; |
| |
| ULARGE_INTEGER t; |
| memcpy(&t, &fileInformation.ftLastWriteTime, sizeof(t)); |
| |
| result = t.QuadPart * 0.0000001 - 11644473600.0; |
| |
| return true; |
| } |
| |
| bool fileExists(const String& path) |
| { |
| String filename = path; |
| HANDLE hFile = CreateFile(filename.charactersWithNullTermination(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE |
| , 0, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, 0); |
| |
| CloseHandle(hFile); |
| |
| return hFile != INVALID_HANDLE_VALUE; |
| } |
| |
| bool deleteFile(const String& path) |
| { |
| String filename = path; |
| return !!DeleteFileW(filename.charactersWithNullTermination()); |
| } |
| |
| |
| bool deleteEmptyDirectory(const String& path) |
| { |
| String filename = path; |
| return !!RemoveDirectoryW(filename.charactersWithNullTermination()); |
| } |
| |
| String pathByAppendingComponent(const String& path, const String& component) |
| { |
| if (component.isEmpty()) |
| return path; |
| |
| Vector<UChar, MAX_PATH> buffer; |
| |
| buffer.append(path.characters(), path.length()); |
| |
| if (buffer.last() != L'\\' && buffer.last() != L'/' |
| && component[0] != L'\\' && component[0] != L'/') |
| buffer.append(L'\\'); |
| |
| buffer.append(component.characters(), component.length()); |
| |
| return String(buffer.data(), buffer.size()); |
| } |
| |
| CString fileSystemRepresentation(const String&) |
| { |
| return ""; |
| } |
| |
| bool makeAllDirectories(const String& path) |
| { |
| size_t lastDivPos = reverseFindPathSeparator(path); |
| unsigned endPos = path.length(); |
| if (lastDivPos == endPos - 1) { |
| --endPos; |
| lastDivPos = reverseFindPathSeparator(path, lastDivPos); |
| } |
| |
| if (lastDivPos != notFound) { |
| if (!makeAllDirectories(path.substring(0, lastDivPos))) |
| return false; |
| } |
| |
| String folder(path.substring(0, endPos)); |
| CreateDirectory(folder.charactersWithNullTermination(), 0); |
| |
| DWORD fileAttr = GetFileAttributes(folder.charactersWithNullTermination()); |
| return fileAttr != 0xFFFFFFFF && (fileAttr & FILE_ATTRIBUTE_DIRECTORY); |
| } |
| |
| String homeDirectoryPath() |
| { |
| notImplemented(); |
| return ""; |
| } |
| |
| String pathGetFileName(const String& path) |
| { |
| size_t pos = reverseFindPathSeparator(path); |
| if (pos == notFound) |
| return path; |
| return path.substring(pos + 1); |
| } |
| |
| String directoryName(const String& path) |
| { |
| size_t pos = reverseFindPathSeparator(path); |
| if (pos == notFound) |
| return String(); |
| return path.left(pos); |
| } |
| |
| CString openTemporaryFile(const char*, PlatformFileHandle& handle) |
| { |
| handle = INVALID_HANDLE_VALUE; |
| |
| wchar_t tempPath[MAX_PATH]; |
| int tempPathLength = ::GetTempPath(WTF_ARRAY_LENGTH(tempPath), tempPath); |
| if (tempPathLength <= 0 || tempPathLength > WTF_ARRAY_LENGTH(tempPath)) |
| return CString(); |
| |
| HCRYPTPROV hCryptProv = 0; |
| if (!CryptAcquireContext(&hCryptProv, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) |
| return CString(); |
| |
| String proposedPath; |
| while (1) { |
| |
| wchar_t tempFile[] = L"XXXXXXXX.tmp"; // Use 8.3 style name (more characters aren't helpful due to 8.3 short file names) |
| const int randomPartLength = 8; |
| if (!CryptGenRandom(hCryptProv, randomPartLength * 2, reinterpret_cast<BYTE*>(tempFile))) |
| break; |
| |
| // Limit to valid filesystem characters, also excluding others that could be problematic, like punctuation. |
| // don't include both upper and lowercase since Windows file systems are typically not case sensitive. |
| const char validChars[] = "0123456789abcdefghijklmnopqrstuvwxyz"; |
| for (int i = 0; i < randomPartLength; ++i) |
| tempFile[i] = validChars[tempFile[i] % (sizeof(validChars) - 1)]; |
| |
| ASSERT(wcslen(tempFile) * 2 == sizeof(tempFile) - 2); |
| |
| proposedPath = pathByAppendingComponent(String(tempPath), String(tempFile)); |
| |
| // use CREATE_NEW to avoid overwriting an existing file with the same name |
| handle = CreateFile(proposedPath.charactersWithNullTermination(), GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0); |
| if (!isHandleValid(handle) && GetLastError() == ERROR_ALREADY_EXISTS) |
| continue; |
| |
| break; |
| } |
| |
| CryptReleaseContext(hCryptProv, 0); |
| |
| if (!isHandleValid(handle)) |
| return CString(); |
| |
| return proposedPath.latin1(); |
| } |
| |
| void closeFile(PlatformFileHandle& handle) |
| { |
| if (isHandleValid(handle)) { |
| ::CloseHandle(handle); |
| handle = invalidPlatformFileHandle; |
| } |
| } |
| |
| int writeToFile(PlatformFileHandle handle, const char* data, int length) |
| { |
| if (!isHandleValid(handle)) |
| return -1; |
| |
| DWORD bytesWritten; |
| bool success = WriteFile(handle, data, length, &bytesWritten, 0); |
| |
| if (!success) |
| return -1; |
| return static_cast<int>(bytesWritten); |
| } |
| |
| bool unloadModule(PlatformModule module) |
| { |
| return ::FreeLibrary(module); |
| } |
| |
| String localUserSpecificStorageDirectory() |
| { |
| return String(L"\\"); |
| } |
| |
| String roamingUserSpecificStorageDirectory() |
| { |
| return String(L"\\"); |
| } |
| |
| Vector<String> listDirectory(const String& path, const String& filter) |
| { |
| Vector<String> entries; |
| |
| Vector<UChar, 256> pattern; |
| pattern.append(path.characters(), path.length()); |
| if (pattern.last() != L'/' && pattern.last() != L'\\') |
| pattern.append(L'\\'); |
| |
| String root(pattern.data(), pattern.size()); |
| pattern.append(filter.characters(), filter.length()); |
| pattern.append(0); |
| |
| WIN32_FIND_DATA findData; |
| HANDLE hFind = FindFirstFile(pattern.data(), &findData); |
| if (INVALID_HANDLE_VALUE != hFind) { |
| do { |
| // FIXEME: should we also add the folders? This function |
| // is so far only called by PluginDatabase.cpp to list |
| // all plugins in a folder, where it's not supposed to list sub-folders. |
| if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) |
| entries.append(root + findData.cFileName); |
| } while (FindNextFile(hFind, &findData)); |
| FindClose(hFind); |
| } |
| |
| return entries; |
| } |
| |
| } // namespace WebCore |