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 // windows.h includes winsock.h which isn't compatible with winsock2.h. To use 8 // winsock2.h you have to include it first. 9 // clang-format off 10 #include <winsock2.h> 11 #include <windows.h> 12 // clang-format on 13 14 #include <io.h> 15 #include <psapi.h> 16 #include <share.h> 17 #include <shellapi.h> 18 #include <shlobj.h> 19 #include <stddef.h> 20 #include <stdint.h> 21 #include <time.h> 22 23 #include <algorithm> 24 #include <iterator> 25 #include <limits> 26 #include <string> 27 #include <string_view> 28 29 #include "base/files/file_enumerator.h" 30 #include "base/files/file_path.h" 31 #include "base/logging.h" 32 #include "base/strings/string_number_conversions.h" 33 #include "base/strings/string_util.h" 34 #include "base/strings/stringprintf.h" 35 #include "base/strings/utf_string_conversions.h" 36 #include "base/win/scoped_handle.h" 37 #include "base/win/win_util.h" 38 39 // #define needed to link in RtlGenRandom(), a.k.a. SystemFunction036. See the 40 // "Community Additions" comment on MSDN here: 41 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx 42 #define SystemFunction036 NTAPI SystemFunction036 43 #include <ntsecapi.h> 44 #undef SystemFunction036 45 46 namespace base { 47 48 namespace { 49 50 const DWORD kFileShareAll = 51 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; 52 53 // Deletes all files and directories in a path. 54 // Returns ERROR_SUCCESS on success or the Windows error code corresponding to 55 // the first error encountered. DeleteFileRecursive(const FilePath & path,const FilePath::StringType & pattern,bool recursive)56 DWORD DeleteFileRecursive(const FilePath& path, 57 const FilePath::StringType& pattern, 58 bool recursive) { 59 FileEnumerator traversal(path, false, 60 FileEnumerator::FILES | FileEnumerator::DIRECTORIES, 61 pattern); 62 DWORD result = ERROR_SUCCESS; 63 for (FilePath current = traversal.Next(); !current.empty(); 64 current = traversal.Next()) { 65 // Try to clear the read-only bit if we find it. 66 FileEnumerator::FileInfo info = traversal.GetInfo(); 67 if ((info.find_data().dwFileAttributes & FILE_ATTRIBUTE_READONLY) && 68 (recursive || !info.IsDirectory())) { 69 ::SetFileAttributes( 70 ToWCharT(¤t.value()), 71 info.find_data().dwFileAttributes & ~FILE_ATTRIBUTE_READONLY); 72 } 73 74 DWORD this_result = ERROR_SUCCESS; 75 if (info.IsDirectory()) { 76 if (recursive) { 77 this_result = DeleteFileRecursive(current, pattern, true); 78 if (this_result == ERROR_SUCCESS && 79 !::RemoveDirectory(ToWCharT(¤t.value()))) { 80 this_result = ::GetLastError(); 81 } 82 } 83 } else if (!::DeleteFile(ToWCharT(¤t.value()))) { 84 this_result = ::GetLastError(); 85 } 86 if (result == ERROR_SUCCESS) 87 result = this_result; 88 } 89 return result; 90 } 91 92 // Appends |mode_char| to |mode| before the optional character set encoding; see 93 // https://msdn.microsoft.com/library/yeby3zcb.aspx for details. AppendModeCharacter(char16_t mode_char,std::u16string * mode)94 void AppendModeCharacter(char16_t mode_char, std::u16string* mode) { 95 size_t comma_pos = mode->find(L','); 96 mode->insert(comma_pos == std::u16string::npos ? mode->length() : comma_pos, 97 1, mode_char); 98 } 99 100 // Returns ERROR_SUCCESS on success, or a Windows error code on failure. DoDeleteFile(const FilePath & path,bool recursive)101 DWORD DoDeleteFile(const FilePath& path, bool recursive) { 102 if (path.empty()) 103 return ERROR_SUCCESS; 104 105 if (path.value().length() >= MAX_PATH) 106 return ERROR_BAD_PATHNAME; 107 108 // Handle any path with wildcards. 109 if (path.BaseName().value().find_first_of(u"*?") != 110 FilePath::StringType::npos) { 111 return DeleteFileRecursive(path.DirName(), path.BaseName().value(), 112 recursive); 113 } 114 115 // Report success if the file or path does not exist. 116 const DWORD attr = ::GetFileAttributes(ToWCharT(&path.value())); 117 if (attr == INVALID_FILE_ATTRIBUTES) { 118 const DWORD error_code = ::GetLastError(); 119 return (error_code == ERROR_FILE_NOT_FOUND || 120 error_code == ERROR_PATH_NOT_FOUND) 121 ? ERROR_SUCCESS 122 : error_code; 123 } 124 125 // Clear the read-only bit if it is set. 126 if ((attr & FILE_ATTRIBUTE_READONLY) && 127 !::SetFileAttributes(ToWCharT(&path.value()), 128 attr & ~FILE_ATTRIBUTE_READONLY)) { 129 return ::GetLastError(); 130 } 131 132 // Perform a simple delete on anything that isn't a directory. 133 if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) { 134 return ::DeleteFile(ToWCharT(&path.value())) ? ERROR_SUCCESS 135 : ::GetLastError(); 136 } 137 138 if (recursive) { 139 const DWORD error_code = DeleteFileRecursive(path, u"*", true); 140 if (error_code != ERROR_SUCCESS) 141 return error_code; 142 } 143 return ::RemoveDirectory(ToWCharT(&path.value())) ? ERROR_SUCCESS 144 : ::GetLastError(); 145 } 146 RandomDataToGUIDString(const uint64_t bytes[2])147 std::string RandomDataToGUIDString(const uint64_t bytes[2]) { 148 return base::StringPrintf( 149 "%08x-%04x-%04x-%04x-%012llx", static_cast<unsigned int>(bytes[0] >> 32), 150 static_cast<unsigned int>((bytes[0] >> 16) & 0x0000ffff), 151 static_cast<unsigned int>(bytes[0] & 0x0000ffff), 152 static_cast<unsigned int>(bytes[1] >> 48), 153 bytes[1] & 0x0000ffff'ffffffffULL); 154 } 155 RandBytes(void * output,size_t output_length)156 void RandBytes(void* output, size_t output_length) { 157 char* output_ptr = static_cast<char*>(output); 158 while (output_length > 0) { 159 const ULONG output_bytes_this_pass = static_cast<ULONG>(std::min( 160 output_length, static_cast<size_t>(std::numeric_limits<ULONG>::max()))); 161 const bool success = 162 RtlGenRandom(output_ptr, output_bytes_this_pass) != FALSE; 163 CHECK(success); 164 output_length -= output_bytes_this_pass; 165 output_ptr += output_bytes_this_pass; 166 } 167 } 168 GenerateGUID()169 std::string GenerateGUID() { 170 uint64_t sixteen_bytes[2]; 171 // Use base::RandBytes instead of crypto::RandBytes, because crypto calls the 172 // base version directly, and to prevent the dependency from base/ to crypto/. 173 RandBytes(&sixteen_bytes, sizeof(sixteen_bytes)); 174 175 // Set the GUID to version 4 as described in RFC 4122, section 4.4. 176 // The format of GUID version 4 must be xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx, 177 // where y is one of [8, 9, A, B]. 178 179 // Clear the version bits and set the version to 4: 180 sixteen_bytes[0] &= 0xffffffff'ffff0fffULL; 181 sixteen_bytes[0] |= 0x00000000'00004000ULL; 182 183 // Set the two most significant bits (bits 6 and 7) of the 184 // clock_seq_hi_and_reserved to zero and one, respectively: 185 sixteen_bytes[1] &= 0x3fffffff'ffffffffULL; 186 sixteen_bytes[1] |= 0x80000000'00000000ULL; 187 188 return RandomDataToGUIDString(sixteen_bytes); 189 } 190 191 } // namespace 192 MakeAbsoluteFilePath(const FilePath & input)193 FilePath MakeAbsoluteFilePath(const FilePath& input) { 194 char16_t file_path[MAX_PATH]; 195 if (!_wfullpath(ToWCharT(file_path), ToWCharT(&input.value()), MAX_PATH)) 196 return FilePath(); 197 return FilePath(file_path); 198 } 199 DeleteFile(const FilePath & path,bool recursive)200 bool DeleteFile(const FilePath& path, bool recursive) { 201 static constexpr char kRecursive[] = "DeleteFile.Recursive"; 202 static constexpr char kNonRecursive[] = "DeleteFile.NonRecursive"; 203 const std::string_view operation(recursive ? kRecursive : kNonRecursive); 204 205 // Metrics for delete failures tracked in https://crbug.com/599084. Delete may 206 // fail for a number of reasons. Log some metrics relating to failures in the 207 // current code so that any improvements or regressions resulting from 208 // subsequent code changes can be detected. 209 const DWORD error = DoDeleteFile(path, recursive); 210 return error == ERROR_SUCCESS; 211 } 212 DeleteFileAfterReboot(const FilePath & path)213 bool DeleteFileAfterReboot(const FilePath& path) { 214 if (path.value().length() >= MAX_PATH) 215 return false; 216 217 return MoveFileEx(ToWCharT(&path.value()), NULL, 218 MOVEFILE_DELAY_UNTIL_REBOOT | MOVEFILE_REPLACE_EXISTING) != 219 FALSE; 220 } 221 ReplaceFile(const FilePath & from_path,const FilePath & to_path,File::Error * error)222 bool ReplaceFile(const FilePath& from_path, 223 const FilePath& to_path, 224 File::Error* error) { 225 // Try a simple move first. It will only succeed when |to_path| doesn't 226 // already exist. 227 if (::MoveFile(ToWCharT(&from_path.value()), ToWCharT(&to_path.value()))) 228 return true; 229 File::Error move_error = File::OSErrorToFileError(GetLastError()); 230 231 // Try the full-blown replace if the move fails, as ReplaceFile will only 232 // succeed when |to_path| does exist. When writing to a network share, we may 233 // not be able to change the ACLs. Ignore ACL errors then 234 // (REPLACEFILE_IGNORE_MERGE_ERRORS). 235 if (::ReplaceFile(ToWCharT(&to_path.value()), ToWCharT(&from_path.value()), 236 NULL, REPLACEFILE_IGNORE_MERGE_ERRORS, NULL, NULL)) { 237 return true; 238 } 239 // In the case of FILE_ERROR_NOT_FOUND from ReplaceFile, it is likely that 240 // |to_path| does not exist. In this case, the more relevant error comes 241 // from the call to MoveFile. 242 if (error) { 243 File::Error replace_error = File::OSErrorToFileError(GetLastError()); 244 *error = replace_error == File::FILE_ERROR_NOT_FOUND ? move_error 245 : replace_error; 246 } 247 return false; 248 } 249 PathExists(const FilePath & path)250 bool PathExists(const FilePath& path) { 251 return (GetFileAttributes(ToWCharT(&path.value())) != 252 INVALID_FILE_ATTRIBUTES); 253 } 254 PathIsWritable(const FilePath & path)255 bool PathIsWritable(const FilePath& path) { 256 HANDLE dir = 257 CreateFile(ToWCharT(&path.value()), FILE_ADD_FILE, kFileShareAll, NULL, 258 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 259 260 if (dir == INVALID_HANDLE_VALUE) 261 return false; 262 263 CloseHandle(dir); 264 return true; 265 } 266 DirectoryExists(const FilePath & path)267 bool DirectoryExists(const FilePath& path) { 268 DWORD fileattr = GetFileAttributes(ToWCharT(&path.value())); 269 if (fileattr != INVALID_FILE_ATTRIBUTES) 270 return (fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0; 271 return false; 272 } 273 GetTempDir(FilePath * path)274 bool GetTempDir(FilePath* path) { 275 char16_t temp_path[MAX_PATH + 1]; 276 DWORD path_len = ::GetTempPath(MAX_PATH, ToWCharT(temp_path)); 277 if (path_len >= MAX_PATH || path_len <= 0) 278 return false; 279 // TODO(evanm): the old behavior of this function was to always strip the 280 // trailing slash. We duplicate this here, but it shouldn't be necessary 281 // when everyone is using the appropriate FilePath APIs. 282 *path = FilePath(temp_path).StripTrailingSeparators(); 283 return true; 284 } 285 CreateTemporaryDirInDir(const FilePath & base_dir,const FilePath::StringType & prefix,FilePath * new_dir)286 bool CreateTemporaryDirInDir(const FilePath& base_dir, 287 const FilePath::StringType& prefix, 288 FilePath* new_dir) { 289 FilePath path_to_create; 290 291 for (int count = 0; count < 50; ++count) { 292 // Try create a new temporary directory with random generated name. If 293 // the one exists, keep trying another path name until we reach some limit. 294 std::u16string new_dir_name; 295 new_dir_name.assign(prefix); 296 new_dir_name.append(IntToString16(::GetCurrentProcessId())); 297 new_dir_name.push_back('_'); 298 new_dir_name.append(UTF8ToUTF16(GenerateGUID())); 299 300 path_to_create = base_dir.Append(new_dir_name); 301 if (::CreateDirectory(ToWCharT(&path_to_create.value()), NULL)) { 302 *new_dir = path_to_create; 303 return true; 304 } 305 } 306 307 return false; 308 } 309 CreateNewTempDirectory(const FilePath::StringType & prefix,FilePath * new_temp_path)310 bool CreateNewTempDirectory(const FilePath::StringType& prefix, 311 FilePath* new_temp_path) { 312 FilePath system_temp_dir; 313 if (!GetTempDir(&system_temp_dir)) 314 return false; 315 316 return CreateTemporaryDirInDir(system_temp_dir, prefix, new_temp_path); 317 } 318 CreateDirectoryAndGetError(const FilePath & full_path,File::Error * error)319 bool CreateDirectoryAndGetError(const FilePath& full_path, File::Error* error) { 320 // If the path exists, we've succeeded if it's a directory, failed otherwise. 321 DWORD fileattr = ::GetFileAttributes(ToWCharT(&full_path.value())); 322 if (fileattr != INVALID_FILE_ATTRIBUTES) { 323 if ((fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0) { 324 return true; 325 } 326 DLOG(WARNING) << "CreateDirectory(" << UTF16ToUTF8(full_path.value()) 327 << "), " 328 << "conflicts with existing file."; 329 if (error) { 330 *error = File::FILE_ERROR_NOT_A_DIRECTORY; 331 } 332 return false; 333 } 334 335 // Invariant: Path does not exist as file or directory. 336 337 // Attempt to create the parent recursively. This will immediately return 338 // true if it already exists, otherwise will create all required parent 339 // directories starting with the highest-level missing parent. 340 FilePath parent_path(full_path.DirName()); 341 if (parent_path.value() == full_path.value()) { 342 if (error) { 343 *error = File::FILE_ERROR_NOT_FOUND; 344 } 345 return false; 346 } 347 if (!CreateDirectoryAndGetError(parent_path, error)) { 348 DLOG(WARNING) << "Failed to create one of the parent directories."; 349 if (error) { 350 DCHECK(*error != File::FILE_OK); 351 } 352 return false; 353 } 354 355 if (!::CreateDirectory(ToWCharT(&full_path.value()), NULL)) { 356 DWORD error_code = ::GetLastError(); 357 if (error_code == ERROR_ALREADY_EXISTS && DirectoryExists(full_path)) { 358 // This error code ERROR_ALREADY_EXISTS doesn't indicate whether we 359 // were racing with someone creating the same directory, or a file 360 // with the same path. If DirectoryExists() returns true, we lost the 361 // race to create the same directory. 362 return true; 363 } else { 364 if (error) 365 *error = File::OSErrorToFileError(error_code); 366 DLOG(WARNING) << "Failed to create directory " 367 << UTF16ToUTF8(full_path.value()) << ", last error is " 368 << error_code << "."; 369 return false; 370 } 371 } else { 372 return true; 373 } 374 } 375 NormalizeFilePath(const FilePath & path,FilePath * real_path)376 bool NormalizeFilePath(const FilePath& path, FilePath* real_path) { 377 FilePath mapped_file; 378 if (!NormalizeToNativeFilePath(path, &mapped_file)) 379 return false; 380 // NormalizeToNativeFilePath() will return a path that starts with 381 // "\Device\Harddisk...". Helper DevicePathToDriveLetterPath() 382 // will find a drive letter which maps to the path's device, so 383 // that we return a path starting with a drive letter. 384 return DevicePathToDriveLetterPath(mapped_file, real_path); 385 } 386 DevicePathToDriveLetterPath(const FilePath & nt_device_path,FilePath * out_drive_letter_path)387 bool DevicePathToDriveLetterPath(const FilePath& nt_device_path, 388 FilePath* out_drive_letter_path) { 389 // Get the mapping of drive letters to device paths. 390 const int kDriveMappingSize = 1024; 391 char16_t drive_mapping[kDriveMappingSize] = {'\0'}; 392 if (!::GetLogicalDriveStrings(kDriveMappingSize - 1, 393 ToWCharT(drive_mapping))) { 394 DLOG(ERROR) << "Failed to get drive mapping."; 395 return false; 396 } 397 398 // The drive mapping is a sequence of null terminated strings. 399 // The last string is empty. 400 char16_t* drive_map_ptr = drive_mapping; 401 char16_t device_path_as_string[MAX_PATH]; 402 char16_t drive[] = u" :"; 403 404 // For each string in the drive mapping, get the junction that links 405 // to it. If that junction is a prefix of |device_path|, then we 406 // know that |drive| is the real path prefix. 407 while (*drive_map_ptr) { 408 drive[0] = drive_map_ptr[0]; // Copy the drive letter. 409 410 if (QueryDosDevice(ToWCharT(drive), ToWCharT(device_path_as_string), 411 MAX_PATH)) { 412 FilePath device_path(device_path_as_string); 413 if (device_path == nt_device_path || 414 device_path.IsParent(nt_device_path)) { 415 *out_drive_letter_path = 416 FilePath(drive + nt_device_path.value().substr( 417 wcslen(ToWCharT(device_path_as_string)))); 418 return true; 419 } 420 } 421 // Move to the next drive letter string, which starts one 422 // increment after the '\0' that terminates the current string. 423 while (*drive_map_ptr++) { 424 } 425 } 426 427 // No drive matched. The path does not start with a device junction 428 // that is mounted as a drive letter. This means there is no drive 429 // letter path to the volume that holds |device_path|, so fail. 430 return false; 431 } 432 NormalizeToNativeFilePath(const FilePath & path,FilePath * nt_path)433 bool NormalizeToNativeFilePath(const FilePath& path, FilePath* nt_path) { 434 // In Vista, GetFinalPathNameByHandle() would give us the real path 435 // from a file handle. If we ever deprecate XP, consider changing the 436 // code below to a call to GetFinalPathNameByHandle(). The method this 437 // function uses is explained in the following msdn article: 438 // http://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx 439 win::ScopedHandle file_handle( 440 ::CreateFile(ToWCharT(&path.value()), GENERIC_READ, kFileShareAll, NULL, 441 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)); 442 if (!file_handle.IsValid()) 443 return false; 444 445 // Create a file mapping object. Can't easily use MemoryMappedFile, because 446 // we only map the first byte, and need direct access to the handle. You can 447 // not map an empty file, this call fails in that case. 448 win::ScopedHandle file_map_handle( 449 ::CreateFileMapping(file_handle.Get(), NULL, PAGE_READONLY, 0, 450 1, // Just one byte. No need to look at the data. 451 NULL)); 452 if (!file_map_handle.IsValid()) 453 return false; 454 455 // Use a view of the file to get the path to the file. 456 void* file_view = 457 MapViewOfFile(file_map_handle.Get(), FILE_MAP_READ, 0, 0, 1); 458 if (!file_view) 459 return false; 460 461 // The expansion of |path| into a full path may make it longer. 462 // GetMappedFileName() will fail if the result is longer than MAX_PATH. 463 // Pad a bit to be safe. If kMaxPathLength is ever changed to be less 464 // than MAX_PATH, it would be nessisary to test that GetMappedFileName() 465 // not return kMaxPathLength. This would mean that only part of the 466 // path fit in |mapped_file_path|. 467 const int kMaxPathLength = MAX_PATH + 10; 468 char16_t mapped_file_path[kMaxPathLength]; 469 bool success = false; 470 HANDLE cp = GetCurrentProcess(); 471 if (::GetMappedFileNameW(cp, file_view, ToWCharT(mapped_file_path), 472 kMaxPathLength)) { 473 *nt_path = FilePath(mapped_file_path); 474 success = true; 475 } 476 ::UnmapViewOfFile(file_view); 477 return success; 478 } 479 480 // TODO(rkc): Work out if we want to handle NTFS junctions here or not, handle 481 // them if we do decide to. IsLink(const FilePath & file_path)482 bool IsLink(const FilePath& file_path) { 483 return false; 484 } 485 GetFileInfo(const FilePath & file_path,File::Info * results)486 bool GetFileInfo(const FilePath& file_path, File::Info* results) { 487 WIN32_FILE_ATTRIBUTE_DATA attr; 488 if (!GetFileAttributesEx(ToWCharT(&file_path.value()), GetFileExInfoStandard, 489 &attr)) { 490 return false; 491 } 492 493 ULARGE_INTEGER size; 494 size.HighPart = attr.nFileSizeHigh; 495 size.LowPart = attr.nFileSizeLow; 496 results->size = size.QuadPart; 497 498 results->is_directory = 499 (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; 500 results->last_modified = *reinterpret_cast<uint64_t*>(&attr.ftLastWriteTime); 501 results->last_accessed = *reinterpret_cast<uint64_t*>(&attr.ftLastAccessTime); 502 results->creation_time = *reinterpret_cast<uint64_t*>(&attr.ftCreationTime); 503 504 return true; 505 } 506 OpenFile(const FilePath & filename,const char * mode)507 FILE* OpenFile(const FilePath& filename, const char* mode) { 508 // 'N' is unconditionally added below, so be sure there is not one already 509 // present before a comma in |mode|. 510 DCHECK( 511 strchr(mode, 'N') == nullptr || 512 (strchr(mode, ',') != nullptr && strchr(mode, 'N') > strchr(mode, ','))); 513 std::u16string w_mode = ASCIIToUTF16(mode); 514 AppendModeCharacter(L'N', &w_mode); 515 return _wfsopen(ToWCharT(&filename.value()), ToWCharT(&w_mode), _SH_DENYNO); 516 } 517 FileToFILE(File file,const char * mode)518 FILE* FileToFILE(File file, const char* mode) { 519 if (!file.IsValid()) 520 return NULL; 521 int fd = 522 _open_osfhandle(reinterpret_cast<intptr_t>(file.GetPlatformFile()), 0); 523 if (fd < 0) 524 return NULL; 525 file.TakePlatformFile(); 526 FILE* stream = _fdopen(fd, mode); 527 if (!stream) 528 _close(fd); 529 return stream; 530 } 531 ReadFile(const FilePath & filename,char * data,int max_size)532 int ReadFile(const FilePath& filename, char* data, int max_size) { 533 win::ScopedHandle file(CreateFile(ToWCharT(&filename.value()), GENERIC_READ, 534 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, 535 OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 536 NULL)); 537 if (!file.IsValid()) 538 return -1; 539 540 DWORD read; 541 if (::ReadFile(file.Get(), data, max_size, &read, NULL)) 542 return read; 543 544 return -1; 545 } 546 WriteFile(const FilePath & filename,const char * data,int size)547 int WriteFile(const FilePath& filename, const char* data, int size) { 548 win::ScopedHandle file(CreateFile(ToWCharT(&filename.value()), GENERIC_WRITE, 549 0, NULL, CREATE_ALWAYS, 550 FILE_ATTRIBUTE_NORMAL, NULL)); 551 if (!file.IsValid()) { 552 DPLOG(WARNING) << "CreateFile failed for path " 553 << UTF16ToUTF8(filename.value()); 554 return -1; 555 } 556 557 DWORD written; 558 BOOL result = ::WriteFile(file.Get(), data, size, &written, NULL); 559 if (result && static_cast<int>(written) == size) 560 return written; 561 562 if (!result) { 563 // WriteFile failed. 564 DPLOG(WARNING) << "writing file " << UTF16ToUTF8(filename.value()) 565 << " failed"; 566 } else { 567 // Didn't write all the bytes. 568 DLOG(WARNING) << "wrote" << written << " bytes to " 569 << UTF16ToUTF8(filename.value()) << " expected " << size; 570 } 571 return -1; 572 } 573 AppendToFile(const FilePath & filename,const char * data,int size)574 bool AppendToFile(const FilePath& filename, const char* data, int size) { 575 win::ScopedHandle file(CreateFile(ToWCharT(&filename.value()), 576 FILE_APPEND_DATA, 0, NULL, OPEN_EXISTING, 0, 577 NULL)); 578 if (!file.IsValid()) { 579 return false; 580 } 581 582 DWORD written; 583 BOOL result = ::WriteFile(file.Get(), data, size, &written, NULL); 584 if (result && static_cast<int>(written) == size) 585 return true; 586 587 return false; 588 } 589 GetCurrentDirectory(FilePath * dir)590 bool GetCurrentDirectory(FilePath* dir) { 591 char16_t system_buffer[MAX_PATH]; 592 system_buffer[0] = 0; 593 DWORD len = ::GetCurrentDirectory(MAX_PATH, ToWCharT(system_buffer)); 594 if (len == 0 || len > MAX_PATH) 595 return false; 596 // TODO(evanm): the old behavior of this function was to always strip the 597 // trailing slash. We duplicate this here, but it shouldn't be necessary 598 // when everyone is using the appropriate FilePath APIs. 599 std::u16string dir_str(system_buffer); 600 *dir = FilePath(dir_str).StripTrailingSeparators(); 601 return true; 602 } 603 SetCurrentDirectory(const FilePath & directory)604 bool SetCurrentDirectory(const FilePath& directory) { 605 return ::SetCurrentDirectory(ToWCharT(&directory.value())) != 0; 606 } 607 GetMaximumPathComponentLength(const FilePath & path)608 int GetMaximumPathComponentLength(const FilePath& path) { 609 char16_t volume_path[MAX_PATH]; 610 if (!GetVolumePathNameW(ToWCharT(&path.NormalizePathSeparators().value()), 611 ToWCharT(volume_path), std::size(volume_path))) { 612 return -1; 613 } 614 615 DWORD max_length = 0; 616 if (!GetVolumeInformationW(ToWCharT(volume_path), NULL, 0, NULL, &max_length, 617 NULL, NULL, 0)) { 618 return -1; 619 } 620 621 // Length of |path| with path separator appended. 622 size_t prefix = path.StripTrailingSeparators().value().size() + 1; 623 // The whole path string must be shorter than MAX_PATH. That is, it must be 624 // prefix + component_length < MAX_PATH (or equivalently, <= MAX_PATH - 1). 625 int whole_path_limit = std::max(0, MAX_PATH - 1 - static_cast<int>(prefix)); 626 return std::min(whole_path_limit, static_cast<int>(max_length)); 627 } 628 SetNonBlocking(int fd)629 bool SetNonBlocking(int fd) { 630 unsigned long nonblocking = 1; 631 if (ioctlsocket(fd, FIONBIO, &nonblocking) == 0) 632 return true; 633 return false; 634 } 635 636 } // namespace base 637