blob: 71e5b7ece32d35ff31003483d8e6493700658011 [file] [log] [blame]
[email protected]71b7da72012-01-17 22:44:311// Copyright (c) 2012 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
[email protected]1010f7d2008-08-06 16:29:444
[email protected]e3177dd52014-08-13 20:22:145#include "base/files/file_util.h"
[email protected]1010f7d2008-08-06 16:29:446
7#include <windows.h>
[email protected]4d358282014-06-10 11:19:508#include <io.h>
[email protected]6f5f4322010-06-09 22:56:489#include <psapi.h>
[email protected]1010f7d2008-08-06 16:29:4410#include <shellapi.h>
11#include <shlobj.h>
avi543540e2015-12-24 05:15:3212#include <stddef.h>
aviab0f6cce2015-11-25 21:01:5713#include <stdint.h>
[email protected]1010f7d2008-08-06 16:29:4414#include <time.h>
tfarina060df7e2015-12-16 05:15:3215#include <winsock2.h>
[email protected]f1ddaa42011-07-19 05:03:0316
[email protected]59480302013-02-21 03:24:0817#include <algorithm>
[email protected]f1ddaa42011-07-19 05:03:0318#include <limits>
[email protected]1010f7d2008-08-06 16:29:4419#include <string>
20
[email protected]0d378d2b2014-02-05 19:55:5221#include "base/files/file_enumerator.h"
[email protected]57999812013-02-24 05:40:5222#include "base/files/file_path.h"
chengx170f1cf2017-04-06 22:46:5723#include "base/guid.h"
[email protected]1010f7d2008-08-06 16:29:4424#include "base/logging.h"
avi543540e2015-12-24 05:15:3225#include "base/macros.h"
Greg Thompson51ebae12018-05-08 17:43:1626#include "base/metrics/histogram_functions.h"
[email protected]dd4b51262013-07-25 21:38:2327#include "base/process/process_handle.h"
[email protected]cd86bca2013-06-17 21:06:3328#include "base/rand_util.h"
[email protected]3ea1b182013-02-08 22:38:4129#include "base/strings/string_number_conversions.h"
Greg Thompson51ebae12018-05-08 17:43:1630#include "base/strings/string_piece.h"
[email protected]251cd6e52013-06-11 13:36:3731#include "base/strings/string_util.h"
[email protected]a4ea1f12013-06-07 18:37:0732#include "base/strings/utf_string_conversions.h"
Etienne Pierre-Doray3879b052018-09-17 14:17:2233#include "base/threading/scoped_blocking_call.h"
[email protected]99084f62013-06-28 00:49:0734#include "base/time/time.h"
[email protected]58b7c5a62011-08-15 13:09:2735#include "base/win/scoped_handle.h"
[email protected]935aa542010-10-15 01:59:1536#include "base/win/windows_version.h"
[email protected]1010f7d2008-08-06 16:29:4437
[email protected]15476932013-04-12 05:17:1538namespace base {
39
[email protected]6f5f4322010-06-09 22:56:4840namespace {
41
[email protected]53af568e2010-08-04 17:10:1742const DWORD kFileShareAll =
43 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
44
Greg Thompson51ebae12018-05-08 17:43:1645// Records a sample in a histogram named
46// "Windows.PostOperationState.|operation|" indicating the state of |path|
47// following the named operation. If |operation_succeeded| is true, the
48// "operation succeeded" sample is recorded. Otherwise, the state of |path| is
49// queried and the most meaningful sample is recorded.
50void RecordPostOperationState(const FilePath& path,
51 StringPiece operation,
52 bool operation_succeeded) {
53 // The state of a filesystem item after an operation.
54 // These values are persisted to logs. Entries should not be renumbered and
55 // numeric values should never be reused.
56 enum class PostOperationState {
57 kOperationSucceeded = 0,
58 kFileNotFoundAfterFailure = 1,
59 kPathNotFoundAfterFailure = 2,
60 kAccessDeniedAfterFailure = 3,
61 kNoAttributesAfterFailure = 4,
62 kEmptyDirectoryAfterFailure = 5,
63 kNonEmptyDirectoryAfterFailure = 6,
64 kNotDirectoryAfterFailure = 7,
65 kCount
66 } metric = PostOperationState::kOperationSucceeded;
67
68 if (!operation_succeeded) {
69 const DWORD attributes = ::GetFileAttributes(path.value().c_str());
70 if (attributes == INVALID_FILE_ATTRIBUTES) {
71 // On failure to delete, one might expect the file/directory to still be
72 // in place. Slice a failure to get its attributes into a few common error
73 // buckets.
74 const DWORD error_code = ::GetLastError();
75 if (error_code == ERROR_FILE_NOT_FOUND)
76 metric = PostOperationState::kFileNotFoundAfterFailure;
77 else if (error_code == ERROR_PATH_NOT_FOUND)
78 metric = PostOperationState::kPathNotFoundAfterFailure;
79 else if (error_code == ERROR_ACCESS_DENIED)
80 metric = PostOperationState::kAccessDeniedAfterFailure;
81 else
82 metric = PostOperationState::kNoAttributesAfterFailure;
83 } else if (attributes & FILE_ATTRIBUTE_DIRECTORY) {
84 if (IsDirectoryEmpty(path))
85 metric = PostOperationState::kEmptyDirectoryAfterFailure;
86 else
87 metric = PostOperationState::kNonEmptyDirectoryAfterFailure;
88 } else {
89 metric = PostOperationState::kNotDirectoryAfterFailure;
90 }
91 }
92
93 std::string histogram_name = "Windows.PostOperationState.";
94 operation.AppendToString(&histogram_name);
95 UmaHistogramEnumeration(histogram_name, metric, PostOperationState::kCount);
96}
97
98// Records the sample |error| in a histogram named
99// "Windows.FilesystemError.|operation|".
100void RecordFilesystemError(StringPiece operation, DWORD error) {
101 std::string histogram_name = "Windows.FilesystemError.";
102 operation.AppendToString(&histogram_name);
103 UmaHistogramSparse(histogram_name, error);
104}
105
Greg Thompson92780982018-05-24 18:20:39106// Returns the Win32 last error code or ERROR_SUCCESS if the last error code is
107// ERROR_FILE_NOT_FOUND or ERROR_PATH_NOT_FOUND. This is useful in cases where
108// the absence of a file or path is a success condition (e.g., when attempting
109// to delete an item in the filesystem).
110DWORD ReturnLastErrorOrSuccessOnNotFound() {
111 const DWORD error_code = ::GetLastError();
112 return (error_code == ERROR_FILE_NOT_FOUND ||
113 error_code == ERROR_PATH_NOT_FOUND)
114 ? ERROR_SUCCESS
115 : error_code;
116}
117
jschuha1d3baae2015-10-23 00:48:28118// Deletes all files and directories in a path.
Greg Thompson51ebae12018-05-08 17:43:16119// Returns ERROR_SUCCESS on success or the Windows error code corresponding to
Greg Thompson92780982018-05-24 18:20:39120// the first error encountered. ERROR_FILE_NOT_FOUND and ERROR_PATH_NOT_FOUND
121// are considered success conditions, and are therefore never returned.
Greg Thompson51ebae12018-05-08 17:43:16122DWORD DeleteFileRecursive(const FilePath& path,
123 const FilePath::StringType& pattern,
124 bool recursive) {
jschuha1d3baae2015-10-23 00:48:28125 FileEnumerator traversal(path, false,
126 FileEnumerator::FILES | FileEnumerator::DIRECTORIES,
127 pattern);
Greg Thompson51ebae12018-05-08 17:43:16128 DWORD result = ERROR_SUCCESS;
jschuha1d3baae2015-10-23 00:48:28129 for (FilePath current = traversal.Next(); !current.empty();
130 current = traversal.Next()) {
131 // Try to clear the read-only bit if we find it.
132 FileEnumerator::FileInfo info = traversal.GetInfo();
133 if ((info.find_data().dwFileAttributes & FILE_ATTRIBUTE_READONLY) &&
134 (recursive || !info.IsDirectory())) {
Greg Thompson51ebae12018-05-08 17:43:16135 ::SetFileAttributes(
jschuha1d3baae2015-10-23 00:48:28136 current.value().c_str(),
137 info.find_data().dwFileAttributes & ~FILE_ATTRIBUTE_READONLY);
138 }
139
Greg Thompson51ebae12018-05-08 17:43:16140 DWORD this_result = ERROR_SUCCESS;
jschuha1d3baae2015-10-23 00:48:28141 if (info.IsDirectory()) {
Greg Thompson51ebae12018-05-08 17:43:16142 if (recursive) {
143 this_result = DeleteFileRecursive(current, pattern, true);
Greg Thompson92780982018-05-24 18:20:39144 DCHECK_NE(static_cast<LONG>(this_result), ERROR_FILE_NOT_FOUND);
145 DCHECK_NE(static_cast<LONG>(this_result), ERROR_PATH_NOT_FOUND);
Greg Thompson51ebae12018-05-08 17:43:16146 if (this_result == ERROR_SUCCESS &&
147 !::RemoveDirectory(current.value().c_str())) {
Greg Thompson92780982018-05-24 18:20:39148 this_result = ReturnLastErrorOrSuccessOnNotFound();
Greg Thompson51ebae12018-05-08 17:43:16149 }
150 }
jschuha1d3baae2015-10-23 00:48:28151 } else if (!::DeleteFile(current.value().c_str())) {
Greg Thompson92780982018-05-24 18:20:39152 this_result = ReturnLastErrorOrSuccessOnNotFound();
jschuha1d3baae2015-10-23 00:48:28153 }
Greg Thompson51ebae12018-05-08 17:43:16154 if (result == ERROR_SUCCESS)
155 result = this_result;
jschuha1d3baae2015-10-23 00:48:28156 }
Greg Thompson51ebae12018-05-08 17:43:16157 return result;
jschuha1d3baae2015-10-23 00:48:28158}
159
grtd0bc44f2017-02-13 17:52:17160// Appends |mode_char| to |mode| before the optional character set encoding; see
161// https://msdn.microsoft.com/library/yeby3zcb.aspx for details.
162void AppendModeCharacter(base::char16 mode_char, base::string16* mode) {
163 size_t comma_pos = mode->find(L',');
164 mode->insert(comma_pos == base::string16::npos ? mode->length() : comma_pos,
165 1, mode_char);
166}
167
Eric Caruso128f0dd2017-12-16 01:32:49168bool DoCopyFile(const FilePath& from_path,
169 const FilePath& to_path,
170 bool fail_if_exists) {
Etienne Pierre-Doray3879b052018-09-17 14:17:22171 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
Eric Caruso128f0dd2017-12-16 01:32:49172 if (from_path.ReferencesParent() || to_path.ReferencesParent())
[email protected]ad56b502010-12-21 15:47:54173 return false;
174
Eric Caruso128f0dd2017-12-16 01:32:49175 // NOTE: I suspect we could support longer paths, but that would involve
176 // analyzing all our usage of files.
177 if (from_path.value().length() >= MAX_PATH ||
178 to_path.value().length() >= MAX_PATH) {
jschuha1d3baae2015-10-23 00:48:28179 return false;
180 }
jschuha1d3baae2015-10-23 00:48:28181
Eric Caruso128f0dd2017-12-16 01:32:49182 // Unlike the posix implementation that copies the file manually and discards
183 // the ACL bits, CopyFile() copies the complete SECURITY_DESCRIPTOR and access
184 // bits, which is usually not what we want. We can't do much about the
185 // SECURITY_DESCRIPTOR but at least remove the read only bit.
186 const wchar_t* dest = to_path.value().c_str();
187 if (!::CopyFile(from_path.value().c_str(), dest, fail_if_exists)) {
188 // Copy failed.
[email protected]6aa4a1c02010-01-15 18:49:58189 return false;
Eric Caruso128f0dd2017-12-16 01:32:49190 }
191 DWORD attrs = GetFileAttributes(dest);
192 if (attrs == INVALID_FILE_ATTRIBUTES) {
193 return false;
194 }
195 if (attrs & FILE_ATTRIBUTE_READONLY) {
196 SetFileAttributes(dest, attrs & ~FILE_ATTRIBUTE_READONLY);
197 }
198 return true;
[email protected]6aa4a1c02010-01-15 18:49:58199}
200
Eric Caruso128f0dd2017-12-16 01:32:49201bool DoCopyDirectory(const FilePath& from_path,
202 const FilePath& to_path,
203 bool recursive,
204 bool fail_if_exists) {
[email protected]0d378d2b2014-02-05 19:55:52205 // NOTE(maruel): Previous version of this function used to call
206 // SHFileOperation(). This used to copy the file attributes and extended
207 // attributes, OLE structured storage, NTFS file system alternate data
208 // streams, SECURITY_DESCRIPTOR. In practice, this is not what we want, we
209 // want the containing directory to propagate its SECURITY_DESCRIPTOR.
Etienne Pierre-Doray3879b052018-09-17 14:17:22210 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
[email protected]45446a52010-11-04 17:41:00211
[email protected]0d378d2b2014-02-05 19:55:52212 // NOTE: I suspect we could support longer paths, but that would involve
213 // analyzing all our usage of files.
214 if (from_path.value().length() >= MAX_PATH ||
215 to_path.value().length() >= MAX_PATH) {
216 return false;
[email protected]1010f7d2008-08-06 16:29:44217 }
218
[email protected]0d378d2b2014-02-05 19:55:52219 // This function does not properly handle destinations within the source.
220 FilePath real_to_path = to_path;
221 if (PathExists(real_to_path)) {
222 real_to_path = MakeAbsoluteFilePath(real_to_path);
223 if (real_to_path.empty())
224 return false;
225 } else {
226 real_to_path = MakeAbsoluteFilePath(real_to_path.DirName());
227 if (real_to_path.empty())
228 return false;
229 }
230 FilePath real_from_path = MakeAbsoluteFilePath(from_path);
231 if (real_from_path.empty())
232 return false;
chengx5f1289d2016-12-16 03:38:32233 if (real_to_path == real_from_path || real_from_path.IsParent(real_to_path))
[email protected]0d378d2b2014-02-05 19:55:52234 return false;
[email protected]0d378d2b2014-02-05 19:55:52235
236 int traverse_type = FileEnumerator::FILES;
237 if (recursive)
238 traverse_type |= FileEnumerator::DIRECTORIES;
239 FileEnumerator traversal(from_path, recursive, traverse_type);
240
241 if (!PathExists(from_path)) {
242 DLOG(ERROR) << "CopyDirectory() couldn't stat source directory: "
243 << from_path.value().c_str();
244 return false;
245 }
246 // TODO(maruel): This is not necessary anymore.
247 DCHECK(recursive || DirectoryExists(from_path));
248
249 FilePath current = from_path;
250 bool from_is_dir = DirectoryExists(from_path);
251 bool success = true;
252 FilePath from_path_base = from_path;
253 if (recursive && DirectoryExists(to_path)) {
254 // If the destination already exists and is a directory, then the
255 // top level of source needs to be copied.
256 from_path_base = from_path.DirName();
257 }
258
259 while (success && !current.empty()) {
260 // current is the source path, including from_path, so append
261 // the suffix after from_path to to_path to create the target_path.
262 FilePath target_path(to_path);
263 if (from_path_base != current) {
264 if (!from_path_base.AppendRelativePath(current, &target_path)) {
265 success = false;
266 break;
267 }
268 }
269
270 if (from_is_dir) {
271 if (!DirectoryExists(target_path) &&
272 !::CreateDirectory(target_path.value().c_str(), NULL)) {
273 DLOG(ERROR) << "CopyDirectory() couldn't create directory: "
274 << target_path.value().c_str();
275 success = false;
276 }
Eric Caruso128f0dd2017-12-16 01:32:49277 } else if (!DoCopyFile(current, target_path, fail_if_exists)) {
[email protected]0d378d2b2014-02-05 19:55:52278 DLOG(ERROR) << "CopyDirectory() couldn't create file: "
279 << target_path.value().c_str();
280 success = false;
281 }
282
283 current = traversal.Next();
284 if (!current.empty())
285 from_is_dir = traversal.GetInfo().IsDirectory();
286 }
287
288 return success;
[email protected]1010f7d2008-08-06 16:29:44289}
290
Greg Thompson51ebae12018-05-08 17:43:16291// Returns ERROR_SUCCESS on success, or a Windows error code on failure.
292DWORD DoDeleteFile(const FilePath& path, bool recursive) {
Etienne Pierre-Doray3879b052018-09-17 14:17:22293 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
Greg Thompson51ebae12018-05-08 17:43:16294
295 if (path.empty())
296 return ERROR_SUCCESS;
297
298 if (path.value().length() >= MAX_PATH)
299 return ERROR_BAD_PATHNAME;
300
301 // Handle any path with wildcards.
302 if (path.BaseName().value().find_first_of(L"*?") !=
303 FilePath::StringType::npos) {
Greg Thompson92780982018-05-24 18:20:39304 const DWORD error_code =
305 DeleteFileRecursive(path.DirName(), path.BaseName().value(), recursive);
306 DCHECK_NE(static_cast<LONG>(error_code), ERROR_FILE_NOT_FOUND);
307 DCHECK_NE(static_cast<LONG>(error_code), ERROR_PATH_NOT_FOUND);
308 return error_code;
Greg Thompson51ebae12018-05-08 17:43:16309 }
310
311 // Report success if the file or path does not exist.
312 const DWORD attr = ::GetFileAttributes(path.value().c_str());
Greg Thompson92780982018-05-24 18:20:39313 if (attr == INVALID_FILE_ATTRIBUTES)
314 return ReturnLastErrorOrSuccessOnNotFound();
Greg Thompson51ebae12018-05-08 17:43:16315
316 // Clear the read-only bit if it is set.
317 if ((attr & FILE_ATTRIBUTE_READONLY) &&
318 !::SetFileAttributes(path.value().c_str(),
319 attr & ~FILE_ATTRIBUTE_READONLY)) {
Greg Thompson92780982018-05-24 18:20:39320 // It's possible for |path| to be gone now under a race with other deleters.
321 return ReturnLastErrorOrSuccessOnNotFound();
Greg Thompson51ebae12018-05-08 17:43:16322 }
323
324 // Perform a simple delete on anything that isn't a directory.
325 if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
Greg Thompson92780982018-05-24 18:20:39326 return ::DeleteFile(path.value().c_str())
327 ? ERROR_SUCCESS
328 : ReturnLastErrorOrSuccessOnNotFound();
Greg Thompson51ebae12018-05-08 17:43:16329 }
330
331 if (recursive) {
332 const DWORD error_code = DeleteFileRecursive(path, L"*", true);
Greg Thompson92780982018-05-24 18:20:39333 DCHECK_NE(static_cast<LONG>(error_code), ERROR_FILE_NOT_FOUND);
334 DCHECK_NE(static_cast<LONG>(error_code), ERROR_PATH_NOT_FOUND);
Greg Thompson51ebae12018-05-08 17:43:16335 if (error_code != ERROR_SUCCESS)
336 return error_code;
337 }
Greg Thompson92780982018-05-24 18:20:39338 return ::RemoveDirectory(path.value().c_str())
339 ? ERROR_SUCCESS
340 : ReturnLastErrorOrSuccessOnNotFound();
Greg Thompson51ebae12018-05-08 17:43:16341}
342
Eric Caruso128f0dd2017-12-16 01:32:49343} // namespace
344
345FilePath MakeAbsoluteFilePath(const FilePath& input) {
Etienne Pierre-Doray3879b052018-09-17 14:17:22346 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
Eric Caruso128f0dd2017-12-16 01:32:49347 wchar_t file_path[MAX_PATH];
348 if (!_wfullpath(file_path, input.value().c_str(), MAX_PATH))
349 return FilePath();
350 return FilePath(file_path);
351}
352
353bool DeleteFile(const FilePath& path, bool recursive) {
Greg Thompson51ebae12018-05-08 17:43:16354 static constexpr char kRecursive[] = "DeleteFile.Recursive";
355 static constexpr char kNonRecursive[] = "DeleteFile.NonRecursive";
356 const StringPiece operation(recursive ? kRecursive : kNonRecursive);
357
Etienne Pierre-Doray3879b052018-09-17 14:17:22358 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
Eric Caruso128f0dd2017-12-16 01:32:49359
Greg Thompson51ebae12018-05-08 17:43:16360 // Metrics for delete failures tracked in https://crbug.com/599084. Delete may
361 // fail for a number of reasons. Log some metrics relating to failures in the
362 // current code so that any improvements or regressions resulting from
363 // subsequent code changes can be detected.
364 const DWORD error = DoDeleteFile(path, recursive);
365 RecordPostOperationState(path, operation, error == ERROR_SUCCESS);
366 if (error == ERROR_SUCCESS)
Eric Caruso128f0dd2017-12-16 01:32:49367 return true;
368
Greg Thompson51ebae12018-05-08 17:43:16369 RecordFilesystemError(operation, error);
Eric Caruso128f0dd2017-12-16 01:32:49370 return false;
371}
372
373bool DeleteFileAfterReboot(const FilePath& path) {
Etienne Pierre-Doray3879b052018-09-17 14:17:22374 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
Eric Caruso128f0dd2017-12-16 01:32:49375
376 if (path.value().length() >= MAX_PATH)
377 return false;
378
S. Ganeshc18d5932018-11-05 03:45:31379 return ::MoveFileEx(path.value().c_str(), nullptr,
380 MOVEFILE_DELAY_UNTIL_REBOOT);
Eric Caruso128f0dd2017-12-16 01:32:49381}
382
383bool ReplaceFile(const FilePath& from_path,
384 const FilePath& to_path,
385 File::Error* error) {
Etienne Pierre-Doray3879b052018-09-17 14:17:22386 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
Eric Caruso128f0dd2017-12-16 01:32:49387 // Try a simple move first. It will only succeed when |to_path| doesn't
388 // already exist.
389 if (::MoveFile(from_path.value().c_str(), to_path.value().c_str()))
390 return true;
391 File::Error move_error = File::OSErrorToFileError(GetLastError());
392
393 // Try the full-blown replace if the move fails, as ReplaceFile will only
394 // succeed when |to_path| does exist. When writing to a network share, we may
395 // not be able to change the ACLs. Ignore ACL errors then
396 // (REPLACEFILE_IGNORE_MERGE_ERRORS).
397 if (::ReplaceFile(to_path.value().c_str(), from_path.value().c_str(), NULL,
398 REPLACEFILE_IGNORE_MERGE_ERRORS, NULL, NULL)) {
399 return true;
400 }
401 // In the case of FILE_ERROR_NOT_FOUND from ReplaceFile, it is likely that
402 // |to_path| does not exist. In this case, the more relevant error comes
403 // from the call to MoveFile.
404 if (error) {
405 File::Error replace_error = File::OSErrorToFileError(GetLastError());
406 *error = replace_error == File::FILE_ERROR_NOT_FOUND ? move_error
407 : replace_error;
408 }
409 return false;
410}
411
412bool CopyDirectory(const FilePath& from_path,
413 const FilePath& to_path,
414 bool recursive) {
415 return DoCopyDirectory(from_path, to_path, recursive, false);
416}
417
418bool CopyDirectoryExcl(const FilePath& from_path,
419 const FilePath& to_path,
420 bool recursive) {
421 return DoCopyDirectory(from_path, to_path, recursive, true);
422}
423
[email protected]7567484142013-07-11 17:36:07424bool PathExists(const FilePath& path) {
Etienne Pierre-Doray3879b052018-09-17 14:17:22425 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
[email protected]7567484142013-07-11 17:36:07426 return (GetFileAttributes(path.value().c_str()) != INVALID_FILE_ATTRIBUTES);
427}
428
[email protected]7e1fde6a2008-12-23 20:20:10429bool PathIsWritable(const FilePath& path) {
Etienne Pierre-Doray3879b052018-09-17 14:17:22430 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
brucedawsond3509432015-09-18 18:28:13431 HANDLE dir =
[email protected]53af568e2010-08-04 17:10:17432 CreateFile(path.value().c_str(), FILE_ADD_FILE, kFileShareAll,
brucedawsond3509432015-09-18 18:28:13433 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
[email protected]1010f7d2008-08-06 16:29:44434
brucedawsond3509432015-09-18 18:28:13435 if (dir == INVALID_HANDLE_VALUE)
436 return false;
437
438 CloseHandle(dir);
439 return true;
[email protected]1010f7d2008-08-06 16:29:44440}
441
[email protected]640517f2008-10-30 23:54:04442bool DirectoryExists(const FilePath& path) {
Etienne Pierre-Doray3879b052018-09-17 14:17:22443 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
[email protected]640517f2008-10-30 23:54:04444 DWORD fileattr = GetFileAttributes(path.value().c_str());
[email protected]806b9c62008-09-11 16:09:11445 if (fileattr != INVALID_FILE_ATTRIBUTES)
446 return (fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0;
447 return false;
448}
449
[email protected]640517f2008-10-30 23:54:04450bool GetTempDir(FilePath* path) {
[email protected]1010f7d2008-08-06 16:29:44451 wchar_t temp_path[MAX_PATH + 1];
452 DWORD path_len = ::GetTempPath(MAX_PATH, temp_path);
453 if (path_len >= MAX_PATH || path_len <= 0)
454 return false;
[email protected]640517f2008-10-30 23:54:04455 // TODO(evanm): the old behavior of this function was to always strip the
456 // trailing slash. We duplicate this here, but it shouldn't be necessary
457 // when everyone is using the appropriate FilePath APIs.
[email protected]7d95aae2009-10-09 07:33:39458 *path = FilePath(temp_path).StripTrailingSeparators();
[email protected]1010f7d2008-08-06 16:29:44459 return true;
460}
461
[email protected]ffaee18e2014-02-19 20:34:23462FilePath GetHomeDir() {
463 char16 result[MAX_PATH];
464 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT,
465 result)) &&
466 result[0]) {
467 return FilePath(result);
468 }
469
470 // Fall back to the temporary directory on failure.
471 FilePath temp;
472 if (GetTempDir(&temp))
473 return temp;
474
475 // Last resort.
476 return FilePath(L"C:\\");
477}
478
[email protected]33edeab2009-08-18 16:07:55479bool CreateTemporaryFile(FilePath* path) {
Etienne Pierre-Doray3879b052018-09-17 14:17:22480 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
[email protected]45446a52010-11-04 17:41:00481
[email protected]33edeab2009-08-18 16:07:55482 FilePath temp_file;
[email protected]1010f7d2008-08-06 16:29:44483
[email protected]33edeab2009-08-18 16:07:55484 if (!GetTempDir(path))
[email protected]1010f7d2008-08-06 16:29:44485 return false;
486
[email protected]33edeab2009-08-18 16:07:55487 if (CreateTemporaryFileInDir(*path, &temp_file)) {
488 *path = temp_file;
[email protected]392264c2008-11-11 00:01:38489 return true;
490 }
491
[email protected]bd05da22009-01-27 19:05:54492 return false;
[email protected]9ccbb372008-10-10 18:50:32493}
494
[email protected]9e51af92009-02-04 00:58:39495// On POSIX we have semantics to create and open a temporary file
496// atomically.
497// TODO(jrg): is there equivalent call to use on Windows instead of
498// going 2-step?
[email protected]6faa0e0d2009-04-28 06:50:36499FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
Etienne Pierre-Doray3879b052018-09-17 14:17:22500 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
[email protected]33edeab2009-08-18 16:07:55501 if (!CreateTemporaryFileInDir(dir, path)) {
[email protected]9e51af92009-02-04 00:58:39502 return NULL;
503 }
[email protected]6faa0e0d2009-04-28 06:50:36504 // Open file in binary mode, to avoid problems with fwrite. On Windows
505 // it replaces \n's with \r\n's, which may surprise you.
506 // Reference: http://msdn.microsoft.com/en-us/library/h9t88zwz(VS.71).aspx
[email protected]7600d0b2013-12-08 21:43:30507 return OpenFile(*path, "wb+");
[email protected]9e51af92009-02-04 00:58:39508}
509
[email protected]03d9afc02013-12-03 17:55:52510bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) {
Etienne Pierre-Doray3879b052018-09-17 14:17:22511 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
[email protected]45446a52010-11-04 17:41:00512
chengx170f1cf2017-04-06 22:46:57513 // Use GUID instead of ::GetTempFileName() to generate unique file names.
514 // "Due to the algorithm used to generate file names, GetTempFileName can
515 // perform poorly when creating a large number of files with the same prefix.
516 // In such cases, it is recommended that you construct unique file names based
517 // on GUIDs."
Greg Thompson51ebae12018-05-08 17:43:16518 // https://msdn.microsoft.com/library/windows/desktop/aa364991.aspx
[email protected]9ccbb372008-10-10 18:50:32519
chengx170f1cf2017-04-06 22:46:57520 FilePath temp_name;
521 bool create_file_success = false;
522
523 // Although it is nearly impossible to get a duplicate name with GUID, we
524 // still use a loop here in case it happens.
525 for (int i = 0; i < 100; ++i) {
526 temp_name = dir.Append(ASCIIToUTF16(base::GenerateGUID()) + L".tmp");
527 File file(temp_name,
528 File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE);
529 if (file.IsValid()) {
530 file.Close();
531 create_file_success = true;
532 break;
533 }
534 }
535
chengx170f1cf2017-04-06 22:46:57536 if (!create_file_success) {
[email protected]03d9afc02013-12-03 17:55:52537 DPLOG(WARNING) << "Failed to get temporary file name in "
538 << UTF16ToUTF8(dir.value());
[email protected]04a6bd3b92010-01-11 20:23:41539 return false;
540 }
[email protected]1010f7d2008-08-06 16:29:44541
[email protected]e1af0d52012-11-12 16:28:07542 wchar_t long_temp_name[MAX_PATH + 1];
chengx170f1cf2017-04-06 22:46:57543 DWORD long_name_len =
544 GetLongPathName(temp_name.value().c_str(), long_temp_name, MAX_PATH);
[email protected]e1af0d52012-11-12 16:28:07545 if (long_name_len > MAX_PATH || long_name_len == 0) {
546 // GetLongPathName() failed, but we still have a temporary file.
chengx170f1cf2017-04-06 22:46:57547 *temp_file = std::move(temp_name);
[email protected]e1af0d52012-11-12 16:28:07548 return true;
[email protected]04a6bd3b92010-01-11 20:23:41549 }
[email protected]1010f7d2008-08-06 16:29:44550
[email protected]e1af0d52012-11-12 16:28:07551 FilePath::StringType long_temp_name_str;
552 long_temp_name_str.assign(long_temp_name, long_name_len);
chengx170f1cf2017-04-06 22:46:57553 *temp_file = FilePath(std::move(long_temp_name_str));
[email protected]1010f7d2008-08-06 16:29:44554 return true;
555}
556
[email protected]b0b3abd92010-04-30 17:00:09557bool CreateTemporaryDirInDir(const FilePath& base_dir,
558 const FilePath::StringType& prefix,
[email protected]046062e82010-06-30 07:19:11559 FilePath* new_dir) {
Etienne Pierre-Doray3879b052018-09-17 14:17:22560 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
[email protected]45446a52010-11-04 17:41:00561
[email protected]7e1fde6a2008-12-23 20:20:10562 FilePath path_to_create;
[email protected]1010f7d2008-08-06 16:29:44563
[email protected]b026e35d2010-10-19 02:31:03564 for (int count = 0; count < 50; ++count) {
[email protected]1010f7d2008-08-06 16:29:44565 // Try create a new temporary directory with random generated name. If
566 // the one exists, keep trying another path name until we reach some limit.
[email protected]528c56d2010-07-30 19:28:44567 string16 new_dir_name;
[email protected]1010f7d2008-08-06 16:29:44568 new_dir_name.assign(prefix);
[email protected]03d9afc02013-12-03 17:55:52569 new_dir_name.append(IntToString16(GetCurrentProcId()));
[email protected]d6c61132012-05-01 23:57:51570 new_dir_name.push_back('_');
aviab0f6cce2015-11-25 21:01:57571 new_dir_name.append(
572 IntToString16(RandInt(0, std::numeric_limits<int16_t>::max())));
[email protected]1010f7d2008-08-06 16:29:44573
[email protected]bdddb672011-02-18 14:54:50574 path_to_create = base_dir.Append(new_dir_name);
[email protected]b026e35d2010-10-19 02:31:03575 if (::CreateDirectory(path_to_create.value().c_str(), NULL)) {
576 *new_dir = path_to_create;
577 return true;
578 }
[email protected]1010f7d2008-08-06 16:29:44579 }
580
[email protected]b026e35d2010-10-19 02:31:03581 return false;
[email protected]1010f7d2008-08-06 16:29:44582}
583
[email protected]b0b3abd92010-04-30 17:00:09584bool CreateNewTempDirectory(const FilePath::StringType& prefix,
585 FilePath* new_temp_path) {
Etienne Pierre-Doray3879b052018-09-17 14:17:22586 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
[email protected]45446a52010-11-04 17:41:00587
[email protected]b0b3abd92010-04-30 17:00:09588 FilePath system_temp_dir;
589 if (!GetTempDir(&system_temp_dir))
590 return false;
591
[email protected]bd781d892010-07-30 15:33:28592 return CreateTemporaryDirInDir(system_temp_dir, prefix, new_temp_path);
[email protected]b0b3abd92010-04-30 17:00:09593}
594
[email protected]cfd23d22013-06-11 03:50:25595bool CreateDirectoryAndGetError(const FilePath& full_path,
[email protected]54124ed02014-01-07 10:06:58596 File::Error* error) {
Etienne Pierre-Doray3879b052018-09-17 14:17:22597 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
[email protected]45446a52010-11-04 17:41:00598
[email protected]40676ab2009-11-27 14:54:41599 // If the path exists, we've succeeded if it's a directory, failed otherwise.
Greg Thompsona995c722018-08-16 21:50:01600 const wchar_t* const full_path_str = full_path.value().c_str();
601 const DWORD fileattr = ::GetFileAttributes(full_path_str);
[email protected]075be5d2010-09-24 15:39:39602 if (fileattr != INVALID_FILE_ATTRIBUTES) {
[email protected]40676ab2009-11-27 14:54:41603 if ((fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
[email protected]b026e35d2010-10-19 02:31:03604 DVLOG(1) << "CreateDirectory(" << full_path_str << "), "
605 << "directory already exists.";
[email protected]40676ab2009-11-27 14:54:41606 return true;
[email protected]40676ab2009-11-27 14:54:41607 }
[email protected]a42d4632011-10-26 21:48:00608 DLOG(WARNING) << "CreateDirectory(" << full_path_str << "), "
609 << "conflicts with existing file.";
Greg Thompsona995c722018-08-16 21:50:01610 if (error)
[email protected]54124ed02014-01-07 10:06:58611 *error = File::FILE_ERROR_NOT_A_DIRECTORY;
Greg Thompsona995c722018-08-16 21:50:01612 ::SetLastError(ERROR_FILE_EXISTS);
[email protected]b026e35d2010-10-19 02:31:03613 return false;
[email protected]40676ab2009-11-27 14:54:41614 }
615
616 // Invariant: Path does not exist as file or directory.
617
618 // Attempt to create the parent recursively. This will immediately return
619 // true if it already exists, otherwise will create all required parent
620 // directories starting with the highest-level missing parent.
[email protected]89b9ae092009-12-17 20:42:40621 FilePath parent_path(full_path.DirName());
622 if (parent_path.value() == full_path.value()) {
Greg Thompsona995c722018-08-16 21:50:01623 if (error)
[email protected]54124ed02014-01-07 10:06:58624 *error = File::FILE_ERROR_NOT_FOUND;
Greg Thompsona995c722018-08-16 21:50:01625 ::SetLastError(ERROR_FILE_NOT_FOUND);
[email protected]89b9ae092009-12-17 20:42:40626 return false;
627 }
[email protected]cfd23d22013-06-11 03:50:25628 if (!CreateDirectoryAndGetError(parent_path, error)) {
[email protected]075be5d2010-09-24 15:39:39629 DLOG(WARNING) << "Failed to create one of the parent directories.";
Greg Thompsona995c722018-08-16 21:50:01630 DCHECK(!error || *error != File::FILE_OK);
[email protected]40676ab2009-11-27 14:54:41631 return false;
632 }
633
Greg Thompsona995c722018-08-16 21:50:01634 if (::CreateDirectory(full_path_str, NULL))
635 return true;
636
637 const DWORD error_code = ::GetLastError();
638 if (error_code == ERROR_ALREADY_EXISTS && DirectoryExists(full_path)) {
639 // This error code ERROR_ALREADY_EXISTS doesn't indicate whether we were
640 // racing with someone creating the same directory, or a file with the same
641 // path. If DirectoryExists() returns true, we lost the race to create the
642 // same directory.
[email protected]81569622008-09-09 22:35:16643 return true;
[email protected]40676ab2009-11-27 14:54:41644 }
Greg Thompsona995c722018-08-16 21:50:01645 if (error)
646 *error = File::OSErrorToFileError(error_code);
647 ::SetLastError(error_code);
648 DPLOG(WARNING) << "Failed to create directory " << full_path_str;
649 return false;
[email protected]1010f7d2008-08-06 16:29:44650}
651
[email protected]56285702013-12-04 18:22:49652bool NormalizeFilePath(const FilePath& path, FilePath* real_path) {
Etienne Pierre-Doray3879b052018-09-17 14:17:22653 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
[email protected]56285702013-12-04 18:22:49654 FilePath mapped_file;
[email protected]9eae4e62013-12-04 20:56:49655 if (!NormalizeToNativeFilePath(path, &mapped_file))
[email protected]56285702013-12-04 18:22:49656 return false;
657 // NormalizeToNativeFilePath() will return a path that starts with
658 // "\Device\Harddisk...". Helper DevicePathToDriveLetterPath()
659 // will find a drive letter which maps to the path's device, so
660 // that we return a path starting with a drive letter.
[email protected]9eae4e62013-12-04 20:56:49661 return DevicePathToDriveLetterPath(mapped_file, real_path);
[email protected]56285702013-12-04 18:22:49662}
663
[email protected]9eae4e62013-12-04 20:56:49664bool DevicePathToDriveLetterPath(const FilePath& nt_device_path,
665 FilePath* out_drive_letter_path) {
Etienne Pierre-Doray3879b052018-09-17 14:17:22666 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
[email protected]426d1c92013-12-03 20:08:54667
[email protected]9eae4e62013-12-04 20:56:49668 // Get the mapping of drive letters to device paths.
669 const int kDriveMappingSize = 1024;
670 wchar_t drive_mapping[kDriveMappingSize] = {'\0'};
671 if (!::GetLogicalDriveStrings(kDriveMappingSize - 1, drive_mapping)) {
672 DLOG(ERROR) << "Failed to get drive mapping.";
673 return false;
674 }
[email protected]426d1c92013-12-03 20:08:54675
[email protected]9eae4e62013-12-04 20:56:49676 // The drive mapping is a sequence of null terminated strings.
677 // The last string is empty.
678 wchar_t* drive_map_ptr = drive_mapping;
679 wchar_t device_path_as_string[MAX_PATH];
680 wchar_t drive[] = L" :";
[email protected]426d1c92013-12-03 20:08:54681
[email protected]9eae4e62013-12-04 20:56:49682 // For each string in the drive mapping, get the junction that links
683 // to it. If that junction is a prefix of |device_path|, then we
684 // know that |drive| is the real path prefix.
685 while (*drive_map_ptr) {
686 drive[0] = drive_map_ptr[0]; // Copy the drive letter.
687
688 if (QueryDosDevice(drive, device_path_as_string, MAX_PATH)) {
689 FilePath device_path(device_path_as_string);
690 if (device_path == nt_device_path ||
691 device_path.IsParent(nt_device_path)) {
692 *out_drive_letter_path = FilePath(drive +
693 nt_device_path.value().substr(wcslen(device_path_as_string)));
694 return true;
695 }
696 }
697 // Move to the next drive letter string, which starts one
698 // increment after the '\0' that terminates the current string.
thestig8badc792014-12-04 22:14:22699 while (*drive_map_ptr++) {}
[email protected]9eae4e62013-12-04 20:56:49700 }
701
702 // No drive matched. The path does not start with a device junction
703 // that is mounted as a drive letter. This means there is no drive
704 // letter path to the volume that holds |device_path|, so fail.
705 return false;
706}
707
708bool NormalizeToNativeFilePath(const FilePath& path, FilePath* nt_path) {
Etienne Pierre-Doray3879b052018-09-17 14:17:22709 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
[email protected]9eae4e62013-12-04 20:56:49710 // In Vista, GetFinalPathNameByHandle() would give us the real path
711 // from a file handle. If we ever deprecate XP, consider changing the
712 // code below to a call to GetFinalPathNameByHandle(). The method this
713 // function uses is explained in the following msdn article:
714 // http://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx
thestig8badc792014-12-04 22:14:22715 win::ScopedHandle file_handle(
[email protected]9eae4e62013-12-04 20:56:49716 ::CreateFile(path.value().c_str(),
717 GENERIC_READ,
718 kFileShareAll,
719 NULL,
720 OPEN_EXISTING,
721 FILE_ATTRIBUTE_NORMAL,
722 NULL));
rvargasecaef8a2014-09-23 22:03:11723 if (!file_handle.IsValid())
[email protected]9eae4e62013-12-04 20:56:49724 return false;
725
726 // Create a file mapping object. Can't easily use MemoryMappedFile, because
727 // we only map the first byte, and need direct access to the handle. You can
728 // not map an empty file, this call fails in that case.
thestig8badc792014-12-04 22:14:22729 win::ScopedHandle file_map_handle(
[email protected]9eae4e62013-12-04 20:56:49730 ::CreateFileMapping(file_handle.Get(),
731 NULL,
732 PAGE_READONLY,
733 0,
734 1, // Just one byte. No need to look at the data.
735 NULL));
rvargasecaef8a2014-09-23 22:03:11736 if (!file_map_handle.IsValid())
[email protected]9eae4e62013-12-04 20:56:49737 return false;
738
739 // Use a view of the file to get the path to the file.
740 void* file_view = MapViewOfFile(file_map_handle.Get(),
741 FILE_MAP_READ, 0, 0, 1);
742 if (!file_view)
743 return false;
744
745 // The expansion of |path| into a full path may make it longer.
746 // GetMappedFileName() will fail if the result is longer than MAX_PATH.
747 // Pad a bit to be safe. If kMaxPathLength is ever changed to be less
748 // than MAX_PATH, it would be nessisary to test that GetMappedFileName()
749 // not return kMaxPathLength. This would mean that only part of the
750 // path fit in |mapped_file_path|.
751 const int kMaxPathLength = MAX_PATH + 10;
752 wchar_t mapped_file_path[kMaxPathLength];
753 bool success = false;
754 HANDLE cp = GetCurrentProcess();
755 if (::GetMappedFileNameW(cp, file_view, mapped_file_path, kMaxPathLength)) {
756 *nt_path = FilePath(mapped_file_path);
757 success = true;
758 }
759 ::UnmapViewOfFile(file_view);
760 return success;
761}
[email protected]426d1c92013-12-03 20:08:54762
[email protected]cd78ad52011-05-31 23:10:06763// TODO(rkc): Work out if we want to handle NTFS junctions here or not, handle
764// them if we do decide to.
765bool IsLink(const FilePath& file_path) {
766 return false;
767}
768
[email protected]54124ed02014-01-07 10:06:58769bool GetFileInfo(const FilePath& file_path, File::Info* results) {
Etienne Pierre-Doray3879b052018-09-17 14:17:22770 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
[email protected]45446a52010-11-04 17:41:00771
[email protected]f5e3da4d2008-09-26 01:04:08772 WIN32_FILE_ATTRIBUTE_DATA attr;
[email protected]83d86252010-05-07 18:58:45773 if (!GetFileAttributesEx(file_path.value().c_str(),
[email protected]759c4f32008-11-04 21:15:00774 GetFileExInfoStandard, &attr)) {
[email protected]1010f7d2008-08-06 16:29:44775 return false;
[email protected]759c4f32008-11-04 21:15:00776 }
[email protected]1010f7d2008-08-06 16:29:44777
[email protected]f5e3da4d2008-09-26 01:04:08778 ULARGE_INTEGER size;
779 size.HighPart = attr.nFileSizeHigh;
780 size.LowPart = attr.nFileSizeLow;
781 results->size = size.QuadPart;
782
783 results->is_directory =
784 (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
[email protected]9eae4e62013-12-04 20:56:49785 results->last_modified = Time::FromFileTime(attr.ftLastWriteTime);
786 results->last_accessed = Time::FromFileTime(attr.ftLastAccessTime);
787 results->creation_time = Time::FromFileTime(attr.ftCreationTime);
[email protected]5ef323892009-07-24 16:13:53788
[email protected]1010f7d2008-08-06 16:29:44789 return true;
790}
791
[email protected]640517f2008-10-30 23:54:04792FILE* OpenFile(const FilePath& filename, const char* mode) {
grtd0bc44f2017-02-13 17:52:17793 // 'N' is unconditionally added below, so be sure there is not one already
794 // present before a comma in |mode|.
795 DCHECK(
796 strchr(mode, 'N') == nullptr ||
797 (strchr(mode, ',') != nullptr && strchr(mode, 'N') > strchr(mode, ',')));
Etienne Pierre-Doray3879b052018-09-17 14:17:22798 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
thestig8badc792014-12-04 22:14:22799 string16 w_mode = ASCIIToUTF16(mode);
grtd0bc44f2017-02-13 17:52:17800 AppendModeCharacter(L'N', &w_mode);
[email protected]e3e9b5e2010-03-05 03:26:16801 return _wfsopen(filename.value().c_str(), w_mode.c_str(), _SH_DENYNO);
[email protected]640517f2008-10-30 23:54:04802}
803
[email protected]4d358282014-06-10 11:19:50804FILE* FileToFILE(File file, const char* mode) {
805 if (!file.IsValid())
806 return NULL;
807 int fd =
808 _open_osfhandle(reinterpret_cast<intptr_t>(file.GetPlatformFile()), 0);
809 if (fd < 0)
810 return NULL;
811 file.TakePlatformFile();
812 FILE* stream = _fdopen(fd, mode);
813 if (!stream)
814 _close(fd);
815 return stream;
816}
817
[email protected]d8b565d2014-04-18 10:34:15818int ReadFile(const FilePath& filename, char* data, int max_size) {
Etienne Pierre-Doray3879b052018-09-17 14:17:22819 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
thestig8badc792014-12-04 22:14:22820 win::ScopedHandle file(CreateFile(filename.value().c_str(),
821 GENERIC_READ,
822 FILE_SHARE_READ | FILE_SHARE_WRITE,
823 NULL,
824 OPEN_EXISTING,
825 FILE_FLAG_SEQUENTIAL_SCAN,
826 NULL));
rvargasecaef8a2014-09-23 22:03:11827 if (!file.IsValid())
[email protected]1010f7d2008-08-06 16:29:44828 return -1;
829
[email protected]1010f7d2008-08-06 16:29:44830 DWORD read;
rvargasecaef8a2014-09-23 22:03:11831 if (::ReadFile(file.Get(), data, max_size, &read, NULL))
[email protected]f55bd4862010-05-27 15:38:07832 return read;
[email protected]d8b565d2014-04-18 10:34:15833
[email protected]f55bd4862010-05-27 15:38:07834 return -1;
[email protected]1010f7d2008-08-06 16:29:44835}
836
[email protected]e5c2a22e2014-03-06 20:42:30837int WriteFile(const FilePath& filename, const char* data, int size) {
Etienne Pierre-Doray3879b052018-09-17 14:17:22838 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
Takuto Ikuta1a1cb7b2017-05-29 08:18:53839 win::ScopedHandle file(CreateFile(filename.value().c_str(), GENERIC_WRITE, 0,
840 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
thestig8badc792014-12-04 22:14:22841 NULL));
rvargasecaef8a2014-09-23 22:03:11842 if (!file.IsValid()) {
[email protected]ad8cfa92014-05-21 20:06:23843 DPLOG(WARNING) << "CreateFile failed for path "
844 << UTF16ToUTF8(filename.value());
[email protected]e5c2a22e2014-03-06 20:42:30845 return -1;
846 }
847
848 DWORD written;
rvargasecaef8a2014-09-23 22:03:11849 BOOL result = ::WriteFile(file.Get(), data, size, &written, NULL);
[email protected]e5c2a22e2014-03-06 20:42:30850 if (result && static_cast<int>(written) == size)
851 return written;
852
853 if (!result) {
854 // WriteFile failed.
[email protected]ad8cfa92014-05-21 20:06:23855 DPLOG(WARNING) << "writing file " << UTF16ToUTF8(filename.value())
856 << " failed";
[email protected]e5c2a22e2014-03-06 20:42:30857 } else {
858 // Didn't write all the bytes.
859 DLOG(WARNING) << "wrote" << written << " bytes to "
860 << UTF16ToUTF8(filename.value()) << " expected " << size;
861 }
862 return -1;
863}
864
chirantan75ea2fd2014-10-07 23:15:30865bool AppendToFile(const FilePath& filename, const char* data, int size) {
Etienne Pierre-Doray3879b052018-09-17 14:17:22866 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
thestig8badc792014-12-04 22:14:22867 win::ScopedHandle file(CreateFile(filename.value().c_str(),
868 FILE_APPEND_DATA,
869 0,
870 NULL,
871 OPEN_EXISTING,
872 0,
873 NULL));
rvargasecaef8a2014-09-23 22:03:11874 if (!file.IsValid()) {
chirantan75ea2fd2014-10-07 23:15:30875 VPLOG(1) << "CreateFile failed for path " << UTF16ToUTF8(filename.value());
876 return false;
[email protected]891128f2012-04-29 12:57:10877 }
878
879 DWORD written;
rvargasecaef8a2014-09-23 22:03:11880 BOOL result = ::WriteFile(file.Get(), data, size, &written, NULL);
[email protected]891128f2012-04-29 12:57:10881 if (result && static_cast<int>(written) == size)
chirantan75ea2fd2014-10-07 23:15:30882 return true;
[email protected]891128f2012-04-29 12:57:10883
884 if (!result) {
885 // WriteFile failed.
chirantan75ea2fd2014-10-07 23:15:30886 VPLOG(1) << "Writing file " << UTF16ToUTF8(filename.value()) << " failed";
[email protected]891128f2012-04-29 12:57:10887 } else {
888 // Didn't write all the bytes.
chirantan75ea2fd2014-10-07 23:15:30889 VPLOG(1) << "Only wrote " << written << " out of " << size << " byte(s) to "
890 << UTF16ToUTF8(filename.value());
[email protected]891128f2012-04-29 12:57:10891 }
chirantan75ea2fd2014-10-07 23:15:30892 return false;
[email protected]891128f2012-04-29 12:57:10893}
894
[email protected]640517f2008-10-30 23:54:04895bool GetCurrentDirectory(FilePath* dir) {
Etienne Pierre-Doray3879b052018-09-17 14:17:22896 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
[email protected]45446a52010-11-04 17:41:00897
[email protected]1010f7d2008-08-06 16:29:44898 wchar_t system_buffer[MAX_PATH];
899 system_buffer[0] = 0;
900 DWORD len = ::GetCurrentDirectory(MAX_PATH, system_buffer);
901 if (len == 0 || len > MAX_PATH)
902 return false;
[email protected]640517f2008-10-30 23:54:04903 // TODO(evanm): the old behavior of this function was to always strip the
904 // trailing slash. We duplicate this here, but it shouldn't be necessary
905 // when everyone is using the appropriate FilePath APIs.
906 std::wstring dir_str(system_buffer);
[email protected]2c8088a42009-10-15 05:00:25907 *dir = FilePath(dir_str).StripTrailingSeparators();
[email protected]1010f7d2008-08-06 16:29:44908 return true;
909}
910
[email protected]a9cd2a652008-11-17 21:01:19911bool SetCurrentDirectory(const FilePath& directory) {
Etienne Pierre-Doray3879b052018-09-17 14:17:22912 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
Thiago Farinad892b4a2017-05-18 22:07:49913 return ::SetCurrentDirectory(directory.value().c_str()) != 0;
[email protected]1010f7d2008-08-06 16:29:44914}
915
[email protected]59480302013-02-21 03:24:08916int GetMaximumPathComponentLength(const FilePath& path) {
Etienne Pierre-Doray3879b052018-09-17 14:17:22917 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
[email protected]59480302013-02-21 03:24:08918
919 wchar_t volume_path[MAX_PATH];
920 if (!GetVolumePathNameW(path.NormalizePathSeparators().value().c_str(),
921 volume_path,
922 arraysize(volume_path))) {
923 return -1;
924 }
925
926 DWORD max_length = 0;
927 if (!GetVolumeInformationW(volume_path, NULL, 0, NULL, &max_length, NULL,
928 NULL, 0)) {
929 return -1;
930 }
931
932 // Length of |path| with path separator appended.
933 size_t prefix = path.StripTrailingSeparators().value().size() + 1;
934 // The whole path string must be shorter than MAX_PATH. That is, it must be
935 // prefix + component_length < MAX_PATH (or equivalently, <= MAX_PATH - 1).
936 int whole_path_limit = std::max(0, MAX_PATH - 1 - static_cast<int>(prefix));
937 return std::min(whole_path_limit, static_cast<int>(max_length));
938}
939
cmumford620ace82014-11-17 18:58:20940bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
Eric Caruso128f0dd2017-12-16 01:32:49941 return DoCopyFile(from_path, to_path, false);
cmumford620ace82014-11-17 18:58:20942}
943
tfarina060df7e2015-12-16 05:15:32944bool SetNonBlocking(int fd) {
945 unsigned long nonblocking = 1;
946 if (ioctlsocket(fd, FIONBIO, &nonblocking) == 0)
947 return true;
948 return false;
949}
950
[email protected]a26f4ae2014-03-13 17:26:21951// -----------------------------------------------------------------------------
[email protected]f0ff2ad2013-07-09 17:42:26952
[email protected]f0ff2ad2013-07-09 17:42:26953namespace internal {
954
955bool MoveUnsafe(const FilePath& from_path, const FilePath& to_path) {
Etienne Pierre-Doray3879b052018-09-17 14:17:22956 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
[email protected]f0ff2ad2013-07-09 17:42:26957
958 // NOTE: I suspect we could support longer paths, but that would involve
959 // analyzing all our usage of files.
960 if (from_path.value().length() >= MAX_PATH ||
961 to_path.value().length() >= MAX_PATH) {
962 return false;
963 }
964 if (MoveFileEx(from_path.value().c_str(), to_path.value().c_str(),
965 MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING) != 0)
966 return true;
967
968 // Keep the last error value from MoveFileEx around in case the below
969 // fails.
970 bool ret = false;
971 DWORD last_error = ::GetLastError();
972
[email protected]dcd16612013-07-15 20:18:09973 if (DirectoryExists(from_path)) {
[email protected]f0ff2ad2013-07-09 17:42:26974 // MoveFileEx fails if moving directory across volumes. We will simulate
975 // the move by using Copy and Delete. Ideally we could check whether
976 // from_path and to_path are indeed in different volumes.
977 ret = internal::CopyAndDeleteDirectory(from_path, to_path);
978 }
979
980 if (!ret) {
981 // Leave a clue about what went wrong so that it can be (at least) picked
982 // up by a PLOG entry.
983 ::SetLastError(last_error);
984 }
985
986 return ret;
987}
988
[email protected]f0ff2ad2013-07-09 17:42:26989bool CopyAndDeleteDirectory(const FilePath& from_path,
990 const FilePath& to_path) {
Etienne Pierre-Doray3879b052018-09-17 14:17:22991 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
[email protected]f0ff2ad2013-07-09 17:42:26992 if (CopyDirectory(from_path, to_path, true)) {
[email protected]dd3aa792013-07-16 19:10:23993 if (DeleteFile(from_path, true))
[email protected]f0ff2ad2013-07-09 17:42:26994 return true;
995
996 // Like Move, this function is not transactional, so we just
997 // leave the copied bits behind if deleting from_path fails.
998 // If to_path exists previously then we have already overwritten
999 // it by now, we don't get better off by deleting the new bits.
1000 }
1001 return false;
1002}
1003
1004} // namespace internal
1005} // namespace base