| OLD | NEW |
| (Empty) |
| 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 | |
| 45 namespace WebCore { | |
| 46 | |
| 47 static const ULONGLONG kSecondsFromFileTimeToTimet = 11644473600; | |
| 48 | |
| 49 static bool getFindData(String path, WIN32_FIND_DATAW& findData) | |
| 50 { | |
| 51 HANDLE handle = FindFirstFileW(path.charactersWithNullTermination(), &findDa
ta); | |
| 52 if (handle == INVALID_HANDLE_VALUE) | |
| 53 return false; | |
| 54 FindClose(handle); | |
| 55 return true; | |
| 56 } | |
| 57 | |
| 58 static 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 | |
| 71 static 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://ms
dn.microsoft.com/en-us/library/ms724228%28v=vs.85%29.aspx | |
| 78 time = fileTime.QuadPart / 10000000 - kSecondsFromFileTimeToTimet; | |
| 79 } | |
| 80 | |
| 81 bool 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 | |
| 90 bool 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 | |
| 100 bool 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) ? Fil
eMetadata::TypeDirectory : FileMetadata::TypeFile; | |
| 114 | |
| 115 return true; | |
| 116 } | |
| 117 | |
| 118 bool fileExists(const String& path) | |
| 119 { | |
| 120 WIN32_FIND_DATAW findData; | |
| 121 return getFindData(path, findData); | |
| 122 } | |
| 123 | |
| 124 bool deleteFile(const String& path) | |
| 125 { | |
| 126 String filename = path; | |
| 127 return !!DeleteFileW(filename.charactersWithNullTermination()); | |
| 128 } | |
| 129 | |
| 130 bool deleteEmptyDirectory(const String& path) | |
| 131 { | |
| 132 String filename = path; | |
| 133 return !!RemoveDirectoryW(filename.charactersWithNullTermination()); | |
| 134 } | |
| 135 | |
| 136 String pathByAppendingComponent(const String& path, const String& component) | |
| 137 { | |
| 138 Vector<UChar> buffer(MAX_PATH); | |
| 139 | |
| 140 if (path.length() + 1 > buffer.size()) | |
| 141 return String(); | |
| 142 | |
| 143 memcpy(buffer.data(), path.characters(), path.length() * sizeof(UChar)); | |
| 144 buffer[path.length()] = '\0'; | |
| 145 | |
| 146 String componentCopy = component; | |
| 147 if (!PathAppendW(buffer.data(), componentCopy.charactersWithNullTermination(
))) | |
| 148 return String(); | |
| 149 | |
| 150 buffer.resize(wcslen(buffer.data())); | |
| 151 | |
| 152 return String::adopt(buffer); | |
| 153 } | |
| 154 | |
| 155 #if !USE(CF) | |
| 156 | |
| 157 CString fileSystemRepresentation(const String& path) | |
| 158 { | |
| 159 const UChar* characters = path.characters(); | |
| 160 int size = WideCharToMultiByte(CP_ACP, 0, characters, path.length(), 0, 0, 0
, 0) - 1; | |
| 161 | |
| 162 char* buffer; | |
| 163 CString string = CString::newUninitialized(size, buffer); | |
| 164 | |
| 165 WideCharToMultiByte(CP_ACP, 0, characters, path.length(), buffer, size, 0, 0
); | |
| 166 | |
| 167 return string; | |
| 168 } | |
| 169 | |
| 170 #endif // !USE(CF) | |
| 171 | |
| 172 bool makeAllDirectories(const String& path) | |
| 173 { | |
| 174 String fullPath = path; | |
| 175 if (SHCreateDirectoryEx(0, fullPath.charactersWithNullTermination(), 0) != E
RROR_SUCCESS) { | |
| 176 DWORD error = GetLastError(); | |
| 177 if (error != ERROR_FILE_EXISTS && error != ERROR_ALREADY_EXISTS) { | |
| 178 LOG_ERROR("Failed to create path %s", path.ascii().data()); | |
| 179 return false; | |
| 180 } | |
| 181 } | |
| 182 return true; | |
| 183 } | |
| 184 | |
| 185 String homeDirectoryPath() | |
| 186 { | |
| 187 notImplemented(); | |
| 188 return ""; | |
| 189 } | |
| 190 | |
| 191 String pathGetFileName(const String& path) | |
| 192 { | |
| 193 return String(::PathFindFileName(String(path).charactersWithNullTermination(
))); | |
| 194 } | |
| 195 | |
| 196 String directoryName(const String& path) | |
| 197 { | |
| 198 String name = path.left(path.length() - pathGetFileName(path).length()); | |
| 199 if (name.characterStartingAt(name.length() - 1) == '\\') { | |
| 200 // Remove any trailing "\". | |
| 201 name.truncate(name.length() - 1); | |
| 202 } | |
| 203 return name; | |
| 204 } | |
| 205 | |
| 206 static String bundleName() | |
| 207 { | |
| 208 DEFINE_STATIC_LOCAL(String, name, (ASCIILiteral("WebKit"))); | |
| 209 | |
| 210 #if USE(CF) | |
| 211 static bool initialized; | |
| 212 | |
| 213 if (!initialized) { | |
| 214 initialized = true; | |
| 215 | |
| 216 if (CFBundleRef bundle = CFBundleGetMainBundle()) | |
| 217 if (CFTypeRef bundleExecutable = CFBundleGetValueForInfoDictionaryKe
y(bundle, kCFBundleExecutableKey)) | |
| 218 if (CFGetTypeID(bundleExecutable) == CFStringGetTypeID()) | |
| 219 name = reinterpret_cast<CFStringRef>(bundleExecutable); | |
| 220 } | |
| 221 #endif | |
| 222 | |
| 223 return name; | |
| 224 } | |
| 225 | |
| 226 static String storageDirectory(DWORD pathIdentifier) | |
| 227 { | |
| 228 Vector<UChar> buffer(MAX_PATH); | |
| 229 if (FAILED(SHGetFolderPathW(0, pathIdentifier | CSIDL_FLAG_CREATE, 0, 0, buf
fer.data()))) | |
| 230 return String(); | |
| 231 buffer.resize(wcslen(buffer.data())); | |
| 232 String directory = String::adopt(buffer); | |
| 233 | |
| 234 DEFINE_STATIC_LOCAL(String, companyNameDirectory, (ASCIILiteral("Apple Compu
ter\\"))); | |
| 235 directory = pathByAppendingComponent(directory, companyNameDirectory + bundl
eName()); | |
| 236 if (!makeAllDirectories(directory)) | |
| 237 return String(); | |
| 238 | |
| 239 return directory; | |
| 240 } | |
| 241 | |
| 242 static String cachedStorageDirectory(DWORD pathIdentifier) | |
| 243 { | |
| 244 static HashMap<DWORD, String> directories; | |
| 245 | |
| 246 HashMap<DWORD, String>::iterator it = directories.find(pathIdentifier); | |
| 247 if (it != directories.end()) | |
| 248 return it->value; | |
| 249 | |
| 250 String directory = storageDirectory(pathIdentifier); | |
| 251 directories.add(pathIdentifier, directory); | |
| 252 | |
| 253 return directory; | |
| 254 } | |
| 255 | |
| 256 String openTemporaryFile(const String&, PlatformFileHandle& handle) | |
| 257 { | |
| 258 handle = INVALID_HANDLE_VALUE; | |
| 259 | |
| 260 wchar_t tempPath[MAX_PATH]; | |
| 261 int tempPathLength = ::GetTempPathW(WTF_ARRAY_LENGTH(tempPath), tempPath); | |
| 262 if (tempPathLength <= 0 || tempPathLength > WTF_ARRAY_LENGTH(tempPath)) | |
| 263 return String(); | |
| 264 | |
| 265 String proposedPath; | |
| 266 do { | |
| 267 wchar_t tempFile[] = L"XXXXXXXX.tmp"; // Use 8.3 style name (more charac
ters aren't helpful due to 8.3 short file names) | |
| 268 const int randomPartLength = 8; | |
| 269 cryptographicallyRandomValues(tempFile, randomPartLength * sizeof(wchar_
t)); | |
| 270 | |
| 271 // Limit to valid filesystem characters, also excluding others that coul
d be problematic, like punctuation. | |
| 272 // don't include both upper and lowercase since Windows file systems are
typically not case sensitive. | |
| 273 const char validChars[] = "0123456789abcdefghijklmnopqrstuvwxyz"; | |
| 274 for (int i = 0; i < randomPartLength; ++i) | |
| 275 tempFile[i] = validChars[tempFile[i] % (sizeof(validChars) - 1)]; | |
| 276 | |
| 277 ASSERT(wcslen(tempFile) == WTF_ARRAY_LENGTH(tempFile) - 1); | |
| 278 | |
| 279 proposedPath = pathByAppendingComponent(tempPath, tempFile); | |
| 280 if (proposedPath.isEmpty()) | |
| 281 break; | |
| 282 | |
| 283 // use CREATE_NEW to avoid overwriting an existing file with the same na
me | |
| 284 handle = ::CreateFileW(proposedPath.charactersWithNullTermination(), GEN
ERIC_READ | GENERIC_WRITE, 0, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0); | |
| 285 } while (!isHandleValid(handle) && GetLastError() == ERROR_ALREADY_EXISTS); | |
| 286 | |
| 287 if (!isHandleValid(handle)) | |
| 288 return String(); | |
| 289 | |
| 290 return proposedPath; | |
| 291 } | |
| 292 | |
| 293 PlatformFileHandle openFile(const String& path, FileOpenMode mode) | |
| 294 { | |
| 295 DWORD desiredAccess = 0; | |
| 296 DWORD creationDisposition = 0; | |
| 297 switch (mode) { | |
| 298 case OpenForRead: | |
| 299 desiredAccess = GENERIC_READ; | |
| 300 creationDisposition = OPEN_EXISTING; | |
| 301 break; | |
| 302 case OpenForWrite: | |
| 303 desiredAccess = GENERIC_WRITE; | |
| 304 creationDisposition = CREATE_ALWAYS; | |
| 305 break; | |
| 306 default: | |
| 307 ASSERT_NOT_REACHED(); | |
| 308 } | |
| 309 | |
| 310 String destination = path; | |
| 311 return CreateFile(destination.charactersWithNullTermination(), desiredAccess
, 0, 0, creationDisposition, FILE_ATTRIBUTE_NORMAL, 0); | |
| 312 } | |
| 313 | |
| 314 void closeFile(PlatformFileHandle& handle) | |
| 315 { | |
| 316 if (isHandleValid(handle)) { | |
| 317 ::CloseHandle(handle); | |
| 318 handle = invalidPlatformFileHandle; | |
| 319 } | |
| 320 } | |
| 321 | |
| 322 int writeToFile(PlatformFileHandle handle, const char* data, int length) | |
| 323 { | |
| 324 if (!isHandleValid(handle)) | |
| 325 return -1; | |
| 326 | |
| 327 DWORD bytesWritten; | |
| 328 bool success = WriteFile(handle, data, length, &bytesWritten, 0); | |
| 329 | |
| 330 if (!success) | |
| 331 return -1; | |
| 332 return static_cast<int>(bytesWritten); | |
| 333 } | |
| 334 | |
| 335 bool unloadModule(PlatformModule module) | |
| 336 { | |
| 337 return ::FreeLibrary(module); | |
| 338 } | |
| 339 | |
| 340 String localUserSpecificStorageDirectory() | |
| 341 { | |
| 342 return cachedStorageDirectory(CSIDL_LOCAL_APPDATA); | |
| 343 } | |
| 344 | |
| 345 String roamingUserSpecificStorageDirectory() | |
| 346 { | |
| 347 return cachedStorageDirectory(CSIDL_APPDATA); | |
| 348 } | |
| 349 | |
| 350 #if USE(CF) | |
| 351 | |
| 352 bool safeCreateFile(const String& path, CFDataRef data) | |
| 353 { | |
| 354 // Create a temporary file. | |
| 355 WCHAR tempDirPath[MAX_PATH]; | |
| 356 if (!GetTempPathW(WTF_ARRAY_LENGTH(tempDirPath), tempDirPath)) | |
| 357 return false; | |
| 358 | |
| 359 WCHAR tempPath[MAX_PATH]; | |
| 360 if (!GetTempFileNameW(tempDirPath, L"WEBKIT", 0, tempPath)) | |
| 361 return false; | |
| 362 | |
| 363 HANDLE tempFileHandle = CreateFileW(tempPath, GENERIC_READ | GENERIC_WRITE,
0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); | |
| 364 if (tempFileHandle == INVALID_HANDLE_VALUE) | |
| 365 return false; | |
| 366 | |
| 367 // Write the data to this temp file. | |
| 368 DWORD written; | |
| 369 if (!WriteFile(tempFileHandle, CFDataGetBytePtr(data), static_cast<DWORD>(CF
DataGetLength(data)), &written, 0)) | |
| 370 return false; | |
| 371 | |
| 372 CloseHandle(tempFileHandle); | |
| 373 | |
| 374 // Copy the temp file to the destination file. | |
| 375 String destination = path; | |
| 376 if (!MoveFileExW(tempPath, destination.charactersWithNullTermination(), MOVE
FILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED)) | |
| 377 return false; | |
| 378 | |
| 379 return true; | |
| 380 } | |
| 381 | |
| 382 #endif // USE(CF) | |
| 383 | |
| 384 Vector<String> listDirectory(const String& directory, const String& filter) | |
| 385 { | |
| 386 Vector<String> entries; | |
| 387 | |
| 388 PathWalker walker(directory, filter); | |
| 389 if (!walker.isValid()) | |
| 390 return entries; | |
| 391 | |
| 392 do { | |
| 393 if (walker.data().dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) | |
| 394 continue; | |
| 395 | |
| 396 entries.append(directory + "\\" + reinterpret_cast<const UChar*>(walker.
data().cFileName)); | |
| 397 } while (walker.step()); | |
| 398 | |
| 399 return entries; | |
| 400 } | |
| 401 | |
| 402 } // namespace WebCore | |
| OLD | NEW |