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