• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
statFile(String path,struct _stat64 & st)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 
getFileSize(const String & path,long long & result)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 
getFileModificationTime(const String & path,time_t & result)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 
fileExists(const String & path)71 bool fileExists(const String& path)
72 {
73     struct _stat64 st;
74     return statFile(path, st);
75 }
76 
deleteFile(const String & path)77 bool deleteFile(const String& path)
78 {
79     String filename = path;
80     return !!DeleteFileW(filename.charactersWithNullTermination());
81 }
82 
deleteEmptyDirectory(const String & path)83 bool deleteEmptyDirectory(const String& path)
84 {
85     String filename = path;
86     return !!RemoveDirectoryW(filename.charactersWithNullTermination());
87 }
88 
pathByAppendingComponent(const String & path,const String & component)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 
fileSystemRepresentation(const String &)108 CString fileSystemRepresentation(const String&)
109 {
110     return "";
111 }
112 
makeAllDirectories(const String & path)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 
homeDirectoryPath()126 String homeDirectoryPath()
127 {
128     notImplemented();
129     return "";
130 }
131 
pathGetFileName(const String & path)132 String pathGetFileName(const String& path)
133 {
134     return String(::PathFindFileName(String(path).charactersWithNullTermination()));
135 }
136 
directoryName(const String & path)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 
bundleName()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 
storageDirectory(DWORD pathIdentifier)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 
cachedStorageDirectory(DWORD pathIdentifier)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 
openTemporaryFile(const String &,PlatformFileHandle & handle)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 
openFile(const String & path,FileOpenMode mode)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 
closeFile(PlatformFileHandle & handle)262 void closeFile(PlatformFileHandle& handle)
263 {
264     if (isHandleValid(handle)) {
265         ::CloseHandle(handle);
266         handle = invalidPlatformFileHandle;
267     }
268 }
269 
writeToFile(PlatformFileHandle handle,const char * data,int length)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 
unloadModule(PlatformModule module)283 bool unloadModule(PlatformModule module)
284 {
285     return ::FreeLibrary(module);
286 }
287 
localUserSpecificStorageDirectory()288 String localUserSpecificStorageDirectory()
289 {
290     return cachedStorageDirectory(CSIDL_LOCAL_APPDATA);
291 }
292 
roamingUserSpecificStorageDirectory()293 String roamingUserSpecificStorageDirectory()
294 {
295     return cachedStorageDirectory(CSIDL_APPDATA);
296 }
297 
safeCreateFile(const String & path,CFDataRef data)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 
listDirectory(const String & directory,const String & filter)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