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