1 /* 2 * Copyright (c) 2014, Oracle and/or its affiliates. 3 * All rights reserved. Use is subject to license terms. 4 * 5 * This file is available and licensed under the following license: 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * - Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * - Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the distribution. 16 * - Neither the name of Oracle Corporation nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 34 #include "FilePath.h" 35 36 #include <algorithm> 37 #include <list> 38 39 #ifdef WINDOWS 40 #include <ShellAPI.h> 41 #endif //WINDOWS 42 43 #ifdef POSIX 44 #include <sys/stat.h> 45 #endif //POSIX 46 47 48 bool FilePath::FileExists(const TString FileName) { 49 bool result = false; 50 #ifdef WINDOWS 51 WIN32_FIND_DATA FindFileData; 52 HANDLE handle = FindFirstFile(FileName.data(), &FindFileData); 53 54 if (handle != INVALID_HANDLE_VALUE) { 55 if (FILE_ATTRIBUTE_DIRECTORY & FindFileData.dwFileAttributes) { 56 result = true; 57 } 58 else { 59 result = true; 60 } 61 62 FindClose(handle); 63 } 64 #endif //WINDOWS 65 #ifdef POSIX 66 struct stat buf; 67 68 if ((stat(StringToFileSystemString(FileName), &buf) == 0) && (S_ISREG(buf.st_mode) != 0)) { 69 result = true; 70 } 71 #endif //POSIX 72 return result; 73 } 74 75 bool FilePath::DirectoryExists(const TString DirectoryName) { 76 bool result = false; 77 #ifdef WINDOWS 78 WIN32_FIND_DATA FindFileData; 79 HANDLE handle = FindFirstFile(DirectoryName.data(), &FindFileData); 80 81 if (handle != INVALID_HANDLE_VALUE) { 82 if (FILE_ATTRIBUTE_DIRECTORY & FindFileData.dwFileAttributes) { 83 result = true; 84 } 85 86 FindClose(handle); 87 } 88 #endif //WINDOWS 89 #ifdef POSIX 90 struct stat buf; 91 92 if ((stat(StringToFileSystemString(DirectoryName), &buf) == 0) && (S_ISDIR(buf.st_mode) != 0)) { 93 result = true; 94 } 95 #endif //POSIX 96 return result; 97 } 98 99 #ifdef WINDOWS 100 std::string GetLastErrorAsString() { 101 //Get the error message, if any. 102 DWORD errorMessageID = ::GetLastError(); 103 104 if (errorMessageID == 0) { 105 return "No error message has been recorded"; 106 } 107 108 LPSTR messageBuffer = NULL; 109 size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 110 NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); 111 112 std::string message(messageBuffer, size); 113 114 // Free the buffer. 115 LocalFree(messageBuffer); 116 117 return message; 118 } 119 #endif //WINDOWS 120 121 bool FilePath::DeleteFile(const TString FileName) { 122 bool result = false; 123 124 if (FileExists(FileName) == true) { 125 #ifdef WINDOWS 126 TString lFileName = FileName; 127 FileAttributes attributes(lFileName); 128 129 if (attributes.Contains(faReadOnly) == true) { 130 attributes.Remove(faReadOnly); 131 } 132 133 result = ::DeleteFile(lFileName.data()) == TRUE; 134 #endif //WINDOWS 135 #ifdef POSIX 136 if (unlink(StringToFileSystemString(FileName)) == 0) { 137 result = true; 138 } 139 #endif //POSIX 140 } 141 142 return result; 143 } 144 145 bool FilePath::DeleteDirectory(const TString DirectoryName) { 146 bool result = false; 147 148 if (DirectoryExists(DirectoryName) == true) { 149 #ifdef WINDOWS 150 SHFILEOPSTRUCTW fos = {0}; 151 DynamicBuffer<TCHAR> lDirectoryName(DirectoryName.size() + 2); 152 memcpy(lDirectoryName.GetData(), DirectoryName.data(), (DirectoryName.size() + 2) * sizeof(TCHAR)); 153 lDirectoryName[DirectoryName.size() + 1] = NULL; // Double null terminate for SHFileOperation. 154 155 // Delete the folder and everything inside. 156 fos.wFunc = FO_DELETE; 157 fos.pFrom = lDirectoryName.GetData(); 158 fos.fFlags = FOF_NO_UI; 159 result = SHFileOperation(&fos) == 0; 160 #endif //WINDOWS 161 #ifdef POSIX 162 if (unlink(StringToFileSystemString(DirectoryName)) == 0) { 163 result = true; 164 } 165 #endif //POSIX 166 } 167 168 return result; 169 } 170 171 TString FilePath::IncludeTrailingSeparater(const TString value) { 172 TString result = value; 173 174 if (value.size() > 0) { 175 TString::iterator i = result.end(); 176 i--; 177 178 if (*i != TRAILING_PATHSEPARATOR) { 179 result += TRAILING_PATHSEPARATOR; 180 } 181 } 182 183 return result; 184 } 185 186 TString FilePath::IncludeTrailingSeparater(const char* value) { 187 TString lvalue = PlatformString(value).toString(); 188 return IncludeTrailingSeparater(lvalue); 189 } 190 191 TString FilePath::IncludeTrailingSeparater(const wchar_t* value) { 192 TString lvalue = PlatformString(value).toString(); 193 return IncludeTrailingSeparater(lvalue); 194 } 195 196 TString FilePath::ExtractFilePath(TString Path) { 197 #ifdef WINDOWS 198 TString result; 199 size_t slash = Path.find_last_of(TRAILING_PATHSEPARATOR); 200 if (slash != TString::npos) 201 result = Path.substr(0, slash); 202 return result; 203 #endif //WINDOWS 204 #ifdef POSIX 205 return dirname(StringToFileSystemString(Path)); 206 #endif //POSIX 207 } 208 209 TString FilePath::ExtractFileExt(TString Path) { 210 TString result; 211 size_t dot = Path.find_last_of('.'); 212 213 if (dot != TString::npos) { 214 result = Path.substr(dot, Path.size() - dot); 215 } 216 217 return result; 218 } 219 220 TString FilePath::ExtractFileName(TString Path) { 221 #ifdef WINDOWS 222 TString result; 223 224 size_t slash = Path.find_last_of(TRAILING_PATHSEPARATOR); 225 if (slash != TString::npos) 226 result = Path.substr(slash + 1, Path.size() - slash - 1); 227 228 return result; 229 #endif // WINDOWS 230 #ifdef POSIX 231 return basename(StringToFileSystemString(Path)); 232 #endif //POSIX 233 } 234 235 TString FilePath::ChangeFileExt(TString Path, TString Extension) { 236 TString result; 237 size_t dot = Path.find_last_of('.'); 238 239 if (dot != TString::npos) { 240 result = Path.substr(0, dot) + Extension; 241 } 242 243 if (result.empty() == true) { 244 result = Path; 245 } 246 247 return result; 248 } 249 250 TString FilePath::FixPathForPlatform(TString Path) { 251 TString result = Path; 252 std::replace(result.begin(), result.end(), BAD_TRAILING_PATHSEPARATOR, TRAILING_PATHSEPARATOR); 253 return result; 254 } 255 256 TString FilePath::FixPathSeparatorForPlatform(TString Path) { 257 TString result = Path; 258 std::replace(result.begin(), result.end(), BAD_PATH_SEPARATOR, PATH_SEPARATOR); 259 return result; 260 } 261 262 TString FilePath::PathSeparator() { 263 TString result; 264 result = PATH_SEPARATOR; 265 return result; 266 } 267 268 bool FilePath::CreateDirectory(TString Path, bool ownerOnly) { 269 bool result = false; 270 271 std::list<TString> paths; 272 TString lpath = Path; 273 274 while (lpath.empty() == false && DirectoryExists(lpath) == false) { 275 paths.push_front(lpath); 276 lpath = ExtractFilePath(lpath); 277 } 278 279 for (std::list<TString>::iterator iterator = paths.begin(); iterator != paths.end(); iterator++) { 280 lpath = *iterator; 281 282 #ifdef WINDOWS 283 if (_wmkdir(lpath.data()) == 0) { 284 #endif // WINDOWS 285 #ifdef POSIX 286 mode_t mode = S_IRWXU; 287 if (!ownerOnly) { 288 mode |= S_IRWXG | S_IROTH | S_IXOTH; 289 } 290 if (mkdir(StringToFileSystemString(lpath), mode) == 0) { 291 #endif //POSIX 292 result = true; 293 } 294 else { 295 result = false; 296 break; 297 } 298 } 299 300 return result; 301 } 302 303 void FilePath::ChangePermissions(TString FileName, bool ownerOnly) { 304 #ifdef POSIX 305 mode_t mode = S_IRWXU; 306 if (!ownerOnly) { 307 mode |= S_IRWXG | S_IROTH | S_IXOTH; 308 } 309 chmod(FileName.data(), mode); 310 #endif // POSIX 311 } 312 313 //-------------------------------------------------------------------------------------------------- 314 315 #include <algorithm> 316 317 FileAttributes::FileAttributes(const TString FileName, bool FollowLink) { 318 FFileName = FileName; 319 FFollowLink = FollowLink; 320 ReadAttributes(); 321 } 322 323 bool FileAttributes::WriteAttributes() { 324 bool result = false; 325 326 #ifdef WINDOWS 327 DWORD attributes = 0; 328 329 for (std::vector<FileAttribute>::const_iterator iterator = FAttributes.begin(); 330 iterator != FAttributes.end(); iterator++) { 331 switch (*iterator) { 332 case faArchive: { 333 attributes = attributes & FILE_ATTRIBUTE_ARCHIVE; 334 break; 335 } 336 case faCompressed: { 337 attributes = attributes & FILE_ATTRIBUTE_COMPRESSED; 338 break; 339 } 340 case faDevice: { 341 attributes = attributes & FILE_ATTRIBUTE_DEVICE; 342 break; 343 } 344 case faDirectory: { 345 attributes = attributes & FILE_ATTRIBUTE_DIRECTORY; 346 break; 347 } 348 case faEncrypted: { 349 attributes = attributes & FILE_ATTRIBUTE_ENCRYPTED; 350 break; 351 } 352 case faHidden: { 353 attributes = attributes & FILE_ATTRIBUTE_HIDDEN; 354 break; 355 } 356 // case faIntegrityStream: { 357 // attributes = attributes & FILE_ATTRIBUTE_INTEGRITY_STREAM; 358 // break; 359 // } 360 case faNormal: { 361 attributes = attributes & FILE_ATTRIBUTE_NORMAL; 362 break; 363 } 364 case faNotContentIndexed: { 365 attributes = attributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED; 366 break; 367 } 368 // case faNoScrubData: { 369 // attributes = attributes & FILE_ATTRIBUTE_NO_SCRUB_DATA; 370 // break; 371 // } 372 case faOffline: { 373 attributes = attributes & FILE_ATTRIBUTE_OFFLINE; 374 break; 375 } 376 case faSystem: { 377 attributes = attributes & FILE_ATTRIBUTE_SYSTEM; 378 break; 379 } 380 case faSymbolicLink: { 381 attributes = attributes & FILE_ATTRIBUTE_REPARSE_POINT; 382 break; 383 } 384 case faSparceFile: { 385 attributes = attributes & FILE_ATTRIBUTE_SPARSE_FILE; 386 break; 387 } 388 case faReadOnly: { 389 attributes = attributes & FILE_ATTRIBUTE_READONLY; 390 break; 391 } 392 case faTemporary: { 393 attributes = attributes & FILE_ATTRIBUTE_TEMPORARY; 394 break; 395 } 396 case faVirtual: { 397 attributes = attributes & FILE_ATTRIBUTE_VIRTUAL; 398 break; 399 } 400 } 401 } 402 403 if (::SetFileAttributes(FFileName.data(), attributes) != 0) { 404 result = true; 405 } 406 #endif // WINDOWS 407 #ifdef POSIX 408 mode_t attributes = 0; 409 410 for (std::vector<FileAttribute>::const_iterator iterator = FAttributes.begin(); 411 iterator != FAttributes.end(); iterator++) { 412 switch (*iterator) { 413 case faBlockSpecial: { 414 attributes |= S_IFBLK; 415 break; 416 } 417 case faCharacterSpecial: { 418 attributes |= S_IFCHR; 419 break; 420 } 421 case faFIFOSpecial: { 422 attributes |= S_IFIFO; 423 break; 424 } 425 case faNormal: { 426 attributes |= S_IFREG; 427 break; 428 } 429 case faDirectory: { 430 attributes |= S_IFDIR; 431 break; 432 } 433 case faSymbolicLink: { 434 attributes |= S_IFLNK; 435 break; 436 } 437 case faSocket: { 438 attributes |= S_IFSOCK; 439 break; 440 } 441 442 // Owner 443 case faReadOnly: { 444 attributes |= S_IRUSR; 445 break; 446 } 447 case faWriteOnly: { 448 attributes |= S_IWUSR; 449 break; 450 } 451 case faReadWrite: { 452 attributes |= S_IRUSR; 453 attributes |= S_IWUSR; 454 break; 455 } 456 case faExecute: { 457 attributes |= S_IXUSR; 458 break; 459 } 460 461 // Group 462 case faGroupReadOnly: { 463 attributes |= S_IRGRP; 464 break; 465 } 466 case faGroupWriteOnly: { 467 attributes |= S_IWGRP; 468 break; 469 } 470 case faGroupReadWrite: { 471 attributes |= S_IRGRP; 472 attributes |= S_IWGRP; 473 break; 474 } 475 case faGroupExecute: { 476 attributes |= S_IXGRP; 477 break; 478 } 479 480 // Others 481 case faOthersReadOnly: { 482 attributes |= S_IROTH; 483 break; 484 } 485 case faOthersWriteOnly: { 486 attributes |= S_IWOTH; 487 break; 488 } 489 case faOthersReadWrite: { 490 attributes |= S_IROTH; 491 attributes |= S_IWOTH; 492 break; 493 } 494 case faOthersExecute: { 495 attributes |= S_IXOTH; 496 break; 497 } 498 } 499 } 500 501 if (chmod(FFileName.data(), attributes) == 0) { 502 result = true; 503 } 504 #endif //POSIX 505 506 return result; 507 } 508 509 #define S_ISRUSR(m) (((m) & S_IRWXU) == S_IRUSR) 510 #define S_ISWUSR(m) (((m) & S_IRWXU) == S_IWUSR) 511 #define S_ISXUSR(m) (((m) & S_IRWXU) == S_IXUSR) 512 513 #define S_ISRGRP(m) (((m) & S_IRWXG) == S_IRGRP) 514 #define S_ISWGRP(m) (((m) & S_IRWXG) == S_IWGRP) 515 #define S_ISXGRP(m) (((m) & S_IRWXG) == S_IXGRP) 516 517 #define S_ISROTH(m) (((m) & S_IRWXO) == S_IROTH) 518 #define S_ISWOTH(m) (((m) & S_IRWXO) == S_IWOTH) 519 #define S_ISXOTH(m) (((m) & S_IRWXO) == S_IXOTH) 520 521 bool FileAttributes::ReadAttributes() { 522 bool result = false; 523 524 #ifdef WINDOWS 525 DWORD attributes = ::GetFileAttributes(FFileName.data()); 526 527 if (attributes != INVALID_FILE_ATTRIBUTES) { 528 result = true; 529 530 if (attributes | FILE_ATTRIBUTE_ARCHIVE) { FAttributes.push_back(faArchive); } 531 if (attributes | FILE_ATTRIBUTE_COMPRESSED) { FAttributes.push_back(faCompressed); } 532 if (attributes | FILE_ATTRIBUTE_DEVICE) { FAttributes.push_back(faDevice); } 533 if (attributes | FILE_ATTRIBUTE_DIRECTORY) { FAttributes.push_back(faDirectory); } 534 if (attributes | FILE_ATTRIBUTE_ENCRYPTED) { FAttributes.push_back(faEncrypted); } 535 if (attributes | FILE_ATTRIBUTE_HIDDEN) { FAttributes.push_back(faHidden); } 536 //if (attributes | FILE_ATTRIBUTE_INTEGRITY_STREAM) { FAttributes.push_back(faIntegrityStream); } 537 if (attributes | FILE_ATTRIBUTE_NORMAL) { FAttributes.push_back(faNormal); } 538 if (attributes | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) { FAttributes.push_back(faNotContentIndexed); } 539 //if (attributes | FILE_ATTRIBUTE_NO_SCRUB_DATA) { FAttributes.push_back(faNoScrubData); } 540 if (attributes | FILE_ATTRIBUTE_SYSTEM) { FAttributes.push_back(faSystem); } 541 if (attributes | FILE_ATTRIBUTE_OFFLINE) { FAttributes.push_back(faOffline); } 542 if (attributes | FILE_ATTRIBUTE_REPARSE_POINT) { FAttributes.push_back(faSymbolicLink); } 543 if (attributes | FILE_ATTRIBUTE_SPARSE_FILE) { FAttributes.push_back(faSparceFile); } 544 if (attributes | FILE_ATTRIBUTE_READONLY ) { FAttributes.push_back(faReadOnly); } 545 if (attributes | FILE_ATTRIBUTE_TEMPORARY) { FAttributes.push_back(faTemporary); } 546 if (attributes | FILE_ATTRIBUTE_VIRTUAL) { FAttributes.push_back(faVirtual); } 547 } 548 #endif // WINDOWS 549 #ifdef POSIX 550 struct stat status; 551 552 if (stat(StringToFileSystemString(FFileName), &status) == 0) { 553 result = true; 554 555 if (S_ISBLK(status.st_mode) != 0) { FAttributes.push_back(faBlockSpecial); } 556 if (S_ISCHR(status.st_mode) != 0) { FAttributes.push_back(faCharacterSpecial); } 557 if (S_ISFIFO(status.st_mode) != 0) { FAttributes.push_back(faFIFOSpecial); } 558 if (S_ISREG(status.st_mode) != 0) { FAttributes.push_back(faNormal); } 559 if (S_ISDIR(status.st_mode) != 0) { FAttributes.push_back(faDirectory); } 560 if (S_ISLNK(status.st_mode) != 0) { FAttributes.push_back(faSymbolicLink); } 561 if (S_ISSOCK(status.st_mode) != 0) { FAttributes.push_back(faSocket); } 562 563 // Owner 564 if (S_ISRUSR(status.st_mode) != 0) { 565 if (S_ISWUSR(status.st_mode) != 0) { FAttributes.push_back(faReadWrite); } 566 else { FAttributes.push_back(faReadOnly); } 567 } 568 else if (S_ISWUSR(status.st_mode) != 0) { FAttributes.push_back(faWriteOnly); } 569 570 if (S_ISXUSR(status.st_mode) != 0) { FAttributes.push_back(faExecute); } 571 572 // Group 573 if (S_ISRGRP(status.st_mode) != 0) { 574 if (S_ISWGRP(status.st_mode) != 0) { FAttributes.push_back(faGroupReadWrite); } 575 else { FAttributes.push_back(faGroupReadOnly); } 576 } 577 else if (S_ISWGRP(status.st_mode) != 0) { FAttributes.push_back(faGroupWriteOnly); } 578 579 if (S_ISXGRP(status.st_mode) != 0) { FAttributes.push_back(faGroupExecute); } 580 581 582 // Others 583 if (S_ISROTH(status.st_mode) != 0) { 584 if (S_ISWOTH(status.st_mode) != 0) { FAttributes.push_back(faOthersReadWrite); } 585 else { FAttributes.push_back(faOthersReadOnly); } 586 } 587 else if (S_ISWOTH(status.st_mode) != 0) { FAttributes.push_back(faOthersWriteOnly); } 588 589 if (S_ISXOTH(status.st_mode) != 0) { FAttributes.push_back(faOthersExecute); } 590 591 if (FFileName.size() > 0 && FFileName[0] == '.') { 592 FAttributes.push_back(faHidden); 593 } 594 } 595 #endif //POSIX 596 597 return result; 598 } 599 600 bool FileAttributes::Valid(const FileAttribute Value) { 601 bool result = false; 602 603 switch (Value) { 604 #ifdef WINDOWS 605 case faHidden: 606 #endif // WINDOWS 607 #ifdef POSIX 608 case faReadWrite: 609 case faWriteOnly: 610 case faExecute: 611 612 case faGroupReadWrite: 613 case faGroupWriteOnly: 614 case faGroupReadOnly: 615 case faGroupExecute: 616 617 case faOthersReadWrite: 618 case faOthersWriteOnly: 619 case faOthersReadOnly: 620 case faOthersExecute: 621 #endif //POSIX 622 623 case faReadOnly: { 624 result = true; 625 break; 626 } 627 } 628 629 return result; 630 } 631 632 void FileAttributes::Append(FileAttribute Value) { 633 if (Valid(Value) == true) { 634 #ifdef POSIX 635 if ((Value == faReadOnly && Contains(faWriteOnly) == true) || 636 (Value == faWriteOnly && Contains(faReadOnly) == true)) { 637 Value = faReadWrite; 638 } 639 #endif //POSIX 640 641 FAttributes.push_back(Value); 642 WriteAttributes(); 643 } 644 } 645 646 bool FileAttributes::Contains(FileAttribute Value) { 647 bool result = false; 648 649 std::vector<FileAttribute>::const_iterator iterator = std::find(FAttributes.begin(), FAttributes.end(), Value); 650 651 if (iterator != FAttributes.end()) { 652 result = true; 653 } 654 655 return result; 656 } 657 658 void FileAttributes::Remove(FileAttribute Value) { 659 if (Valid(Value) == true) { 660 #ifdef POSIX 661 if (Value == faReadOnly && Contains(faReadWrite) == true) { 662 Append(faWriteOnly); 663 Remove(faReadWrite); 664 } 665 else if (Value == faWriteOnly && Contains(faReadWrite) == true) { 666 Append(faReadOnly); 667 Remove(faReadWrite); 668 } 669 #endif //POSIX 670 671 std::vector<FileAttribute>::iterator iterator = std::find(FAttributes.begin(), FAttributes.end(), Value); 672 673 if (iterator != FAttributes.end()) { 674 FAttributes.erase(iterator); 675 WriteAttributes(); 676 } 677 } 678 }