1//===- llvm/Support/Win32/Path.cpp - Win32 Path Implementation ---*- 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 provides the Win32 specific implementation of the Path class. 11// 12//===----------------------------------------------------------------------===// 13 14//===----------------------------------------------------------------------===// 15//=== WARNING: Implementation here must contain only generic Win32 code that 16//=== is guaranteed to work on *all* Win32 variants. 17//===----------------------------------------------------------------------===// 18 19#include "Windows.h" 20#include <malloc.h> 21#include <cstdio> 22 23// We need to undo a macro defined in Windows.h, otherwise we won't compile: 24#undef CopyFile 25#undef GetCurrentDirectory 26 27// Windows happily accepts either forward or backward slashes, though any path 28// returned by a Win32 API will have backward slashes. As LLVM code basically 29// assumes forward slashes are used, backward slashs are converted where they 30// can be introduced into a path. 31// 32// Another invariant is that a path ends with a slash if and only if the path 33// is a root directory. Any other use of a trailing slash is stripped. Unlike 34// in Unix, Windows has a rather complicated notion of a root path and this 35// invariant helps simply the code. 36 37static void FlipBackSlashes(std::string& s) { 38 for (size_t i = 0; i < s.size(); i++) 39 if (s[i] == '\\') 40 s[i] = '/'; 41} 42 43namespace llvm { 44namespace sys { 45 46const char PathSeparator = ';'; 47 48StringRef Path::GetEXESuffix() { 49 return "exe"; 50} 51 52Path::Path(llvm::StringRef p) 53 : path(p) { 54 FlipBackSlashes(path); 55} 56 57Path::Path(const char *StrStart, unsigned StrLen) 58 : path(StrStart, StrLen) { 59 FlipBackSlashes(path); 60} 61 62Path& 63Path::operator=(StringRef that) { 64 path.assign(that.data(), that.size()); 65 FlipBackSlashes(path); 66 return *this; 67} 68 69// push_back 0 on create, and pop_back on delete. 70struct ScopedNullTerminator { 71 std::string &str; 72 ScopedNullTerminator(std::string &s) : str(s) { str.push_back(0); } 73 ~ScopedNullTerminator() { 74 // str.pop_back(); But wait, C++03 doesn't have this... 75 assert(!str.empty() && str[str.size() - 1] == 0 76 && "Null char not present!"); 77 str.resize(str.size() - 1); 78 } 79}; 80 81bool 82Path::isValid() const { 83 if (path.empty()) 84 return false; 85 86 // If there is a colon, it must be the second character, preceded by a letter 87 // and followed by something. 88 size_t len = path.size(); 89 // This code assumes that path is null terminated, so make sure it is. 90 ScopedNullTerminator snt(path); 91 size_t pos = path.rfind(':',len); 92 size_t rootslash = 0; 93 if (pos != std::string::npos) { 94 if (pos != 1 || !isalpha(path[0]) || len < 3) 95 return false; 96 rootslash = 2; 97 } 98 99 // Look for a UNC path, and if found adjust our notion of the root slash. 100 if (len > 3 && path[0] == '/' && path[1] == '/') { 101 rootslash = path.find('/', 2); 102 if (rootslash == std::string::npos) 103 rootslash = 0; 104 } 105 106 // Check for illegal characters. 107 if (path.find_first_of("\\<>\"|\001\002\003\004\005\006\007\010\011\012" 108 "\013\014\015\016\017\020\021\022\023\024\025\026" 109 "\027\030\031\032\033\034\035\036\037") 110 != std::string::npos) 111 return false; 112 113 // Remove trailing slash, unless it's a root slash. 114 if (len > rootslash+1 && path[len-1] == '/') 115 path.erase(--len); 116 117 // Check each component for legality. 118 for (pos = 0; pos < len; ++pos) { 119 // A component may not end in a space. 120 if (path[pos] == ' ') { 121 if (path[pos+1] == '/' || path[pos+1] == '\0') 122 return false; 123 } 124 125 // A component may not end in a period. 126 if (path[pos] == '.') { 127 if (path[pos+1] == '/' || path[pos+1] == '\0') { 128 // Unless it is the pseudo-directory "."... 129 if (pos == 0 || path[pos-1] == '/' || path[pos-1] == ':') 130 return true; 131 // or "..". 132 if (pos > 0 && path[pos-1] == '.') { 133 if (pos == 1 || path[pos-2] == '/' || path[pos-2] == ':') 134 return true; 135 } 136 return false; 137 } 138 } 139 } 140 141 return true; 142} 143 144void Path::makeAbsolute() { 145 TCHAR FullPath[MAX_PATH + 1] = {0}; 146 LPTSTR FilePart = NULL; 147 148 DWORD RetLength = ::GetFullPathNameA(path.c_str(), 149 sizeof(FullPath)/sizeof(FullPath[0]), 150 FullPath, &FilePart); 151 152 if (0 == RetLength) { 153 // FIXME: Report the error GetLastError() 154 assert(0 && "Unable to make absolute path!"); 155 } else if (RetLength > MAX_PATH) { 156 // FIXME: Report too small buffer (needed RetLength bytes). 157 assert(0 && "Unable to make absolute path!"); 158 } else { 159 path = FullPath; 160 } 161} 162 163bool 164Path::isAbsolute(const char *NameStart, unsigned NameLen) { 165 assert(NameStart); 166 // FIXME: This does not handle correctly an absolute path starting from 167 // a drive letter or in UNC format. 168 switch (NameLen) { 169 case 0: 170 return false; 171 case 1: 172 case 2: 173 return NameStart[0] == '/'; 174 default: 175 return 176 (NameStart[0] == '/' || (NameStart[1] == ':' && NameStart[2] == '/')) || 177 (NameStart[0] == '\\' || (NameStart[1] == ':' && NameStart[2] == '\\')); 178 } 179} 180 181bool 182Path::isAbsolute() const { 183 // FIXME: This does not handle correctly an absolute path starting from 184 // a drive letter or in UNC format. 185 switch (path.length()) { 186 case 0: 187 return false; 188 case 1: 189 case 2: 190 return path[0] == '/'; 191 default: 192 return path[0] == '/' || (path[1] == ':' && path[2] == '/'); 193 } 194} 195 196static Path *TempDirectory; 197 198Path 199Path::GetTemporaryDirectory(std::string* ErrMsg) { 200 if (TempDirectory) 201 return *TempDirectory; 202 203 char pathname[MAX_PATH]; 204 if (!GetTempPath(MAX_PATH, pathname)) { 205 if (ErrMsg) 206 *ErrMsg = "Can't determine temporary directory"; 207 return Path(); 208 } 209 210 Path result; 211 result.set(pathname); 212 213 // Append a subdirectory passed on our process id so multiple LLVMs don't 214 // step on each other's toes. 215#ifdef __MINGW32__ 216 // Mingw's Win32 header files are broken. 217 sprintf(pathname, "LLVM_%u", unsigned(GetCurrentProcessId())); 218#else 219 sprintf(pathname, "LLVM_%u", GetCurrentProcessId()); 220#endif 221 result.appendComponent(pathname); 222 223 // If there's a directory left over from a previous LLVM execution that 224 // happened to have the same process id, get rid of it. 225 result.eraseFromDisk(true); 226 227 // And finally (re-)create the empty directory. 228 result.createDirectoryOnDisk(false); 229 TempDirectory = new Path(result); 230 return *TempDirectory; 231} 232 233// FIXME: the following set of functions don't map to Windows very well. 234Path 235Path::GetRootDirectory() { 236 // This is the only notion that that Windows has of a root directory. Nothing 237 // is here except for drives. 238 return Path("file:///"); 239} 240 241void 242Path::GetSystemLibraryPaths(std::vector<sys::Path>& Paths) { 243 char buff[MAX_PATH]; 244 // Generic form of C:\Windows\System32 245 HRESULT res = SHGetFolderPathA(NULL, 246 CSIDL_FLAG_CREATE | CSIDL_SYSTEM, 247 NULL, 248 SHGFP_TYPE_CURRENT, 249 buff); 250 if (res != S_OK) { 251 assert(0 && "Failed to get system directory"); 252 return; 253 } 254 Paths.push_back(sys::Path(buff)); 255 256 // Reset buff. 257 buff[0] = 0; 258 // Generic form of C:\Windows 259 res = SHGetFolderPathA(NULL, 260 CSIDL_FLAG_CREATE | CSIDL_WINDOWS, 261 NULL, 262 SHGFP_TYPE_CURRENT, 263 buff); 264 if (res != S_OK) { 265 assert(0 && "Failed to get windows directory"); 266 return; 267 } 268 Paths.push_back(sys::Path(buff)); 269} 270 271void 272Path::GetBitcodeLibraryPaths(std::vector<sys::Path>& Paths) { 273 char * env_var = getenv("LLVM_LIB_SEARCH_PATH"); 274 if (env_var != 0) { 275 getPathList(env_var,Paths); 276 } 277#ifdef LLVM_LIBDIR 278 { 279 Path tmpPath; 280 if (tmpPath.set(LLVM_LIBDIR)) 281 if (tmpPath.canRead()) 282 Paths.push_back(tmpPath); 283 } 284#endif 285 GetSystemLibraryPaths(Paths); 286} 287 288Path 289Path::GetLLVMDefaultConfigDir() { 290 Path ret = GetUserHomeDirectory(); 291 if (!ret.appendComponent(".llvm")) 292 assert(0 && "Failed to append .llvm"); 293 return ret; 294} 295 296Path 297Path::GetUserHomeDirectory() { 298 char buff[MAX_PATH]; 299 HRESULT res = SHGetFolderPathA(NULL, 300 CSIDL_FLAG_CREATE | CSIDL_APPDATA, 301 NULL, 302 SHGFP_TYPE_CURRENT, 303 buff); 304 if (res != S_OK) 305 assert(0 && "Failed to get user home directory"); 306 return Path(buff); 307} 308 309Path 310Path::GetCurrentDirectory() { 311 char pathname[MAX_PATH]; 312 ::GetCurrentDirectoryA(MAX_PATH,pathname); 313 return Path(pathname); 314} 315 316/// GetMainExecutable - Return the path to the main executable, given the 317/// value of argv[0] from program startup. 318Path Path::GetMainExecutable(const char *argv0, void *MainAddr) { 319 char pathname[MAX_PATH]; 320 DWORD ret = ::GetModuleFileNameA(NULL, pathname, MAX_PATH); 321 return ret != MAX_PATH ? Path(pathname) : Path(); 322} 323 324 325// FIXME: the above set of functions don't map to Windows very well. 326 327 328StringRef Path::getDirname() const { 329 return getDirnameCharSep(path, "/"); 330} 331 332StringRef 333Path::getBasename() const { 334 // Find the last slash 335 size_t slash = path.rfind('/'); 336 if (slash == std::string::npos) 337 slash = 0; 338 else 339 slash++; 340 341 size_t dot = path.rfind('.'); 342 if (dot == std::string::npos || dot < slash) 343 return StringRef(path).substr(slash); 344 else 345 return StringRef(path).substr(slash, dot - slash); 346} 347 348StringRef 349Path::getSuffix() const { 350 // Find the last slash 351 size_t slash = path.rfind('/'); 352 if (slash == std::string::npos) 353 slash = 0; 354 else 355 slash++; 356 357 size_t dot = path.rfind('.'); 358 if (dot == std::string::npos || dot < slash) 359 return StringRef(""); 360 else 361 return StringRef(path).substr(dot + 1); 362} 363 364bool 365Path::exists() const { 366 DWORD attr = GetFileAttributes(path.c_str()); 367 return attr != INVALID_FILE_ATTRIBUTES; 368} 369 370bool 371Path::isDirectory() const { 372 DWORD attr = GetFileAttributes(path.c_str()); 373 return (attr != INVALID_FILE_ATTRIBUTES) && 374 (attr & FILE_ATTRIBUTE_DIRECTORY); 375} 376 377bool 378Path::isSymLink() const { 379 DWORD attributes = GetFileAttributes(path.c_str()); 380 381 if (attributes == INVALID_FILE_ATTRIBUTES) 382 // There's no sane way to report this :(. 383 assert(0 && "GetFileAttributes returned INVALID_FILE_ATTRIBUTES"); 384 385 // This isn't exactly what defines a NTFS symlink, but it is only true for 386 // paths that act like a symlink. 387 return attributes & FILE_ATTRIBUTE_REPARSE_POINT; 388} 389 390bool 391Path::canRead() const { 392 // FIXME: take security attributes into account. 393 DWORD attr = GetFileAttributes(path.c_str()); 394 return attr != INVALID_FILE_ATTRIBUTES; 395} 396 397bool 398Path::canWrite() const { 399 // FIXME: take security attributes into account. 400 DWORD attr = GetFileAttributes(path.c_str()); 401 return (attr != INVALID_FILE_ATTRIBUTES) && !(attr & FILE_ATTRIBUTE_READONLY); 402} 403 404bool 405Path::canExecute() const { 406 // FIXME: take security attributes into account. 407 DWORD attr = GetFileAttributes(path.c_str()); 408 return attr != INVALID_FILE_ATTRIBUTES; 409} 410 411bool 412Path::isRegularFile() const { 413 bool res; 414 if (fs::is_regular_file(path, res)) 415 return false; 416 return res; 417} 418 419StringRef 420Path::getLast() const { 421 // Find the last slash 422 size_t pos = path.rfind('/'); 423 424 // Handle the corner cases 425 if (pos == std::string::npos) 426 return path; 427 428 // If the last character is a slash, we have a root directory 429 if (pos == path.length()-1) 430 return path; 431 432 // Return everything after the last slash 433 return StringRef(path).substr(pos+1); 434} 435 436const FileStatus * 437PathWithStatus::getFileStatus(bool update, std::string *ErrStr) const { 438 if (!fsIsValid || update) { 439 WIN32_FILE_ATTRIBUTE_DATA fi; 440 if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi)) { 441 MakeErrMsg(ErrStr, "getStatusInfo():" + std::string(path) + 442 ": Can't get status: "); 443 return 0; 444 } 445 446 status.fileSize = fi.nFileSizeHigh; 447 status.fileSize <<= sizeof(fi.nFileSizeHigh)*8; 448 status.fileSize += fi.nFileSizeLow; 449 450 status.mode = fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY ? 0555 : 0777; 451 status.user = 9999; // Not applicable to Windows, so... 452 status.group = 9999; // Not applicable to Windows, so... 453 454 // FIXME: this is only unique if the file is accessed by the same file path. 455 // How do we do this for C:\dir\file and ..\dir\file ? Unix has inode 456 // numbers, but the concept doesn't exist in Windows. 457 status.uniqueID = 0; 458 for (unsigned i = 0; i < path.length(); ++i) 459 status.uniqueID += path[i]; 460 461 ULARGE_INTEGER ui; 462 ui.LowPart = fi.ftLastWriteTime.dwLowDateTime; 463 ui.HighPart = fi.ftLastWriteTime.dwHighDateTime; 464 status.modTime.fromWin32Time(ui.QuadPart); 465 466 status.isDir = fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; 467 fsIsValid = true; 468 } 469 return &status; 470} 471 472bool Path::makeReadableOnDisk(std::string* ErrMsg) { 473 // All files are readable on Windows (ignoring security attributes). 474 return false; 475} 476 477bool Path::makeWriteableOnDisk(std::string* ErrMsg) { 478 DWORD attr = GetFileAttributes(path.c_str()); 479 480 // If it doesn't exist, we're done. 481 if (attr == INVALID_FILE_ATTRIBUTES) 482 return false; 483 484 if (attr & FILE_ATTRIBUTE_READONLY) { 485 if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY)) { 486 MakeErrMsg(ErrMsg, std::string(path) + ": Can't make file writable: "); 487 return true; 488 } 489 } 490 return false; 491} 492 493bool Path::makeExecutableOnDisk(std::string* ErrMsg) { 494 // All files are executable on Windows (ignoring security attributes). 495 return false; 496} 497 498bool 499Path::getDirectoryContents(std::set<Path>& result, std::string* ErrMsg) const { 500 WIN32_FILE_ATTRIBUTE_DATA fi; 501 if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi)) { 502 MakeErrMsg(ErrMsg, path + ": can't get status of file"); 503 return true; 504 } 505 506 if (!(fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { 507 if (ErrMsg) 508 *ErrMsg = path + ": not a directory"; 509 return true; 510 } 511 512 result.clear(); 513 WIN32_FIND_DATA fd; 514 std::string searchpath = path; 515 if (path.size() == 0 || searchpath[path.size()-1] == '/') 516 searchpath += "*"; 517 else 518 searchpath += "/*"; 519 520 HANDLE h = FindFirstFile(searchpath.c_str(), &fd); 521 if (h == INVALID_HANDLE_VALUE) { 522 if (GetLastError() == ERROR_FILE_NOT_FOUND) 523 return true; // not really an error, now is it? 524 MakeErrMsg(ErrMsg, path + ": Can't read directory: "); 525 return true; 526 } 527 528 do { 529 if (fd.cFileName[0] == '.') 530 continue; 531 Path aPath(path); 532 aPath.appendComponent(&fd.cFileName[0]); 533 result.insert(aPath); 534 } while (FindNextFile(h, &fd)); 535 536 DWORD err = GetLastError(); 537 FindClose(h); 538 if (err != ERROR_NO_MORE_FILES) { 539 SetLastError(err); 540 MakeErrMsg(ErrMsg, path + ": Can't read directory: "); 541 return true; 542 } 543 return false; 544} 545 546bool 547Path::set(StringRef a_path) { 548 if (a_path.empty()) 549 return false; 550 std::string save(path); 551 path = a_path; 552 FlipBackSlashes(path); 553 if (!isValid()) { 554 path = save; 555 return false; 556 } 557 return true; 558} 559 560bool 561Path::appendComponent(StringRef name) { 562 if (name.empty()) 563 return false; 564 std::string save(path); 565 if (!path.empty()) { 566 size_t last = path.size() - 1; 567 if (path[last] != '/') 568 path += '/'; 569 } 570 path += name; 571 if (!isValid()) { 572 path = save; 573 return false; 574 } 575 return true; 576} 577 578bool 579Path::eraseComponent() { 580 size_t slashpos = path.rfind('/',path.size()); 581 if (slashpos == path.size() - 1 || slashpos == std::string::npos) 582 return false; 583 std::string save(path); 584 path.erase(slashpos); 585 if (!isValid()) { 586 path = save; 587 return false; 588 } 589 return true; 590} 591 592bool 593Path::eraseSuffix() { 594 size_t dotpos = path.rfind('.',path.size()); 595 size_t slashpos = path.rfind('/',path.size()); 596 if (dotpos != std::string::npos) { 597 if (slashpos == std::string::npos || dotpos > slashpos+1) { 598 std::string save(path); 599 path.erase(dotpos, path.size()-dotpos); 600 if (!isValid()) { 601 path = save; 602 return false; 603 } 604 return true; 605 } 606 } 607 return false; 608} 609 610inline bool PathMsg(std::string* ErrMsg, const char* pathname, const char*msg) { 611 if (ErrMsg) 612 *ErrMsg = std::string(pathname) + ": " + std::string(msg); 613 return true; 614} 615 616bool 617Path::createDirectoryOnDisk(bool create_parents, std::string* ErrMsg) { 618 // Get a writeable copy of the path name 619 size_t len = path.length(); 620 char *pathname = reinterpret_cast<char *>(_alloca(len+2)); 621 path.copy(pathname, len); 622 pathname[len] = 0; 623 624 // Make sure it ends with a slash. 625 if (len == 0 || pathname[len - 1] != '/') { 626 pathname[len] = '/'; 627 pathname[++len] = 0; 628 } 629 630 // Determine starting point for initial / search. 631 char *next = pathname; 632 if (pathname[0] == '/' && pathname[1] == '/') { 633 // Skip host name. 634 next = strchr(pathname+2, '/'); 635 if (next == NULL) 636 return PathMsg(ErrMsg, pathname, "badly formed remote directory"); 637 638 // Skip share name. 639 next = strchr(next+1, '/'); 640 if (next == NULL) 641 return PathMsg(ErrMsg, pathname,"badly formed remote directory"); 642 643 next++; 644 if (*next == 0) 645 return PathMsg(ErrMsg, pathname, "badly formed remote directory"); 646 647 } else { 648 if (pathname[1] == ':') 649 next += 2; // skip drive letter 650 if (*next == '/') 651 next++; // skip root directory 652 } 653 654 // If we're supposed to create intermediate directories 655 if (create_parents) { 656 // Loop through the directory components until we're done 657 while (*next) { 658 next = strchr(next, '/'); 659 *next = 0; 660 if (!CreateDirectory(pathname, NULL) && 661 GetLastError() != ERROR_ALREADY_EXISTS) 662 return MakeErrMsg(ErrMsg, 663 std::string(pathname) + ": Can't create directory: "); 664 *next++ = '/'; 665 } 666 } else { 667 // Drop trailing slash. 668 pathname[len-1] = 0; 669 if (!CreateDirectory(pathname, NULL) && 670 GetLastError() != ERROR_ALREADY_EXISTS) { 671 return MakeErrMsg(ErrMsg, std::string(pathname) + 672 ": Can't create directory: "); 673 } 674 } 675 return false; 676} 677 678bool 679Path::createFileOnDisk(std::string* ErrMsg) { 680 // Create the file 681 HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW, 682 FILE_ATTRIBUTE_NORMAL, NULL); 683 if (h == INVALID_HANDLE_VALUE) 684 return MakeErrMsg(ErrMsg, path + ": Can't create file: "); 685 686 CloseHandle(h); 687 return false; 688} 689 690bool 691Path::eraseFromDisk(bool remove_contents, std::string *ErrStr) const { 692 WIN32_FILE_ATTRIBUTE_DATA fi; 693 if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi)) 694 return true; 695 696 if (fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { 697 // If it doesn't exist, we're done. 698 bool Exists; 699 if (fs::exists(path, Exists) || !Exists) 700 return false; 701 702 char *pathname = reinterpret_cast<char *>(_alloca(path.length()+3)); 703 int lastchar = path.length() - 1 ; 704 path.copy(pathname, lastchar+1); 705 706 // Make path end with '/*'. 707 if (pathname[lastchar] != '/') 708 pathname[++lastchar] = '/'; 709 pathname[lastchar+1] = '*'; 710 pathname[lastchar+2] = 0; 711 712 if (remove_contents) { 713 WIN32_FIND_DATA fd; 714 HANDLE h = FindFirstFile(pathname, &fd); 715 716 // It's a bad idea to alter the contents of a directory while enumerating 717 // its contents. So build a list of its contents first, then destroy them. 718 719 if (h != INVALID_HANDLE_VALUE) { 720 std::vector<Path> list; 721 722 do { 723 if (strcmp(fd.cFileName, ".") == 0) 724 continue; 725 if (strcmp(fd.cFileName, "..") == 0) 726 continue; 727 728 Path aPath(path); 729 aPath.appendComponent(&fd.cFileName[0]); 730 list.push_back(aPath); 731 } while (FindNextFile(h, &fd)); 732 733 DWORD err = GetLastError(); 734 FindClose(h); 735 if (err != ERROR_NO_MORE_FILES) { 736 SetLastError(err); 737 return MakeErrMsg(ErrStr, path + ": Can't read directory: "); 738 } 739 740 for (std::vector<Path>::iterator I = list.begin(); I != list.end(); 741 ++I) { 742 Path &aPath = *I; 743 aPath.eraseFromDisk(true); 744 } 745 } else { 746 if (GetLastError() != ERROR_FILE_NOT_FOUND) 747 return MakeErrMsg(ErrStr, path + ": Can't read directory: "); 748 } 749 } 750 751 pathname[lastchar] = 0; 752 if (!RemoveDirectory(pathname)) 753 return MakeErrMsg(ErrStr, 754 std::string(pathname) + ": Can't destroy directory: "); 755 return false; 756 } else { 757 // Read-only files cannot be deleted on Windows. Must remove the read-only 758 // attribute first. 759 if (fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY) { 760 if (!SetFileAttributes(path.c_str(), 761 fi.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY)) 762 return MakeErrMsg(ErrStr, path + ": Can't destroy file: "); 763 } 764 765 if (!DeleteFile(path.c_str())) 766 return MakeErrMsg(ErrStr, path + ": Can't destroy file: "); 767 return false; 768 } 769} 770 771bool Path::getMagicNumber(std::string& Magic, unsigned len) const { 772 assert(len < 1024 && "Request for magic string too long"); 773 char* buf = reinterpret_cast<char*>(alloca(len)); 774 775 HANDLE h = CreateFile(path.c_str(), 776 GENERIC_READ, 777 FILE_SHARE_READ, 778 NULL, 779 OPEN_EXISTING, 780 FILE_ATTRIBUTE_NORMAL, 781 NULL); 782 if (h == INVALID_HANDLE_VALUE) 783 return false; 784 785 DWORD nRead = 0; 786 BOOL ret = ReadFile(h, buf, len, &nRead, NULL); 787 CloseHandle(h); 788 789 if (!ret || nRead != len) 790 return false; 791 792 Magic = std::string(buf, len); 793 return true; 794} 795 796bool 797Path::renamePathOnDisk(const Path& newName, std::string* ErrMsg) { 798 if (!MoveFileEx(path.c_str(), newName.c_str(), MOVEFILE_REPLACE_EXISTING)) 799 return MakeErrMsg(ErrMsg, "Can't move '" + path + "' to '" + newName.path 800 + "': "); 801 return false; 802} 803 804bool 805Path::setStatusInfoOnDisk(const FileStatus &si, std::string *ErrMsg) const { 806 // FIXME: should work on directories also. 807 if (!si.isFile) { 808 return true; 809 } 810 811 HANDLE h = CreateFile(path.c_str(), 812 FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, 813 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 814 NULL, 815 OPEN_EXISTING, 816 FILE_ATTRIBUTE_NORMAL, 817 NULL); 818 if (h == INVALID_HANDLE_VALUE) 819 return true; 820 821 BY_HANDLE_FILE_INFORMATION bhfi; 822 if (!GetFileInformationByHandle(h, &bhfi)) { 823 DWORD err = GetLastError(); 824 CloseHandle(h); 825 SetLastError(err); 826 return MakeErrMsg(ErrMsg, path + ": GetFileInformationByHandle: "); 827 } 828 829 ULARGE_INTEGER ui; 830 ui.QuadPart = si.modTime.toWin32Time(); 831 FILETIME ft; 832 ft.dwLowDateTime = ui.LowPart; 833 ft.dwHighDateTime = ui.HighPart; 834 BOOL ret = SetFileTime(h, NULL, &ft, &ft); 835 DWORD err = GetLastError(); 836 CloseHandle(h); 837 if (!ret) { 838 SetLastError(err); 839 return MakeErrMsg(ErrMsg, path + ": SetFileTime: "); 840 } 841 842 // Best we can do with Unix permission bits is to interpret the owner 843 // writable bit. 844 if (si.mode & 0200) { 845 if (bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY) { 846 if (!SetFileAttributes(path.c_str(), 847 bhfi.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY)) 848 return MakeErrMsg(ErrMsg, path + ": SetFileAttributes: "); 849 } 850 } else { 851 if (!(bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) { 852 if (!SetFileAttributes(path.c_str(), 853 bhfi.dwFileAttributes | FILE_ATTRIBUTE_READONLY)) 854 return MakeErrMsg(ErrMsg, path + ": SetFileAttributes: "); 855 } 856 } 857 858 return false; 859} 860 861bool 862CopyFile(const sys::Path &Dest, const sys::Path &Src, std::string* ErrMsg) { 863 // Can't use CopyFile macro defined in Windows.h because it would mess up the 864 // above line. We use the expansion it would have in a non-UNICODE build. 865 if (!::CopyFileA(Src.c_str(), Dest.c_str(), false)) 866 return MakeErrMsg(ErrMsg, "Can't copy '" + Src.str() + 867 "' to '" + Dest.str() + "': "); 868 return false; 869} 870 871bool 872Path::makeUnique(bool reuse_current, std::string* ErrMsg) { 873 bool Exists; 874 if (reuse_current && (fs::exists(path, Exists) || !Exists)) 875 return false; // File doesn't exist already, just use it! 876 877 // Reserve space for -XXXXXX at the end. 878 char *FNBuffer = (char*) alloca(path.size()+8); 879 unsigned offset = path.size(); 880 path.copy(FNBuffer, offset); 881 882 // Find a numeric suffix that isn't used by an existing file. Assume there 883 // won't be more than 1 million files with the same prefix. Probably a safe 884 // bet. 885 static int FCounter = -1; 886 if (FCounter < 0) { 887 // Give arbitrary initial seed. 888 // FIXME: We should use sys::fs::unique_file() in future. 889 LARGE_INTEGER cnt64; 890 DWORD x = GetCurrentProcessId(); 891 x = (x << 16) | (x >> 16); 892 if (QueryPerformanceCounter(&cnt64)) // RDTSC 893 x ^= cnt64.HighPart ^ cnt64.LowPart; 894 FCounter = x % 1000000; 895 } 896 do { 897 sprintf(FNBuffer+offset, "-%06u", FCounter); 898 if (++FCounter > 999999) 899 FCounter = 0; 900 path = FNBuffer; 901 } while (!fs::exists(path, Exists) && Exists); 902 return false; 903} 904 905bool 906Path::createTemporaryFileOnDisk(bool reuse_current, std::string* ErrMsg) { 907 // Make this into a unique file name 908 makeUnique(reuse_current, ErrMsg); 909 910 // Now go and create it 911 HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW, 912 FILE_ATTRIBUTE_NORMAL, NULL); 913 if (h == INVALID_HANDLE_VALUE) 914 return MakeErrMsg(ErrMsg, path + ": can't create file"); 915 916 CloseHandle(h); 917 return false; 918} 919 920/// MapInFilePages - Not yet implemented on win32. 921const char *Path::MapInFilePages(int FD, size_t FileSize, off_t Offset) { 922 return 0; 923} 924 925/// MapInFilePages - Not yet implemented on win32. 926void Path::UnMapFilePages(const char *Base, size_t FileSize) { 927 assert(0 && "NOT IMPLEMENTED"); 928} 929 930} 931} 932