Home | History | Annotate | Download | only in win
      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