file_util_win.cc 40 KB

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