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 "NotImplemented.h" 34 #include "PathWalker.h" 35 #include "PlatformString.h" 36 #include <wtf/HashMap.h> 37 #include <wtf/text/CString.h> 38 #include <wtf/text/StringConcatenate.h> 39 40 #include <windows.h> 41 #include <winbase.h> 42 #include <shlobj.h> 43 #include <shlwapi.h> 44 45 namespace WebCore { 46 47 static bool statFile(String path, struct _stat64& st) 48 { 49 ASSERT_ARG(path, !path.isNull()); 50 return !_wstat64(path.charactersWithNullTermination(), &st) && (st.st_mode & _S_IFMT) == _S_IFREG; 51 } 52 53 bool getFileSize(const String& path, long long& result) 54 { 55 struct _stat64 sb; 56 if (!statFile(path, sb)) 57 return false; 58 result = sb.st_size; 59 return true; 60 } 61 62 bool getFileModificationTime(const String& path, time_t& result) 63 { 64 struct _stat64 st; 65 if (!statFile(path, st)) 66 return false; 67 result = st.st_mtime; 68 return true; 69 } 70 71 bool fileExists(const String& path) 72 { 73 struct _stat64 st; 74 return statFile(path, st); 75 } 76 77 bool deleteFile(const String& path) 78 { 79 String filename = path; 80 return !!DeleteFileW(filename.charactersWithNullTermination()); 81 } 82 83 bool deleteEmptyDirectory(const String& path) 84 { 85 String filename = path; 86 return !!RemoveDirectoryW(filename.charactersWithNullTermination()); 87 } 88 89 String pathByAppendingComponent(const String& path, const String& component) 90 { 91 Vector<UChar> buffer(MAX_PATH); 92 93 if (path.length() + 1 > buffer.size()) 94 return String(); 95 96 memcpy(buffer.data(), path.characters(), path.length() * sizeof(UChar)); 97 buffer[path.length()] = '\0'; 98 99 String componentCopy = component; 100 if (!PathAppendW(buffer.data(), componentCopy.charactersWithNullTermination())) 101 return String(); 102 103 buffer.resize(wcslen(buffer.data())); 104 105 return String::adopt(buffer); 106 } 107 108 CString fileSystemRepresentation(const String&) 109 { 110 return ""; 111 } 112 113 bool makeAllDirectories(const String& path) 114 { 115 String fullPath = path; 116 if (SHCreateDirectoryEx(0, fullPath.charactersWithNullTermination(), 0) != ERROR_SUCCESS) { 117 DWORD error = GetLastError(); 118 if (error != ERROR_FILE_EXISTS && error != ERROR_ALREADY_EXISTS) { 119 LOG_ERROR("Failed to create path %s", path.ascii().data()); 120 return false; 121 } 122 } 123 return true; 124 } 125 126 String homeDirectoryPath() 127 { 128 notImplemented(); 129 return ""; 130 } 131 132 String pathGetFileName(const String& path) 133 { 134 return String(::PathFindFileName(String(path).charactersWithNullTermination())); 135 } 136 137 String directoryName(const String& path) 138 { 139 String name = path.left(path.length() - pathGetFileName(path).length()); 140 if (name.characterStartingAt(name.length() - 1) == '\\') { 141 // Remove any trailing "\". 142 name.truncate(name.length() - 1); 143 } 144 return name; 145 } 146 147 static String bundleName() 148 { 149 static bool initialized; 150 static String name = "WebKit"; 151 152 if (!initialized) { 153 initialized = true; 154 155 if (CFBundleRef bundle = CFBundleGetMainBundle()) 156 if (CFTypeRef bundleExecutable = CFBundleGetValueForInfoDictionaryKey(bundle, kCFBundleExecutableKey)) 157 if (CFGetTypeID(bundleExecutable) == CFStringGetTypeID()) 158 name = reinterpret_cast<CFStringRef>(bundleExecutable); 159 } 160 161 return name; 162 } 163 164 static String storageDirectory(DWORD pathIdentifier) 165 { 166 Vector<UChar> buffer(MAX_PATH); 167 if (FAILED(SHGetFolderPathW(0, pathIdentifier | CSIDL_FLAG_CREATE, 0, 0, buffer.data()))) 168 return String(); 169 buffer.resize(wcslen(buffer.data())); 170 String directory = String::adopt(buffer); 171 172 static const String companyNameDirectory = "Apple Computer\\"; 173 directory = pathByAppendingComponent(directory, companyNameDirectory + bundleName()); 174 if (!makeAllDirectories(directory)) 175 return String(); 176 177 return directory; 178 } 179 180 static String cachedStorageDirectory(DWORD pathIdentifier) 181 { 182 static HashMap<DWORD, String> directories; 183 184 HashMap<DWORD, String>::iterator it = directories.find(pathIdentifier); 185 if (it != directories.end()) 186 return it->second; 187 188 String directory = storageDirectory(pathIdentifier); 189 directories.add(pathIdentifier, directory); 190 191 return directory; 192 } 193 194 String openTemporaryFile(const String&, PlatformFileHandle& handle) 195 { 196 handle = INVALID_HANDLE_VALUE; 197 198 char tempPath[MAX_PATH]; 199 int tempPathLength = ::GetTempPathA(WTF_ARRAY_LENGTH(tempPath), tempPath); 200 if (tempPathLength <= 0 || tempPathLength > WTF_ARRAY_LENGTH(tempPath)) 201 return String(); 202 203 HCRYPTPROV hCryptProv = 0; 204 if (!CryptAcquireContext(&hCryptProv, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) 205 return String(); 206 207 char proposedPath[MAX_PATH]; 208 while (1) { 209 char tempFile[] = "XXXXXXXX.tmp"; // Use 8.3 style name (more characters aren't helpful due to 8.3 short file names) 210 const int randomPartLength = 8; 211 if (!CryptGenRandom(hCryptProv, randomPartLength, reinterpret_cast<BYTE*>(tempFile))) 212 break; 213 214 // Limit to valid filesystem characters, also excluding others that could be problematic, like punctuation. 215 // don't include both upper and lowercase since Windows file systems are typically not case sensitive. 216 const char validChars[] = "0123456789abcdefghijklmnopqrstuvwxyz"; 217 for (int i = 0; i < randomPartLength; ++i) 218 tempFile[i] = validChars[tempFile[i] % (sizeof(validChars) - 1)]; 219 220 ASSERT(strlen(tempFile) == sizeof(tempFile) - 1); 221 222 if (!PathCombineA(proposedPath, tempPath, tempFile)) 223 break; 224 225 // use CREATE_NEW to avoid overwriting an existing file with the same name 226 handle = CreateFileA(proposedPath, GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0); 227 if (!isHandleValid(handle) && GetLastError() == ERROR_ALREADY_EXISTS) 228 continue; 229 230 break; 231 } 232 233 CryptReleaseContext(hCryptProv, 0); 234 235 if (!isHandleValid(handle)) 236 return String(); 237 238 return String::fromUTF8(proposedPath); 239 } 240 241 PlatformFileHandle openFile(const String& path, FileOpenMode mode) 242 { 243 DWORD desiredAccess = 0; 244 DWORD creationDisposition = 0; 245 switch (mode) { 246 case OpenForRead: 247 desiredAccess = GENERIC_READ; 248 creationDisposition = OPEN_EXISTING; 249 break; 250 case OpenForWrite: 251 desiredAccess = GENERIC_WRITE; 252 creationDisposition = CREATE_ALWAYS; 253 break; 254 default: 255 ASSERT_NOT_REACHED(); 256 } 257 258 String destination = path; 259 return CreateFile(destination.charactersWithNullTermination(), desiredAccess, 0, 0, creationDisposition, FILE_ATTRIBUTE_NORMAL, 0); 260 } 261 262 void closeFile(PlatformFileHandle& handle) 263 { 264 if (isHandleValid(handle)) { 265 ::CloseHandle(handle); 266 handle = invalidPlatformFileHandle; 267 } 268 } 269 270 int writeToFile(PlatformFileHandle handle, const char* data, int length) 271 { 272 if (!isHandleValid(handle)) 273 return -1; 274 275 DWORD bytesWritten; 276 bool success = WriteFile(handle, data, length, &bytesWritten, 0); 277 278 if (!success) 279 return -1; 280 return static_cast<int>(bytesWritten); 281 } 282 283 bool unloadModule(PlatformModule module) 284 { 285 return ::FreeLibrary(module); 286 } 287 288 String localUserSpecificStorageDirectory() 289 { 290 return cachedStorageDirectory(CSIDL_LOCAL_APPDATA); 291 } 292 293 String roamingUserSpecificStorageDirectory() 294 { 295 return cachedStorageDirectory(CSIDL_APPDATA); 296 } 297 298 bool safeCreateFile(const String& path, CFDataRef data) 299 { 300 // Create a temporary file. 301 WCHAR tempDirPath[MAX_PATH]; 302 if (!GetTempPathW(WTF_ARRAY_LENGTH(tempDirPath), tempDirPath)) 303 return false; 304 305 WCHAR tempPath[MAX_PATH]; 306 if (!GetTempFileNameW(tempDirPath, L"WEBKIT", 0, tempPath)) 307 return false; 308 309 HANDLE tempFileHandle = CreateFileW(tempPath, GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); 310 if (tempFileHandle == INVALID_HANDLE_VALUE) 311 return false; 312 313 // Write the data to this temp file. 314 DWORD written; 315 if (!WriteFile(tempFileHandle, CFDataGetBytePtr(data), static_cast<DWORD>(CFDataGetLength(data)), &written, 0)) 316 return false; 317 318 CloseHandle(tempFileHandle); 319 320 // Copy the temp file to the destination file. 321 String destination = path; 322 if (!MoveFileExW(tempPath, destination.charactersWithNullTermination(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED)) 323 return false; 324 325 return true; 326 } 327 328 Vector<String> listDirectory(const String& directory, const String& filter) 329 { 330 Vector<String> entries; 331 332 PathWalker walker(directory, filter); 333 if (!walker.isValid()) 334 return entries; 335 336 do { 337 if (walker.data().dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 338 continue; 339 340 entries.append(makeString(directory, "\\", reinterpret_cast<const UChar*>(walker.data().cFileName))); 341 } while (walker.step()); 342 343 return entries; 344 } 345 346 } // namespace WebCore 347