PathV2.inc revision d45fbe62270eaf826419b93f1da66f1850f37faf
1//===- llvm/Support/Windows/PathV2.inc - Windows Path Impl ------*- C++ -*-===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This file implements the Windows specific implementation of the PathV2 API. 11// 12//===----------------------------------------------------------------------===// 13 14//===----------------------------------------------------------------------===// 15//=== WARNING: Implementation here must contain only generic Windows code that 16//=== is guaranteed to work on *all* Windows variants. 17//===----------------------------------------------------------------------===// 18 19#include "Windows.h" 20#include <fcntl.h> 21#include <io.h> 22#include <sys/stat.h> 23#include <sys/types.h> 24 25// MinGW doesn't define this. 26#ifndef _ERRNO_T_DEFINED 27#define _ERRNO_T_DEFINED 28typedef int errno_t; 29#endif 30 31using namespace llvm; 32 33namespace { 34 typedef BOOLEAN (WINAPI *PtrCreateSymbolicLinkW)( 35 /*__in*/ LPCWSTR lpSymlinkFileName, 36 /*__in*/ LPCWSTR lpTargetFileName, 37 /*__in*/ DWORD dwFlags); 38 39 PtrCreateSymbolicLinkW create_symbolic_link_api = PtrCreateSymbolicLinkW( 40 ::GetProcAddress(::GetModuleHandleA("kernel32.dll"), 41 "CreateSymbolicLinkW")); 42 43 error_code UTF8ToUTF16(StringRef utf8, SmallVectorImpl<wchar_t> &utf16) { 44 int len = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, 45 utf8.begin(), utf8.size(), 46 utf16.begin(), 0); 47 48 if (len == 0) 49 return windows_error(::GetLastError()); 50 51 utf16.reserve(len + 1); 52 utf16.set_size(len); 53 54 len = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, 55 utf8.begin(), utf8.size(), 56 utf16.begin(), utf16.size()); 57 58 if (len == 0) 59 return windows_error(::GetLastError()); 60 61 // Make utf16 null terminated. 62 utf16.push_back(0); 63 utf16.pop_back(); 64 65 return success; 66 } 67 68 error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len, 69 SmallVectorImpl<char> &utf8) { 70 // Get length. 71 int len = ::WideCharToMultiByte(CP_UTF8, 0, 72 utf16, utf16_len, 73 utf8.begin(), 0, 74 NULL, NULL); 75 76 if (len == 0) 77 return windows_error(::GetLastError()); 78 79 utf8.reserve(len); 80 utf8.set_size(len); 81 82 // Now do the actual conversion. 83 len = ::WideCharToMultiByte(CP_UTF8, 0, 84 utf16, utf16_len, 85 utf8.data(), utf8.size(), 86 NULL, NULL); 87 88 if (len == 0) 89 return windows_error(::GetLastError()); 90 91 // Make utf8 null terminated. 92 utf8.push_back(0); 93 utf8.pop_back(); 94 95 return success; 96 } 97 98 error_code TempDir(SmallVectorImpl<wchar_t> &result) { 99 retry_temp_dir: 100 DWORD len = ::GetTempPathW(result.capacity(), result.begin()); 101 102 if (len == 0) 103 return windows_error(::GetLastError()); 104 105 if (len > result.capacity()) { 106 result.reserve(len); 107 goto retry_temp_dir; 108 } 109 110 result.set_size(len); 111 return success; 112 } 113 114 bool is_separator(const wchar_t value) { 115 switch (value) { 116 case L'\\': 117 case L'/': 118 return true; 119 default: 120 return false; 121 } 122 } 123} 124 125namespace llvm { 126namespace sys { 127namespace fs { 128 129error_code current_path(SmallVectorImpl<char> &result) { 130 SmallVector<wchar_t, 128> cur_path; 131 cur_path.reserve(128); 132retry_cur_dir: 133 DWORD len = ::GetCurrentDirectoryW(cur_path.capacity(), cur_path.data()); 134 135 // A zero return value indicates a failure other than insufficient space. 136 if (len == 0) 137 return windows_error(::GetLastError()); 138 139 // If there's insufficient space, the len returned is larger than the len 140 // given. 141 if (len > cur_path.capacity()) { 142 cur_path.reserve(len); 143 goto retry_cur_dir; 144 } 145 146 cur_path.set_size(len); 147 // cur_path now holds the current directory in utf-16. Convert to utf-8. 148 149 // Find out how much space we need. Sadly, this function doesn't return the 150 // size needed unless you tell it the result size is 0, which means you 151 // _always_ have to call it twice. 152 len = ::WideCharToMultiByte(CP_UTF8, 0, 153 cur_path.data(), cur_path.size(), 154 result.data(), 0, 155 NULL, NULL); 156 157 if (len == 0) 158 return make_error_code(windows_error(::GetLastError())); 159 160 result.reserve(len); 161 result.set_size(len); 162 // Now do the actual conversion. 163 len = ::WideCharToMultiByte(CP_UTF8, 0, 164 cur_path.data(), cur_path.size(), 165 result.data(), result.size(), 166 NULL, NULL); 167 if (len == 0) 168 return windows_error(::GetLastError()); 169 170 return success; 171} 172 173error_code copy_file(const Twine &from, const Twine &to, copy_option copt) { 174 // Get arguments. 175 SmallString<128> from_storage; 176 SmallString<128> to_storage; 177 StringRef f = from.toStringRef(from_storage); 178 StringRef t = to.toStringRef(to_storage); 179 180 // Convert to utf-16. 181 SmallVector<wchar_t, 128> wide_from; 182 SmallVector<wchar_t, 128> wide_to; 183 if (error_code ec = UTF8ToUTF16(f, wide_from)) return ec; 184 if (error_code ec = UTF8ToUTF16(t, wide_to)) return ec; 185 186 // Copy the file. 187 BOOL res = ::CopyFileW(wide_from.begin(), wide_to.begin(), 188 copt != copy_option::overwrite_if_exists); 189 190 if (res == 0) 191 return windows_error(::GetLastError()); 192 193 return success; 194} 195 196error_code create_directory(const Twine &path, bool &existed) { 197 SmallString<128> path_storage; 198 SmallVector<wchar_t, 128> path_utf16; 199 200 if (error_code ec = UTF8ToUTF16(path.toStringRef(path_storage), 201 path_utf16)) 202 return ec; 203 204 if (!::CreateDirectoryW(path_utf16.begin(), NULL)) { 205 error_code ec = windows_error(::GetLastError()); 206 if (ec == windows_error::already_exists) 207 existed = true; 208 else 209 return ec; 210 } else 211 existed = false; 212 213 return success; 214} 215 216error_code create_hard_link(const Twine &to, const Twine &from) { 217 // Get arguments. 218 SmallString<128> from_storage; 219 SmallString<128> to_storage; 220 StringRef f = from.toStringRef(from_storage); 221 StringRef t = to.toStringRef(to_storage); 222 223 // Convert to utf-16. 224 SmallVector<wchar_t, 128> wide_from; 225 SmallVector<wchar_t, 128> wide_to; 226 if (error_code ec = UTF8ToUTF16(f, wide_from)) return ec; 227 if (error_code ec = UTF8ToUTF16(t, wide_to)) return ec; 228 229 if (!::CreateHardLinkW(wide_from.begin(), wide_to.begin(), NULL)) 230 return windows_error(::GetLastError()); 231 232 return success; 233} 234 235error_code create_symlink(const Twine &to, const Twine &from) { 236 // Only do it if the function is available at runtime. 237 if (!create_symbolic_link_api) 238 return make_error_code(errc::function_not_supported); 239 240 // Get arguments. 241 SmallString<128> from_storage; 242 SmallString<128> to_storage; 243 StringRef f = from.toStringRef(from_storage); 244 StringRef t = to.toStringRef(to_storage); 245 246 // Convert to utf-16. 247 SmallVector<wchar_t, 128> wide_from; 248 SmallVector<wchar_t, 128> wide_to; 249 if (error_code ec = UTF8ToUTF16(f, wide_from)) return ec; 250 if (error_code ec = UTF8ToUTF16(t, wide_to)) return ec; 251 252 if (!create_symbolic_link_api(wide_from.begin(), wide_to.begin(), 0)) 253 return windows_error(::GetLastError()); 254 255 return success; 256} 257 258error_code remove(const Twine &path, bool &existed) { 259 SmallString<128> path_storage; 260 SmallVector<wchar_t, 128> path_utf16; 261 262 file_status st; 263 if (error_code ec = status(path, st)) 264 return ec; 265 266 if (error_code ec = UTF8ToUTF16(path.toStringRef(path_storage), 267 path_utf16)) 268 return ec; 269 270 if (st.type() == file_type::directory_file) { 271 if (!::RemoveDirectoryW(c_str(path_utf16))) { 272 error_code ec = windows_error(::GetLastError()); 273 if (ec != windows_error::file_not_found) 274 return ec; 275 existed = false; 276 } else 277 existed = true; 278 } else { 279 if (!::DeleteFileW(c_str(path_utf16))) { 280 error_code ec = windows_error(::GetLastError()); 281 if (ec != windows_error::file_not_found) 282 return ec; 283 existed = false; 284 } else 285 existed = true; 286 } 287 288 return success; 289} 290 291error_code rename(const Twine &from, const Twine &to) { 292 // Get arguments. 293 SmallString<128> from_storage; 294 SmallString<128> to_storage; 295 StringRef f = from.toStringRef(from_storage); 296 StringRef t = to.toStringRef(to_storage); 297 298 // Convert to utf-16. 299 SmallVector<wchar_t, 128> wide_from; 300 SmallVector<wchar_t, 128> wide_to; 301 if (error_code ec = UTF8ToUTF16(f, wide_from)) return ec; 302 if (error_code ec = UTF8ToUTF16(t, wide_to)) return ec; 303 304 if (!::MoveFileExW(wide_from.begin(), wide_to.begin(), 305 MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING)) 306 return windows_error(::GetLastError()); 307 308 return success; 309} 310 311error_code resize_file(const Twine &path, uint64_t size) { 312 SmallString<128> path_storage; 313 SmallVector<wchar_t, 128> path_utf16; 314 315 if (error_code ec = UTF8ToUTF16(path.toStringRef(path_storage), 316 path_utf16)) 317 return ec; 318 319 int fd = ::_wopen(path_utf16.begin(), O_BINARY, S_IREAD | S_IWRITE); 320 if (fd == -1) 321 return error_code(errno, generic_category()); 322#ifdef HAVE__CHSIZE_S 323 errno_t error = ::_chsize_s(fd, size); 324#else 325 errno_t error = ::_chsize(fd, size); 326#endif 327 ::close(fd); 328 return error_code(error, generic_category()); 329} 330 331error_code exists(const Twine &path, bool &result) { 332 SmallString<128> path_storage; 333 SmallVector<wchar_t, 128> path_utf16; 334 335 if (error_code ec = UTF8ToUTF16(path.toStringRef(path_storage), 336 path_utf16)) 337 return ec; 338 339 DWORD attributes = ::GetFileAttributesW(path_utf16.begin()); 340 341 if (attributes == INVALID_FILE_ATTRIBUTES) { 342 // See if the file didn't actually exist. 343 error_code ec = make_error_code(windows_error(::GetLastError())); 344 if (ec != windows_error::file_not_found && 345 ec != windows_error::path_not_found) 346 return ec; 347 result = false; 348 } else 349 result = true; 350 return success; 351} 352 353bool equivalent(file_status A, file_status B) { 354 assert(status_known(A) && status_known(B)); 355 return A.FileIndexHigh == B.FileIndexHigh && 356 A.FileIndexLow == B.FileIndexLow && 357 A.FileSizeHigh == B.FileSizeHigh && 358 A.FileSizeLow == B.FileSizeLow && 359 A.LastWriteTimeHigh == B.LastWriteTimeHigh && 360 A.LastWriteTimeLow == B.LastWriteTimeLow && 361 A.VolumeSerialNumber == B.VolumeSerialNumber; 362} 363 364error_code equivalent(const Twine &A, const Twine &B, bool &result) { 365 file_status fsA, fsB; 366 if (error_code ec = status(A, fsA)) return ec; 367 if (error_code ec = status(B, fsB)) return ec; 368 result = equivalent(fsA, fsB); 369 return success; 370} 371 372error_code file_size(const Twine &path, uint64_t &result) { 373 SmallString<128> path_storage; 374 SmallVector<wchar_t, 128> path_utf16; 375 376 if (error_code ec = UTF8ToUTF16(path.toStringRef(path_storage), 377 path_utf16)) 378 return ec; 379 380 WIN32_FILE_ATTRIBUTE_DATA FileData; 381 if (!::GetFileAttributesExW(path_utf16.begin(), 382 ::GetFileExInfoStandard, 383 &FileData)) 384 return windows_error(::GetLastError()); 385 386 result = 387 (uint64_t(FileData.nFileSizeHigh) << (sizeof(FileData.nFileSizeLow) * 8)) 388 + FileData.nFileSizeLow; 389 390 return success; 391} 392 393static bool isReservedName(StringRef path) { 394 // This list of reserved names comes from MSDN, at: 395 // http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx 396 static const char *sReservedNames[] = { "nul", "con", "prn", "aux", 397 "com1", "com2", "com3", "com4", "com5", "com6", 398 "com7", "com8", "com9", "lpt1", "lpt2", "lpt3", 399 "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9" }; 400 401 // First, check to see if this is a device namespace, which always 402 // starts with \\.\, since device namespaces are not legal file paths. 403 if (path.startswith("\\\\.\\")) 404 return true; 405 406 // Then compare against the list of ancient reserved names 407 for (size_t i = 0; i < sizeof(sReservedNames) / sizeof(const char *); ++i) { 408 if (path.equals_lower(sReservedNames[i])) 409 return true; 410 } 411 412 // The path isn't what we consider reserved. 413 return false; 414} 415 416error_code status(const Twine &path, file_status &result) { 417 SmallString<128> path_storage; 418 SmallVector<wchar_t, 128> path_utf16; 419 420 StringRef path8 = path.toStringRef(path_storage); 421 if (isReservedName(path8)) { 422 result = file_status(file_type::character_file); 423 return success; 424 } 425 426 if (error_code ec = UTF8ToUTF16(path8, path_utf16)) 427 return ec; 428 429 DWORD attr = ::GetFileAttributesW(path_utf16.begin()); 430 if (attr == INVALID_FILE_ATTRIBUTES) 431 goto handle_status_error; 432 433 // Handle reparse points. 434 if (attr & FILE_ATTRIBUTE_REPARSE_POINT) { 435 ScopedFileHandle h( 436 ::CreateFileW(path_utf16.begin(), 437 0, // Attributes only. 438 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 439 NULL, 440 OPEN_EXISTING, 441 FILE_FLAG_BACKUP_SEMANTICS, 442 0)); 443 if (!h) 444 goto handle_status_error; 445 } 446 447 if (attr & FILE_ATTRIBUTE_DIRECTORY) 448 result = file_status(file_type::directory_file); 449 else { 450 result = file_status(file_type::regular_file); 451 ScopedFileHandle h( 452 ::CreateFileW(path_utf16.begin(), 453 0, // Attributes only. 454 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 455 NULL, 456 OPEN_EXISTING, 457 FILE_FLAG_BACKUP_SEMANTICS, 458 0)); 459 if (!h) 460 goto handle_status_error; 461 BY_HANDLE_FILE_INFORMATION Info; 462 if (!::GetFileInformationByHandle(h, &Info)) 463 goto handle_status_error; 464 result.FileIndexHigh = Info.nFileIndexHigh; 465 result.FileIndexLow = Info.nFileIndexLow; 466 result.FileSizeHigh = Info.nFileSizeHigh; 467 result.FileSizeLow = Info.nFileSizeLow; 468 result.LastWriteTimeHigh = Info.ftLastWriteTime.dwHighDateTime; 469 result.LastWriteTimeLow = Info.ftLastWriteTime.dwLowDateTime; 470 result.VolumeSerialNumber = Info.dwVolumeSerialNumber; 471 } 472 473 return success; 474 475handle_status_error: 476 error_code ec = windows_error(::GetLastError()); 477 if (ec == windows_error::file_not_found || 478 ec == windows_error::path_not_found) 479 result = file_status(file_type::file_not_found); 480 else if (ec == windows_error::sharing_violation) 481 result = file_status(file_type::type_unknown); 482 else { 483 result = file_status(file_type::status_error); 484 return ec; 485 } 486 487 return success; 488} 489 490error_code unique_file(const Twine &model, int &result_fd, 491 SmallVectorImpl<char> &result_path, 492 bool makeAbsolute) { 493 // Use result_path as temp storage. 494 result_path.set_size(0); 495 StringRef m = model.toStringRef(result_path); 496 497 SmallVector<wchar_t, 128> model_utf16; 498 if (error_code ec = UTF8ToUTF16(m, model_utf16)) return ec; 499 500 if (makeAbsolute) { 501 // Make model absolute by prepending a temp directory if it's not already. 502 bool absolute = path::is_absolute(m); 503 504 if (!absolute) { 505 SmallVector<wchar_t, 64> temp_dir; 506 if (error_code ec = TempDir(temp_dir)) return ec; 507 // Handle c: by removing it. 508 if (model_utf16.size() > 2 && model_utf16[1] == L':') { 509 model_utf16.erase(model_utf16.begin(), model_utf16.begin() + 2); 510 } 511 model_utf16.insert(model_utf16.begin(), temp_dir.begin(), temp_dir.end()); 512 } 513 } 514 515 // Replace '%' with random chars. From here on, DO NOT modify model. It may be 516 // needed if the randomly chosen path already exists. 517 SmallVector<wchar_t, 128> random_path_utf16; 518 519 // Get a Crypto Provider for CryptGenRandom. 520 HCRYPTPROV HCPC; 521 if (!::CryptAcquireContextW(&HCPC, 522 NULL, 523 NULL, 524 PROV_RSA_FULL, 525 CRYPT_VERIFYCONTEXT)) 526 return windows_error(::GetLastError()); 527 ScopedCryptContext CryptoProvider(HCPC); 528 529retry_random_path: 530 random_path_utf16.set_size(0); 531 for (SmallVectorImpl<wchar_t>::const_iterator i = model_utf16.begin(), 532 e = model_utf16.end(); 533 i != e; ++i) { 534 if (*i == L'%') { 535 BYTE val = 0; 536 if (!::CryptGenRandom(CryptoProvider, 1, &val)) 537 return windows_error(::GetLastError()); 538 random_path_utf16.push_back("0123456789abcdef"[val & 15]); 539 } 540 else 541 random_path_utf16.push_back(*i); 542 } 543 // Make random_path_utf16 null terminated. 544 random_path_utf16.push_back(0); 545 random_path_utf16.pop_back(); 546 547 // Try to create + open the path. 548retry_create_file: 549 HANDLE TempFileHandle = ::CreateFileW(random_path_utf16.begin(), 550 GENERIC_READ | GENERIC_WRITE, 551 FILE_SHARE_READ, 552 NULL, 553 // Return ERROR_FILE_EXISTS if the file 554 // already exists. 555 CREATE_NEW, 556 FILE_ATTRIBUTE_TEMPORARY, 557 NULL); 558 if (TempFileHandle == INVALID_HANDLE_VALUE) { 559 // If the file existed, try again, otherwise, error. 560 error_code ec = windows_error(::GetLastError()); 561 if (ec == windows_error::file_exists) 562 goto retry_random_path; 563 // Check for non-existing parent directories. 564 if (ec == windows_error::path_not_found) { 565 // Create the directories using result_path as temp storage. 566 if (error_code ec = UTF16ToUTF8(random_path_utf16.begin(), 567 random_path_utf16.size(), result_path)) 568 return ec; 569 StringRef p(result_path.begin(), result_path.size()); 570 SmallString<64> dir_to_create; 571 for (path::const_iterator i = path::begin(p), 572 e = --path::end(p); i != e; ++i) { 573 path::append(dir_to_create, *i); 574 bool Exists; 575 if (error_code ec = exists(Twine(dir_to_create), Exists)) return ec; 576 if (!Exists) { 577 // If c: doesn't exist, bail. 578 if (i->endswith(":")) 579 return ec; 580 581 SmallVector<wchar_t, 64> dir_to_create_utf16; 582 if (error_code ec = UTF8ToUTF16(dir_to_create, dir_to_create_utf16)) 583 return ec; 584 585 // Create the directory. 586 if (!::CreateDirectoryW(dir_to_create_utf16.begin(), NULL)) 587 return windows_error(::GetLastError()); 588 } 589 } 590 goto retry_create_file; 591 } 592 return ec; 593 } 594 595 // Set result_path to the utf-8 representation of the path. 596 if (error_code ec = UTF16ToUTF8(random_path_utf16.begin(), 597 random_path_utf16.size(), result_path)) { 598 ::CloseHandle(TempFileHandle); 599 ::DeleteFileW(random_path_utf16.begin()); 600 return ec; 601 } 602 603 // Convert the Windows API file handle into a C-runtime handle. 604 int fd = ::_open_osfhandle(intptr_t(TempFileHandle), 0); 605 if (fd == -1) { 606 ::CloseHandle(TempFileHandle); 607 ::DeleteFileW(random_path_utf16.begin()); 608 // MSDN doesn't say anything about _open_osfhandle setting errno or 609 // GetLastError(), so just return invalid_handle. 610 return windows_error::invalid_handle; 611 } 612 613 result_fd = fd; 614 return success; 615} 616 617error_code canonicalize(const Twine &path, SmallVectorImpl<char> &result) { 618 assert(path::is_absolute(path) && "path must be absolute!"); 619 SmallString<128> path_storage; 620 StringRef p = path.toStringRef(path_storage); 621 SmallVector<wchar_t, 128> path_utf16; 622 result.set_size(0); 623 624 // Convert path to UTF-16. 625 if (error_code ec = UTF8ToUTF16(p, path_utf16)) 626 return ec; 627 628 DWORD size = ::GetShortPathNameW(c_str(path_utf16), NULL, 0); 629 SmallVector<wchar_t, 128> short_path; 630 short_path.reserve(size + 1); 631 size = ::GetShortPathNameW( c_str(path_utf16) 632 , short_path.data() 633 , short_path.capacity()); 634 if (!size) 635 return windows_error(::GetLastError()); 636 637 short_path.set_size(size); 638 639 size = ::GetLongPathNameW(c_str(short_path), NULL, 0); 640 path_utf16.reserve(size + 1); 641 size = ::GetLongPathNameW( c_str(short_path) 642 , path_utf16.data() 643 , path_utf16.capacity()); 644 if (!size) 645 return windows_error(::GetLastError()); 646 647 path_utf16.set_size(size); 648 649 if (error_code ec = UTF16ToUTF8(path_utf16.data(), path_utf16.size(), result)) 650 return ec; 651 652 return success; 653} 654 655error_code get_magic(const Twine &path, uint32_t len, 656 SmallVectorImpl<char> &result) { 657 SmallString<128> path_storage; 658 SmallVector<wchar_t, 128> path_utf16; 659 result.set_size(0); 660 661 // Convert path to UTF-16. 662 if (error_code ec = UTF8ToUTF16(path.toStringRef(path_storage), 663 path_utf16)) 664 return ec; 665 666 // Open file. 667 HANDLE file = ::CreateFileW(c_str(path_utf16), 668 GENERIC_READ, 669 FILE_SHARE_READ, 670 NULL, 671 OPEN_EXISTING, 672 FILE_ATTRIBUTE_READONLY, 673 NULL); 674 if (file == INVALID_HANDLE_VALUE) 675 return windows_error(::GetLastError()); 676 677 // Allocate buffer. 678 result.reserve(len); 679 680 // Get magic! 681 DWORD bytes_read = 0; 682 BOOL read_success = ::ReadFile(file, result.data(), len, &bytes_read, NULL); 683 error_code ec = windows_error(::GetLastError()); 684 ::CloseHandle(file); 685 if (!read_success || (bytes_read != len)) { 686 // Set result size to the number of bytes read if it's valid. 687 if (bytes_read <= len) 688 result.set_size(bytes_read); 689 // ERROR_HANDLE_EOF is mapped to errc::value_too_large. 690 return ec; 691 } 692 693 result.set_size(len); 694 return success; 695} 696 697error_code detail::directory_iterator_construct(detail::DirIterState &it, 698 StringRef path){ 699 SmallVector<wchar_t, 128> path_utf16; 700 701 if (error_code ec = UTF8ToUTF16(path, 702 path_utf16)) 703 return ec; 704 705 // Convert path to the format that Windows is happy with. 706 if (path_utf16.size() > 0 && 707 !is_separator(path_utf16[path.size() - 1]) && 708 path_utf16[path.size() - 1] != L':') { 709 path_utf16.push_back(L'\\'); 710 path_utf16.push_back(L'*'); 711 } else { 712 path_utf16.push_back(L'*'); 713 } 714 715 // Get the first directory entry. 716 WIN32_FIND_DATAW FirstFind; 717 ScopedFindHandle FindHandle(::FindFirstFileW(c_str(path_utf16), &FirstFind)); 718 if (!FindHandle) 719 return windows_error(::GetLastError()); 720 721 size_t FilenameLen = ::wcslen(FirstFind.cFileName); 722 while ((FilenameLen == 1 && FirstFind.cFileName[0] == L'.') || 723 (FilenameLen == 2 && FirstFind.cFileName[0] == L'.' && 724 FirstFind.cFileName[1] == L'.')) 725 if (!::FindNextFileW(FindHandle, &FirstFind)) { 726 error_code ec = windows_error(::GetLastError()); 727 // Check for end. 728 if (ec == windows_error::no_more_files) 729 return detail::directory_iterator_destruct(it); 730 return ec; 731 } else 732 FilenameLen = ::wcslen(FirstFind.cFileName); 733 734 // Construct the current directory entry. 735 SmallString<128> directory_entry_name_utf8; 736 if (error_code ec = UTF16ToUTF8(FirstFind.cFileName, 737 ::wcslen(FirstFind.cFileName), 738 directory_entry_name_utf8)) 739 return ec; 740 741 it.IterationHandle = intptr_t(FindHandle.take()); 742 SmallString<128> directory_entry_path(path); 743 path::append(directory_entry_path, directory_entry_name_utf8.str()); 744 it.CurrentEntry = directory_entry(directory_entry_path.str()); 745 746 return success; 747} 748 749error_code detail::directory_iterator_destruct(detail::DirIterState &it) { 750 if (it.IterationHandle != 0) 751 // Closes the handle if it's valid. 752 ScopedFindHandle close(HANDLE(it.IterationHandle)); 753 it.IterationHandle = 0; 754 it.CurrentEntry = directory_entry(); 755 return success; 756} 757 758error_code detail::directory_iterator_increment(detail::DirIterState &it) { 759 WIN32_FIND_DATAW FindData; 760 if (!::FindNextFileW(HANDLE(it.IterationHandle), &FindData)) { 761 error_code ec = windows_error(::GetLastError()); 762 // Check for end. 763 if (ec == windows_error::no_more_files) 764 return detail::directory_iterator_destruct(it); 765 return ec; 766 } 767 768 size_t FilenameLen = ::wcslen(FindData.cFileName); 769 if ((FilenameLen == 1 && FindData.cFileName[0] == L'.') || 770 (FilenameLen == 2 && FindData.cFileName[0] == L'.' && 771 FindData.cFileName[1] == L'.')) 772 return directory_iterator_increment(it); 773 774 SmallString<128> directory_entry_path_utf8; 775 if (error_code ec = UTF16ToUTF8(FindData.cFileName, 776 ::wcslen(FindData.cFileName), 777 directory_entry_path_utf8)) 778 return ec; 779 780 it.CurrentEntry.replace_filename(Twine(directory_entry_path_utf8)); 781 return success; 782} 783 784} // end namespace fs 785} // end namespace sys 786} // end namespace llvm 787