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