Home | History | Annotate | Download | only in base
      1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/file_util.h"
      6 
      7 #include <windows.h>
      8 #include <propvarutil.h>
      9 #include <shellapi.h>
     10 #include <shlobj.h>
     11 #include <time.h>
     12 #include <string>
     13 
     14 #include "base/file_path.h"
     15 #include "base/logging.h"
     16 #include "base/scoped_comptr_win.h"
     17 #include "base/scoped_handle.h"
     18 #include "base/string_util.h"
     19 #include "base/time.h"
     20 #include "base/win_util.h"
     21 
     22 namespace file_util {
     23 
     24 std::wstring GetDirectoryFromPath(const std::wstring& path) {
     25   wchar_t path_buffer[MAX_PATH];
     26   wchar_t* file_ptr = NULL;
     27   if (GetFullPathName(path.c_str(), MAX_PATH, path_buffer, &file_ptr) == 0)
     28     return L"";
     29 
     30   std::wstring::size_type length =
     31       file_ptr ? file_ptr - path_buffer : path.length();
     32   std::wstring directory(path, 0, length);
     33   return FilePath(directory).StripTrailingSeparators().value();
     34 }
     35 
     36 bool AbsolutePath(FilePath* path) {
     37   wchar_t file_path_buf[MAX_PATH];
     38   if (!_wfullpath(file_path_buf, path->value().c_str(), MAX_PATH))
     39     return false;
     40   *path = FilePath(file_path_buf);
     41   return true;
     42 }
     43 
     44 int CountFilesCreatedAfter(const FilePath& path,
     45                            const base::Time& comparison_time) {
     46   int file_count = 0;
     47   FILETIME comparison_filetime(comparison_time.ToFileTime());
     48 
     49   WIN32_FIND_DATA find_file_data;
     50   // All files in given dir
     51   std::wstring filename_spec = path.Append(L"*").value();
     52   HANDLE find_handle = FindFirstFile(filename_spec.c_str(), &find_file_data);
     53   if (find_handle != INVALID_HANDLE_VALUE) {
     54     do {
     55       // Don't count current or parent directories.
     56       if ((wcscmp(find_file_data.cFileName, L"..") == 0) ||
     57           (wcscmp(find_file_data.cFileName, L".") == 0))
     58         continue;
     59 
     60       long result = CompareFileTime(&find_file_data.ftCreationTime,
     61                                     &comparison_filetime);
     62       // File was created after or on comparison time
     63       if ((result == 1) || (result == 0))
     64         ++file_count;
     65     } while (FindNextFile(find_handle,  &find_file_data));
     66     FindClose(find_handle);
     67   }
     68 
     69   return file_count;
     70 }
     71 
     72 bool Delete(const FilePath& path, bool recursive) {
     73   if (path.value().length() >= MAX_PATH)
     74     return false;
     75 
     76   // If we're not recursing use DeleteFile; it should be faster. DeleteFile
     77   // fails if passed a directory though, which is why we fall through on
     78   // failure to the SHFileOperation.
     79   if (!recursive && DeleteFile(path.value().c_str()) != 0)
     80     return true;
     81 
     82   // SHFILEOPSTRUCT wants the path to be terminated with two NULLs,
     83   // so we have to use wcscpy because wcscpy_s writes non-NULLs
     84   // into the rest of the buffer.
     85   wchar_t double_terminated_path[MAX_PATH + 1] = {0};
     86 #pragma warning(suppress:4996)  // don't complain about wcscpy deprecation
     87   wcscpy(double_terminated_path, path.value().c_str());
     88 
     89   SHFILEOPSTRUCT file_operation = {0};
     90   file_operation.wFunc = FO_DELETE;
     91   file_operation.pFrom = double_terminated_path;
     92   file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION;
     93   if (!recursive)
     94     file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY;
     95   int err = SHFileOperation(&file_operation);
     96   // Some versions of Windows return ERROR_FILE_NOT_FOUND when
     97   // deleting an empty directory.
     98   return (err == 0 || err == ERROR_FILE_NOT_FOUND);
     99 }
    100 
    101 bool DeleteAfterReboot(const FilePath& path) {
    102   if (path.value().length() >= MAX_PATH)
    103     return false;
    104 
    105   return MoveFileEx(path.value().c_str(), NULL,
    106                     MOVEFILE_DELAY_UNTIL_REBOOT |
    107                         MOVEFILE_REPLACE_EXISTING) != FALSE;
    108 }
    109 
    110 bool Move(const FilePath& from_path, const FilePath& to_path) {
    111   // NOTE: I suspect we could support longer paths, but that would involve
    112   // analyzing all our usage of files.
    113   if (from_path.value().length() >= MAX_PATH ||
    114       to_path.value().length() >= MAX_PATH) {
    115     return false;
    116   }
    117   if (MoveFileEx(from_path.value().c_str(), to_path.value().c_str(),
    118                  MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING) != 0)
    119     return true;
    120   if (DirectoryExists(from_path)) {
    121     // MoveFileEx fails if moving directory across volumes. We will simulate
    122     // the move by using Copy and Delete. Ideally we could check whether
    123     // from_path and to_path are indeed in different volumes.
    124     return CopyAndDeleteDirectory(from_path, to_path);
    125   }
    126   return false;
    127 }
    128 
    129 bool ReplaceFile(const FilePath& from_path, const FilePath& to_path) {
    130   // Make sure that the target file exists.
    131   HANDLE target_file = ::CreateFile(to_path.value().c_str(), 0,
    132                                     FILE_SHARE_READ | FILE_SHARE_WRITE,
    133                                     NULL, CREATE_NEW,
    134                                     FILE_ATTRIBUTE_NORMAL, NULL);
    135   if (target_file != INVALID_HANDLE_VALUE)
    136     ::CloseHandle(target_file);
    137   // When writing to a network share, we may not be able to change the ACLs.
    138   // Ignore ACL errors then (REPLACEFILE_IGNORE_MERGE_ERRORS).
    139   return ::ReplaceFile(to_path.value().c_str(),
    140       from_path.value().c_str(), NULL,
    141       REPLACEFILE_IGNORE_MERGE_ERRORS, NULL, NULL) ? true : false;
    142 }
    143 
    144 bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
    145   // NOTE: I suspect we could support longer paths, but that would involve
    146   // analyzing all our usage of files.
    147   if (from_path.value().length() >= MAX_PATH ||
    148       to_path.value().length() >= MAX_PATH) {
    149     return false;
    150   }
    151   return (::CopyFile(from_path.value().c_str(), to_path.value().c_str(),
    152                      false) != 0);
    153 }
    154 
    155 bool ShellCopy(const FilePath& from_path, const FilePath& to_path,
    156                bool recursive) {
    157   // NOTE: I suspect we could support longer paths, but that would involve
    158   // analyzing all our usage of files.
    159   if (from_path.value().length() >= MAX_PATH ||
    160       to_path.value().length() >= MAX_PATH) {
    161     return false;
    162   }
    163 
    164   // SHFILEOPSTRUCT wants the path to be terminated with two NULLs,
    165   // so we have to use wcscpy because wcscpy_s writes non-NULLs
    166   // into the rest of the buffer.
    167   wchar_t double_terminated_path_from[MAX_PATH + 1] = {0};
    168   wchar_t double_terminated_path_to[MAX_PATH + 1] = {0};
    169 #pragma warning(suppress:4996)  // don't complain about wcscpy deprecation
    170   wcscpy(double_terminated_path_from, from_path.value().c_str());
    171 #pragma warning(suppress:4996)  // don't complain about wcscpy deprecation
    172   wcscpy(double_terminated_path_to, to_path.value().c_str());
    173 
    174   SHFILEOPSTRUCT file_operation = {0};
    175   file_operation.wFunc = FO_COPY;
    176   file_operation.pFrom = double_terminated_path_from;
    177   file_operation.pTo = double_terminated_path_to;
    178   file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION |
    179                           FOF_NOCONFIRMMKDIR;
    180   if (!recursive)
    181     file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY;
    182 
    183   return (SHFileOperation(&file_operation) == 0);
    184 }
    185 
    186 bool CopyDirectory(const FilePath& from_path, const FilePath& to_path,
    187                    bool recursive) {
    188   if (recursive)
    189     return ShellCopy(from_path, to_path, true);
    190 
    191   // The following code assumes that from path is a directory.
    192   DCHECK(DirectoryExists(from_path));
    193 
    194   // Instead of creating a new directory, we copy the old one to include the
    195   // security information of the folder as part of the copy.
    196   if (!PathExists(to_path)) {
    197     // Except that Vista fails to do that, and instead do a recursive copy if
    198     // the target directory doesn't exist.
    199     if (win_util::GetWinVersion() >= win_util::WINVERSION_VISTA)
    200       CreateDirectory(to_path);
    201     else
    202       ShellCopy(from_path, to_path, false);
    203   }
    204 
    205   FilePath directory = from_path.Append(L"*.*");
    206   return ShellCopy(directory, to_path, false);
    207 }
    208 
    209 bool CopyAndDeleteDirectory(const FilePath& from_path,
    210                             const FilePath& to_path) {
    211   if (CopyDirectory(from_path, to_path, true)) {
    212     if (Delete(from_path, true)) {
    213       return true;
    214     }
    215     // Like Move, this function is not transactional, so we just
    216     // leave the copied bits behind if deleting from_path fails.
    217     // If to_path exists previously then we have already overwritten
    218     // it by now, we don't get better off by deleting the new bits.
    219   }
    220   return false;
    221 }
    222 
    223 
    224 bool PathExists(const FilePath& path) {
    225   return (GetFileAttributes(path.value().c_str()) != INVALID_FILE_ATTRIBUTES);
    226 }
    227 
    228 bool PathIsWritable(const FilePath& path) {
    229   HANDLE dir =
    230       CreateFile(path.value().c_str(), FILE_ADD_FILE,
    231                  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
    232                  NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
    233 
    234   if (dir == INVALID_HANDLE_VALUE)
    235     return false;
    236 
    237   CloseHandle(dir);
    238   return true;
    239 }
    240 
    241 bool DirectoryExists(const FilePath& path) {
    242   DWORD fileattr = GetFileAttributes(path.value().c_str());
    243   if (fileattr != INVALID_FILE_ATTRIBUTES)
    244     return (fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0;
    245   return false;
    246 }
    247 
    248 bool GetFileCreationLocalTimeFromHandle(HANDLE file_handle,
    249                                         LPSYSTEMTIME creation_time) {
    250   if (!file_handle)
    251     return false;
    252 
    253   FILETIME utc_filetime;
    254   if (!GetFileTime(file_handle, &utc_filetime, NULL, NULL))
    255     return false;
    256 
    257   FILETIME local_filetime;
    258   if (!FileTimeToLocalFileTime(&utc_filetime, &local_filetime))
    259     return false;
    260 
    261   return !!FileTimeToSystemTime(&local_filetime, creation_time);
    262 }
    263 
    264 bool GetFileCreationLocalTime(const std::wstring& filename,
    265                               LPSYSTEMTIME creation_time) {
    266   ScopedHandle file_handle(
    267       CreateFile(filename.c_str(), GENERIC_READ,
    268                  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
    269                  OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
    270   return GetFileCreationLocalTimeFromHandle(file_handle.Get(), creation_time);
    271 }
    272 
    273 bool ResolveShortcut(FilePath* path) {
    274   HRESULT result;
    275   ScopedComPtr<IShellLink> i_shell_link;
    276   bool is_resolved = false;
    277 
    278   // Get pointer to the IShellLink interface
    279   result = i_shell_link.CreateInstance(CLSID_ShellLink, NULL,
    280                                        CLSCTX_INPROC_SERVER);
    281   if (SUCCEEDED(result)) {
    282     ScopedComPtr<IPersistFile> persist;
    283     // Query IShellLink for the IPersistFile interface
    284     result = persist.QueryFrom(i_shell_link);
    285     if (SUCCEEDED(result)) {
    286       WCHAR temp_path[MAX_PATH];
    287       // Load the shell link
    288       result = persist->Load(path->value().c_str(), STGM_READ);
    289       if (SUCCEEDED(result)) {
    290         // Try to find the target of a shortcut
    291         result = i_shell_link->Resolve(0, SLR_NO_UI);
    292         if (SUCCEEDED(result)) {
    293           result = i_shell_link->GetPath(temp_path, MAX_PATH,
    294                                   NULL, SLGP_UNCPRIORITY);
    295           *path = FilePath(temp_path);
    296           is_resolved = true;
    297         }
    298       }
    299     }
    300   }
    301 
    302   return is_resolved;
    303 }
    304 
    305 bool CreateShortcutLink(const wchar_t *source, const wchar_t *destination,
    306                         const wchar_t *working_dir, const wchar_t *arguments,
    307                         const wchar_t *description, const wchar_t *icon,
    308                         int icon_index, const wchar_t* app_id) {
    309   ScopedComPtr<IShellLink> i_shell_link;
    310   ScopedComPtr<IPersistFile> i_persist_file;
    311 
    312   // Get pointer to the IShellLink interface
    313   HRESULT result = i_shell_link.CreateInstance(CLSID_ShellLink, NULL,
    314                                                CLSCTX_INPROC_SERVER);
    315   if (FAILED(result))
    316     return false;
    317 
    318   // Query IShellLink for the IPersistFile interface
    319   result = i_persist_file.QueryFrom(i_shell_link);
    320   if (FAILED(result))
    321     return false;
    322 
    323   if (FAILED(i_shell_link->SetPath(source)))
    324     return false;
    325 
    326   if (working_dir && FAILED(i_shell_link->SetWorkingDirectory(working_dir)))
    327     return false;
    328 
    329   if (arguments && FAILED(i_shell_link->SetArguments(arguments)))
    330     return false;
    331 
    332   if (description && FAILED(i_shell_link->SetDescription(description)))
    333     return false;
    334 
    335   if (icon && FAILED(i_shell_link->SetIconLocation(icon, icon_index)))
    336     return false;
    337 
    338   if (app_id && (win_util::GetWinVersion() >= win_util::WINVERSION_WIN7)) {
    339     ScopedComPtr<IPropertyStore> property_store;
    340     if (FAILED(property_store.QueryFrom(i_shell_link)))
    341       return false;
    342 
    343     if (!win_util::SetAppIdForPropertyStore(property_store, app_id))
    344       return false;
    345   }
    346 
    347   result = i_persist_file->Save(destination, TRUE);
    348   return SUCCEEDED(result);
    349 }
    350 
    351 
    352 bool UpdateShortcutLink(const wchar_t *source, const wchar_t *destination,
    353                         const wchar_t *working_dir, const wchar_t *arguments,
    354                         const wchar_t *description, const wchar_t *icon,
    355                         int icon_index, const wchar_t* app_id) {
    356   // Get pointer to the IPersistFile interface and load existing link
    357   ScopedComPtr<IShellLink> i_shell_link;
    358   if (FAILED(i_shell_link.CreateInstance(CLSID_ShellLink, NULL,
    359                                          CLSCTX_INPROC_SERVER)))
    360     return false;
    361 
    362   ScopedComPtr<IPersistFile> i_persist_file;
    363   if (FAILED(i_persist_file.QueryFrom(i_shell_link)))
    364     return false;
    365 
    366   if (FAILED(i_persist_file->Load(destination, STGM_READWRITE)))
    367     return false;
    368 
    369   if (source && FAILED(i_shell_link->SetPath(source)))
    370     return false;
    371 
    372   if (working_dir && FAILED(i_shell_link->SetWorkingDirectory(working_dir)))
    373     return false;
    374 
    375   if (arguments && FAILED(i_shell_link->SetArguments(arguments)))
    376     return false;
    377 
    378   if (description && FAILED(i_shell_link->SetDescription(description)))
    379     return false;
    380 
    381   if (icon && FAILED(i_shell_link->SetIconLocation(icon, icon_index)))
    382     return false;
    383 
    384   if (app_id && win_util::GetWinVersion() >= win_util::WINVERSION_WIN7) {
    385     ScopedComPtr<IPropertyStore> property_store;
    386     if (FAILED(property_store.QueryFrom(i_shell_link)))
    387       return false;
    388 
    389     if (!win_util::SetAppIdForPropertyStore(property_store, app_id))
    390       return false;
    391   }
    392 
    393   HRESULT result = i_persist_file->Save(destination, TRUE);
    394   return SUCCEEDED(result);
    395 }
    396 
    397 bool TaskbarPinShortcutLink(const wchar_t* shortcut) {
    398   // "Pin to taskbar" is only supported after Win7.
    399   if (win_util::GetWinVersion() < win_util::WINVERSION_WIN7)
    400     return false;
    401 
    402   int result = reinterpret_cast<int>(ShellExecute(NULL, L"taskbarpin", shortcut,
    403       NULL, NULL, 0));
    404   return result > 32;
    405 }
    406 
    407 bool TaskbarUnpinShortcutLink(const wchar_t* shortcut) {
    408   // "Unpin from taskbar" is only supported after Win7.
    409   if (win_util::GetWinVersion() < win_util::WINVERSION_WIN7)
    410     return false;
    411 
    412   int result = reinterpret_cast<int>(ShellExecute(NULL, L"taskbarunpin",
    413       shortcut, NULL, NULL, 0));
    414   return result > 32;
    415 }
    416 
    417 bool IsDirectoryEmpty(const std::wstring& dir_path) {
    418   FileEnumerator files(FilePath(dir_path), false,
    419     static_cast<FileEnumerator::FILE_TYPE>(
    420         FileEnumerator::FILES | FileEnumerator::DIRECTORIES));
    421   if (files.Next().value().empty())
    422     return true;
    423   return false;
    424 }
    425 
    426 bool GetTempDir(FilePath* path) {
    427   wchar_t temp_path[MAX_PATH + 1];
    428   DWORD path_len = ::GetTempPath(MAX_PATH, temp_path);
    429   if (path_len >= MAX_PATH || path_len <= 0)
    430     return false;
    431   // TODO(evanm): the old behavior of this function was to always strip the
    432   // trailing slash.  We duplicate this here, but it shouldn't be necessary
    433   // when everyone is using the appropriate FilePath APIs.
    434   *path = FilePath(temp_path).StripTrailingSeparators();
    435   return true;
    436 }
    437 
    438 bool GetShmemTempDir(FilePath* path) {
    439   return GetTempDir(path);
    440 }
    441 
    442 bool CreateTemporaryFile(FilePath* path) {
    443   FilePath temp_file;
    444 
    445   if (!GetTempDir(path))
    446     return false;
    447 
    448   if (CreateTemporaryFileInDir(*path, &temp_file)) {
    449     *path = temp_file;
    450     return true;
    451   }
    452 
    453   return false;
    454 }
    455 
    456 FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) {
    457   return CreateAndOpenTemporaryFile(path);
    458 }
    459 
    460 // On POSIX we have semantics to create and open a temporary file
    461 // atomically.
    462 // TODO(jrg): is there equivalent call to use on Windows instead of
    463 // going 2-step?
    464 FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
    465   if (!CreateTemporaryFileInDir(dir, path)) {
    466     return NULL;
    467   }
    468   // Open file in binary mode, to avoid problems with fwrite. On Windows
    469   // it replaces \n's with \r\n's, which may surprise you.
    470   // Reference: http://msdn.microsoft.com/en-us/library/h9t88zwz(VS.71).aspx
    471   return OpenFile(*path, "wb+");
    472 }
    473 
    474 bool CreateTemporaryFileInDir(const FilePath& dir,
    475                               FilePath* temp_file) {
    476   wchar_t temp_name[MAX_PATH + 1];
    477 
    478   if (!GetTempFileName(dir.value().c_str(), L"", 0, temp_name)) {
    479     PLOG(WARNING) << "Failed to get temporary file name in " << dir.value();
    480     return false;
    481   }
    482 
    483   DWORD path_len = GetLongPathName(temp_name, temp_name, MAX_PATH);
    484   if (path_len > MAX_PATH + 1 || path_len == 0) {
    485     PLOG(WARNING) << "Failed to get long path name for " << temp_name;
    486     return false;
    487   }
    488 
    489   std::wstring temp_file_str;
    490   temp_file_str.assign(temp_name, path_len);
    491   *temp_file = FilePath(temp_file_str);
    492   return true;
    493 }
    494 
    495 bool CreateNewTempDirectory(const FilePath::StringType& prefix,
    496                             FilePath* new_temp_path) {
    497   FilePath system_temp_dir;
    498   if (!GetTempDir(&system_temp_dir))
    499     return false;
    500 
    501   FilePath path_to_create;
    502   srand(static_cast<uint32>(time(NULL)));
    503 
    504   int count = 0;
    505   while (count < 50) {
    506     // Try create a new temporary directory with random generated name. If
    507     // the one exists, keep trying another path name until we reach some limit.
    508     path_to_create = system_temp_dir;
    509     std::wstring new_dir_name;
    510     new_dir_name.assign(prefix);
    511     new_dir_name.append(IntToWString(rand() % kint16max));
    512     path_to_create = path_to_create.Append(new_dir_name);
    513 
    514     if (::CreateDirectory(path_to_create.value().c_str(), NULL))
    515       break;
    516     count++;
    517   }
    518 
    519   if (count == 50) {
    520     return false;
    521   }
    522 
    523   *new_temp_path = path_to_create;
    524   return true;
    525 }
    526 
    527 bool CreateDirectory(const FilePath& full_path) {
    528   // If the path exists, we've succeeded if it's a directory, failed otherwise.
    529   const wchar_t* full_path_str = full_path.value().c_str();
    530   DWORD fileattr = ::GetFileAttributes(full_path_str);
    531   if (fileattr != INVALID_FILE_ATTRIBUTES) {
    532     if ((fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
    533       DLOG(INFO) << "CreateDirectory(" << full_path_str << "), " <<
    534           "directory already exists.";
    535       return true;
    536     } else {
    537       LOG(WARNING) << "CreateDirectory(" << full_path_str << "), " <<
    538           "conflicts with existing file.";
    539     }
    540   }
    541 
    542   // Invariant:  Path does not exist as file or directory.
    543 
    544   // Attempt to create the parent recursively.  This will immediately return
    545   // true if it already exists, otherwise will create all required parent
    546   // directories starting with the highest-level missing parent.
    547   FilePath parent_path(full_path.DirName());
    548   if (parent_path.value() == full_path.value()) {
    549     return false;
    550   }
    551   if (!CreateDirectory(parent_path)) {
    552     DLOG(WARNING) << "Failed to create one of the parent directories.";
    553     return false;
    554   }
    555 
    556   if (!::CreateDirectory(full_path_str, NULL)) {
    557     DWORD error_code = ::GetLastError();
    558     if (error_code == ERROR_ALREADY_EXISTS && DirectoryExists(full_path)) {
    559       // This error code doesn't indicate whether we were racing with someone
    560       // creating the same directory, or a file with the same path, therefore
    561       // we check.
    562       return true;
    563     } else {
    564       LOG(WARNING) << "Failed to create directory " << full_path_str <<
    565           ", le=" << error_code;
    566       return false;
    567     }
    568   } else {
    569     return true;
    570   }
    571 }
    572 
    573 bool GetFileInfo(const FilePath& file_path, FileInfo* results) {
    574   WIN32_FILE_ATTRIBUTE_DATA attr;
    575   if (!GetFileAttributesEx(file_path.ToWStringHack().c_str(),
    576                            GetFileExInfoStandard, &attr)) {
    577     return false;
    578   }
    579 
    580   ULARGE_INTEGER size;
    581   size.HighPart = attr.nFileSizeHigh;
    582   size.LowPart = attr.nFileSizeLow;
    583   results->size = size.QuadPart;
    584 
    585   results->is_directory =
    586       (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
    587   results->last_modified = base::Time::FromFileTime(attr.ftLastWriteTime);
    588 
    589   return true;
    590 }
    591 
    592 FILE* OpenFile(const FilePath& filename, const char* mode) {
    593   std::wstring w_mode = ASCIIToWide(std::string(mode));
    594   FILE* file;
    595   if (_wfopen_s(&file, filename.value().c_str(), w_mode.c_str()) != 0) {
    596     return NULL;
    597   }
    598   return file;
    599 }
    600 
    601 FILE* OpenFile(const std::string& filename, const char* mode) {
    602   FILE* file;
    603   if (fopen_s(&file, filename.c_str(), mode) != 0) {
    604     return NULL;
    605   }
    606   return file;
    607 }
    608 
    609 int ReadFile(const FilePath& filename, char* data, int size) {
    610   ScopedHandle file(CreateFile(filename.value().c_str(),
    611                                GENERIC_READ,
    612                                FILE_SHARE_READ | FILE_SHARE_WRITE,
    613                                NULL,
    614                                OPEN_EXISTING,
    615                                FILE_FLAG_SEQUENTIAL_SCAN,
    616                                NULL));
    617   if (file == INVALID_HANDLE_VALUE)
    618     return -1;
    619 
    620   int ret_value;
    621   DWORD read;
    622   if (::ReadFile(file, data, size, &read, NULL) && read == size) {
    623     ret_value = static_cast<int>(read);
    624   } else {
    625     ret_value = -1;
    626   }
    627 
    628   return ret_value;
    629 }
    630 
    631 int WriteFile(const FilePath& filename, const char* data, int size) {
    632   ScopedHandle file(CreateFile(filename.value().c_str(),
    633                                GENERIC_WRITE,
    634                                0,
    635                                NULL,
    636                                CREATE_ALWAYS,
    637                                0,
    638                                NULL));
    639   if (file == INVALID_HANDLE_VALUE) {
    640     LOG(WARNING) << "CreateFile failed for path " << filename.value() <<
    641         " error code=" << GetLastError() <<
    642         " error text=" << win_util::FormatLastWin32Error();
    643     return -1;
    644   }
    645 
    646   DWORD written;
    647   BOOL result = ::WriteFile(file, data, size, &written, NULL);
    648   if (result && written == size)
    649     return static_cast<int>(written);
    650 
    651   if (!result) {
    652     // WriteFile failed.
    653     LOG(WARNING) << "writing file " << filename.value() <<
    654         " failed, error code=" << GetLastError() <<
    655         " description=" << win_util::FormatLastWin32Error();
    656   } else {
    657     // Didn't write all the bytes.
    658     LOG(WARNING) << "wrote" << written << " bytes to " <<
    659         filename.value() << " expected " << size;
    660   }
    661   return -1;
    662 }
    663 
    664 bool RenameFileAndResetSecurityDescriptor(const FilePath& source_file_path,
    665                                           const FilePath& target_file_path) {
    666   // The parameters to SHFileOperation must be terminated with 2 NULL chars.
    667   std::wstring source = source_file_path.value();
    668   std::wstring target = target_file_path.value();
    669 
    670   source.append(1, L'\0');
    671   target.append(1, L'\0');
    672 
    673   SHFILEOPSTRUCT move_info = {0};
    674   move_info.wFunc = FO_MOVE;
    675   move_info.pFrom = source.c_str();
    676   move_info.pTo = target.c_str();
    677   move_info.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI |
    678                      FOF_NOCONFIRMMKDIR | FOF_NOCOPYSECURITYATTRIBS;
    679 
    680   if (0 != SHFileOperation(&move_info))
    681     return false;
    682 
    683   return true;
    684 }
    685 
    686 // Gets the current working directory for the process.
    687 bool GetCurrentDirectory(FilePath* dir) {
    688   wchar_t system_buffer[MAX_PATH];
    689   system_buffer[0] = 0;
    690   DWORD len = ::GetCurrentDirectory(MAX_PATH, system_buffer);
    691   if (len == 0 || len > MAX_PATH)
    692     return false;
    693   // TODO(evanm): the old behavior of this function was to always strip the
    694   // trailing slash.  We duplicate this here, but it shouldn't be necessary
    695   // when everyone is using the appropriate FilePath APIs.
    696   std::wstring dir_str(system_buffer);
    697   *dir = FilePath(dir_str).StripTrailingSeparators();
    698   return true;
    699 }
    700 
    701 // Sets the current working directory for the process.
    702 bool SetCurrentDirectory(const FilePath& directory) {
    703   BOOL ret = ::SetCurrentDirectory(directory.value().c_str());
    704   return ret != 0;
    705 }
    706 
    707 ///////////////////////////////////////////////
    708 // FileEnumerator
    709 
    710 FileEnumerator::FileEnumerator(const FilePath& root_path,
    711                                bool recursive,
    712                                FileEnumerator::FILE_TYPE file_type)
    713     : recursive_(recursive),
    714       file_type_(file_type),
    715       is_in_find_op_(false),
    716       find_handle_(INVALID_HANDLE_VALUE) {
    717   // INCLUDE_DOT_DOT must not be specified if recursive.
    718   DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
    719   pending_paths_.push(root_path);
    720 }
    721 
    722 FileEnumerator::FileEnumerator(const FilePath& root_path,
    723                                bool recursive,
    724                                FileEnumerator::FILE_TYPE file_type,
    725                                const FilePath::StringType& pattern)
    726     : recursive_(recursive),
    727       file_type_(file_type),
    728       is_in_find_op_(false),
    729       pattern_(pattern),
    730       find_handle_(INVALID_HANDLE_VALUE) {
    731   // INCLUDE_DOT_DOT must not be specified if recursive.
    732   DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
    733   pending_paths_.push(root_path);
    734 }
    735 
    736 FileEnumerator::~FileEnumerator() {
    737   if (find_handle_ != INVALID_HANDLE_VALUE)
    738     FindClose(find_handle_);
    739 }
    740 
    741 void FileEnumerator::GetFindInfo(FindInfo* info) {
    742   DCHECK(info);
    743 
    744   if (!is_in_find_op_)
    745     return;
    746 
    747   memcpy(info, &find_data_, sizeof(*info));
    748 }
    749 
    750 bool FileEnumerator::IsDirectory(const FindInfo& info) {
    751   return (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
    752 }
    753 
    754 FilePath FileEnumerator::Next() {
    755   if (!is_in_find_op_) {
    756     if (pending_paths_.empty())
    757       return FilePath();
    758 
    759     // The last find FindFirstFile operation is done, prepare a new one.
    760     root_path_ = pending_paths_.top();
    761     pending_paths_.pop();
    762 
    763     // Start a new find operation.
    764     FilePath src = root_path_;
    765 
    766     if (pattern_.empty())
    767       src = src.Append(L"*");  // No pattern = match everything.
    768     else
    769       src = src.Append(pattern_);
    770 
    771     find_handle_ = FindFirstFile(src.value().c_str(), &find_data_);
    772     is_in_find_op_ = true;
    773 
    774   } else {
    775     // Search for the next file/directory.
    776     if (!FindNextFile(find_handle_, &find_data_)) {
    777       FindClose(find_handle_);
    778       find_handle_ = INVALID_HANDLE_VALUE;
    779     }
    780   }
    781 
    782   if (INVALID_HANDLE_VALUE == find_handle_) {
    783     is_in_find_op_ = false;
    784 
    785     // This is reached when we have finished a directory and are advancing to
    786     // the next one in the queue. We applied the pattern (if any) to the files
    787     // in the root search directory, but for those directories which were
    788     // matched, we want to enumerate all files inside them. This will happen
    789     // when the handle is empty.
    790     pattern_ = FilePath::StringType();
    791 
    792     return Next();
    793   }
    794 
    795   FilePath cur_file(find_data_.cFileName);
    796   if (ShouldSkip(cur_file))
    797     return Next();
    798 
    799   // Construct the absolute filename.
    800   cur_file = root_path_.Append(cur_file);
    801 
    802   if (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
    803     if (recursive_) {
    804       // If |cur_file| is a directory, and we are doing recursive searching, add
    805       // it to pending_paths_ so we scan it after we finish scanning this
    806       // directory.
    807       pending_paths_.push(cur_file);
    808     }
    809     return (file_type_ & FileEnumerator::DIRECTORIES) ? cur_file : Next();
    810   }
    811   return (file_type_ & FileEnumerator::FILES) ? cur_file : Next();
    812 }
    813 
    814 ///////////////////////////////////////////////
    815 // MemoryMappedFile
    816 
    817 MemoryMappedFile::MemoryMappedFile()
    818     : file_(INVALID_HANDLE_VALUE),
    819       file_mapping_(INVALID_HANDLE_VALUE),
    820       data_(NULL),
    821       length_(INVALID_FILE_SIZE) {
    822 }
    823 
    824 bool MemoryMappedFile::MapFileToMemoryInternal() {
    825   if (file_ == INVALID_HANDLE_VALUE)
    826     return false;
    827 
    828   length_ = ::GetFileSize(file_, NULL);
    829   if (length_ == INVALID_FILE_SIZE)
    830     return false;
    831 
    832   // length_ value comes from GetFileSize() above. GetFileSize() returns DWORD,
    833   // therefore the cast here is safe.
    834   file_mapping_ = ::CreateFileMapping(file_, NULL, PAGE_READONLY,
    835                                       0, static_cast<DWORD>(length_), NULL);
    836   if (file_mapping_ == INVALID_HANDLE_VALUE)
    837     return false;
    838 
    839   data_ = static_cast<uint8*>(
    840       ::MapViewOfFile(file_mapping_, FILE_MAP_READ, 0, 0, length_));
    841   return data_ != NULL;
    842 }
    843 
    844 void MemoryMappedFile::CloseHandles() {
    845   if (data_)
    846     ::UnmapViewOfFile(data_);
    847   if (file_mapping_ != INVALID_HANDLE_VALUE)
    848     ::CloseHandle(file_mapping_);
    849   if (file_ != INVALID_HANDLE_VALUE)
    850     ::CloseHandle(file_);
    851 
    852   data_ = NULL;
    853   file_mapping_ = file_ = INVALID_HANDLE_VALUE;
    854   length_ = INVALID_FILE_SIZE;
    855 }
    856 
    857 bool HasFileBeenModifiedSince(const FileEnumerator::FindInfo& find_info,
    858                               const base::Time& cutoff_time) {
    859   long result = CompareFileTime(&find_info.ftLastWriteTime,
    860                                 &cutoff_time.ToFileTime());
    861   return result == 1 || result == 0;
    862 }
    863 
    864 }  // namespace file_util
    865