1 // Copyright (c) 2012 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/files/file_util.h"
6 
7 #include <windows.h>
8 #include <io.h>
9 #include <psapi.h>
10 #include <shellapi.h>
11 #include <shlobj.h>
12 #include <stddef.h>
13 #include <stdint.h>
14 #include <time.h>
15 #include <winsock2.h>
16 
17 #include <algorithm>
18 #include <limits>
19 #include <string>
20 
21 #include "base/debug/alias.h"
22 #include "base/files/file_enumerator.h"
23 #include "base/files/file_path.h"
24 #include "base/files/memory_mapped_file.h"
25 #include "base/guid.h"
26 #include "base/logging.h"
27 #include "base/metrics/histogram_functions.h"
28 #include "base/numerics/safe_conversions.h"
29 #include "base/process/process_handle.h"
30 #include "base/rand_util.h"
31 #include "base/stl_util.h"
32 #include "base/strings/strcat.h"
33 #include "base/strings/string_number_conversions.h"
34 #include "base/strings/string_piece.h"
35 #include "base/strings/string_util.h"
36 #include "base/strings/string_util_win.h"
37 #include "base/strings/utf_string_conversions.h"
38 #include "base/threading/scoped_blocking_call.h"
39 #include "base/threading/scoped_thread_priority.h"
40 #include "base/time/time.h"
41 #include "base/win/scoped_handle.h"
42 #include "base/win/windows_types.h"
43 #include "base/win/windows_version.h"
44 
45 namespace base {
46 
47 namespace {
48 
49 const DWORD kFileShareAll =
50     FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
51 
52 // Records a sample in a histogram named
53 // "Windows.PostOperationState.|operation|" indicating the state of |path|
54 // following the named operation. If |operation_succeeded| is true, the
55 // "operation succeeded" sample is recorded. Otherwise, the state of |path| is
56 // queried and the most meaningful sample is recorded.
RecordPostOperationState(const FilePath & path,StringPiece operation,bool operation_succeeded)57 void RecordPostOperationState(const FilePath& path,
58                               StringPiece operation,
59                               bool operation_succeeded) {
60   // The state of a filesystem item after an operation.
61   // These values are persisted to logs. Entries should not be renumbered and
62   // numeric values should never be reused.
63   enum class PostOperationState {
64     kOperationSucceeded = 0,
65     kFileNotFoundAfterFailure = 1,
66     kPathNotFoundAfterFailure = 2,
67     kAccessDeniedAfterFailure = 3,
68     kNoAttributesAfterFailure = 4,
69     kEmptyDirectoryAfterFailure = 5,
70     kNonEmptyDirectoryAfterFailure = 6,
71     kNotDirectoryAfterFailure = 7,
72     kCount
73   } metric = PostOperationState::kOperationSucceeded;
74 
75   if (!operation_succeeded) {
76     const DWORD attributes = ::GetFileAttributes(path.value().c_str());
77     if (attributes == INVALID_FILE_ATTRIBUTES) {
78       // On failure to delete, one might expect the file/directory to still be
79       // in place. Slice a failure to get its attributes into a few common error
80       // buckets.
81       const DWORD error_code = ::GetLastError();
82       if (error_code == ERROR_FILE_NOT_FOUND)
83         metric = PostOperationState::kFileNotFoundAfterFailure;
84       else if (error_code == ERROR_PATH_NOT_FOUND)
85         metric = PostOperationState::kPathNotFoundAfterFailure;
86       else if (error_code == ERROR_ACCESS_DENIED)
87         metric = PostOperationState::kAccessDeniedAfterFailure;
88       else
89         metric = PostOperationState::kNoAttributesAfterFailure;
90     } else if (attributes & FILE_ATTRIBUTE_DIRECTORY) {
91       if (IsDirectoryEmpty(path))
92         metric = PostOperationState::kEmptyDirectoryAfterFailure;
93       else
94         metric = PostOperationState::kNonEmptyDirectoryAfterFailure;
95     } else {
96       metric = PostOperationState::kNotDirectoryAfterFailure;
97     }
98   }
99 
100   std::string histogram_name =
101       base::StrCat({"Windows.PostOperationState.", operation});
102   UmaHistogramEnumeration(histogram_name, metric, PostOperationState::kCount);
103 }
104 
105 // Records the sample |error| in a histogram named
106 // "Windows.FilesystemError.|operation|".
RecordFilesystemError(StringPiece operation,DWORD error)107 void RecordFilesystemError(StringPiece operation, DWORD error) {
108   std::string histogram_name =
109       base::StrCat({"Windows.FilesystemError.", operation});
110   UmaHistogramSparse(histogram_name, error);
111 }
112 
113 // Returns the Win32 last error code or ERROR_SUCCESS if the last error code is
114 // ERROR_FILE_NOT_FOUND or ERROR_PATH_NOT_FOUND. This is useful in cases where
115 // the absence of a file or path is a success condition (e.g., when attempting
116 // to delete an item in the filesystem).
ReturnLastErrorOrSuccessOnNotFound()117 DWORD ReturnLastErrorOrSuccessOnNotFound() {
118   const DWORD error_code = ::GetLastError();
119   return (error_code == ERROR_FILE_NOT_FOUND ||
120           error_code == ERROR_PATH_NOT_FOUND)
121              ? ERROR_SUCCESS
122              : error_code;
123 }
124 
125 // Deletes all files and directories in a path.
126 // Returns ERROR_SUCCESS on success or the Windows error code corresponding to
127 // the first error encountered. ERROR_FILE_NOT_FOUND and ERROR_PATH_NOT_FOUND
128 // are considered success conditions, and are therefore never returned.
DeleteFileRecursive(const FilePath & path,const FilePath::StringType & pattern,bool recursive)129 DWORD DeleteFileRecursive(const FilePath& path,
130                           const FilePath::StringType& pattern,
131                           bool recursive) {
132   FileEnumerator traversal(path, false,
133                            FileEnumerator::FILES | FileEnumerator::DIRECTORIES,
134                            pattern);
135   DWORD result = ERROR_SUCCESS;
136   for (FilePath current = traversal.Next(); !current.empty();
137        current = traversal.Next()) {
138     // Try to clear the read-only bit if we find it.
139     FileEnumerator::FileInfo info = traversal.GetInfo();
140     if ((info.find_data().dwFileAttributes & FILE_ATTRIBUTE_READONLY) &&
141         (recursive || !info.IsDirectory())) {
142       ::SetFileAttributes(
143           current.value().c_str(),
144           info.find_data().dwFileAttributes & ~FILE_ATTRIBUTE_READONLY);
145     }
146 
147     DWORD this_result = ERROR_SUCCESS;
148     if (info.IsDirectory()) {
149       if (recursive) {
150         this_result = DeleteFileRecursive(current, pattern, true);
151         DCHECK_NE(static_cast<LONG>(this_result), ERROR_FILE_NOT_FOUND);
152         DCHECK_NE(static_cast<LONG>(this_result), ERROR_PATH_NOT_FOUND);
153         if (this_result == ERROR_SUCCESS &&
154             !::RemoveDirectory(current.value().c_str())) {
155           this_result = ReturnLastErrorOrSuccessOnNotFound();
156         }
157       }
158     } else if (!::DeleteFile(current.value().c_str())) {
159       this_result = ReturnLastErrorOrSuccessOnNotFound();
160     }
161     if (result == ERROR_SUCCESS)
162       result = this_result;
163   }
164   return result;
165 }
166 
167 // Appends |mode_char| to |mode| before the optional character set encoding; see
168 // https://msdn.microsoft.com/library/yeby3zcb.aspx for details.
AppendModeCharacter(wchar_t mode_char,std::wstring * mode)169 void AppendModeCharacter(wchar_t mode_char, std::wstring* mode) {
170   size_t comma_pos = mode->find(L',');
171   mode->insert(comma_pos == std::wstring::npos ? mode->length() : comma_pos, 1,
172                mode_char);
173 }
174 
DoCopyFile(const FilePath & from_path,const FilePath & to_path,bool fail_if_exists)175 bool DoCopyFile(const FilePath& from_path,
176                 const FilePath& to_path,
177                 bool fail_if_exists) {
178   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
179   if (from_path.ReferencesParent() || to_path.ReferencesParent())
180     return false;
181 
182   // NOTE: I suspect we could support longer paths, but that would involve
183   // analyzing all our usage of files.
184   if (from_path.value().length() >= MAX_PATH ||
185       to_path.value().length() >= MAX_PATH) {
186     return false;
187   }
188 
189   // Mitigate the issues caused by loading DLLs on a background thread
190   // (http://crbug/973868).
191   SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
192 
193   // Unlike the posix implementation that copies the file manually and discards
194   // the ACL bits, CopyFile() copies the complete SECURITY_DESCRIPTOR and access
195   // bits, which is usually not what we want. We can't do much about the
196   // SECURITY_DESCRIPTOR but at least remove the read only bit.
197   const wchar_t* dest = to_path.value().c_str();
198   if (!::CopyFile(from_path.value().c_str(), dest, fail_if_exists)) {
199     // Copy failed.
200     return false;
201   }
202   DWORD attrs = GetFileAttributes(dest);
203   if (attrs == INVALID_FILE_ATTRIBUTES) {
204     return false;
205   }
206   if (attrs & FILE_ATTRIBUTE_READONLY) {
207     SetFileAttributes(dest, attrs & ~FILE_ATTRIBUTE_READONLY);
208   }
209   return true;
210 }
211 
DoCopyDirectory(const FilePath & from_path,const FilePath & to_path,bool recursive,bool fail_if_exists)212 bool DoCopyDirectory(const FilePath& from_path,
213                      const FilePath& to_path,
214                      bool recursive,
215                      bool fail_if_exists) {
216   // NOTE(maruel): Previous version of this function used to call
217   // SHFileOperation().  This used to copy the file attributes and extended
218   // attributes, OLE structured storage, NTFS file system alternate data
219   // streams, SECURITY_DESCRIPTOR. In practice, this is not what we want, we
220   // want the containing directory to propagate its SECURITY_DESCRIPTOR.
221   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
222 
223   // NOTE: I suspect we could support longer paths, but that would involve
224   // analyzing all our usage of files.
225   if (from_path.value().length() >= MAX_PATH ||
226       to_path.value().length() >= MAX_PATH) {
227     return false;
228   }
229 
230   // This function does not properly handle destinations within the source.
231   FilePath real_to_path = to_path;
232   if (PathExists(real_to_path)) {
233     real_to_path = MakeAbsoluteFilePath(real_to_path);
234     if (real_to_path.empty())
235       return false;
236   } else {
237     real_to_path = MakeAbsoluteFilePath(real_to_path.DirName());
238     if (real_to_path.empty())
239       return false;
240   }
241   FilePath real_from_path = MakeAbsoluteFilePath(from_path);
242   if (real_from_path.empty())
243     return false;
244   if (real_to_path == real_from_path || real_from_path.IsParent(real_to_path))
245     return false;
246 
247   int traverse_type = FileEnumerator::FILES;
248   if (recursive)
249     traverse_type |= FileEnumerator::DIRECTORIES;
250   FileEnumerator traversal(from_path, recursive, traverse_type);
251 
252   if (!PathExists(from_path)) {
253     DLOG(ERROR) << "CopyDirectory() couldn't stat source directory: "
254                 << from_path.value().c_str();
255     return false;
256   }
257   // TODO(maruel): This is not necessary anymore.
258   DCHECK(recursive || DirectoryExists(from_path));
259 
260   FilePath current = from_path;
261   bool from_is_dir = DirectoryExists(from_path);
262   bool success = true;
263   FilePath from_path_base = from_path;
264   if (recursive && DirectoryExists(to_path)) {
265     // If the destination already exists and is a directory, then the
266     // top level of source needs to be copied.
267     from_path_base = from_path.DirName();
268   }
269 
270   while (success && !current.empty()) {
271     // current is the source path, including from_path, so append
272     // the suffix after from_path to to_path to create the target_path.
273     FilePath target_path(to_path);
274     if (from_path_base != current) {
275       if (!from_path_base.AppendRelativePath(current, &target_path)) {
276         success = false;
277         break;
278       }
279     }
280 
281     if (from_is_dir) {
282       if (!DirectoryExists(target_path) &&
283           !::CreateDirectory(target_path.value().c_str(), NULL)) {
284         DLOG(ERROR) << "CopyDirectory() couldn't create directory: "
285                     << target_path.value().c_str();
286         success = false;
287       }
288     } else if (!DoCopyFile(current, target_path, fail_if_exists)) {
289       DLOG(ERROR) << "CopyDirectory() couldn't create file: "
290                   << target_path.value().c_str();
291       success = false;
292     }
293 
294     current = traversal.Next();
295     if (!current.empty())
296       from_is_dir = traversal.GetInfo().IsDirectory();
297   }
298 
299   return success;
300 }
301 
302 // Returns ERROR_SUCCESS on success, or a Windows error code on failure.
DoDeleteFile(const FilePath & path,bool recursive)303 DWORD DoDeleteFile(const FilePath& path, bool recursive) {
304   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
305 
306   if (path.empty())
307     return ERROR_SUCCESS;
308 
309   if (path.value().length() >= MAX_PATH)
310     return ERROR_BAD_PATHNAME;
311 
312   // Handle any path with wildcards.
313   if (path.BaseName().value().find_first_of(FILE_PATH_LITERAL("*?")) !=
314       FilePath::StringType::npos) {
315     const DWORD error_code =
316         DeleteFileRecursive(path.DirName(), path.BaseName().value(), recursive);
317     DCHECK_NE(static_cast<LONG>(error_code), ERROR_FILE_NOT_FOUND);
318     DCHECK_NE(static_cast<LONG>(error_code), ERROR_PATH_NOT_FOUND);
319     return error_code;
320   }
321 
322   // Report success if the file or path does not exist.
323   const DWORD attr = ::GetFileAttributes(path.value().c_str());
324   if (attr == INVALID_FILE_ATTRIBUTES)
325     return ReturnLastErrorOrSuccessOnNotFound();
326 
327   // Clear the read-only bit if it is set.
328   if ((attr & FILE_ATTRIBUTE_READONLY) &&
329       !::SetFileAttributes(path.value().c_str(),
330                            attr & ~FILE_ATTRIBUTE_READONLY)) {
331     // It's possible for |path| to be gone now under a race with other deleters.
332     return ReturnLastErrorOrSuccessOnNotFound();
333   }
334 
335   // Perform a simple delete on anything that isn't a directory.
336   if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
337     return ::DeleteFile(path.value().c_str())
338                ? ERROR_SUCCESS
339                : ReturnLastErrorOrSuccessOnNotFound();
340   }
341 
342   if (recursive) {
343     const DWORD error_code =
344         DeleteFileRecursive(path, FILE_PATH_LITERAL("*"), true);
345     DCHECK_NE(static_cast<LONG>(error_code), ERROR_FILE_NOT_FOUND);
346     DCHECK_NE(static_cast<LONG>(error_code), ERROR_PATH_NOT_FOUND);
347     if (error_code != ERROR_SUCCESS)
348       return error_code;
349   }
350   return ::RemoveDirectory(path.value().c_str())
351              ? ERROR_SUCCESS
352              : ReturnLastErrorOrSuccessOnNotFound();
353 }
354 
DeleteFileAndRecordMetrics(const FilePath & path,bool recursive)355 bool DeleteFileAndRecordMetrics(const FilePath& path, bool recursive) {
356   static constexpr char kRecursive[] = "DeleteFile.Recursive";
357   static constexpr char kNonRecursive[] = "DeleteFile.NonRecursive";
358   const StringPiece operation(recursive ? kRecursive : kNonRecursive);
359 
360   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
361 
362   // Metrics for delete failures tracked in https://crbug.com/599084. Delete may
363   // fail for a number of reasons. Log some metrics relating to failures in the
364   // current code so that any improvements or regressions resulting from
365   // subsequent code changes can be detected.
366   const DWORD error = DoDeleteFile(path, recursive);
367   RecordPostOperationState(path, operation, error == ERROR_SUCCESS);
368   if (error == ERROR_SUCCESS)
369     return true;
370 
371   RecordFilesystemError(operation, error);
372   return false;
373 }
374 
375 }  // namespace
376 
MakeAbsoluteFilePath(const FilePath & input)377 FilePath MakeAbsoluteFilePath(const FilePath& input) {
378   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
379   wchar_t file_path[MAX_PATH];
380   if (!_wfullpath(file_path, input.value().c_str(), MAX_PATH))
381     return FilePath();
382   return FilePath(file_path);
383 }
384 
DeleteFile(const FilePath & path,bool recursive)385 bool DeleteFile(const FilePath& path, bool recursive) {
386   return DeleteFileAndRecordMetrics(path, recursive);
387 }
388 
DeleteFileRecursively(const FilePath & path)389 bool DeleteFileRecursively(const FilePath& path) {
390   return DeleteFileAndRecordMetrics(path, /*recursive=*/true);
391 }
392 
DeleteFileAfterReboot(const FilePath & path)393 bool DeleteFileAfterReboot(const FilePath& path) {
394   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
395 
396   if (path.value().length() >= MAX_PATH)
397     return false;
398 
399   return ::MoveFileEx(path.value().c_str(), nullptr,
400                       MOVEFILE_DELAY_UNTIL_REBOOT);
401 }
402 
ReplaceFile(const FilePath & from_path,const FilePath & to_path,File::Error * error)403 bool ReplaceFile(const FilePath& from_path,
404                  const FilePath& to_path,
405                  File::Error* error) {
406   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
407   // Try a simple move first.  It will only succeed when |to_path| doesn't
408   // already exist.
409   if (::MoveFile(from_path.value().c_str(), to_path.value().c_str()))
410     return true;
411   File::Error move_error = File::OSErrorToFileError(GetLastError());
412 
413   // Alias paths for investigation of shutdown hangs. crbug.com/1054164
414   FilePath::CharType from_path_str[MAX_PATH];
415   base::wcslcpy(from_path_str, from_path.value().c_str(),
416                 base::size(from_path_str));
417   base::debug::Alias(from_path_str);
418   FilePath::CharType to_path_str[MAX_PATH];
419   base::wcslcpy(to_path_str, to_path.value().c_str(), base::size(to_path_str));
420   base::debug::Alias(to_path_str);
421 
422   // Try the full-blown replace if the move fails, as ReplaceFile will only
423   // succeed when |to_path| does exist. When writing to a network share, we
424   // may not be able to change the ACLs. Ignore ACL errors then
425   // (REPLACEFILE_IGNORE_MERGE_ERRORS).
426   if (::ReplaceFile(to_path.value().c_str(), from_path.value().c_str(), NULL,
427                     REPLACEFILE_IGNORE_MERGE_ERRORS, NULL, NULL)) {
428     return true;
429   }
430   // In the case of FILE_ERROR_NOT_FOUND from ReplaceFile, it is likely that
431   // |to_path| does not exist. In this case, the more relevant error comes
432   // from the call to MoveFile.
433   if (error) {
434     File::Error replace_error = File::OSErrorToFileError(GetLastError());
435     *error = replace_error == File::FILE_ERROR_NOT_FOUND ? move_error
436                                                          : replace_error;
437   }
438   return false;
439 }
440 
CopyDirectory(const FilePath & from_path,const FilePath & to_path,bool recursive)441 bool CopyDirectory(const FilePath& from_path,
442                    const FilePath& to_path,
443                    bool recursive) {
444   return DoCopyDirectory(from_path, to_path, recursive, false);
445 }
446 
CopyDirectoryExcl(const FilePath & from_path,const FilePath & to_path,bool recursive)447 bool CopyDirectoryExcl(const FilePath& from_path,
448                        const FilePath& to_path,
449                        bool recursive) {
450   return DoCopyDirectory(from_path, to_path, recursive, true);
451 }
452 
PathExists(const FilePath & path)453 bool PathExists(const FilePath& path) {
454   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
455   return (GetFileAttributes(path.value().c_str()) != INVALID_FILE_ATTRIBUTES);
456 }
457 
PathIsWritable(const FilePath & path)458 bool PathIsWritable(const FilePath& path) {
459   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
460   HANDLE dir =
461       CreateFile(path.value().c_str(), FILE_ADD_FILE, kFileShareAll, NULL,
462                  OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
463 
464   if (dir == INVALID_HANDLE_VALUE)
465     return false;
466 
467   CloseHandle(dir);
468   return true;
469 }
470 
DirectoryExists(const FilePath & path)471 bool DirectoryExists(const FilePath& path) {
472   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
473   DWORD fileattr = GetFileAttributes(path.value().c_str());
474   if (fileattr != INVALID_FILE_ATTRIBUTES)
475     return (fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0;
476   return false;
477 }
478 
GetTempDir(FilePath * path)479 bool GetTempDir(FilePath* path) {
480   wchar_t temp_path[MAX_PATH + 1];
481   DWORD path_len = ::GetTempPath(MAX_PATH, temp_path);
482   if (path_len >= MAX_PATH || path_len <= 0)
483     return false;
484   // TODO(evanm): the old behavior of this function was to always strip the
485   // trailing slash.  We duplicate this here, but it shouldn't be necessary
486   // when everyone is using the appropriate FilePath APIs.
487   *path = FilePath(temp_path).StripTrailingSeparators();
488   return true;
489 }
490 
GetHomeDir()491 FilePath GetHomeDir() {
492   wchar_t result[MAX_PATH];
493   if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT,
494                                 result)) &&
495       result[0]) {
496     return FilePath(result);
497   }
498 
499   // Fall back to the temporary directory on failure.
500   FilePath temp;
501   if (GetTempDir(&temp))
502     return temp;
503 
504   // Last resort.
505   return FilePath(FILE_PATH_LITERAL("C:\\"));
506 }
507 
CreateTemporaryFile(FilePath * path)508 bool CreateTemporaryFile(FilePath* path) {
509   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
510 
511   FilePath temp_file;
512 
513   if (!GetTempDir(path))
514     return false;
515 
516   if (CreateTemporaryFileInDir(*path, &temp_file)) {
517     *path = temp_file;
518     return true;
519   }
520 
521   return false;
522 }
523 
524 // On POSIX we have semantics to create and open a temporary file
525 // atomically.
526 // TODO(jrg): is there equivalent call to use on Windows instead of
527 // going 2-step?
CreateAndOpenTemporaryFileInDir(const FilePath & dir,FilePath * path)528 FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
529   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
530   if (!CreateTemporaryFileInDir(dir, path)) {
531     return NULL;
532   }
533   // Open file in binary mode, to avoid problems with fwrite. On Windows
534   // it replaces \n's with \r\n's, which may surprise you.
535   // Reference: http://msdn.microsoft.com/en-us/library/h9t88zwz(VS.71).aspx
536   return OpenFile(*path, "wb+");
537 }
538 
CreateTemporaryFileInDir(const FilePath & dir,FilePath * temp_file)539 bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) {
540   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
541 
542   // Use GUID instead of ::GetTempFileName() to generate unique file names.
543   // "Due to the algorithm used to generate file names, GetTempFileName can
544   // perform poorly when creating a large number of files with the same prefix.
545   // In such cases, it is recommended that you construct unique file names based
546   // on GUIDs."
547   // https://msdn.microsoft.com/library/windows/desktop/aa364991.aspx
548 
549   FilePath temp_name;
550   bool create_file_success = false;
551 
552   // Although it is nearly impossible to get a duplicate name with GUID, we
553   // still use a loop here in case it happens.
554   for (int i = 0; i < 100; ++i) {
555     temp_name =
556         dir.Append(UTF8ToWide(GenerateGUID()) + FILE_PATH_LITERAL(".tmp"));
557     File file(temp_name,
558               File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE);
559     if (file.IsValid()) {
560       file.Close();
561       create_file_success = true;
562       break;
563     }
564   }
565 
566   if (!create_file_success) {
567     DPLOG(WARNING) << "Failed to get temporary file name in " << dir.value();
568     return false;
569   }
570 
571   wchar_t long_temp_name[MAX_PATH + 1];
572   DWORD long_name_len =
573       GetLongPathName(temp_name.value().c_str(), long_temp_name, MAX_PATH);
574   if (long_name_len > MAX_PATH || long_name_len == 0) {
575     // GetLongPathName() failed, but we still have a temporary file.
576     *temp_file = std::move(temp_name);
577     return true;
578   }
579 
580   FilePath::StringPieceType long_temp_name_str(long_temp_name, long_name_len);
581   *temp_file = FilePath(long_temp_name_str);
582   return true;
583 }
584 
CreateTemporaryDirInDir(const FilePath & base_dir,const FilePath::StringType & prefix,FilePath * new_dir)585 bool CreateTemporaryDirInDir(const FilePath& base_dir,
586                              const FilePath::StringType& prefix,
587                              FilePath* new_dir) {
588   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
589 
590   FilePath path_to_create;
591 
592   for (int count = 0; count < 50; ++count) {
593     // Try create a new temporary directory with random generated name. If
594     // the one exists, keep trying another path name until we reach some limit.
595     std::wstring new_dir_name;
596     new_dir_name.assign(prefix);
597     new_dir_name.append(AsWString(NumberToString16(GetCurrentProcId())));
598     new_dir_name.push_back('_');
599     new_dir_name.append(AsWString(
600         NumberToString16(RandInt(0, std::numeric_limits<int32_t>::max()))));
601 
602     path_to_create = base_dir.Append(new_dir_name);
603     if (::CreateDirectory(path_to_create.value().c_str(), NULL)) {
604       *new_dir = path_to_create;
605       return true;
606     }
607   }
608 
609   return false;
610 }
611 
CreateNewTempDirectory(const FilePath::StringType & prefix,FilePath * new_temp_path)612 bool CreateNewTempDirectory(const FilePath::StringType& prefix,
613                             FilePath* new_temp_path) {
614   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
615 
616   FilePath system_temp_dir;
617   if (!GetTempDir(&system_temp_dir))
618     return false;
619 
620   return CreateTemporaryDirInDir(system_temp_dir, prefix, new_temp_path);
621 }
622 
CreateDirectoryAndGetError(const FilePath & full_path,File::Error * error)623 bool CreateDirectoryAndGetError(const FilePath& full_path,
624                                 File::Error* error) {
625   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
626 
627   // If the path exists, we've succeeded if it's a directory, failed otherwise.
628   const wchar_t* const full_path_str = full_path.value().c_str();
629   const DWORD fileattr = ::GetFileAttributes(full_path_str);
630   if (fileattr != INVALID_FILE_ATTRIBUTES) {
631     if ((fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
632       DVLOG(1) << "CreateDirectory(" << full_path_str << "), "
633                << "directory already exists.";
634       return true;
635     }
636     DLOG(WARNING) << "CreateDirectory(" << full_path_str << "), "
637                   << "conflicts with existing file.";
638     if (error)
639       *error = File::FILE_ERROR_NOT_A_DIRECTORY;
640     ::SetLastError(ERROR_FILE_EXISTS);
641     return false;
642   }
643 
644   // Invariant:  Path does not exist as file or directory.
645 
646   // Attempt to create the parent recursively.  This will immediately return
647   // true if it already exists, otherwise will create all required parent
648   // directories starting with the highest-level missing parent.
649   FilePath parent_path(full_path.DirName());
650   if (parent_path.value() == full_path.value()) {
651     if (error)
652       *error = File::FILE_ERROR_NOT_FOUND;
653     ::SetLastError(ERROR_FILE_NOT_FOUND);
654     return false;
655   }
656   if (!CreateDirectoryAndGetError(parent_path, error)) {
657     DLOG(WARNING) << "Failed to create one of the parent directories.";
658     DCHECK(!error || *error != File::FILE_OK);
659     return false;
660   }
661 
662   if (::CreateDirectory(full_path_str, NULL))
663     return true;
664 
665   const DWORD error_code = ::GetLastError();
666   if (error_code == ERROR_ALREADY_EXISTS && DirectoryExists(full_path)) {
667     // This error code ERROR_ALREADY_EXISTS doesn't indicate whether we were
668     // racing with someone creating the same directory, or a file with the same
669     // path.  If DirectoryExists() returns true, we lost the race to create the
670     // same directory.
671     return true;
672   }
673   if (error)
674     *error = File::OSErrorToFileError(error_code);
675   ::SetLastError(error_code);
676   DPLOG(WARNING) << "Failed to create directory " << full_path_str;
677   return false;
678 }
679 
NormalizeFilePath(const FilePath & path,FilePath * real_path)680 bool NormalizeFilePath(const FilePath& path, FilePath* real_path) {
681   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
682   File file(path, File::FLAG_OPEN | File::FLAG_READ | File::FLAG_SHARE_DELETE);
683   if (!file.IsValid())
684     return false;
685 
686   // The expansion of |path| into a full path may make it longer.
687   constexpr int kMaxPathLength = MAX_PATH + 10;
688   wchar_t native_file_path[kMaxPathLength];
689   // kMaxPathLength includes space for trailing '\0' so we subtract 1.
690   // Returned length, used_wchars, does not include trailing '\0'.
691   // Failure is indicated by returning 0 or >= kMaxPathLength.
692   DWORD used_wchars = ::GetFinalPathNameByHandle(
693       file.GetPlatformFile(), native_file_path, kMaxPathLength - 1,
694       FILE_NAME_NORMALIZED | VOLUME_NAME_NT);
695 
696   if (used_wchars >= kMaxPathLength || used_wchars == 0)
697     return false;
698 
699   // GetFinalPathNameByHandle() returns the \\?\ syntax for file names and
700   // existing code expects we return a path starting 'X:\' so we call
701   // DevicePathToDriveLetterPath rather than using VOLUME_NAME_DOS above.
702   return DevicePathToDriveLetterPath(
703       FilePath(FilePath::StringPieceType(native_file_path, used_wchars)),
704       real_path);
705 }
706 
DevicePathToDriveLetterPath(const FilePath & nt_device_path,FilePath * out_drive_letter_path)707 bool DevicePathToDriveLetterPath(const FilePath& nt_device_path,
708                                  FilePath* out_drive_letter_path) {
709   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
710 
711   // Get the mapping of drive letters to device paths.
712   const int kDriveMappingSize = 1024;
713   wchar_t drive_mapping[kDriveMappingSize] = {'\0'};
714   if (!::GetLogicalDriveStrings(kDriveMappingSize - 1, drive_mapping)) {
715     DLOG(ERROR) << "Failed to get drive mapping.";
716     return false;
717   }
718 
719   // The drive mapping is a sequence of null terminated strings.
720   // The last string is empty.
721   wchar_t* drive_map_ptr = drive_mapping;
722   wchar_t device_path_as_string[MAX_PATH];
723   wchar_t drive[] = FILE_PATH_LITERAL(" :");
724 
725   // For each string in the drive mapping, get the junction that links
726   // to it.  If that junction is a prefix of |device_path|, then we
727   // know that |drive| is the real path prefix.
728   while (*drive_map_ptr) {
729     drive[0] = drive_map_ptr[0];  // Copy the drive letter.
730 
731     if (QueryDosDevice(drive, device_path_as_string, MAX_PATH)) {
732       FilePath device_path(device_path_as_string);
733       if (device_path == nt_device_path ||
734           device_path.IsParent(nt_device_path)) {
735         *out_drive_letter_path =
736             FilePath(drive + nt_device_path.value().substr(
737                                  wcslen(device_path_as_string)));
738         return true;
739       }
740     }
741     // Move to the next drive letter string, which starts one
742     // increment after the '\0' that terminates the current string.
743     while (*drive_map_ptr++) {}
744   }
745 
746   // No drive matched.  The path does not start with a device junction
747   // that is mounted as a drive letter.  This means there is no drive
748   // letter path to the volume that holds |device_path|, so fail.
749   return false;
750 }
751 
MakeLongFilePath(const FilePath & input)752 FilePath MakeLongFilePath(const FilePath& input) {
753   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
754 
755   DWORD path_long_len = ::GetLongPathName(input.value().c_str(), nullptr, 0);
756   if (path_long_len == 0UL)
757     return FilePath();
758 
759   std::wstring path_long_str;
760   path_long_len = ::GetLongPathName(input.value().c_str(),
761                                     WriteInto(&path_long_str, path_long_len),
762                                     path_long_len);
763   if (path_long_len == 0UL)
764     return FilePath();
765 
766   return FilePath(path_long_str);
767 }
768 
CreateWinHardLink(const FilePath & to_file,const FilePath & from_file)769 bool CreateWinHardLink(const FilePath& to_file, const FilePath& from_file) {
770   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
771   return ::CreateHardLink(to_file.value().c_str(), from_file.value().c_str(),
772                           nullptr);
773 }
774 
775 // TODO(rkc): Work out if we want to handle NTFS junctions here or not, handle
776 // them if we do decide to.
IsLink(const FilePath & file_path)777 bool IsLink(const FilePath& file_path) {
778   return false;
779 }
780 
GetFileInfo(const FilePath & file_path,File::Info * results)781 bool GetFileInfo(const FilePath& file_path, File::Info* results) {
782   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
783 
784   WIN32_FILE_ATTRIBUTE_DATA attr;
785   if (!GetFileAttributesEx(file_path.value().c_str(), GetFileExInfoStandard,
786                            &attr)) {
787     return false;
788   }
789 
790   ULARGE_INTEGER size;
791   size.HighPart = attr.nFileSizeHigh;
792   size.LowPart = attr.nFileSizeLow;
793   results->size = size.QuadPart;
794 
795   results->is_directory =
796       (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
797   results->last_modified = Time::FromFileTime(attr.ftLastWriteTime);
798   results->last_accessed = Time::FromFileTime(attr.ftLastAccessTime);
799   results->creation_time = Time::FromFileTime(attr.ftCreationTime);
800 
801   return true;
802 }
803 
OpenFile(const FilePath & filename,const char * mode)804 FILE* OpenFile(const FilePath& filename, const char* mode) {
805   // 'N' is unconditionally added below, so be sure there is not one already
806   // present before a comma in |mode|.
807   DCHECK(
808       strchr(mode, 'N') == nullptr ||
809       (strchr(mode, ',') != nullptr && strchr(mode, 'N') > strchr(mode, ',')));
810   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
811   std::wstring w_mode = UTF8ToWide(mode);
812   AppendModeCharacter(L'N', &w_mode);
813   return _wfsopen(filename.value().c_str(), w_mode.c_str(), _SH_DENYNO);
814 }
815 
FileToFILE(File file,const char * mode)816 FILE* FileToFILE(File file, const char* mode) {
817   if (!file.IsValid())
818     return NULL;
819   int fd =
820       _open_osfhandle(reinterpret_cast<intptr_t>(file.GetPlatformFile()), 0);
821   if (fd < 0)
822     return NULL;
823   file.TakePlatformFile();
824   FILE* stream = _fdopen(fd, mode);
825   if (!stream)
826     _close(fd);
827   return stream;
828 }
829 
ReadFile(const FilePath & filename,char * data,int max_size)830 int ReadFile(const FilePath& filename, char* data, int max_size) {
831   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
832   win::ScopedHandle file(CreateFile(filename.value().c_str(), GENERIC_READ,
833                                     FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
834                                     OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN,
835                                     NULL));
836   if (!file.IsValid())
837     return -1;
838 
839   DWORD read;
840   if (::ReadFile(file.Get(), data, max_size, &read, NULL))
841     return read;
842 
843   return -1;
844 }
845 
WriteFile(const FilePath & filename,const char * data,int size)846 int WriteFile(const FilePath& filename, const char* data, int size) {
847   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
848   win::ScopedHandle file(CreateFile(filename.value().c_str(), GENERIC_WRITE, 0,
849                                     NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
850                                     NULL));
851   if (!file.IsValid()) {
852     DPLOG(WARNING) << "CreateFile failed for path " << filename.value();
853     return -1;
854   }
855 
856   DWORD written;
857   BOOL result = ::WriteFile(file.Get(), data, size, &written, NULL);
858   if (result && static_cast<int>(written) == size)
859     return written;
860 
861   if (!result) {
862     // WriteFile failed.
863     DPLOG(WARNING) << "writing file " << filename.value() << " failed";
864   } else {
865     // Didn't write all the bytes.
866     DLOG(WARNING) << "wrote" << written << " bytes to " << filename.value()
867                   << " expected " << size;
868   }
869   return -1;
870 }
871 
AppendToFile(const FilePath & filename,const char * data,int size)872 bool AppendToFile(const FilePath& filename, const char* data, int size) {
873   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
874   win::ScopedHandle file(CreateFile(filename.value().c_str(), FILE_APPEND_DATA,
875                                     0, NULL, OPEN_EXISTING, 0, NULL));
876   if (!file.IsValid()) {
877     VPLOG(1) << "CreateFile failed for path " << filename.value();
878     return false;
879   }
880 
881   DWORD written;
882   BOOL result = ::WriteFile(file.Get(), data, size, &written, NULL);
883   if (result && static_cast<int>(written) == size)
884     return true;
885 
886   if (!result) {
887     // WriteFile failed.
888     VPLOG(1) << "Writing file " << filename.value() << " failed";
889   } else {
890     // Didn't write all the bytes.
891     VPLOG(1) << "Only wrote " << written << " out of " << size << " byte(s) to "
892              << filename.value();
893   }
894   return false;
895 }
896 
GetCurrentDirectory(FilePath * dir)897 bool GetCurrentDirectory(FilePath* dir) {
898   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
899 
900   wchar_t system_buffer[MAX_PATH];
901   system_buffer[0] = 0;
902   DWORD len = ::GetCurrentDirectory(MAX_PATH, system_buffer);
903   if (len == 0 || len > MAX_PATH)
904     return false;
905   // TODO(evanm): the old behavior of this function was to always strip the
906   // trailing slash.  We duplicate this here, but it shouldn't be necessary
907   // when everyone is using the appropriate FilePath APIs.
908   *dir = FilePath(FilePath::StringPieceType(system_buffer))
909              .StripTrailingSeparators();
910   return true;
911 }
912 
SetCurrentDirectory(const FilePath & directory)913 bool SetCurrentDirectory(const FilePath& directory) {
914   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
915   return ::SetCurrentDirectory(directory.value().c_str()) != 0;
916 }
917 
GetMaximumPathComponentLength(const FilePath & path)918 int GetMaximumPathComponentLength(const FilePath& path) {
919   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
920 
921   wchar_t volume_path[MAX_PATH];
922   if (!GetVolumePathNameW(path.NormalizePathSeparators().value().c_str(),
923                           volume_path, size(volume_path))) {
924     return -1;
925   }
926 
927   DWORD max_length = 0;
928   if (!GetVolumeInformationW(volume_path, NULL, 0, NULL, &max_length, NULL,
929                              NULL, 0)) {
930     return -1;
931   }
932 
933   // Length of |path| with path separator appended.
934   size_t prefix = path.StripTrailingSeparators().value().size() + 1;
935   // The whole path string must be shorter than MAX_PATH. That is, it must be
936   // prefix + component_length < MAX_PATH (or equivalently, <= MAX_PATH - 1).
937   int whole_path_limit = std::max(0, MAX_PATH - 1 - static_cast<int>(prefix));
938   return std::min(whole_path_limit, static_cast<int>(max_length));
939 }
940 
CopyFile(const FilePath & from_path,const FilePath & to_path)941 bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
942   return DoCopyFile(from_path, to_path, false);
943 }
944 
SetNonBlocking(int fd)945 bool SetNonBlocking(int fd) {
946   unsigned long nonblocking = 1;
947   if (ioctlsocket(fd, FIONBIO, &nonblocking) == 0)
948     return true;
949   return false;
950 }
951 
952 namespace {
953 
954 // ::PrefetchVirtualMemory() is only available on Windows 8 and above. Chrome
955 // supports Windows 7, so we need to check for the function's presence
956 // dynamically.
957 using PrefetchVirtualMemoryPtr = decltype(&::PrefetchVirtualMemory);
958 
959 // Returns null if ::PrefetchVirtualMemory() is not available.
GetPrefetchVirtualMemoryPtr()960 PrefetchVirtualMemoryPtr GetPrefetchVirtualMemoryPtr() {
961   HMODULE kernel32_dll = ::GetModuleHandleA("kernel32.dll");
962   return reinterpret_cast<decltype(&::PrefetchVirtualMemory)>(
963       GetProcAddress(kernel32_dll, "PrefetchVirtualMemory"));
964 }
965 
966 }  // namespace
967 
PreReadFile(const FilePath & file_path,bool is_executable,int64_t max_bytes)968 bool PreReadFile(const FilePath& file_path,
969                  bool is_executable,
970                  int64_t max_bytes) {
971   DCHECK_GE(max_bytes, 0);
972 
973   // On Win8 and higher use ::PrefetchVirtualMemory(). This is better than a
974   // simple data file read, more from a RAM perspective than CPU. This is
975   // because reading the file as data results in double mapping to
976   // Image/executable pages for all pages of code executed.
977   static PrefetchVirtualMemoryPtr prefetch_virtual_memory =
978       GetPrefetchVirtualMemoryPtr();
979 
980   if (prefetch_virtual_memory == nullptr)
981     return internal::PreReadFileSlow(file_path, max_bytes);
982 
983   if (max_bytes == 0) {
984     // PrefetchVirtualMemory() fails when asked to read zero bytes.
985     // base::MemoryMappedFile::Initialize() fails on an empty file.
986     return true;
987   }
988 
989   // PrefetchVirtualMemory() fails if the file is opened with write access.
990   MemoryMappedFile::Access access = is_executable
991                                         ? MemoryMappedFile::READ_CODE_IMAGE
992                                         : MemoryMappedFile::READ_ONLY;
993   MemoryMappedFile mapped_file;
994   if (!mapped_file.Initialize(file_path, access))
995     return false;
996 
997   const ::SIZE_T length =
998       std::min(base::saturated_cast<::SIZE_T>(max_bytes),
999                base::saturated_cast<::SIZE_T>(mapped_file.length()));
1000   ::_WIN32_MEMORY_RANGE_ENTRY address_range = {mapped_file.data(), length};
1001   return (*prefetch_virtual_memory)(::GetCurrentProcess(),
1002                                     /*NumberOfEntries=*/1, &address_range,
1003                                     /*Flags=*/0);
1004 }
1005 
1006 // -----------------------------------------------------------------------------
1007 
1008 namespace internal {
1009 
MoveUnsafe(const FilePath & from_path,const FilePath & to_path)1010 bool MoveUnsafe(const FilePath& from_path, const FilePath& to_path) {
1011   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
1012 
1013   // NOTE: I suspect we could support longer paths, but that would involve
1014   // analyzing all our usage of files.
1015   if (from_path.value().length() >= MAX_PATH ||
1016       to_path.value().length() >= MAX_PATH) {
1017     return false;
1018   }
1019   if (MoveFileEx(from_path.value().c_str(), to_path.value().c_str(),
1020                  MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING) != 0)
1021     return true;
1022 
1023   // Keep the last error value from MoveFileEx around in case the below
1024   // fails.
1025   bool ret = false;
1026   DWORD last_error = ::GetLastError();
1027 
1028   if (DirectoryExists(from_path)) {
1029     // MoveFileEx fails if moving directory across volumes. We will simulate
1030     // the move by using Copy and Delete. Ideally we could check whether
1031     // from_path and to_path are indeed in different volumes.
1032     ret = internal::CopyAndDeleteDirectory(from_path, to_path);
1033   }
1034 
1035   if (!ret) {
1036     // Leave a clue about what went wrong so that it can be (at least) picked
1037     // up by a PLOG entry.
1038     ::SetLastError(last_error);
1039   }
1040 
1041   return ret;
1042 }
1043 
CopyAndDeleteDirectory(const FilePath & from_path,const FilePath & to_path)1044 bool CopyAndDeleteDirectory(const FilePath& from_path,
1045                             const FilePath& to_path) {
1046   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
1047   if (CopyDirectory(from_path, to_path, true)) {
1048     if (DeleteFileRecursively(from_path))
1049       return true;
1050 
1051     // Like Move, this function is not transactional, so we just
1052     // leave the copied bits behind if deleting from_path fails.
1053     // If to_path exists previously then we have already overwritten
1054     // it by now, we don't get better off by deleting the new bits.
1055   }
1056   return false;
1057 }
1058 
1059 }  // namespace internal
1060 }  // namespace base
1061