1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/files/file_util.h" 6 7 #include <windows.h> 8 #include <io.h> 9 #include <psapi.h> 10 #include <shellapi.h> 11 #include <shlobj.h> 12 #include <time.h> 13 14 #include <algorithm> 15 #include <limits> 16 #include <string> 17 18 #include "base/files/file_enumerator.h" 19 #include "base/files/file_path.h" 20 #include "base/logging.h" 21 #include "base/metrics/histogram.h" 22 #include "base/process/process_handle.h" 23 #include "base/rand_util.h" 24 #include "base/strings/string_number_conversions.h" 25 #include "base/strings/string_util.h" 26 #include "base/strings/utf_string_conversions.h" 27 #include "base/threading/thread_restrictions.h" 28 #include "base/time/time.h" 29 #include "base/win/scoped_handle.h" 30 #include "base/win/windows_version.h" 31 32 namespace base { 33 34 namespace { 35 36 const DWORD kFileShareAll = 37 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; 38 39 } // namespace 40 41 FilePath MakeAbsoluteFilePath(const FilePath& input) { 42 ThreadRestrictions::AssertIOAllowed(); 43 wchar_t file_path[MAX_PATH]; 44 if (!_wfullpath(file_path, input.value().c_str(), MAX_PATH)) 45 return FilePath(); 46 return FilePath(file_path); 47 } 48 49 bool DeleteFile(const FilePath& path, bool recursive) { 50 ThreadRestrictions::AssertIOAllowed(); 51 52 if (path.value().length() >= MAX_PATH) 53 return false; 54 55 // On XP SHFileOperation will return ERROR_ACCESS_DENIED instead of 56 // ERROR_FILE_NOT_FOUND, so just shortcut this here. 57 if (path.empty()) 58 return true; 59 60 if (!recursive) { 61 // If not recursing, then first check to see if |path| is a directory. 62 // If it is, then remove it with RemoveDirectory. 63 File::Info file_info; 64 if (GetFileInfo(path, &file_info) && file_info.is_directory) 65 return RemoveDirectory(path.value().c_str()) != 0; 66 67 // Otherwise, it's a file, wildcard or non-existant. Try DeleteFile first 68 // because it should be faster. If DeleteFile fails, then we fall through 69 // to SHFileOperation, which will do the right thing. 70 if (::DeleteFile(path.value().c_str()) != 0) 71 return true; 72 } 73 74 // SHFILEOPSTRUCT wants the path to be terminated with two NULLs, 75 // so we have to use wcscpy because wcscpy_s writes non-NULLs 76 // into the rest of the buffer. 77 wchar_t double_terminated_path[MAX_PATH + 1] = {0}; 78 #pragma warning(suppress:4996) // don't complain about wcscpy deprecation 79 wcscpy(double_terminated_path, path.value().c_str()); 80 81 SHFILEOPSTRUCT file_operation = {0}; 82 file_operation.wFunc = FO_DELETE; 83 file_operation.pFrom = double_terminated_path; 84 file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION; 85 if (!recursive) 86 file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY; 87 int err = SHFileOperation(&file_operation); 88 89 // Since we're passing flags to the operation telling it to be silent, 90 // it's possible for the operation to be aborted/cancelled without err 91 // being set (although MSDN doesn't give any scenarios for how this can 92 // happen). See MSDN for SHFileOperation and SHFILEOPTSTRUCT. 93 if (file_operation.fAnyOperationsAborted) 94 return false; 95 96 // Some versions of Windows return ERROR_FILE_NOT_FOUND (0x2) when deleting 97 // an empty directory and some return 0x402 when they should be returning 98 // ERROR_FILE_NOT_FOUND. MSDN says Vista and up won't return 0x402. Windows 7 99 // can return DE_INVALIDFILES (0x7C) for nonexistent directories. 100 return (err == 0 || err == ERROR_FILE_NOT_FOUND || err == 0x402 || 101 err == 0x7C); 102 } 103 104 bool DeleteFileAfterReboot(const FilePath& path) { 105 ThreadRestrictions::AssertIOAllowed(); 106 107 if (path.value().length() >= MAX_PATH) 108 return false; 109 110 return MoveFileEx(path.value().c_str(), NULL, 111 MOVEFILE_DELAY_UNTIL_REBOOT | 112 MOVEFILE_REPLACE_EXISTING) != FALSE; 113 } 114 115 bool ReplaceFile(const FilePath& from_path, 116 const FilePath& to_path, 117 File::Error* error) { 118 ThreadRestrictions::AssertIOAllowed(); 119 // Try a simple move first. It will only succeed when |to_path| doesn't 120 // already exist. 121 if (::MoveFile(from_path.value().c_str(), to_path.value().c_str())) 122 return true; 123 // Try the full-blown replace if the move fails, as ReplaceFile will only 124 // succeed when |to_path| does exist. When writing to a network share, we may 125 // not be able to change the ACLs. Ignore ACL errors then 126 // (REPLACEFILE_IGNORE_MERGE_ERRORS). 127 if (::ReplaceFile(to_path.value().c_str(), from_path.value().c_str(), NULL, 128 REPLACEFILE_IGNORE_MERGE_ERRORS, NULL, NULL)) { 129 return true; 130 } 131 if (error) 132 *error = File::OSErrorToFileError(GetLastError()); 133 return false; 134 } 135 136 bool CopyDirectory(const FilePath& from_path, const FilePath& to_path, 137 bool recursive) { 138 // NOTE(maruel): Previous version of this function used to call 139 // SHFileOperation(). This used to copy the file attributes and extended 140 // attributes, OLE structured storage, NTFS file system alternate data 141 // streams, SECURITY_DESCRIPTOR. In practice, this is not what we want, we 142 // want the containing directory to propagate its SECURITY_DESCRIPTOR. 143 ThreadRestrictions::AssertIOAllowed(); 144 145 // NOTE: I suspect we could support longer paths, but that would involve 146 // analyzing all our usage of files. 147 if (from_path.value().length() >= MAX_PATH || 148 to_path.value().length() >= MAX_PATH) { 149 return false; 150 } 151 152 // This function does not properly handle destinations within the source. 153 FilePath real_to_path = to_path; 154 if (PathExists(real_to_path)) { 155 real_to_path = MakeAbsoluteFilePath(real_to_path); 156 if (real_to_path.empty()) 157 return false; 158 } else { 159 real_to_path = MakeAbsoluteFilePath(real_to_path.DirName()); 160 if (real_to_path.empty()) 161 return false; 162 } 163 FilePath real_from_path = MakeAbsoluteFilePath(from_path); 164 if (real_from_path.empty()) 165 return false; 166 if (real_to_path.value().size() >= real_from_path.value().size() && 167 real_to_path.value().compare(0, real_from_path.value().size(), 168 real_from_path.value()) == 0) { 169 return false; 170 } 171 172 int traverse_type = FileEnumerator::FILES; 173 if (recursive) 174 traverse_type |= FileEnumerator::DIRECTORIES; 175 FileEnumerator traversal(from_path, recursive, traverse_type); 176 177 if (!PathExists(from_path)) { 178 DLOG(ERROR) << "CopyDirectory() couldn't stat source directory: " 179 << from_path.value().c_str(); 180 return false; 181 } 182 // TODO(maruel): This is not necessary anymore. 183 DCHECK(recursive || DirectoryExists(from_path)); 184 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 195 while (success && !current.empty()) { 196 // current is the source path, including from_path, so append 197 // the suffix after from_path to to_path to create the target_path. 198 FilePath target_path(to_path); 199 if (from_path_base != current) { 200 if (!from_path_base.AppendRelativePath(current, &target_path)) { 201 success = false; 202 break; 203 } 204 } 205 206 if (from_is_dir) { 207 if (!DirectoryExists(target_path) && 208 !::CreateDirectory(target_path.value().c_str(), NULL)) { 209 DLOG(ERROR) << "CopyDirectory() couldn't create directory: " 210 << target_path.value().c_str(); 211 success = false; 212 } 213 } else if (!internal::CopyFileUnsafe(current, target_path)) { 214 DLOG(ERROR) << "CopyDirectory() couldn't create file: " 215 << target_path.value().c_str(); 216 success = false; 217 } 218 219 current = traversal.Next(); 220 if (!current.empty()) 221 from_is_dir = traversal.GetInfo().IsDirectory(); 222 } 223 224 return success; 225 } 226 227 bool PathExists(const FilePath& path) { 228 ThreadRestrictions::AssertIOAllowed(); 229 return (GetFileAttributes(path.value().c_str()) != INVALID_FILE_ATTRIBUTES); 230 } 231 232 bool PathIsWritable(const FilePath& path) { 233 ThreadRestrictions::AssertIOAllowed(); 234 HANDLE dir = 235 CreateFile(path.value().c_str(), FILE_ADD_FILE, kFileShareAll, 236 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 237 238 if (dir == INVALID_HANDLE_VALUE) 239 return false; 240 241 CloseHandle(dir); 242 return true; 243 } 244 245 bool DirectoryExists(const FilePath& path) { 246 ThreadRestrictions::AssertIOAllowed(); 247 DWORD fileattr = GetFileAttributes(path.value().c_str()); 248 if (fileattr != INVALID_FILE_ATTRIBUTES) 249 return (fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0; 250 return false; 251 } 252 253 bool GetTempDir(FilePath* path) { 254 wchar_t temp_path[MAX_PATH + 1]; 255 DWORD path_len = ::GetTempPath(MAX_PATH, temp_path); 256 if (path_len >= MAX_PATH || path_len <= 0) 257 return false; 258 // TODO(evanm): the old behavior of this function was to always strip the 259 // trailing slash. We duplicate this here, but it shouldn't be necessary 260 // when everyone is using the appropriate FilePath APIs. 261 *path = FilePath(temp_path).StripTrailingSeparators(); 262 return true; 263 } 264 265 FilePath GetHomeDir() { 266 char16 result[MAX_PATH]; 267 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, 268 result)) && 269 result[0]) { 270 return FilePath(result); 271 } 272 273 // Fall back to the temporary directory on failure. 274 FilePath temp; 275 if (GetTempDir(&temp)) 276 return temp; 277 278 // Last resort. 279 return FilePath(L"C:\\"); 280 } 281 282 bool CreateTemporaryFile(FilePath* path) { 283 ThreadRestrictions::AssertIOAllowed(); 284 285 FilePath temp_file; 286 287 if (!GetTempDir(path)) 288 return false; 289 290 if (CreateTemporaryFileInDir(*path, &temp_file)) { 291 *path = temp_file; 292 return true; 293 } 294 295 return false; 296 } 297 298 // On POSIX we have semantics to create and open a temporary file 299 // atomically. 300 // TODO(jrg): is there equivalent call to use on Windows instead of 301 // going 2-step? 302 FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) { 303 ThreadRestrictions::AssertIOAllowed(); 304 if (!CreateTemporaryFileInDir(dir, path)) { 305 return NULL; 306 } 307 // Open file in binary mode, to avoid problems with fwrite. On Windows 308 // it replaces \n's with \r\n's, which may surprise you. 309 // Reference: http://msdn.microsoft.com/en-us/library/h9t88zwz(VS.71).aspx 310 return OpenFile(*path, "wb+"); 311 } 312 313 bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) { 314 ThreadRestrictions::AssertIOAllowed(); 315 316 wchar_t temp_name[MAX_PATH + 1]; 317 318 if (!GetTempFileName(dir.value().c_str(), L"", 0, temp_name)) { 319 DPLOG(WARNING) << "Failed to get temporary file name in " 320 << UTF16ToUTF8(dir.value()); 321 return false; 322 } 323 324 wchar_t long_temp_name[MAX_PATH + 1]; 325 DWORD long_name_len = GetLongPathName(temp_name, long_temp_name, MAX_PATH); 326 if (long_name_len > MAX_PATH || long_name_len == 0) { 327 // GetLongPathName() failed, but we still have a temporary file. 328 *temp_file = FilePath(temp_name); 329 return true; 330 } 331 332 FilePath::StringType long_temp_name_str; 333 long_temp_name_str.assign(long_temp_name, long_name_len); 334 *temp_file = FilePath(long_temp_name_str); 335 return true; 336 } 337 338 bool CreateTemporaryDirInDir(const FilePath& base_dir, 339 const FilePath::StringType& prefix, 340 FilePath* new_dir) { 341 ThreadRestrictions::AssertIOAllowed(); 342 343 FilePath path_to_create; 344 345 for (int count = 0; count < 50; ++count) { 346 // Try create a new temporary directory with random generated name. If 347 // the one exists, keep trying another path name until we reach some limit. 348 string16 new_dir_name; 349 new_dir_name.assign(prefix); 350 new_dir_name.append(IntToString16(GetCurrentProcId())); 351 new_dir_name.push_back('_'); 352 new_dir_name.append(IntToString16(RandInt(0, kint16max))); 353 354 path_to_create = base_dir.Append(new_dir_name); 355 if (::CreateDirectory(path_to_create.value().c_str(), NULL)) { 356 *new_dir = path_to_create; 357 return true; 358 } 359 } 360 361 return false; 362 } 363 364 bool CreateNewTempDirectory(const FilePath::StringType& prefix, 365 FilePath* new_temp_path) { 366 ThreadRestrictions::AssertIOAllowed(); 367 368 FilePath system_temp_dir; 369 if (!GetTempDir(&system_temp_dir)) 370 return false; 371 372 return CreateTemporaryDirInDir(system_temp_dir, prefix, new_temp_path); 373 } 374 375 bool CreateDirectoryAndGetError(const FilePath& full_path, 376 File::Error* error) { 377 ThreadRestrictions::AssertIOAllowed(); 378 379 // If the path exists, we've succeeded if it's a directory, failed otherwise. 380 const wchar_t* full_path_str = full_path.value().c_str(); 381 DWORD fileattr = ::GetFileAttributes(full_path_str); 382 if (fileattr != INVALID_FILE_ATTRIBUTES) { 383 if ((fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0) { 384 DVLOG(1) << "CreateDirectory(" << full_path_str << "), " 385 << "directory already exists."; 386 return true; 387 } 388 DLOG(WARNING) << "CreateDirectory(" << full_path_str << "), " 389 << "conflicts with existing file."; 390 if (error) { 391 *error = File::FILE_ERROR_NOT_A_DIRECTORY; 392 } 393 return false; 394 } 395 396 // Invariant: Path does not exist as file or directory. 397 398 // Attempt to create the parent recursively. This will immediately return 399 // true if it already exists, otherwise will create all required parent 400 // directories starting with the highest-level missing parent. 401 FilePath parent_path(full_path.DirName()); 402 if (parent_path.value() == full_path.value()) { 403 if (error) { 404 *error = File::FILE_ERROR_NOT_FOUND; 405 } 406 return false; 407 } 408 if (!CreateDirectoryAndGetError(parent_path, error)) { 409 DLOG(WARNING) << "Failed to create one of the parent directories."; 410 if (error) { 411 DCHECK(*error != File::FILE_OK); 412 } 413 return false; 414 } 415 416 if (!::CreateDirectory(full_path_str, NULL)) { 417 DWORD error_code = ::GetLastError(); 418 if (error_code == ERROR_ALREADY_EXISTS && DirectoryExists(full_path)) { 419 // This error code ERROR_ALREADY_EXISTS doesn't indicate whether we 420 // were racing with someone creating the same directory, or a file 421 // with the same path. If DirectoryExists() returns true, we lost the 422 // race to create the same directory. 423 return true; 424 } else { 425 if (error) 426 *error = File::OSErrorToFileError(error_code); 427 DLOG(WARNING) << "Failed to create directory " << full_path_str 428 << ", last error is " << error_code << "."; 429 return false; 430 } 431 } else { 432 return true; 433 } 434 } 435 436 bool NormalizeFilePath(const FilePath& path, FilePath* real_path) { 437 ThreadRestrictions::AssertIOAllowed(); 438 FilePath mapped_file; 439 if (!NormalizeToNativeFilePath(path, &mapped_file)) 440 return false; 441 // NormalizeToNativeFilePath() will return a path that starts with 442 // "\Device\Harddisk...". Helper DevicePathToDriveLetterPath() 443 // will find a drive letter which maps to the path's device, so 444 // that we return a path starting with a drive letter. 445 return DevicePathToDriveLetterPath(mapped_file, real_path); 446 } 447 448 bool DevicePathToDriveLetterPath(const FilePath& nt_device_path, 449 FilePath* out_drive_letter_path) { 450 ThreadRestrictions::AssertIOAllowed(); 451 452 // Get the mapping of drive letters to device paths. 453 const int kDriveMappingSize = 1024; 454 wchar_t drive_mapping[kDriveMappingSize] = {'\0'}; 455 if (!::GetLogicalDriveStrings(kDriveMappingSize - 1, drive_mapping)) { 456 DLOG(ERROR) << "Failed to get drive mapping."; 457 return false; 458 } 459 460 // The drive mapping is a sequence of null terminated strings. 461 // The last string is empty. 462 wchar_t* drive_map_ptr = drive_mapping; 463 wchar_t device_path_as_string[MAX_PATH]; 464 wchar_t drive[] = L" :"; 465 466 // For each string in the drive mapping, get the junction that links 467 // to it. If that junction is a prefix of |device_path|, then we 468 // know that |drive| is the real path prefix. 469 while (*drive_map_ptr) { 470 drive[0] = drive_map_ptr[0]; // Copy the drive letter. 471 472 if (QueryDosDevice(drive, device_path_as_string, MAX_PATH)) { 473 FilePath device_path(device_path_as_string); 474 if (device_path == nt_device_path || 475 device_path.IsParent(nt_device_path)) { 476 *out_drive_letter_path = FilePath(drive + 477 nt_device_path.value().substr(wcslen(device_path_as_string))); 478 return true; 479 } 480 } 481 // Move to the next drive letter string, which starts one 482 // increment after the '\0' that terminates the current string. 483 while (*drive_map_ptr++); 484 } 485 486 // No drive matched. The path does not start with a device junction 487 // that is mounted as a drive letter. This means there is no drive 488 // letter path to the volume that holds |device_path|, so fail. 489 return false; 490 } 491 492 bool NormalizeToNativeFilePath(const FilePath& path, FilePath* nt_path) { 493 ThreadRestrictions::AssertIOAllowed(); 494 // In Vista, GetFinalPathNameByHandle() would give us the real path 495 // from a file handle. If we ever deprecate XP, consider changing the 496 // code below to a call to GetFinalPathNameByHandle(). The method this 497 // function uses is explained in the following msdn article: 498 // http://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx 499 base::win::ScopedHandle file_handle( 500 ::CreateFile(path.value().c_str(), 501 GENERIC_READ, 502 kFileShareAll, 503 NULL, 504 OPEN_EXISTING, 505 FILE_ATTRIBUTE_NORMAL, 506 NULL)); 507 if (!file_handle.IsValid()) 508 return false; 509 510 // Create a file mapping object. Can't easily use MemoryMappedFile, because 511 // we only map the first byte, and need direct access to the handle. You can 512 // not map an empty file, this call fails in that case. 513 base::win::ScopedHandle file_map_handle( 514 ::CreateFileMapping(file_handle.Get(), 515 NULL, 516 PAGE_READONLY, 517 0, 518 1, // Just one byte. No need to look at the data. 519 NULL)); 520 if (!file_map_handle.IsValid()) 521 return false; 522 523 // Use a view of the file to get the path to the file. 524 void* file_view = MapViewOfFile(file_map_handle.Get(), 525 FILE_MAP_READ, 0, 0, 1); 526 if (!file_view) 527 return false; 528 529 // The expansion of |path| into a full path may make it longer. 530 // GetMappedFileName() will fail if the result is longer than MAX_PATH. 531 // Pad a bit to be safe. If kMaxPathLength is ever changed to be less 532 // than MAX_PATH, it would be nessisary to test that GetMappedFileName() 533 // not return kMaxPathLength. This would mean that only part of the 534 // path fit in |mapped_file_path|. 535 const int kMaxPathLength = MAX_PATH + 10; 536 wchar_t mapped_file_path[kMaxPathLength]; 537 bool success = false; 538 HANDLE cp = GetCurrentProcess(); 539 if (::GetMappedFileNameW(cp, file_view, mapped_file_path, kMaxPathLength)) { 540 *nt_path = FilePath(mapped_file_path); 541 success = true; 542 } 543 ::UnmapViewOfFile(file_view); 544 return success; 545 } 546 547 // TODO(rkc): Work out if we want to handle NTFS junctions here or not, handle 548 // them if we do decide to. 549 bool IsLink(const FilePath& file_path) { 550 return false; 551 } 552 553 bool GetFileInfo(const FilePath& file_path, File::Info* results) { 554 ThreadRestrictions::AssertIOAllowed(); 555 556 WIN32_FILE_ATTRIBUTE_DATA attr; 557 if (!GetFileAttributesEx(file_path.value().c_str(), 558 GetFileExInfoStandard, &attr)) { 559 return false; 560 } 561 562 ULARGE_INTEGER size; 563 size.HighPart = attr.nFileSizeHigh; 564 size.LowPart = attr.nFileSizeLow; 565 results->size = size.QuadPart; 566 567 results->is_directory = 568 (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; 569 results->last_modified = Time::FromFileTime(attr.ftLastWriteTime); 570 results->last_accessed = Time::FromFileTime(attr.ftLastAccessTime); 571 results->creation_time = Time::FromFileTime(attr.ftCreationTime); 572 573 return true; 574 } 575 576 FILE* OpenFile(const FilePath& filename, const char* mode) { 577 ThreadRestrictions::AssertIOAllowed(); 578 std::wstring w_mode = ASCIIToWide(std::string(mode)); 579 return _wfsopen(filename.value().c_str(), w_mode.c_str(), _SH_DENYNO); 580 } 581 582 FILE* FileToFILE(File file, const char* mode) { 583 if (!file.IsValid()) 584 return NULL; 585 int fd = 586 _open_osfhandle(reinterpret_cast<intptr_t>(file.GetPlatformFile()), 0); 587 if (fd < 0) 588 return NULL; 589 file.TakePlatformFile(); 590 FILE* stream = _fdopen(fd, mode); 591 if (!stream) 592 _close(fd); 593 return stream; 594 } 595 596 int ReadFile(const FilePath& filename, char* data, int max_size) { 597 ThreadRestrictions::AssertIOAllowed(); 598 base::win::ScopedHandle file(CreateFile(filename.value().c_str(), 599 GENERIC_READ, 600 FILE_SHARE_READ | FILE_SHARE_WRITE, 601 NULL, 602 OPEN_EXISTING, 603 FILE_FLAG_SEQUENTIAL_SCAN, 604 NULL)); 605 if (!file.IsValid()) 606 return -1; 607 608 DWORD read; 609 if (::ReadFile(file.Get(), data, max_size, &read, NULL)) 610 return read; 611 612 return -1; 613 } 614 615 int WriteFile(const FilePath& filename, const char* data, int size) { 616 ThreadRestrictions::AssertIOAllowed(); 617 base::win::ScopedHandle file(CreateFile(filename.value().c_str(), 618 GENERIC_WRITE, 619 0, 620 NULL, 621 CREATE_ALWAYS, 622 0, 623 NULL)); 624 if (!file.IsValid()) { 625 DPLOG(WARNING) << "CreateFile failed for path " 626 << UTF16ToUTF8(filename.value()); 627 return -1; 628 } 629 630 DWORD written; 631 BOOL result = ::WriteFile(file.Get(), data, size, &written, NULL); 632 if (result && static_cast<int>(written) == size) 633 return written; 634 635 if (!result) { 636 // WriteFile failed. 637 DPLOG(WARNING) << "writing file " << UTF16ToUTF8(filename.value()) 638 << " failed"; 639 } else { 640 // Didn't write all the bytes. 641 DLOG(WARNING) << "wrote" << written << " bytes to " 642 << UTF16ToUTF8(filename.value()) << " expected " << size; 643 } 644 return -1; 645 } 646 647 int AppendToFile(const FilePath& filename, const char* data, int size) { 648 ThreadRestrictions::AssertIOAllowed(); 649 base::win::ScopedHandle file(CreateFile(filename.value().c_str(), 650 FILE_APPEND_DATA, 651 0, 652 NULL, 653 OPEN_EXISTING, 654 0, 655 NULL)); 656 if (!file.IsValid()) { 657 DPLOG(WARNING) << "CreateFile failed for path " 658 << UTF16ToUTF8(filename.value()); 659 return -1; 660 } 661 662 DWORD written; 663 BOOL result = ::WriteFile(file.Get(), data, size, &written, NULL); 664 if (result && static_cast<int>(written) == size) 665 return written; 666 667 if (!result) { 668 // WriteFile failed. 669 DPLOG(WARNING) << "writing file " << UTF16ToUTF8(filename.value()) 670 << " failed"; 671 } else { 672 // Didn't write all the bytes. 673 DLOG(WARNING) << "wrote" << written << " bytes to " 674 << UTF16ToUTF8(filename.value()) << " expected " << size; 675 } 676 return -1; 677 } 678 679 // Gets the current working directory for the process. 680 bool GetCurrentDirectory(FilePath* dir) { 681 ThreadRestrictions::AssertIOAllowed(); 682 683 wchar_t system_buffer[MAX_PATH]; 684 system_buffer[0] = 0; 685 DWORD len = ::GetCurrentDirectory(MAX_PATH, system_buffer); 686 if (len == 0 || len > MAX_PATH) 687 return false; 688 // TODO(evanm): the old behavior of this function was to always strip the 689 // trailing slash. We duplicate this here, but it shouldn't be necessary 690 // when everyone is using the appropriate FilePath APIs. 691 std::wstring dir_str(system_buffer); 692 *dir = FilePath(dir_str).StripTrailingSeparators(); 693 return true; 694 } 695 696 // Sets the current working directory for the process. 697 bool SetCurrentDirectory(const FilePath& directory) { 698 ThreadRestrictions::AssertIOAllowed(); 699 BOOL ret = ::SetCurrentDirectory(directory.value().c_str()); 700 return ret != 0; 701 } 702 703 int GetMaximumPathComponentLength(const FilePath& path) { 704 ThreadRestrictions::AssertIOAllowed(); 705 706 wchar_t volume_path[MAX_PATH]; 707 if (!GetVolumePathNameW(path.NormalizePathSeparators().value().c_str(), 708 volume_path, 709 arraysize(volume_path))) { 710 return -1; 711 } 712 713 DWORD max_length = 0; 714 if (!GetVolumeInformationW(volume_path, NULL, 0, NULL, &max_length, NULL, 715 NULL, 0)) { 716 return -1; 717 } 718 719 // Length of |path| with path separator appended. 720 size_t prefix = path.StripTrailingSeparators().value().size() + 1; 721 // The whole path string must be shorter than MAX_PATH. That is, it must be 722 // prefix + component_length < MAX_PATH (or equivalently, <= MAX_PATH - 1). 723 int whole_path_limit = std::max(0, MAX_PATH - 1 - static_cast<int>(prefix)); 724 return std::min(whole_path_limit, static_cast<int>(max_length)); 725 } 726 727 // ----------------------------------------------------------------------------- 728 729 namespace internal { 730 731 bool MoveUnsafe(const FilePath& from_path, const FilePath& to_path) { 732 ThreadRestrictions::AssertIOAllowed(); 733 734 // NOTE: I suspect we could support longer paths, but that would involve 735 // analyzing all our usage of files. 736 if (from_path.value().length() >= MAX_PATH || 737 to_path.value().length() >= MAX_PATH) { 738 return false; 739 } 740 if (MoveFileEx(from_path.value().c_str(), to_path.value().c_str(), 741 MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING) != 0) 742 return true; 743 744 // Keep the last error value from MoveFileEx around in case the below 745 // fails. 746 bool ret = false; 747 DWORD last_error = ::GetLastError(); 748 749 if (DirectoryExists(from_path)) { 750 // MoveFileEx fails if moving directory across volumes. We will simulate 751 // the move by using Copy and Delete. Ideally we could check whether 752 // from_path and to_path are indeed in different volumes. 753 ret = internal::CopyAndDeleteDirectory(from_path, to_path); 754 } 755 756 if (!ret) { 757 // Leave a clue about what went wrong so that it can be (at least) picked 758 // up by a PLOG entry. 759 ::SetLastError(last_error); 760 } 761 762 return ret; 763 } 764 765 bool CopyFileUnsafe(const FilePath& from_path, const FilePath& to_path) { 766 ThreadRestrictions::AssertIOAllowed(); 767 768 // NOTE: I suspect we could support longer paths, but that would involve 769 // analyzing all our usage of files. 770 if (from_path.value().length() >= MAX_PATH || 771 to_path.value().length() >= MAX_PATH) { 772 return false; 773 } 774 775 // Unlike the posix implementation that copies the file manually and discards 776 // the ACL bits, CopyFile() copies the complete SECURITY_DESCRIPTOR and access 777 // bits, which is usually not what we want. We can't do much about the 778 // SECURITY_DESCRIPTOR but at least remove the read only bit. 779 const wchar_t* dest = to_path.value().c_str(); 780 if (!::CopyFile(from_path.value().c_str(), dest, false)) { 781 // Copy failed. 782 return false; 783 } 784 DWORD attrs = GetFileAttributes(dest); 785 if (attrs == INVALID_FILE_ATTRIBUTES) { 786 return false; 787 } 788 if (attrs & FILE_ATTRIBUTE_READONLY) { 789 SetFileAttributes(dest, attrs & ~FILE_ATTRIBUTE_READONLY); 790 } 791 return true; 792 } 793 794 bool CopyAndDeleteDirectory(const FilePath& from_path, 795 const FilePath& to_path) { 796 ThreadRestrictions::AssertIOAllowed(); 797 if (CopyDirectory(from_path, to_path, true)) { 798 if (DeleteFile(from_path, true)) 799 return true; 800 801 // Like Move, this function is not transactional, so we just 802 // leave the copied bits behind if deleting from_path fails. 803 // If to_path exists previously then we have already overwritten 804 // it by now, we don't get better off by deleting the new bits. 805 } 806 return false; 807 } 808 809 } // namespace internal 810 } // namespace base 811