1/* 2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. 3 * Copyright (C) 2008 Collabora, Ltd. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 15 * its contributors may be used to endorse or promote products derived 16 * from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30#include "config.h" 31#include "FileSystem.h" 32 33#include "FileMetadata.h" 34#include "NotImplemented.h" 35#include "PathWalker.h" 36#include <wtf/CryptographicallyRandomNumber.h> 37#include <wtf/HashMap.h> 38#include <wtf/text/CString.h> 39#include <wtf/text/WTFString.h> 40 41#include <windows.h> 42#include <shlobj.h> 43#include <shlwapi.h> 44 45namespace WebCore { 46 47static const ULONGLONG kSecondsFromFileTimeToTimet = 11644473600; 48 49static bool getFindData(String path, WIN32_FIND_DATAW& findData) 50{ 51 HANDLE handle = FindFirstFileW(path.charactersWithNullTermination(), &findData); 52 if (handle == INVALID_HANDLE_VALUE) 53 return false; 54 FindClose(handle); 55 return true; 56} 57 58static bool getFileSizeFromFindData(const WIN32_FIND_DATAW& findData, long long& size) 59{ 60 ULARGE_INTEGER fileSize; 61 fileSize.HighPart = findData.nFileSizeHigh; 62 fileSize.LowPart = findData.nFileSizeLow; 63 64 if (fileSize.QuadPart > static_cast<ULONGLONG>(std::numeric_limits<long long>::max())) 65 return false; 66 67 size = fileSize.QuadPart; 68 return true; 69} 70 71static void getFileModificationTimeFromFindData(const WIN32_FIND_DATAW& findData, time_t& time) 72{ 73 ULARGE_INTEGER fileTime; 74 fileTime.HighPart = findData.ftLastWriteTime.dwHighDateTime; 75 fileTime.LowPart = findData.ftLastWriteTime.dwLowDateTime; 76 77 // Information about converting time_t to FileTime is available at http://msdn.microsoft.com/en-us/library/ms724228%28v=vs.85%29.aspx 78 time = fileTime.QuadPart / 10000000 - kSecondsFromFileTimeToTimet; 79} 80 81bool getFileSize(const String& path, long long& size) 82{ 83 WIN32_FIND_DATAW findData; 84 if (!getFindData(path, findData)) 85 return false; 86 87 return getFileSizeFromFindData(findData, size); 88} 89 90bool getFileModificationTime(const String& path, time_t& time) 91{ 92 WIN32_FIND_DATAW findData; 93 if (!getFindData(path, findData)) 94 return false; 95 96 getFileModificationTimeFromFindData(findData, time); 97 return true; 98} 99 100bool getFileMetadata(const String& path, FileMetadata& metadata) 101{ 102 WIN32_FIND_DATAW findData; 103 if (!getFindData(path, findData)) 104 return false; 105 106 if (!getFileSizeFromFindData(findData, metadata.length)) 107 return false; 108 109 time_t modificationTime; 110 getFileModificationTimeFromFindData(findData, modificationTime); 111 metadata.modificationTime = modificationTime; 112 113 metadata.type = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? FileMetadata::TypeDirectory : FileMetadata::TypeFile; 114 115 return true; 116} 117 118bool fileExists(const String& path) 119{ 120 WIN32_FIND_DATAW findData; 121 return getFindData(path, findData); 122} 123 124bool deleteFile(const String& path) 125{ 126 String filename = path; 127 return !!DeleteFileW(filename.charactersWithNullTermination()); 128} 129 130bool deleteEmptyDirectory(const String& path) 131{ 132 String filename = path; 133 return !!RemoveDirectoryW(filename.charactersWithNullTermination()); 134} 135 136String pathByAppendingComponent(const String& path, const String& component) 137{ 138 Vector<UChar> buffer(MAX_PATH); 139 140#if OS(WINCE) 141 buffer.append(path.characters(), path.length()); 142 143 UChar lastPathCharacter = path[path.length() - 1]; 144 if (lastPathCharacter != L'\\' && lastPathCharacter != L'/' && component[0] != L'\\' && component[0] != L'/') 145 buffer.append(PlatformFilePathSeparator); 146 147 buffer.append(component.characters(), component.length()); 148 buffer.shrinkToFit(); 149#else 150 if (path.length() + 1 > buffer.size()) 151 return String(); 152 153 memcpy(buffer.data(), path.characters(), path.length() * sizeof(UChar)); 154 buffer[path.length()] = '\0'; 155 156 String componentCopy = component; 157 if (!PathAppendW(buffer.data(), componentCopy.charactersWithNullTermination())) 158 return String(); 159 160 buffer.resize(wcslen(buffer.data())); 161#endif 162 163 return String::adopt(buffer); 164} 165 166#if !USE(CF) 167 168CString fileSystemRepresentation(const String& path) 169{ 170 const UChar* characters = path.characters(); 171 int size = WideCharToMultiByte(CP_ACP, 0, characters, path.length(), 0, 0, 0, 0) - 1; 172 173 char* buffer; 174 CString string = CString::newUninitialized(size, buffer); 175 176 WideCharToMultiByte(CP_ACP, 0, characters, path.length(), buffer, size, 0, 0); 177 178 return string; 179} 180 181#endif // !USE(CF) 182 183bool makeAllDirectories(const String& path) 184{ 185 String fullPath = path; 186 if (SHCreateDirectoryEx(0, fullPath.charactersWithNullTermination(), 0) != ERROR_SUCCESS) { 187 DWORD error = GetLastError(); 188 if (error != ERROR_FILE_EXISTS && error != ERROR_ALREADY_EXISTS) { 189 LOG_ERROR("Failed to create path %s", path.ascii().data()); 190 return false; 191 } 192 } 193 return true; 194} 195 196String homeDirectoryPath() 197{ 198 notImplemented(); 199 return ""; 200} 201 202String pathGetFileName(const String& path) 203{ 204#if OS(WINCE) 205 size_t positionSlash = path.reverseFind('/'); 206 size_t positionBackslash = path.reverseFind('\\'); 207 208 size_t position; 209 if (positionSlash == notFound) 210 position = positionBackslash; 211 else if (positionBackslash == notFound) 212 position = positionSlash; 213 else 214 position = std::max(positionSlash, positionBackslash); 215 216 if (position == notFound) 217 return path; 218 return path.substring(position + 1); 219#else 220 return String(::PathFindFileName(String(path).charactersWithNullTermination())); 221#endif 222} 223 224String directoryName(const String& path) 225{ 226 String name = path.left(path.length() - pathGetFileName(path).length()); 227 if (name.characterStartingAt(name.length() - 1) == '\\') { 228 // Remove any trailing "\". 229 name.truncate(name.length() - 1); 230 } 231 return name; 232} 233 234static String bundleName() 235{ 236 DEFINE_STATIC_LOCAL(String, name, (ASCIILiteral("WebKit"))); 237 238#if USE(CF) 239 static bool initialized; 240 241 if (!initialized) { 242 initialized = true; 243 244 if (CFBundleRef bundle = CFBundleGetMainBundle()) 245 if (CFTypeRef bundleExecutable = CFBundleGetValueForInfoDictionaryKey(bundle, kCFBundleExecutableKey)) 246 if (CFGetTypeID(bundleExecutable) == CFStringGetTypeID()) 247 name = reinterpret_cast<CFStringRef>(bundleExecutable); 248 } 249#endif 250 251 return name; 252} 253 254static String storageDirectory(DWORD pathIdentifier) 255{ 256 Vector<UChar> buffer(MAX_PATH); 257 if (FAILED(SHGetFolderPathW(0, pathIdentifier | CSIDL_FLAG_CREATE, 0, 0, buffer.data()))) 258 return String(); 259 buffer.resize(wcslen(buffer.data())); 260 String directory = String::adopt(buffer); 261 262 DEFINE_STATIC_LOCAL(String, companyNameDirectory, (ASCIILiteral("Apple Computer\\"))); 263 directory = pathByAppendingComponent(directory, companyNameDirectory + bundleName()); 264 if (!makeAllDirectories(directory)) 265 return String(); 266 267 return directory; 268} 269 270static String cachedStorageDirectory(DWORD pathIdentifier) 271{ 272 static HashMap<DWORD, String> directories; 273 274 HashMap<DWORD, String>::iterator it = directories.find(pathIdentifier); 275 if (it != directories.end()) 276 return it->value; 277 278 String directory = storageDirectory(pathIdentifier); 279 directories.add(pathIdentifier, directory); 280 281 return directory; 282} 283 284String openTemporaryFile(const String&, PlatformFileHandle& handle) 285{ 286 handle = INVALID_HANDLE_VALUE; 287 288 wchar_t tempPath[MAX_PATH]; 289 int tempPathLength = ::GetTempPathW(WTF_ARRAY_LENGTH(tempPath), tempPath); 290 if (tempPathLength <= 0 || tempPathLength > WTF_ARRAY_LENGTH(tempPath)) 291 return String(); 292 293 String proposedPath; 294 do { 295 wchar_t tempFile[] = L"XXXXXXXX.tmp"; // Use 8.3 style name (more characters aren't helpful due to 8.3 short file names) 296 const int randomPartLength = 8; 297 cryptographicallyRandomValues(tempFile, randomPartLength * sizeof(wchar_t)); 298 299 // Limit to valid filesystem characters, also excluding others that could be problematic, like punctuation. 300 // don't include both upper and lowercase since Windows file systems are typically not case sensitive. 301 const char validChars[] = "0123456789abcdefghijklmnopqrstuvwxyz"; 302 for (int i = 0; i < randomPartLength; ++i) 303 tempFile[i] = validChars[tempFile[i] % (sizeof(validChars) - 1)]; 304 305 ASSERT(wcslen(tempFile) == WTF_ARRAY_LENGTH(tempFile) - 1); 306 307 proposedPath = pathByAppendingComponent(tempPath, tempFile); 308 if (proposedPath.isEmpty()) 309 break; 310 311 // use CREATE_NEW to avoid overwriting an existing file with the same name 312 handle = ::CreateFileW(proposedPath.charactersWithNullTermination(), GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0); 313 } while (!isHandleValid(handle) && GetLastError() == ERROR_ALREADY_EXISTS); 314 315 if (!isHandleValid(handle)) 316 return String(); 317 318 return proposedPath; 319} 320 321PlatformFileHandle openFile(const String& path, FileOpenMode mode) 322{ 323 DWORD desiredAccess = 0; 324 DWORD creationDisposition = 0; 325 switch (mode) { 326 case OpenForRead: 327 desiredAccess = GENERIC_READ; 328 creationDisposition = OPEN_EXISTING; 329 break; 330 case OpenForWrite: 331 desiredAccess = GENERIC_WRITE; 332 creationDisposition = CREATE_ALWAYS; 333 break; 334 default: 335 ASSERT_NOT_REACHED(); 336 } 337 338 String destination = path; 339 return CreateFile(destination.charactersWithNullTermination(), desiredAccess, 0, 0, creationDisposition, FILE_ATTRIBUTE_NORMAL, 0); 340} 341 342void closeFile(PlatformFileHandle& handle) 343{ 344 if (isHandleValid(handle)) { 345 ::CloseHandle(handle); 346 handle = invalidPlatformFileHandle; 347 } 348} 349 350long long seekFile(PlatformFileHandle handle, long long offset, FileSeekOrigin origin) 351{ 352 DWORD moveMethod = FILE_BEGIN; 353 354 if (origin == SeekFromCurrent) 355 moveMethod = FILE_CURRENT; 356 else if (origin == SeekFromEnd) 357 moveMethod = FILE_END; 358 359 LARGE_INTEGER largeOffset; 360 largeOffset.QuadPart = offset; 361 362 LARGE_INTEGER newOffset; 363 newOffset.QuadPart = 0; 364 365 SetFilePointerEx(handle, largeOffset, &newOffset, moveMethod); 366 367 return newOffset.QuadPart; 368} 369 370int writeToFile(PlatformFileHandle handle, const char* data, int length) 371{ 372 if (!isHandleValid(handle)) 373 return -1; 374 375 DWORD bytesWritten; 376 bool success = WriteFile(handle, data, length, &bytesWritten, 0); 377 378 if (!success) 379 return -1; 380 return static_cast<int>(bytesWritten); 381} 382 383bool unloadModule(PlatformModule module) 384{ 385 return ::FreeLibrary(module); 386} 387 388String localUserSpecificStorageDirectory() 389{ 390 return cachedStorageDirectory(CSIDL_LOCAL_APPDATA); 391} 392 393String roamingUserSpecificStorageDirectory() 394{ 395 return cachedStorageDirectory(CSIDL_APPDATA); 396} 397 398Vector<String> listDirectory(const String& directory, const String& filter) 399{ 400 Vector<String> entries; 401 402 PathWalker walker(directory, filter); 403 if (!walker.isValid()) 404 return entries; 405 406 do { 407 if (walker.data().dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 408 continue; 409 410 entries.append(directory + "\\" + reinterpret_cast<const UChar*>(walker.data().cFileName)); 411 } while (walker.step()); 412 413 return entries; 414} 415 416} // namespace WebCore 417