file_util_win.cc revision 3345a6884c488ff3a535c2c9acdd33d74b37e311
1// Copyright (c) 2010 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 <psapi.h>
10#include <shellapi.h>
11#include <shlobj.h>
12#include <time.h>
13#include <string>
14
15#include "base/file_path.h"
16#include "base/logging.h"
17#include "base/pe_image.h"
18#include "base/scoped_comptr_win.h"
19#include "base/scoped_handle.h"
20#include "base/string_number_conversions.h"
21#include "base/string_util.h"
22#include "base/time.h"
23#include "base/utf_string_conversions.h"
24#include "base/win_util.h"
25
26namespace file_util {
27
28namespace {
29
30const DWORD kFileShareAll =
31    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
32
33// Helper for NormalizeFilePath(), defined below.
34bool DevicePathToDriveLetterPath(const FilePath& device_path,
35                                 FilePath* drive_letter_path) {
36  // Get the mapping of drive letters to device paths.
37  const int kDriveMappingSize = 1024;
38  wchar_t drive_mapping[kDriveMappingSize] = {'\0'};
39  if (!::GetLogicalDriveStrings(kDriveMappingSize - 1, drive_mapping)) {
40    LOG(ERROR) << "Failed to get drive mapping.";
41    return false;
42  }
43
44  // The drive mapping is a sequence of null terminated strings.
45  // The last string is empty.
46  wchar_t* drive_map_ptr = drive_mapping;
47  wchar_t device_name[MAX_PATH];
48  wchar_t drive[] = L" :";
49
50  // For each string in the drive mapping, get the junction that links
51  // to it.  If that junction is a prefix of |device_path|, then we
52  // know that |drive| is the real path prefix.
53  while(*drive_map_ptr) {
54    drive[0] = drive_map_ptr[0];  // Copy the drive letter.
55
56    if (QueryDosDevice(drive, device_name, MAX_PATH) &&
57        StartsWith(device_path.value(), device_name, true)) {
58      *drive_letter_path = FilePath(drive +
59          device_path.value().substr(wcslen(device_name)));
60      return true;
61    }
62    // Move to the next drive letter string, which starts one
63    // increment after the '\0' that terminates the current string.
64    while(*drive_map_ptr++);
65  }
66
67  // No drive matched.  The path does not start with a device junction
68  // that is mounted as a drive letter.  This means there is no drive
69  // letter path to the volume that holds |device_path|, so fail.
70  return false;
71}
72
73}  // namespace
74
75std::wstring GetDirectoryFromPath(const std::wstring& path) {
76  wchar_t path_buffer[MAX_PATH];
77  wchar_t* file_ptr = NULL;
78  if (GetFullPathName(path.c_str(), MAX_PATH, path_buffer, &file_ptr) == 0)
79    return L"";
80
81  std::wstring::size_type length =
82      file_ptr ? file_ptr - path_buffer : path.length();
83  std::wstring directory(path, 0, length);
84  return FilePath(directory).StripTrailingSeparators().value();
85}
86
87bool AbsolutePath(FilePath* path) {
88  wchar_t file_path_buf[MAX_PATH];
89  if (!_wfullpath(file_path_buf, path->value().c_str(), MAX_PATH))
90    return false;
91  *path = FilePath(file_path_buf);
92  return true;
93}
94
95int CountFilesCreatedAfter(const FilePath& path,
96                           const base::Time& comparison_time) {
97  int file_count = 0;
98  FILETIME comparison_filetime(comparison_time.ToFileTime());
99
100  WIN32_FIND_DATA find_file_data;
101  // All files in given dir
102  std::wstring filename_spec = path.Append(L"*").value();
103  HANDLE find_handle = FindFirstFile(filename_spec.c_str(), &find_file_data);
104  if (find_handle != INVALID_HANDLE_VALUE) {
105    do {
106      // Don't count current or parent directories.
107      if ((wcscmp(find_file_data.cFileName, L"..") == 0) ||
108          (wcscmp(find_file_data.cFileName, L".") == 0))
109        continue;
110
111      long result = CompareFileTime(&find_file_data.ftCreationTime,
112                                    &comparison_filetime);
113      // File was created after or on comparison time
114      if ((result == 1) || (result == 0))
115        ++file_count;
116    } while (FindNextFile(find_handle,  &find_file_data));
117    FindClose(find_handle);
118  }
119
120  return file_count;
121}
122
123bool Delete(const FilePath& path, bool recursive) {
124  if (path.value().length() >= MAX_PATH)
125    return false;
126
127  if (!recursive) {
128    // If not recursing, then first check to see if |path| is a directory.
129    // If it is, then remove it with RemoveDirectory.
130    base::PlatformFileInfo file_info;
131    if (GetFileInfo(path, &file_info) && file_info.is_directory)
132      return RemoveDirectory(path.value().c_str()) != 0;
133
134    // Otherwise, it's a file, wildcard or non-existant. Try DeleteFile first
135    // because it should be faster. If DeleteFile fails, then we fall through
136    // to SHFileOperation, which will do the right thing.
137    if (DeleteFile(path.value().c_str()) != 0)
138      return true;
139  }
140
141  // SHFILEOPSTRUCT wants the path to be terminated with two NULLs,
142  // so we have to use wcscpy because wcscpy_s writes non-NULLs
143  // into the rest of the buffer.
144  wchar_t double_terminated_path[MAX_PATH + 1] = {0};
145#pragma warning(suppress:4996)  // don't complain about wcscpy deprecation
146  wcscpy(double_terminated_path, path.value().c_str());
147
148  SHFILEOPSTRUCT file_operation = {0};
149  file_operation.wFunc = FO_DELETE;
150  file_operation.pFrom = double_terminated_path;
151  file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION;
152  if (!recursive)
153    file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY;
154  int err = SHFileOperation(&file_operation);
155  // Some versions of Windows return ERROR_FILE_NOT_FOUND (0x2) when deleting
156  // an empty directory and some return 0x402 when they should be returning
157  // ERROR_FILE_NOT_FOUND. MSDN says Vista and up won't return 0x402.
158  return (err == 0 || err == ERROR_FILE_NOT_FOUND || err == 0x402);
159}
160
161bool DeleteAfterReboot(const FilePath& path) {
162  if (path.value().length() >= MAX_PATH)
163    return false;
164
165  return MoveFileEx(path.value().c_str(), NULL,
166                    MOVEFILE_DELAY_UNTIL_REBOOT |
167                        MOVEFILE_REPLACE_EXISTING) != FALSE;
168}
169
170bool Move(const FilePath& from_path, const FilePath& to_path) {
171  // NOTE: I suspect we could support longer paths, but that would involve
172  // analyzing all our usage of files.
173  if (from_path.value().length() >= MAX_PATH ||
174      to_path.value().length() >= MAX_PATH) {
175    return false;
176  }
177  if (MoveFileEx(from_path.value().c_str(), to_path.value().c_str(),
178                 MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING) != 0)
179    return true;
180  if (DirectoryExists(from_path)) {
181    // MoveFileEx fails if moving directory across volumes. We will simulate
182    // the move by using Copy and Delete. Ideally we could check whether
183    // from_path and to_path are indeed in different volumes.
184    return CopyAndDeleteDirectory(from_path, to_path);
185  }
186  return false;
187}
188
189bool ReplaceFile(const FilePath& from_path, const FilePath& to_path) {
190  // Make sure that the target file exists.
191  HANDLE target_file = ::CreateFile(
192      to_path.value().c_str(),
193      0,
194      FILE_SHARE_READ | FILE_SHARE_WRITE,
195      NULL,
196      CREATE_NEW,
197      FILE_ATTRIBUTE_NORMAL,
198      NULL);
199  if (target_file != INVALID_HANDLE_VALUE)
200    ::CloseHandle(target_file);
201  // When writing to a network share, we may not be able to change the ACLs.
202  // Ignore ACL errors then (REPLACEFILE_IGNORE_MERGE_ERRORS).
203  return ::ReplaceFile(to_path.value().c_str(),
204      from_path.value().c_str(), NULL,
205      REPLACEFILE_IGNORE_MERGE_ERRORS, NULL, NULL) ? true : false;
206}
207
208bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
209  // NOTE: I suspect we could support longer paths, but that would involve
210  // analyzing all our usage of files.
211  if (from_path.value().length() >= MAX_PATH ||
212      to_path.value().length() >= MAX_PATH) {
213    return false;
214  }
215  return (::CopyFile(from_path.value().c_str(), to_path.value().c_str(),
216                     false) != 0);
217}
218
219bool ShellCopy(const FilePath& from_path, const FilePath& to_path,
220               bool recursive) {
221  // NOTE: I suspect we could support longer paths, but that would involve
222  // analyzing all our usage of files.
223  if (from_path.value().length() >= MAX_PATH ||
224      to_path.value().length() >= MAX_PATH) {
225    return false;
226  }
227
228  // SHFILEOPSTRUCT wants the path to be terminated with two NULLs,
229  // so we have to use wcscpy because wcscpy_s writes non-NULLs
230  // into the rest of the buffer.
231  wchar_t double_terminated_path_from[MAX_PATH + 1] = {0};
232  wchar_t double_terminated_path_to[MAX_PATH + 1] = {0};
233#pragma warning(suppress:4996)  // don't complain about wcscpy deprecation
234  wcscpy(double_terminated_path_from, from_path.value().c_str());
235#pragma warning(suppress:4996)  // don't complain about wcscpy deprecation
236  wcscpy(double_terminated_path_to, to_path.value().c_str());
237
238  SHFILEOPSTRUCT file_operation = {0};
239  file_operation.wFunc = FO_COPY;
240  file_operation.pFrom = double_terminated_path_from;
241  file_operation.pTo = double_terminated_path_to;
242  file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION |
243                          FOF_NOCONFIRMMKDIR;
244  if (!recursive)
245    file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY;
246
247  return (SHFileOperation(&file_operation) == 0);
248}
249
250bool CopyDirectory(const FilePath& from_path, const FilePath& to_path,
251                   bool recursive) {
252  if (recursive)
253    return ShellCopy(from_path, to_path, true);
254
255  // The following code assumes that from path is a directory.
256  DCHECK(DirectoryExists(from_path));
257
258  // Instead of creating a new directory, we copy the old one to include the
259  // security information of the folder as part of the copy.
260  if (!PathExists(to_path)) {
261    // Except that Vista fails to do that, and instead do a recursive copy if
262    // the target directory doesn't exist.
263    if (win_util::GetWinVersion() >= win_util::WINVERSION_VISTA)
264      CreateDirectory(to_path);
265    else
266      ShellCopy(from_path, to_path, false);
267  }
268
269  FilePath directory = from_path.Append(L"*.*");
270  return ShellCopy(directory, to_path, false);
271}
272
273bool CopyAndDeleteDirectory(const FilePath& from_path,
274                            const FilePath& to_path) {
275  if (CopyDirectory(from_path, to_path, true)) {
276    if (Delete(from_path, true)) {
277      return true;
278    }
279    // Like Move, this function is not transactional, so we just
280    // leave the copied bits behind if deleting from_path fails.
281    // If to_path exists previously then we have already overwritten
282    // it by now, we don't get better off by deleting the new bits.
283  }
284  return false;
285}
286
287
288bool PathExists(const FilePath& path) {
289  return (GetFileAttributes(path.value().c_str()) != INVALID_FILE_ATTRIBUTES);
290}
291
292bool PathIsWritable(const FilePath& path) {
293  HANDLE dir =
294      CreateFile(path.value().c_str(), FILE_ADD_FILE, kFileShareAll,
295                 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
296
297  if (dir == INVALID_HANDLE_VALUE)
298    return false;
299
300  CloseHandle(dir);
301  return true;
302}
303
304bool DirectoryExists(const FilePath& path) {
305  DWORD fileattr = GetFileAttributes(path.value().c_str());
306  if (fileattr != INVALID_FILE_ATTRIBUTES)
307    return (fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0;
308  return false;
309}
310
311bool GetFileCreationLocalTimeFromHandle(HANDLE file_handle,
312                                        LPSYSTEMTIME creation_time) {
313  if (!file_handle)
314    return false;
315
316  FILETIME utc_filetime;
317  if (!GetFileTime(file_handle, &utc_filetime, NULL, NULL))
318    return false;
319
320  FILETIME local_filetime;
321  if (!FileTimeToLocalFileTime(&utc_filetime, &local_filetime))
322    return false;
323
324  return !!FileTimeToSystemTime(&local_filetime, creation_time);
325}
326
327bool GetFileCreationLocalTime(const std::wstring& filename,
328                              LPSYSTEMTIME creation_time) {
329  ScopedHandle file_handle(
330      CreateFile(filename.c_str(), GENERIC_READ, kFileShareAll, NULL,
331                 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
332  return GetFileCreationLocalTimeFromHandle(file_handle.Get(), creation_time);
333}
334
335bool ResolveShortcut(FilePath* path) {
336  HRESULT result;
337  ScopedComPtr<IShellLink> i_shell_link;
338  bool is_resolved = false;
339
340  // Get pointer to the IShellLink interface
341  result = i_shell_link.CreateInstance(CLSID_ShellLink, NULL,
342                                       CLSCTX_INPROC_SERVER);
343  if (SUCCEEDED(result)) {
344    ScopedComPtr<IPersistFile> persist;
345    // Query IShellLink for the IPersistFile interface
346    result = persist.QueryFrom(i_shell_link);
347    if (SUCCEEDED(result)) {
348      WCHAR temp_path[MAX_PATH];
349      // Load the shell link
350      result = persist->Load(path->value().c_str(), STGM_READ);
351      if (SUCCEEDED(result)) {
352        // Try to find the target of a shortcut
353        result = i_shell_link->Resolve(0, SLR_NO_UI);
354        if (SUCCEEDED(result)) {
355          result = i_shell_link->GetPath(temp_path, MAX_PATH,
356                                  NULL, SLGP_UNCPRIORITY);
357          *path = FilePath(temp_path);
358          is_resolved = true;
359        }
360      }
361    }
362  }
363
364  return is_resolved;
365}
366
367bool CreateShortcutLink(const wchar_t *source, const wchar_t *destination,
368                        const wchar_t *working_dir, const wchar_t *arguments,
369                        const wchar_t *description, const wchar_t *icon,
370                        int icon_index, const wchar_t* app_id) {
371  // Length of arguments and description must be less than MAX_PATH.
372  DCHECK(lstrlen(arguments) < MAX_PATH);
373  DCHECK(lstrlen(description) < MAX_PATH);
374
375  ScopedComPtr<IShellLink> i_shell_link;
376  ScopedComPtr<IPersistFile> i_persist_file;
377
378  // Get pointer to the IShellLink interface
379  HRESULT result = i_shell_link.CreateInstance(CLSID_ShellLink, NULL,
380                                               CLSCTX_INPROC_SERVER);
381  if (FAILED(result))
382    return false;
383
384  // Query IShellLink for the IPersistFile interface
385  result = i_persist_file.QueryFrom(i_shell_link);
386  if (FAILED(result))
387    return false;
388
389  if (FAILED(i_shell_link->SetPath(source)))
390    return false;
391
392  if (working_dir && FAILED(i_shell_link->SetWorkingDirectory(working_dir)))
393    return false;
394
395  if (arguments && FAILED(i_shell_link->SetArguments(arguments)))
396    return false;
397
398  if (description && FAILED(i_shell_link->SetDescription(description)))
399    return false;
400
401  if (icon && FAILED(i_shell_link->SetIconLocation(icon, icon_index)))
402    return false;
403
404  if (app_id && (win_util::GetWinVersion() >= win_util::WINVERSION_WIN7)) {
405    ScopedComPtr<IPropertyStore> property_store;
406    if (FAILED(property_store.QueryFrom(i_shell_link)))
407      return false;
408
409    if (!win_util::SetAppIdForPropertyStore(property_store, app_id))
410      return false;
411  }
412
413  result = i_persist_file->Save(destination, TRUE);
414  return SUCCEEDED(result);
415}
416
417
418bool UpdateShortcutLink(const wchar_t *source, const wchar_t *destination,
419                        const wchar_t *working_dir, const wchar_t *arguments,
420                        const wchar_t *description, const wchar_t *icon,
421                        int icon_index, const wchar_t* app_id) {
422  // Length of arguments and description must be less than MAX_PATH.
423  DCHECK(lstrlen(arguments) < MAX_PATH);
424  DCHECK(lstrlen(description) < MAX_PATH);
425
426  // Get pointer to the IPersistFile interface and load existing link
427  ScopedComPtr<IShellLink> i_shell_link;
428  if (FAILED(i_shell_link.CreateInstance(CLSID_ShellLink, NULL,
429                                         CLSCTX_INPROC_SERVER)))
430    return false;
431
432  ScopedComPtr<IPersistFile> i_persist_file;
433  if (FAILED(i_persist_file.QueryFrom(i_shell_link)))
434    return false;
435
436  if (FAILED(i_persist_file->Load(destination, STGM_READWRITE)))
437    return false;
438
439  if (source && FAILED(i_shell_link->SetPath(source)))
440    return false;
441
442  if (working_dir && FAILED(i_shell_link->SetWorkingDirectory(working_dir)))
443    return false;
444
445  if (arguments && FAILED(i_shell_link->SetArguments(arguments)))
446    return false;
447
448  if (description && FAILED(i_shell_link->SetDescription(description)))
449    return false;
450
451  if (icon && FAILED(i_shell_link->SetIconLocation(icon, icon_index)))
452    return false;
453
454  if (app_id && win_util::GetWinVersion() >= win_util::WINVERSION_WIN7) {
455    ScopedComPtr<IPropertyStore> property_store;
456    if (FAILED(property_store.QueryFrom(i_shell_link)))
457      return false;
458
459    if (!win_util::SetAppIdForPropertyStore(property_store, app_id))
460      return false;
461  }
462
463  HRESULT result = i_persist_file->Save(destination, TRUE);
464  return SUCCEEDED(result);
465}
466
467bool TaskbarPinShortcutLink(const wchar_t* shortcut) {
468  // "Pin to taskbar" is only supported after Win7.
469  if (win_util::GetWinVersion() < win_util::WINVERSION_WIN7)
470    return false;
471
472  int result = reinterpret_cast<int>(ShellExecute(NULL, L"taskbarpin", shortcut,
473      NULL, NULL, 0));
474  return result > 32;
475}
476
477bool TaskbarUnpinShortcutLink(const wchar_t* shortcut) {
478  // "Unpin from taskbar" is only supported after Win7.
479  if (win_util::GetWinVersion() < win_util::WINVERSION_WIN7)
480    return false;
481
482  int result = reinterpret_cast<int>(ShellExecute(NULL, L"taskbarunpin",
483      shortcut, NULL, NULL, 0));
484  return result > 32;
485}
486
487bool GetTempDir(FilePath* path) {
488  wchar_t temp_path[MAX_PATH + 1];
489  DWORD path_len = ::GetTempPath(MAX_PATH, temp_path);
490  if (path_len >= MAX_PATH || path_len <= 0)
491    return false;
492  // TODO(evanm): the old behavior of this function was to always strip the
493  // trailing slash.  We duplicate this here, but it shouldn't be necessary
494  // when everyone is using the appropriate FilePath APIs.
495  *path = FilePath(temp_path).StripTrailingSeparators();
496  return true;
497}
498
499bool GetShmemTempDir(FilePath* path) {
500  return GetTempDir(path);
501}
502
503bool CreateTemporaryFile(FilePath* path) {
504  FilePath temp_file;
505
506  if (!GetTempDir(path))
507    return false;
508
509  if (CreateTemporaryFileInDir(*path, &temp_file)) {
510    *path = temp_file;
511    return true;
512  }
513
514  return false;
515}
516
517FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) {
518  return CreateAndOpenTemporaryFile(path);
519}
520
521// On POSIX we have semantics to create and open a temporary file
522// atomically.
523// TODO(jrg): is there equivalent call to use on Windows instead of
524// going 2-step?
525FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
526  if (!CreateTemporaryFileInDir(dir, path)) {
527    return NULL;
528  }
529  // Open file in binary mode, to avoid problems with fwrite. On Windows
530  // it replaces \n's with \r\n's, which may surprise you.
531  // Reference: http://msdn.microsoft.com/en-us/library/h9t88zwz(VS.71).aspx
532  return OpenFile(*path, "wb+");
533}
534
535bool CreateTemporaryFileInDir(const FilePath& dir,
536                              FilePath* temp_file) {
537  wchar_t temp_name[MAX_PATH + 1];
538
539  if (!GetTempFileName(dir.value().c_str(), L"", 0, temp_name)) {
540    PLOG(WARNING) << "Failed to get temporary file name in " << dir.value();
541    return false;
542  }
543
544  DWORD path_len = GetLongPathName(temp_name, temp_name, MAX_PATH);
545  if (path_len > MAX_PATH + 1 || path_len == 0) {
546    PLOG(WARNING) << "Failed to get long path name for " << temp_name;
547    return false;
548  }
549
550  std::wstring temp_file_str;
551  temp_file_str.assign(temp_name, path_len);
552  *temp_file = FilePath(temp_file_str);
553  return true;
554}
555
556bool CreateTemporaryDirInDir(const FilePath& base_dir,
557                             const FilePath::StringType& prefix,
558                             FilePath* new_dir) {
559  FilePath path_to_create;
560  srand(static_cast<uint32>(time(NULL)));
561
562  int count = 0;
563  while (count < 50) {
564    // Try create a new temporary directory with random generated name. If
565    // the one exists, keep trying another path name until we reach some limit.
566    path_to_create = base_dir;
567
568    string16 new_dir_name;
569    new_dir_name.assign(prefix);
570    new_dir_name.append(base::IntToString16(rand() % kint16max));
571
572    path_to_create = path_to_create.Append(new_dir_name);
573    if (::CreateDirectory(path_to_create.value().c_str(), NULL))
574      break;
575    count++;
576  }
577
578  if (count == 50) {
579    return false;
580  }
581
582  *new_dir = path_to_create;
583  return true;
584}
585
586bool CreateNewTempDirectory(const FilePath::StringType& prefix,
587                            FilePath* new_temp_path) {
588  FilePath system_temp_dir;
589  if (!GetTempDir(&system_temp_dir))
590    return false;
591
592  return CreateTemporaryDirInDir(system_temp_dir, prefix, new_temp_path);
593}
594
595bool CreateDirectory(const FilePath& full_path) {
596  // If the path exists, we've succeeded if it's a directory, failed otherwise.
597  const wchar_t* full_path_str = full_path.value().c_str();
598  DWORD fileattr = ::GetFileAttributes(full_path_str);
599  if (fileattr != INVALID_FILE_ATTRIBUTES) {
600    if ((fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
601      DLOG(INFO) << "CreateDirectory(" << full_path_str << "), "
602                 << "directory already exists.";
603      return true;
604    } else {
605      LOG(WARNING) << "CreateDirectory(" << full_path_str << "), "
606                   << "conflicts with existing file.";
607      return false;
608    }
609  }
610
611  // Invariant:  Path does not exist as file or directory.
612
613  // Attempt to create the parent recursively.  This will immediately return
614  // true if it already exists, otherwise will create all required parent
615  // directories starting with the highest-level missing parent.
616  FilePath parent_path(full_path.DirName());
617  if (parent_path.value() == full_path.value()) {
618    return false;
619  }
620  if (!CreateDirectory(parent_path)) {
621    DLOG(WARNING) << "Failed to create one of the parent directories.";
622    return false;
623  }
624
625  if (!::CreateDirectory(full_path_str, NULL)) {
626    DWORD error_code = ::GetLastError();
627    if (error_code == ERROR_ALREADY_EXISTS && DirectoryExists(full_path)) {
628      // This error code doesn't indicate whether we were racing with someone
629      // creating the same directory, or a file with the same path, therefore
630      // we check.
631      return true;
632    } else {
633      LOG(WARNING) << "Failed to create directory " << full_path_str
634                   << ", last error is " << error_code << ".";
635      return false;
636    }
637  } else {
638    return true;
639  }
640}
641
642bool GetFileInfo(const FilePath& file_path, base::PlatformFileInfo* results) {
643  WIN32_FILE_ATTRIBUTE_DATA attr;
644  if (!GetFileAttributesEx(file_path.value().c_str(),
645                           GetFileExInfoStandard, &attr)) {
646    return false;
647  }
648
649  ULARGE_INTEGER size;
650  size.HighPart = attr.nFileSizeHigh;
651  size.LowPart = attr.nFileSizeLow;
652  results->size = size.QuadPart;
653
654  results->is_directory =
655      (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
656  results->last_modified = base::Time::FromFileTime(attr.ftLastWriteTime);
657  results->last_accessed = base::Time::FromFileTime(attr.ftLastAccessTime);
658  results->creation_time = base::Time::FromFileTime(attr.ftCreationTime);
659
660  return true;
661}
662
663FILE* OpenFile(const FilePath& filename, const char* mode) {
664  std::wstring w_mode = ASCIIToWide(std::string(mode));
665  return _wfsopen(filename.value().c_str(), w_mode.c_str(), _SH_DENYNO);
666}
667
668FILE* OpenFile(const std::string& filename, const char* mode) {
669  return _fsopen(filename.c_str(), mode, _SH_DENYNO);
670}
671
672int ReadFile(const FilePath& filename, char* data, int size) {
673  ScopedHandle file(CreateFile(filename.value().c_str(),
674                               GENERIC_READ,
675                               FILE_SHARE_READ | FILE_SHARE_WRITE,
676                               NULL,
677                               OPEN_EXISTING,
678                               FILE_FLAG_SEQUENTIAL_SCAN,
679                               NULL));
680  if (!file)
681    return -1;
682
683  DWORD read;
684  if (::ReadFile(file, data, size, &read, NULL) &&
685      static_cast<int>(read) == size)
686    return read;
687  return -1;
688}
689
690int WriteFile(const FilePath& filename, const char* data, int size) {
691  ScopedHandle file(CreateFile(filename.value().c_str(),
692                               GENERIC_WRITE,
693                               0,
694                               NULL,
695                               CREATE_ALWAYS,
696                               0,
697                               NULL));
698  if (!file) {
699    LOG(WARNING) << "CreateFile failed for path " << filename.value() <<
700        " error code=" << GetLastError() <<
701        " error text=" << win_util::FormatLastWin32Error();
702    return -1;
703  }
704
705  DWORD written;
706  BOOL result = ::WriteFile(file, data, size, &written, NULL);
707  if (result && static_cast<int>(written) == size)
708    return written;
709
710  if (!result) {
711    // WriteFile failed.
712    LOG(WARNING) << "writing file " << filename.value() <<
713        " failed, error code=" << GetLastError() <<
714        " description=" << win_util::FormatLastWin32Error();
715  } else {
716    // Didn't write all the bytes.
717    LOG(WARNING) << "wrote" << written << " bytes to " <<
718        filename.value() << " expected " << size;
719  }
720  return -1;
721}
722
723bool RenameFileAndResetSecurityDescriptor(const FilePath& source_file_path,
724                                          const FilePath& target_file_path) {
725  // The parameters to SHFileOperation must be terminated with 2 NULL chars.
726  std::wstring source = source_file_path.value();
727  std::wstring target = target_file_path.value();
728
729  source.append(1, L'\0');
730  target.append(1, L'\0');
731
732  SHFILEOPSTRUCT move_info = {0};
733  move_info.wFunc = FO_MOVE;
734  move_info.pFrom = source.c_str();
735  move_info.pTo = target.c_str();
736  move_info.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI |
737                     FOF_NOCONFIRMMKDIR | FOF_NOCOPYSECURITYATTRIBS;
738
739  if (0 != SHFileOperation(&move_info))
740    return false;
741
742  return true;
743}
744
745// Gets the current working directory for the process.
746bool GetCurrentDirectory(FilePath* dir) {
747  wchar_t system_buffer[MAX_PATH];
748  system_buffer[0] = 0;
749  DWORD len = ::GetCurrentDirectory(MAX_PATH, system_buffer);
750  if (len == 0 || len > MAX_PATH)
751    return false;
752  // TODO(evanm): the old behavior of this function was to always strip the
753  // trailing slash.  We duplicate this here, but it shouldn't be necessary
754  // when everyone is using the appropriate FilePath APIs.
755  std::wstring dir_str(system_buffer);
756  *dir = FilePath(dir_str).StripTrailingSeparators();
757  return true;
758}
759
760// Sets the current working directory for the process.
761bool SetCurrentDirectory(const FilePath& directory) {
762  BOOL ret = ::SetCurrentDirectory(directory.value().c_str());
763  return ret != 0;
764}
765
766///////////////////////////////////////////////
767// FileEnumerator
768
769FileEnumerator::FileEnumerator(const FilePath& root_path,
770                               bool recursive,
771                               FileEnumerator::FILE_TYPE file_type)
772    : recursive_(recursive),
773      file_type_(file_type),
774      is_in_find_op_(false),
775      find_handle_(INVALID_HANDLE_VALUE) {
776  // INCLUDE_DOT_DOT must not be specified if recursive.
777  DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
778  pending_paths_.push(root_path);
779}
780
781FileEnumerator::FileEnumerator(const FilePath& root_path,
782                               bool recursive,
783                               FileEnumerator::FILE_TYPE file_type,
784                               const FilePath::StringType& pattern)
785    : recursive_(recursive),
786      file_type_(file_type),
787      is_in_find_op_(false),
788      pattern_(pattern),
789      find_handle_(INVALID_HANDLE_VALUE) {
790  // INCLUDE_DOT_DOT must not be specified if recursive.
791  DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
792  pending_paths_.push(root_path);
793}
794
795FileEnumerator::~FileEnumerator() {
796  if (find_handle_ != INVALID_HANDLE_VALUE)
797    FindClose(find_handle_);
798}
799
800void FileEnumerator::GetFindInfo(FindInfo* info) {
801  DCHECK(info);
802
803  if (!is_in_find_op_)
804    return;
805
806  memcpy(info, &find_data_, sizeof(*info));
807}
808
809bool FileEnumerator::IsDirectory(const FindInfo& info) {
810  return (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
811}
812
813// static
814FilePath FileEnumerator::GetFilename(const FindInfo& find_info) {
815  return FilePath(find_info.cFileName);
816}
817
818FilePath FileEnumerator::Next() {
819  if (!is_in_find_op_) {
820    if (pending_paths_.empty())
821      return FilePath();
822
823    // The last find FindFirstFile operation is done, prepare a new one.
824    root_path_ = pending_paths_.top();
825    pending_paths_.pop();
826
827    // Start a new find operation.
828    FilePath src = root_path_;
829
830    if (pattern_.empty())
831      src = src.Append(L"*");  // No pattern = match everything.
832    else
833      src = src.Append(pattern_);
834
835    find_handle_ = FindFirstFile(src.value().c_str(), &find_data_);
836    is_in_find_op_ = true;
837
838  } else {
839    // Search for the next file/directory.
840    if (!FindNextFile(find_handle_, &find_data_)) {
841      FindClose(find_handle_);
842      find_handle_ = INVALID_HANDLE_VALUE;
843    }
844  }
845
846  if (INVALID_HANDLE_VALUE == find_handle_) {
847    is_in_find_op_ = false;
848
849    // This is reached when we have finished a directory and are advancing to
850    // the next one in the queue. We applied the pattern (if any) to the files
851    // in the root search directory, but for those directories which were
852    // matched, we want to enumerate all files inside them. This will happen
853    // when the handle is empty.
854    pattern_ = FilePath::StringType();
855
856    return Next();
857  }
858
859  FilePath cur_file(find_data_.cFileName);
860  if (ShouldSkip(cur_file))
861    return Next();
862
863  // Construct the absolute filename.
864  cur_file = root_path_.Append(cur_file);
865
866  if (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
867    if (recursive_) {
868      // If |cur_file| is a directory, and we are doing recursive searching, add
869      // it to pending_paths_ so we scan it after we finish scanning this
870      // directory.
871      pending_paths_.push(cur_file);
872    }
873    return (file_type_ & FileEnumerator::DIRECTORIES) ? cur_file : Next();
874  }
875  return (file_type_ & FileEnumerator::FILES) ? cur_file : Next();
876}
877
878///////////////////////////////////////////////
879// MemoryMappedFile
880
881MemoryMappedFile::MemoryMappedFile()
882    : file_(INVALID_HANDLE_VALUE),
883      file_mapping_(INVALID_HANDLE_VALUE),
884      data_(NULL),
885      length_(INVALID_FILE_SIZE) {
886}
887
888bool MemoryMappedFile::MapFileToMemoryInternal() {
889  if (file_ == INVALID_HANDLE_VALUE)
890    return false;
891
892  length_ = ::GetFileSize(file_, NULL);
893  if (length_ == INVALID_FILE_SIZE)
894    return false;
895
896  // length_ value comes from GetFileSize() above. GetFileSize() returns DWORD,
897  // therefore the cast here is safe.
898  file_mapping_ = ::CreateFileMapping(file_, NULL, PAGE_READONLY,
899                                      0, static_cast<DWORD>(length_), NULL);
900  if (file_mapping_ == INVALID_HANDLE_VALUE)
901    return false;
902
903  data_ = static_cast<uint8*>(
904      ::MapViewOfFile(file_mapping_, FILE_MAP_READ, 0, 0, length_));
905  return data_ != NULL;
906}
907
908void MemoryMappedFile::CloseHandles() {
909  if (data_)
910    ::UnmapViewOfFile(data_);
911  if (file_mapping_ != INVALID_HANDLE_VALUE)
912    ::CloseHandle(file_mapping_);
913  if (file_ != INVALID_HANDLE_VALUE)
914    ::CloseHandle(file_);
915
916  data_ = NULL;
917  file_mapping_ = file_ = INVALID_HANDLE_VALUE;
918  length_ = INVALID_FILE_SIZE;
919}
920
921bool HasFileBeenModifiedSince(const FileEnumerator::FindInfo& find_info,
922                              const base::Time& cutoff_time) {
923  long result = CompareFileTime(&find_info.ftLastWriteTime,
924                                &cutoff_time.ToFileTime());
925  return result == 1 || result == 0;
926}
927
928bool NormalizeFilePath(const FilePath& path, FilePath* real_path) {
929  FilePath mapped_file;
930  if (!NormalizeToNativeFilePath(path, &mapped_file))
931    return false;
932  // NormalizeToNativeFilePath() will return a path that starts with
933  // "\Device\Harddisk...".  Helper DevicePathToDriveLetterPath()
934  // will find a drive letter which maps to the path's device, so
935  // that we return a path starting with a drive letter.
936  return DevicePathToDriveLetterPath(mapped_file, real_path);
937}
938
939bool NormalizeToNativeFilePath(const FilePath& path, FilePath* nt_path) {
940  // In Vista, GetFinalPathNameByHandle() would give us the real path
941  // from a file handle.  If we ever deprecate XP, consider changing the
942  // code below to a call to GetFinalPathNameByHandle().  The method this
943  // function uses is explained in the following msdn article:
944  // http://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx
945  ScopedHandle file_handle(
946      ::CreateFile(path.value().c_str(),
947                   GENERIC_READ,
948                   kFileShareAll,
949                   NULL,
950                   OPEN_EXISTING,
951                   FILE_ATTRIBUTE_NORMAL,
952                   NULL));
953  if (!file_handle)
954    return false;
955
956  // Create a file mapping object.  Can't easily use MemoryMappedFile, because
957  // we only map the first byte, and need direct access to the handle. You can
958  // not map an empty file, this call fails in that case.
959  ScopedHandle file_map_handle(
960      ::CreateFileMapping(file_handle.Get(),
961                          NULL,
962                          PAGE_READONLY,
963                          0,
964                          1, // Just one byte.  No need to look at the data.
965                          NULL));
966  if (!file_map_handle)
967    return false;
968
969  // Use a view of the file to get the path to the file.
970  void* file_view = MapViewOfFile(file_map_handle.Get(),
971                                  FILE_MAP_READ, 0, 0, 1);
972  if (!file_view)
973    return false;
974
975  // The expansion of |path| into a full path may make it longer.
976  // GetMappedFileName() will fail if the result is longer than MAX_PATH.
977  // Pad a bit to be safe.  If kMaxPathLength is ever changed to be less
978  // than MAX_PATH, it would be nessisary to test that GetMappedFileName()
979  // not return kMaxPathLength.  This would mean that only part of the
980  // path fit in |mapped_file_path|.
981  const int kMaxPathLength = MAX_PATH + 10;
982  wchar_t mapped_file_path[kMaxPathLength];
983  bool success = false;
984  HANDLE cp = GetCurrentProcess();
985  if (::GetMappedFileNameW(cp, file_view, mapped_file_path, kMaxPathLength)) {
986    *nt_path = FilePath(mapped_file_path);
987    success = true;
988  }
989  ::UnmapViewOfFile(file_view);
990  return success;
991}
992
993bool PreReadImage(const wchar_t* file_path, size_t size_to_read,
994                  size_t step_size) {
995  if (win_util::GetWinVersion() > win_util::WINVERSION_XP) {
996    // Vista+ branch. On these OSes, the forced reads through the DLL actually
997    // slows warm starts. The solution is to sequentially read file contents
998    // with an optional cap on total amount to read.
999    ScopedHandle file(
1000        CreateFile(file_path,
1001                   GENERIC_READ,
1002                   FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1003                   NULL,
1004                   OPEN_EXISTING,
1005                   FILE_FLAG_SEQUENTIAL_SCAN,
1006                   NULL));
1007
1008    if (!file.IsValid())
1009      return false;
1010
1011    // Default to 1MB sequential reads.
1012    const DWORD actual_step_size = std::max(static_cast<DWORD>(step_size),
1013                                            static_cast<DWORD>(1024*1024));
1014    LPVOID buffer = ::VirtualAlloc(NULL,
1015                                   actual_step_size,
1016                                   MEM_COMMIT,
1017                                   PAGE_READWRITE);
1018
1019    if (buffer == NULL)
1020      return false;
1021
1022    DWORD len;
1023    size_t total_read = 0;
1024    while (::ReadFile(file, buffer, actual_step_size, &len, NULL) &&
1025           len > 0 &&
1026           (size_to_read ? total_read < size_to_read : true)) {
1027      total_read += static_cast<size_t>(len);
1028    }
1029    ::VirtualFree(buffer, 0, MEM_RELEASE);
1030  } else {
1031    // WinXP branch. Here, reading the DLL from disk doesn't do
1032    // what we want so instead we pull the pages into memory by loading
1033    // the DLL and touching pages at a stride.
1034    HMODULE dll_module = ::LoadLibraryExW(
1035        file_path,
1036        NULL,
1037        LOAD_WITH_ALTERED_SEARCH_PATH | DONT_RESOLVE_DLL_REFERENCES);
1038
1039    if (!dll_module)
1040      return false;
1041
1042    PEImage pe_image(dll_module);
1043    PIMAGE_NT_HEADERS nt_headers = pe_image.GetNTHeaders();
1044    size_t actual_size_to_read = size_to_read ? size_to_read :
1045                                 nt_headers->OptionalHeader.SizeOfImage;
1046    volatile uint8* touch = reinterpret_cast<uint8*>(dll_module);
1047    size_t offset = 0;
1048    while (offset < actual_size_to_read) {
1049      uint8 unused = *(touch + offset);
1050      offset += step_size;
1051    }
1052    FreeLibrary(dll_module);
1053  }
1054
1055  return true;
1056}
1057
1058}  // namespace file_util
1059