/* * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. * Copyright (C) 2008 Collabora, Ltd. All rights reserved. * * 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 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 "FileMetadata.h" #include "NotImplemented.h" #include "PathWalker.h" #include #include #include #include #include #include #include namespace WebCore { static const ULONGLONG kSecondsFromFileTimeToTimet = 11644473600; static bool getFindData(String path, WIN32_FIND_DATAW& findData) { HANDLE handle = FindFirstFileW(path.charactersWithNullTermination().data(), &findData); if (handle == INVALID_HANDLE_VALUE) return false; FindClose(handle); return true; } static bool getFileSizeFromFindData(const WIN32_FIND_DATAW& findData, long long& size) { ULARGE_INTEGER fileSize; fileSize.HighPart = findData.nFileSizeHigh; fileSize.LowPart = findData.nFileSizeLow; if (fileSize.QuadPart > static_cast(std::numeric_limits::max())) return false; size = fileSize.QuadPart; return true; } static bool getFileSizeFromByHandleFileInformationStructure(const BY_HANDLE_FILE_INFORMATION& fileInformation, long long& size) { ULARGE_INTEGER fileSize; fileSize.HighPart = fileInformation.nFileSizeHigh; fileSize.LowPart = fileInformation.nFileSizeLow; if (fileSize.QuadPart > static_cast(std::numeric_limits::max())) return false; size = fileSize.QuadPart; return true; } static void getFileCreationTimeFromFindData(const WIN32_FIND_DATAW& findData, time_t& time) { ULARGE_INTEGER fileTime; fileTime.HighPart = findData.ftCreationTime.dwHighDateTime; fileTime.LowPart = findData.ftCreationTime.dwLowDateTime; // Information about converting time_t to FileTime is available at http://msdn.microsoft.com/en-us/library/ms724228%28v=vs.85%29.aspx time = fileTime.QuadPart / 10000000 - kSecondsFromFileTimeToTimet; } static void getFileModificationTimeFromFindData(const WIN32_FIND_DATAW& findData, time_t& time) { ULARGE_INTEGER fileTime; fileTime.HighPart = findData.ftLastWriteTime.dwHighDateTime; fileTime.LowPart = findData.ftLastWriteTime.dwLowDateTime; // Information about converting time_t to FileTime is available at http://msdn.microsoft.com/en-us/library/ms724228%28v=vs.85%29.aspx time = fileTime.QuadPart / 10000000 - kSecondsFromFileTimeToTimet; } bool getFileSize(const String& path, long long& size) { WIN32_FIND_DATAW findData; if (!getFindData(path, findData)) return false; return getFileSizeFromFindData(findData, size); } bool getFileSize(PlatformFileHandle fileHandle, long long& size) { BY_HANDLE_FILE_INFORMATION fileInformation; if (!::GetFileInformationByHandle(fileHandle, &fileInformation)) return false; return getFileSizeFromByHandleFileInformationStructure(fileInformation, size); } bool getFileModificationTime(const String& path, time_t& time) { WIN32_FIND_DATAW findData; if (!getFindData(path, findData)) return false; getFileModificationTimeFromFindData(findData, time); return true; } bool getFileCreationTime(const String& path, time_t& time) { WIN32_FIND_DATAW findData; if (!getFindData(path, findData)) return false; getFileCreationTimeFromFindData(findData, time); return true; } bool getFileMetadata(const String& path, FileMetadata& metadata) { WIN32_FIND_DATAW findData; if (!getFindData(path, findData)) return false; if (!getFileSizeFromFindData(findData, metadata.length)) return false; time_t modificationTime; getFileModificationTimeFromFindData(findData, modificationTime); metadata.modificationTime = modificationTime; metadata.type = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? FileMetadata::TypeDirectory : FileMetadata::TypeFile; return true; } bool fileExists(const String& path) { WIN32_FIND_DATAW findData; return getFindData(path, findData); } bool deleteFile(const String& path) { String filename = path; return !!DeleteFileW(filename.charactersWithNullTermination().data()); } bool deleteEmptyDirectory(const String& path) { String filename = path; return !!RemoveDirectoryW(filename.charactersWithNullTermination().data()); } String pathByAppendingComponent(const String& path, const String& component) { Vector buffer(MAX_PATH); if (path.length() + 1 > buffer.size()) return String(); StringView(path).getCharactersWithUpconvert(buffer.data()); buffer[path.length()] = '\0'; if (!PathAppendW(buffer.data(), component.charactersWithNullTermination().data())) return String(); buffer.shrink(wcslen(buffer.data())); return String::adopt(buffer); } #if !USE(CF) CString fileSystemRepresentation(const String& path) { auto upconvertedCharacters = path.upconvertedCharacters(); const UChar* characters = upconvertedCharacters; int size = WideCharToMultiByte(CP_ACP, 0, characters, path.length(), 0, 0, 0, 0) - 1; char* buffer; CString string = CString::newUninitialized(size, buffer); WideCharToMultiByte(CP_ACP, 0, characters, path.length(), buffer, size, 0, 0); return string; } #endif // !USE(CF) bool makeAllDirectories(const String& path) { String fullPath = path; if (SHCreateDirectoryEx(0, fullPath.charactersWithNullTermination().data(), 0) != ERROR_SUCCESS) { DWORD error = GetLastError(); if (error != ERROR_FILE_EXISTS && error != ERROR_ALREADY_EXISTS) { LOG_ERROR("Failed to create path %s", path.ascii().data()); return false; } } return true; } String homeDirectoryPath() { notImplemented(); return ""; } String pathGetFileName(const String& path) { return String(::PathFindFileName(String(path).charactersWithNullTermination().data())); } String directoryName(const String& path) { String name = path.left(path.length() - pathGetFileName(path).length()); if (name.characterStartingAt(name.length() - 1) == '\\') { // Remove any trailing "\". name.truncate(name.length() - 1); } return name; } static String bundleName() { DEPRECATED_DEFINE_STATIC_LOCAL(String, name, (ASCIILiteral("WebKit"))); #if USE(CF) static bool initialized; if (!initialized) { initialized = true; if (CFBundleRef bundle = CFBundleGetMainBundle()) if (CFTypeRef bundleExecutable = CFBundleGetValueForInfoDictionaryKey(bundle, kCFBundleExecutableKey)) if (CFGetTypeID(bundleExecutable) == CFStringGetTypeID()) name = reinterpret_cast(bundleExecutable); } #endif return name; } static String storageDirectory(DWORD pathIdentifier) { Vector buffer(MAX_PATH); if (FAILED(SHGetFolderPathW(0, pathIdentifier | CSIDL_FLAG_CREATE, 0, 0, buffer.data()))) return String(); buffer.resize(wcslen(buffer.data())); String directory = String::adopt(buffer); DEPRECATED_DEFINE_STATIC_LOCAL(String, companyNameDirectory, (ASCIILiteral("Apple Computer\\"))); directory = pathByAppendingComponent(directory, companyNameDirectory + bundleName()); if (!makeAllDirectories(directory)) return String(); return directory; } static String cachedStorageDirectory(DWORD pathIdentifier) { static HashMap directories; HashMap::iterator it = directories.find(pathIdentifier); if (it != directories.end()) return it->value; String directory = storageDirectory(pathIdentifier); directories.add(pathIdentifier, directory); return directory; } String openTemporaryFile(const String&, PlatformFileHandle& handle) { handle = INVALID_HANDLE_VALUE; wchar_t tempPath[MAX_PATH]; int tempPathLength = ::GetTempPathW(WTF_ARRAY_LENGTH(tempPath), tempPath); if (tempPathLength <= 0 || tempPathLength > WTF_ARRAY_LENGTH(tempPath)) return String(); String proposedPath; do { 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; cryptographicallyRandomValues(tempFile, randomPartLength * sizeof(wchar_t)); // 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) == WTF_ARRAY_LENGTH(tempFile) - 1); proposedPath = pathByAppendingComponent(tempPath, tempFile); if (proposedPath.isEmpty()) break; // use CREATE_NEW to avoid overwriting an existing file with the same name handle = ::CreateFileW(proposedPath.charactersWithNullTermination().data(), GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0); } while (!isHandleValid(handle) && GetLastError() == ERROR_ALREADY_EXISTS); if (!isHandleValid(handle)) return String(); return proposedPath; } PlatformFileHandle openFile(const String& path, FileOpenMode mode) { DWORD desiredAccess = 0; DWORD creationDisposition = 0; switch (mode) { case OpenForRead: desiredAccess = GENERIC_READ; creationDisposition = OPEN_EXISTING; break; case OpenForWrite: desiredAccess = GENERIC_WRITE; creationDisposition = CREATE_ALWAYS; break; default: ASSERT_NOT_REACHED(); } String destination = path; return CreateFile(destination.charactersWithNullTermination().data(), desiredAccess, 0, 0, creationDisposition, FILE_ATTRIBUTE_NORMAL, 0); } void closeFile(PlatformFileHandle& handle) { if (isHandleValid(handle)) { ::CloseHandle(handle); handle = invalidPlatformFileHandle; } } long long seekFile(PlatformFileHandle handle, long long offset, FileSeekOrigin origin) { DWORD moveMethod = FILE_BEGIN; if (origin == SeekFromCurrent) moveMethod = FILE_CURRENT; else if (origin == SeekFromEnd) moveMethod = FILE_END; LARGE_INTEGER largeOffset; largeOffset.QuadPart = offset; largeOffset.LowPart = SetFilePointer(handle, largeOffset.LowPart, &largeOffset.HighPart, moveMethod); if (largeOffset.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) return -1; return largeOffset.QuadPart; } 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(bytesWritten); } int readFromFile(PlatformFileHandle handle, char* data, int length) { if (!isHandleValid(handle)) return -1; DWORD bytesRead; bool success = ::ReadFile(handle, data, length, &bytesRead, 0); if (!success) return -1; return static_cast(bytesRead); } bool hardLinkOrCopyFile(const String& source, const String& destination) { return !!::CopyFile(source.charactersWithNullTermination().data(), destination.charactersWithNullTermination().data(), TRUE); } bool unloadModule(PlatformModule module) { return ::FreeLibrary(module); } String localUserSpecificStorageDirectory() { return cachedStorageDirectory(CSIDL_LOCAL_APPDATA); } String roamingUserSpecificStorageDirectory() { return cachedStorageDirectory(CSIDL_APPDATA); } Vector listDirectory(const String& directory, const String& filter) { Vector entries; PathWalker walker(directory, filter); if (!walker.isValid()) return entries; do { if (walker.data().dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue; entries.append(directory + "\\" + reinterpret_cast(walker.data().cFileName)); } while (walker.step()); return entries; } } // namespace WebCore