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 37 static void FlipBackSlashes(std::string& s) { 38 for (size_t i = 0; i < s.size(); i++) 39 if (s[i] == '\\') 40 s[i] = '/'; 41 } 42 43 namespace llvm { 44 namespace sys { 45 46 const char PathSeparator = ';'; 47 48 StringRef Path::GetEXESuffix() { 49 return "exe"; 50 } 51 52 Path::Path(llvm::StringRef p) 53 : path(p) { 54 FlipBackSlashes(path); 55 } 56 57 Path::Path(const char *StrStart, unsigned StrLen) 58 : path(StrStart, StrLen) { 59 FlipBackSlashes(path); 60 } 61 62 Path& 63 Path::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. 70 struct 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 81 bool 82 Path::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 144 void 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 163 bool 164 Path::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 181 bool 182 Path::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 196 static Path *TempDirectory; 197 198 Path 199 Path::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. 234 Path 235 Path::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 241 void 242 Path::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 271 void 272 Path::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 288 Path 289 Path::GetLLVMDefaultConfigDir() { 290 Path ret = GetUserHomeDirectory(); 291 if (!ret.appendComponent(".llvm")) 292 assert(0 && "Failed to append .llvm"); 293 return ret; 294 } 295 296 Path 297 Path::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 309 Path 310 Path::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. 318 Path 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 328 StringRef Path::getDirname() const { 329 return getDirnameCharSep(path, "/"); 330 } 331 332 StringRef 333 Path::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 348 StringRef 349 Path::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 364 bool 365 Path::exists() const { 366 DWORD attr = GetFileAttributes(path.c_str()); 367 return attr != INVALID_FILE_ATTRIBUTES; 368 } 369 370 bool 371 Path::isDirectory() const { 372 DWORD attr = GetFileAttributes(path.c_str()); 373 return (attr != INVALID_FILE_ATTRIBUTES) && 374 (attr & FILE_ATTRIBUTE_DIRECTORY); 375 } 376 377 bool 378 Path::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 390 bool 391 Path::canRead() const { 392 // FIXME: take security attributes into account. 393 DWORD attr = GetFileAttributes(path.c_str()); 394 return attr != INVALID_FILE_ATTRIBUTES; 395 } 396 397 bool 398 Path::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 404 bool 405 Path::canExecute() const { 406 // FIXME: take security attributes into account. 407 DWORD attr = GetFileAttributes(path.c_str()); 408 return attr != INVALID_FILE_ATTRIBUTES; 409 } 410 411 bool 412 Path::isRegularFile() const { 413 bool res; 414 if (fs::is_regular_file(path, res)) 415 return false; 416 return res; 417 } 418 419 StringRef 420 Path::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 436 const FileStatus * 437 PathWithStatus::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 472 bool Path::makeReadableOnDisk(std::string* ErrMsg) { 473 // All files are readable on Windows (ignoring security attributes). 474 return false; 475 } 476 477 bool 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 493 bool Path::makeExecutableOnDisk(std::string* ErrMsg) { 494 // All files are executable on Windows (ignoring security attributes). 495 return false; 496 } 497 498 bool 499 Path::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 546 bool 547 Path::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 560 bool 561 Path::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 578 bool 579 Path::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 592 bool 593 Path::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 610 inline 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 616 bool 617 Path::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 678 bool 679 Path::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 690 bool 691 Path::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 771 bool 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 796 bool 797 Path::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 804 bool 805 Path::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 861 bool 862 CopyFile(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 871 bool 872 Path::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 905 bool 906 Path::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. 921 const char *Path::MapInFilePages(int FD, size_t FileSize, off_t Offset) { 922 return 0; 923 } 924 925 /// MapInFilePages - Not yet implemented on win32. 926 void Path::UnMapFilePages(const char *Base, size_t FileSize) { 927 assert(0 && "NOT IMPLEMENTED"); 928 } 929 930 } 931 } 932