1// Windows/FileDir.cpp 2 3#include "StdAfx.h" 4 5 6#ifndef _WIN32 7#include <stdio.h> 8#include <stdlib.h> 9#include <errno.h> 10#include <limits.h> 11#include <unistd.h> 12#include <time.h> 13#include <utime.h> 14#include <fcntl.h> 15#include <sys/stat.h> 16#include <sys/types.h> 17 18#include "../Common/StringConvert.h" 19#include "../Common/C_FileIO.h" 20#endif 21 22#include "FileDir.h" 23#include "FileFind.h" 24#include "FileName.h" 25 26#ifndef _UNICODE 27extern bool g_IsNT; 28#endif 29 30using namespace NWindows; 31using namespace NFile; 32using namespace NName; 33 34#ifndef _WIN32 35 36static bool FiTime_To_timespec(const CFiTime *ft, timespec &ts) 37{ 38 if (ft) 39 { 40 ts = *ft; 41 return true; 42 } 43 // else 44 { 45 ts.tv_sec = 0; 46 ts.tv_nsec = 47 #ifdef UTIME_OMIT 48 UTIME_OMIT; // -2 keep old timesptamp 49 #else 50 // UTIME_NOW; -1 // set to the current time 51 0; 52 #endif 53 return false; 54 } 55} 56#endif 57 58namespace NWindows { 59namespace NFile { 60namespace NDir { 61 62#ifdef _WIN32 63 64#ifndef UNDER_CE 65 66bool GetWindowsDir(FString &path) 67{ 68 const unsigned kBufSize = MAX_PATH + 16; 69 UINT len; 70 #ifndef _UNICODE 71 if (!g_IsNT) 72 { 73 TCHAR s[kBufSize + 1]; 74 s[0] = 0; 75 len = ::GetWindowsDirectory(s, kBufSize); 76 path = fas2fs(s); 77 } 78 else 79 #endif 80 { 81 WCHAR s[kBufSize + 1]; 82 s[0] = 0; 83 len = ::GetWindowsDirectoryW(s, kBufSize); 84 path = us2fs(s); 85 } 86 return (len != 0 && len < kBufSize); 87} 88 89 90/* 91new DOCs for GetSystemDirectory: 92 returned path does not end with a backslash unless the 93 system directory is the root directory. 94*/ 95 96bool GetSystemDir(FString &path) 97{ 98 const unsigned kBufSize = MAX_PATH + 16; 99 UINT len; 100 #ifndef _UNICODE 101 if (!g_IsNT) 102 { 103 TCHAR s[kBufSize + 1]; 104 s[0] = 0; 105 len = ::GetSystemDirectory(s, kBufSize); 106 path = fas2fs(s); 107 } 108 else 109 #endif 110 { 111 WCHAR s[kBufSize + 1]; 112 s[0] = 0; 113 len = ::GetSystemDirectoryW(s, kBufSize); 114 path = us2fs(s); 115 } 116 return (len != 0 && len < kBufSize); 117} 118#endif // UNDER_CE 119 120 121bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime) 122{ 123 #ifndef _UNICODE 124 if (!g_IsNT) 125 { 126 ::SetLastError(ERROR_CALL_NOT_IMPLEMENTED); 127 return false; 128 } 129 #endif 130 131 HANDLE hDir = INVALID_HANDLE_VALUE; 132 IF_USE_MAIN_PATH 133 hDir = ::CreateFileW(fs2us(path), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 134 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 135 #ifdef Z7_LONG_PATH 136 if (hDir == INVALID_HANDLE_VALUE && USE_SUPER_PATH) 137 { 138 UString superPath; 139 if (GetSuperPath(path, superPath, USE_MAIN_PATH)) 140 hDir = ::CreateFileW(superPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 141 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 142 } 143 #endif 144 145 bool res = false; 146 if (hDir != INVALID_HANDLE_VALUE) 147 { 148 res = BOOLToBool(::SetFileTime(hDir, cTime, aTime, mTime)); 149 ::CloseHandle(hDir); 150 } 151 return res; 152} 153 154 155 156bool SetFileAttrib(CFSTR path, DWORD attrib) 157{ 158 #ifndef _UNICODE 159 if (!g_IsNT) 160 { 161 if (::SetFileAttributes(fs2fas(path), attrib)) 162 return true; 163 } 164 else 165 #endif 166 { 167 IF_USE_MAIN_PATH 168 if (::SetFileAttributesW(fs2us(path), attrib)) 169 return true; 170 #ifdef Z7_LONG_PATH 171 if (USE_SUPER_PATH) 172 { 173 UString superPath; 174 if (GetSuperPath(path, superPath, USE_MAIN_PATH)) 175 return BOOLToBool(::SetFileAttributesW(superPath, attrib)); 176 } 177 #endif 178 } 179 return false; 180} 181 182 183bool SetFileAttrib_PosixHighDetect(CFSTR path, DWORD attrib) 184{ 185 #ifdef _WIN32 186 if ((attrib & 0xF0000000) != 0) 187 attrib &= 0x3FFF; 188 #endif 189 return SetFileAttrib(path, attrib); 190} 191 192 193bool RemoveDir(CFSTR path) 194{ 195 #ifndef _UNICODE 196 if (!g_IsNT) 197 { 198 if (::RemoveDirectory(fs2fas(path))) 199 return true; 200 } 201 else 202 #endif 203 { 204 IF_USE_MAIN_PATH 205 if (::RemoveDirectoryW(fs2us(path))) 206 return true; 207 #ifdef Z7_LONG_PATH 208 if (USE_SUPER_PATH) 209 { 210 UString superPath; 211 if (GetSuperPath(path, superPath, USE_MAIN_PATH)) 212 return BOOLToBool(::RemoveDirectoryW(superPath)); 213 } 214 #endif 215 } 216 return false; 217} 218 219 220bool MyMoveFile(CFSTR oldFile, CFSTR newFile) 221{ 222 #ifndef _UNICODE 223 if (!g_IsNT) 224 { 225 if (::MoveFile(fs2fas(oldFile), fs2fas(newFile))) 226 return true; 227 } 228 else 229 #endif 230 { 231 IF_USE_MAIN_PATH_2(oldFile, newFile) 232 { 233 if (::MoveFileW(fs2us(oldFile), fs2us(newFile))) 234 return true; 235 } 236 #ifdef Z7_LONG_PATH 237 if (USE_SUPER_PATH_2) 238 { 239 UString d1, d2; 240 if (GetSuperPaths(oldFile, newFile, d1, d2, USE_MAIN_PATH_2)) 241 return BOOLToBool(::MoveFileW(d1, d2)); 242 } 243 #endif 244 } 245 return false; 246} 247 248#ifndef UNDER_CE 249EXTERN_C_BEGIN 250typedef BOOL (WINAPI *Func_CreateHardLinkW)( 251 LPCWSTR lpFileName, 252 LPCWSTR lpExistingFileName, 253 LPSECURITY_ATTRIBUTES lpSecurityAttributes 254 ); 255EXTERN_C_END 256#endif // UNDER_CE 257 258bool MyCreateHardLink(CFSTR newFileName, CFSTR existFileName) 259{ 260 #ifndef _UNICODE 261 if (!g_IsNT) 262 { 263 SetLastError(ERROR_CALL_NOT_IMPLEMENTED); 264 return false; 265 /* 266 if (::CreateHardLink(fs2fas(newFileName), fs2fas(existFileName), NULL)) 267 return true; 268 */ 269 } 270 else 271 #endif 272 { 273 const 274 Func_CreateHardLinkW 275 my_CreateHardLinkW = Z7_GET_PROC_ADDRESS( 276 Func_CreateHardLinkW, ::GetModuleHandleW(L"kernel32.dll"), 277 "CreateHardLinkW"); 278 if (!my_CreateHardLinkW) 279 return false; 280 IF_USE_MAIN_PATH_2(newFileName, existFileName) 281 { 282 if (my_CreateHardLinkW(fs2us(newFileName), fs2us(existFileName), NULL)) 283 return true; 284 } 285 #ifdef Z7_LONG_PATH 286 if (USE_SUPER_PATH_2) 287 { 288 UString d1, d2; 289 if (GetSuperPaths(newFileName, existFileName, d1, d2, USE_MAIN_PATH_2)) 290 return BOOLToBool(my_CreateHardLinkW(d1, d2, NULL)); 291 } 292 #endif 293 } 294 return false; 295} 296 297 298/* 299WinXP-64 CreateDir(): 300 "" - ERROR_PATH_NOT_FOUND 301 \ - ERROR_ACCESS_DENIED 302 C:\ - ERROR_ACCESS_DENIED, if there is such drive, 303 304 D:\folder - ERROR_PATH_NOT_FOUND, if there is no such drive, 305 C:\nonExistent\folder - ERROR_PATH_NOT_FOUND 306 307 C:\existFolder - ERROR_ALREADY_EXISTS 308 C:\existFolder\ - ERROR_ALREADY_EXISTS 309 310 C:\folder - OK 311 C:\folder\ - OK 312 313 \\Server\nonExistent - ERROR_BAD_NETPATH 314 \\Server\Share_Readonly - ERROR_ACCESS_DENIED 315 \\Server\Share - ERROR_ALREADY_EXISTS 316 317 \\Server\Share_NTFS_drive - ERROR_ACCESS_DENIED 318 \\Server\Share_FAT_drive - ERROR_ALREADY_EXISTS 319*/ 320 321bool CreateDir(CFSTR path) 322{ 323 #ifndef _UNICODE 324 if (!g_IsNT) 325 { 326 if (::CreateDirectory(fs2fas(path), NULL)) 327 return true; 328 } 329 else 330 #endif 331 { 332 IF_USE_MAIN_PATH 333 if (::CreateDirectoryW(fs2us(path), NULL)) 334 return true; 335 #ifdef Z7_LONG_PATH 336 if ((!USE_MAIN_PATH || ::GetLastError() != ERROR_ALREADY_EXISTS) && USE_SUPER_PATH) 337 { 338 UString superPath; 339 if (GetSuperPath(path, superPath, USE_MAIN_PATH)) 340 return BOOLToBool(::CreateDirectoryW(superPath, NULL)); 341 } 342 #endif 343 } 344 return false; 345} 346 347/* 348 CreateDir2 returns true, if directory can contain files after the call (two cases): 349 1) the directory already exists 350 2) the directory was created 351 path must be WITHOUT trailing path separator. 352 353 We need CreateDir2, since fileInfo.Find() for reserved names like "com8" 354 returns FILE instead of DIRECTORY. And we need to use SuperPath */ 355 356static bool CreateDir2(CFSTR path) 357{ 358 #ifndef _UNICODE 359 if (!g_IsNT) 360 { 361 if (::CreateDirectory(fs2fas(path), NULL)) 362 return true; 363 } 364 else 365 #endif 366 { 367 IF_USE_MAIN_PATH 368 if (::CreateDirectoryW(fs2us(path), NULL)) 369 return true; 370 #ifdef Z7_LONG_PATH 371 if ((!USE_MAIN_PATH || ::GetLastError() != ERROR_ALREADY_EXISTS) && USE_SUPER_PATH) 372 { 373 UString superPath; 374 if (GetSuperPath(path, superPath, USE_MAIN_PATH)) 375 { 376 if (::CreateDirectoryW(superPath, NULL)) 377 return true; 378 if (::GetLastError() != ERROR_ALREADY_EXISTS) 379 return false; 380 NFind::CFileInfo fi; 381 if (!fi.Find(us2fs(superPath))) 382 return false; 383 return fi.IsDir(); 384 } 385 } 386 #endif 387 } 388 if (::GetLastError() != ERROR_ALREADY_EXISTS) 389 return false; 390 NFind::CFileInfo fi; 391 if (!fi.Find(path)) 392 return false; 393 return fi.IsDir(); 394} 395 396#endif // _WIN32 397 398static bool CreateDir2(CFSTR path); 399 400bool CreateComplexDir(CFSTR _path) 401{ 402 #ifdef _WIN32 403 404 { 405 const DWORD attrib = NFind::GetFileAttrib(_path); 406 if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0) 407 return true; 408 } 409 410 #ifndef UNDER_CE 411 412 if (IsDriveRootPath_SuperAllowed(_path)) 413 return false; 414 415 const unsigned prefixSize = GetRootPrefixSize(_path); 416 417 #endif // UNDER_CE 418 419 #else // _WIN32 420 421 // Posix 422 NFind::CFileInfo fi; 423 if (fi.Find(_path)) 424 { 425 if (fi.IsDir()) 426 return true; 427 } 428 429 #endif // _WIN32 430 431 FString path (_path); 432 433 int pos = path.ReverseFind_PathSepar(); 434 if (pos >= 0 && (unsigned)pos == path.Len() - 1) 435 { 436 if (path.Len() == 1) 437 return true; 438 path.DeleteBack(); 439 } 440 441 const FString path2 (path); 442 pos = (int)path.Len(); 443 444 for (;;) 445 { 446 if (CreateDir2(path)) 447 break; 448 if (::GetLastError() == ERROR_ALREADY_EXISTS) 449 return false; 450 pos = path.ReverseFind_PathSepar(); 451 if (pos < 0 || pos == 0) 452 return false; 453 454 #if defined(_WIN32) && !defined(UNDER_CE) 455 if (pos == 1 && IS_PATH_SEPAR(path[0])) 456 return false; 457 if (prefixSize >= (unsigned)pos + 1) 458 return false; 459 #endif 460 461 path.DeleteFrom((unsigned)pos); 462 } 463 464 while (pos < (int)path2.Len()) 465 { 466 int pos2 = NName::FindSepar(path2.Ptr((unsigned)pos + 1)); 467 if (pos2 < 0) 468 pos = (int)path2.Len(); 469 else 470 pos += 1 + pos2; 471 path.SetFrom(path2, (unsigned)pos); 472 if (!CreateDir(path)) 473 return false; 474 } 475 476 return true; 477} 478 479 480#ifdef _WIN32 481 482bool DeleteFileAlways(CFSTR path) 483{ 484 /* If alt stream, we also need to clear READ-ONLY attribute of main file before delete. 485 SetFileAttrib("name:stream", ) changes attributes of main file. */ 486 { 487 DWORD attrib = NFind::GetFileAttrib(path); 488 if (attrib != INVALID_FILE_ATTRIBUTES 489 && (attrib & FILE_ATTRIBUTE_DIRECTORY) == 0 490 && (attrib & FILE_ATTRIBUTE_READONLY) != 0) 491 { 492 if (!SetFileAttrib(path, attrib & ~(DWORD)FILE_ATTRIBUTE_READONLY)) 493 return false; 494 } 495 } 496 497 #ifndef _UNICODE 498 if (!g_IsNT) 499 { 500 if (::DeleteFile(fs2fas(path))) 501 return true; 502 } 503 else 504 #endif 505 { 506 /* DeleteFile("name::$DATA") deletes all alt streams (same as delete DeleteFile("name")). 507 Maybe it's better to open "name::$DATA" and clear data for unnamed stream? */ 508 IF_USE_MAIN_PATH 509 if (::DeleteFileW(fs2us(path))) 510 return true; 511 #ifdef Z7_LONG_PATH 512 if (USE_SUPER_PATH) 513 { 514 UString superPath; 515 if (GetSuperPath(path, superPath, USE_MAIN_PATH)) 516 return BOOLToBool(::DeleteFileW(superPath)); 517 } 518 #endif 519 } 520 return false; 521} 522 523 524 525bool RemoveDirWithSubItems(const FString &path) 526{ 527 bool needRemoveSubItems = true; 528 { 529 NFind::CFileInfo fi; 530 if (!fi.Find(path)) 531 return false; 532 if (!fi.IsDir()) 533 { 534 ::SetLastError(ERROR_DIRECTORY); 535 return false; 536 } 537 if (fi.HasReparsePoint()) 538 needRemoveSubItems = false; 539 } 540 541 if (needRemoveSubItems) 542 { 543 FString s (path); 544 s.Add_PathSepar(); 545 const unsigned prefixSize = s.Len(); 546 NFind::CEnumerator enumerator; 547 enumerator.SetDirPrefix(s); 548 NFind::CDirEntry fi; 549 bool isError = false; 550 DWORD lastError = 0; 551 while (enumerator.Next(fi)) 552 { 553 s.DeleteFrom(prefixSize); 554 s += fi.Name; 555 if (fi.IsDir()) 556 { 557 if (!RemoveDirWithSubItems(s)) 558 { 559 lastError = GetLastError(); 560 isError = true; 561 } 562 } 563 else if (!DeleteFileAlways(s)) 564 { 565 lastError = GetLastError(); 566 isError = false; 567 } 568 } 569 if (isError) 570 { 571 SetLastError(lastError); 572 return false; 573 } 574 } 575 576 // we clear read-only attrib to remove read-only dir 577 if (!SetFileAttrib(path, 0)) 578 return false; 579 return RemoveDir(path); 580} 581 582#endif // _WIN32 583 584#ifdef UNDER_CE 585 586bool MyGetFullPathName(CFSTR path, FString &resFullPath) 587{ 588 resFullPath = path; 589 return true; 590} 591 592#else 593 594bool MyGetFullPathName(CFSTR path, FString &resFullPath) 595{ 596 return GetFullPath(path, resFullPath); 597} 598 599#ifdef _WIN32 600 601/* Win10: SetCurrentDirectory() doesn't support long paths and 602 doesn't support super prefix "\\?\", if long path behavior is not 603 enabled in registry (LongPathsEnabled) and in manifest (longPathAware). */ 604 605bool SetCurrentDir(CFSTR path) 606{ 607 #ifndef _UNICODE 608 if (!g_IsNT) 609 { 610 return BOOLToBool(::SetCurrentDirectory(fs2fas(path))); 611 } 612 else 613 #endif 614 { 615 return BOOLToBool(::SetCurrentDirectoryW(fs2us(path))); 616 } 617} 618 619 620/* 621we use system function GetCurrentDirectory() 622new GetCurrentDirectory() DOCs: 623 - If the function fails, the return value is zero. 624 - If the function succeeds, the return value specifies 625 the number of characters that are written to the buffer, 626 not including the terminating null character. 627 - If the buffer is not large enough, the return value specifies 628 the required size of the buffer, in characters, 629 including the null-terminating character. 630 631GetCurrentDir() calls GetCurrentDirectory(). 632GetCurrentDirectory() in win10 in tests: 633 the returned (path) does not end with a backslash, if 634 current directory is not root directory of drive. 635 But that behavior is not guarantied in specification docs. 636*/ 637 638bool GetCurrentDir(FString &path) 639{ 640 const unsigned kBufSize = MAX_PATH + 16; 641 path.Empty(); 642 643 #ifndef _UNICODE 644 if (!g_IsNT) 645 { 646 TCHAR s[kBufSize + 1]; 647 s[0] = 0; 648 const DWORD len = ::GetCurrentDirectory(kBufSize, s); 649 if (len == 0 || len >= kBufSize) 650 return false; 651 s[kBufSize] = 0; // optional guard 652 path = fas2fs(s); 653 return true; 654 } 655 else 656 #endif 657 { 658 DWORD len; 659 { 660 WCHAR s[kBufSize + 1]; 661 s[0] = 0; 662 len = ::GetCurrentDirectoryW(kBufSize, s); 663 if (len == 0) 664 return false; 665 if (len < kBufSize) 666 { 667 s[kBufSize] = 0; // optional guard 668 path = us2fs(s); 669 return true; 670 } 671 } 672 UString temp; 673 const DWORD len2 = ::GetCurrentDirectoryW(len, temp.GetBuf(len)); 674 if (len2 == 0) 675 return false; 676 temp.ReleaseBuf_CalcLen(len); 677 if (temp.Len() != len2 || len - 1 != len2) 678 { 679 /* it's unexpected case, if current dir of process 680 was changed between two function calls, 681 or some unexpected function implementation */ 682 // SetLastError((DWORD)E_FAIL); // we can set some error code 683 return false; 684 } 685 path = us2fs(temp); 686 return true; 687 } 688} 689 690#endif // _WIN32 691#endif // UNDER_CE 692 693 694bool GetFullPathAndSplit(CFSTR path, FString &resDirPrefix, FString &resFileName) 695{ 696 bool res = MyGetFullPathName(path, resDirPrefix); 697 if (!res) 698 resDirPrefix = path; 699 int pos = resDirPrefix.ReverseFind_PathSepar(); 700 pos++; 701 resFileName = resDirPrefix.Ptr((unsigned)pos); 702 resDirPrefix.DeleteFrom((unsigned)pos); 703 return res; 704} 705 706bool GetOnlyDirPrefix(CFSTR path, FString &resDirPrefix) 707{ 708 FString resFileName; 709 return GetFullPathAndSplit(path, resDirPrefix, resFileName); 710} 711 712 713 714bool MyGetTempPath(FString &path) 715{ 716 #ifdef _WIN32 717 718 /* 719 new DOCs for GetTempPathW(): 720 - The returned string ends with a backslash. 721 - The maximum possible return value is MAX_PATH+1 (261). 722 */ 723 724 const unsigned kBufSize = MAX_PATH + 16; 725 DWORD len; 726 #ifndef _UNICODE 727 if (!g_IsNT) 728 { 729 TCHAR s[kBufSize + 1]; 730 s[0] = 0; 731 len = ::GetTempPath(kBufSize, s); 732 path = fas2fs(s); 733 } 734 else 735 #endif 736 { 737 WCHAR s[kBufSize + 1]; 738 s[0] = 0; 739 len = ::GetTempPathW(kBufSize, s); 740 path = us2fs(s); 741 } 742 /* win10: GetTempPathW() doesn't set backslash at the end of path, 743 if (buffer_size == len_of(path_with_backslash)). 744 So we normalize path here: */ 745 NormalizeDirPathPrefix(path); 746 return (len != 0 && len < kBufSize); 747 748 #else // !_WIN32 749 750 // FIXME: improve that code 751 path = STRING_PATH_SEPARATOR "tmp"; 752 const char *s; 753 if (NFind::DoesDirExist_FollowLink(path)) 754 s = STRING_PATH_SEPARATOR "tmp" STRING_PATH_SEPARATOR; 755 else 756 s = "." STRING_PATH_SEPARATOR; 757 path = s; 758 return true; 759 760 #endif 761} 762 763 764bool CreateTempFile2(CFSTR prefix, bool addRandom, AString &postfix, NIO::COutFile *outFile) 765{ 766 UInt32 d = 767 #ifdef _WIN32 768 (GetTickCount() << 12) ^ (GetCurrentThreadId() << 14) ^ GetCurrentProcessId(); 769 #else 770 (UInt32)(time(NULL) << 12) ^ ((UInt32)getppid() << 14) ^ (UInt32)(getpid()); 771 #endif 772 773 for (unsigned i = 0; i < 100; i++) 774 { 775 postfix.Empty(); 776 if (addRandom) 777 { 778 char s[16]; 779 UInt32 val = d; 780 unsigned k; 781 for (k = 0; k < 8; k++) 782 { 783 const unsigned t = val & 0xF; 784 val >>= 4; 785 s[k] = (char)((t < 10) ? ('0' + t) : ('A' + (t - 10))); 786 } 787 s[k] = '\0'; 788 if (outFile) 789 postfix.Add_Dot(); 790 postfix += s; 791 UInt32 step = GetTickCount() + 2; 792 if (step == 0) 793 step = 1; 794 d += step; 795 } 796 addRandom = true; 797 if (outFile) 798 postfix += ".tmp"; 799 FString path (prefix); 800 path += postfix; 801 if (NFind::DoesFileOrDirExist(path)) 802 { 803 SetLastError(ERROR_ALREADY_EXISTS); 804 continue; 805 } 806 if (outFile) 807 { 808 if (outFile->Create(path, false)) 809 return true; 810 } 811 else 812 { 813 if (CreateDir(path)) 814 return true; 815 } 816 const DWORD error = GetLastError(); 817 if (error != ERROR_FILE_EXISTS && 818 error != ERROR_ALREADY_EXISTS) 819 break; 820 } 821 postfix.Empty(); 822 return false; 823} 824 825bool CTempFile::Create(CFSTR prefix, NIO::COutFile *outFile) 826{ 827 if (!Remove()) 828 return false; 829 _path.Empty(); 830 AString postfix; 831 if (!CreateTempFile2(prefix, false, postfix, outFile)) 832 return false; 833 _path = prefix; 834 _path += postfix; 835 _mustBeDeleted = true; 836 return true; 837} 838 839bool CTempFile::CreateRandomInTempFolder(CFSTR namePrefix, NIO::COutFile *outFile) 840{ 841 if (!Remove()) 842 return false; 843 _path.Empty(); 844 FString tempPath; 845 if (!MyGetTempPath(tempPath)) 846 return false; 847 AString postfix; 848 tempPath += namePrefix; 849 if (!CreateTempFile2(tempPath, true, postfix, outFile)) 850 return false; 851 _path = tempPath; 852 _path += postfix; 853 _mustBeDeleted = true; 854 return true; 855} 856 857bool CTempFile::Remove() 858{ 859 if (!_mustBeDeleted) 860 return true; 861 _mustBeDeleted = !DeleteFileAlways(_path); 862 return !_mustBeDeleted; 863} 864 865bool CTempFile::MoveTo(CFSTR name, bool deleteDestBefore) 866{ 867 // DWORD attrib = 0; 868 if (deleteDestBefore) 869 { 870 if (NFind::DoesFileExist_Raw(name)) 871 { 872 // attrib = NFind::GetFileAttrib(name); 873 if (!DeleteFileAlways(name)) 874 return false; 875 } 876 } 877 DisableDeleting(); 878 return MyMoveFile(_path, name); 879 880 /* 881 if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_READONLY)) 882 { 883 DWORD attrib2 = NFind::GetFileAttrib(name); 884 if (attrib2 != INVALID_FILE_ATTRIBUTES) 885 SetFileAttrib(name, attrib2 | FILE_ATTRIBUTE_READONLY); 886 } 887 */ 888} 889 890#ifdef _WIN32 891bool CTempDir::Create(CFSTR prefix) 892{ 893 if (!Remove()) 894 return false; 895 _path.Empty(); 896 FString tempPath; 897 if (!MyGetTempPath(tempPath)) 898 return false; 899 tempPath += prefix; 900 AString postfix; 901 if (!CreateTempFile2(tempPath, true, postfix, NULL)) 902 return false; 903 _path = tempPath; 904 _path += postfix; 905 _mustBeDeleted = true; 906 return true; 907} 908 909bool CTempDir::Remove() 910{ 911 if (!_mustBeDeleted) 912 return true; 913 _mustBeDeleted = !RemoveDirWithSubItems(_path); 914 return !_mustBeDeleted; 915} 916#endif 917 918 919 920#ifndef _WIN32 921 922bool RemoveDir(CFSTR path) 923{ 924 return (rmdir(path) == 0); 925} 926 927 928static BOOL My_CopyFile(CFSTR oldFile, CFSTR newFile) 929{ 930 NWindows::NFile::NIO::COutFile outFile; 931 if (!outFile.Create(newFile, false)) 932 return FALSE; 933 934 NWindows::NFile::NIO::CInFile inFile; 935 if (!inFile.Open(oldFile)) 936 return FALSE; 937 938 char buf[1 << 14]; 939 940 for (;;) 941 { 942 const ssize_t num = inFile.read_part(buf, sizeof(buf)); 943 if (num == 0) 944 return TRUE; 945 if (num < 0) 946 return FALSE; 947 size_t processed; 948 const ssize_t num2 = outFile.write_full(buf, (size_t)num, processed); 949 if (num2 != num || processed != (size_t)num) 950 return FALSE; 951 } 952} 953 954 955bool MyMoveFile(CFSTR oldFile, CFSTR newFile) 956{ 957 int res = rename(oldFile, newFile); 958 if (res == 0) 959 return true; 960 if (errno != EXDEV) // (oldFile and newFile are not on the same mounted filesystem) 961 return false; 962 963 if (My_CopyFile(oldFile, newFile) == FALSE) 964 return false; 965 966 struct stat info_file; 967 res = stat(oldFile, &info_file); 968 if (res != 0) 969 return false; 970 971 /* 972 ret = chmod(dst,info_file.st_mode & g_umask.mask); 973 */ 974 return (unlink(oldFile) == 0); 975} 976 977 978bool CreateDir(CFSTR path) 979{ 980 return (mkdir(path, 0777) == 0); // change it 981} 982 983static bool CreateDir2(CFSTR path) 984{ 985 return (mkdir(path, 0777) == 0); // change it 986} 987 988 989bool DeleteFileAlways(CFSTR path) 990{ 991 return (remove(path) == 0); 992} 993 994bool SetCurrentDir(CFSTR path) 995{ 996 return (chdir(path) == 0); 997} 998 999 1000bool GetCurrentDir(FString &path) 1001{ 1002 path.Empty(); 1003 1004 #define MY_PATH_MAX PATH_MAX 1005 // #define MY_PATH_MAX 1024 1006 1007 char s[MY_PATH_MAX + 1]; 1008 char *res = getcwd(s, MY_PATH_MAX); 1009 if (res) 1010 { 1011 path = fas2fs(s); 1012 return true; 1013 } 1014 { 1015 // if (errno != ERANGE) return false; 1016 #if defined(__GLIBC__) || defined(__APPLE__) 1017 /* As an extension to the POSIX.1-2001 standard, glibc's getcwd() 1018 allocates the buffer dynamically using malloc(3) if buf is NULL. */ 1019 res = getcwd(NULL, 0); 1020 if (res) 1021 { 1022 path = fas2fs(res); 1023 ::free(res); 1024 return true; 1025 } 1026 #endif 1027 return false; 1028 } 1029} 1030 1031 1032 1033// #undef UTIME_OMIT // to debug 1034 1035#ifndef UTIME_OMIT 1036 /* we can define UTIME_OMIT for debian and another systems. 1037 Is it OK to define UTIME_OMIT to -2 here, if UTIME_OMIT is not defined? */ 1038 // #define UTIME_OMIT -2 1039#endif 1040 1041 1042 1043 1044 1045bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime) 1046{ 1047 // need testing 1048 /* 1049 struct utimbuf buf; 1050 struct stat st; 1051 UNUSED_VAR(cTime) 1052 1053 printf("\nstat = %s\n", path); 1054 int ret = stat(path, &st); 1055 1056 if (ret == 0) 1057 { 1058 buf.actime = st.st_atime; 1059 buf.modtime = st.st_mtime; 1060 } 1061 else 1062 { 1063 time_t cur_time = time(0); 1064 buf.actime = cur_time; 1065 buf.modtime = cur_time; 1066 } 1067 1068 if (aTime) 1069 { 1070 UInt32 ut; 1071 if (NTime::FileTimeToUnixTime(*aTime, ut)) 1072 buf.actime = ut; 1073 } 1074 1075 if (mTime) 1076 { 1077 UInt32 ut; 1078 if (NTime::FileTimeToUnixTime(*mTime, ut)) 1079 buf.modtime = ut; 1080 } 1081 1082 return utime(path, &buf) == 0; 1083 */ 1084 1085 // if (!aTime && !mTime) return true; 1086 1087 struct timespec times[2]; 1088 UNUSED_VAR(cTime) 1089 1090 bool needChange; 1091 needChange = FiTime_To_timespec(aTime, times[0]); 1092 needChange |= FiTime_To_timespec(mTime, times[1]); 1093 1094 /* 1095 if (mTime) 1096 { 1097 printf("\n time = %ld.%9ld\n", mTime->tv_sec, mTime->tv_nsec); 1098 } 1099 */ 1100 1101 if (!needChange) 1102 return true; 1103 const int flags = 0; // follow link 1104 // = AT_SYMLINK_NOFOLLOW; // don't follow link 1105 return utimensat(AT_FDCWD, path, times, flags) == 0; 1106} 1107 1108 1109 1110struct C_umask 1111{ 1112 mode_t mask; 1113 1114 C_umask() 1115 { 1116 /* by security reasons we restrict attributes according 1117 with process's file mode creation mask (umask) */ 1118 const mode_t um = umask(0); // octal :0022 is expected 1119 mask = 0777 & (~um); // octal: 0755 is expected 1120 umask(um); // restore the umask 1121 // printf("\n umask = 0%03o mask = 0%03o\n", um, mask); 1122 1123 // mask = 0777; // debug we can disable the restriction: 1124 } 1125}; 1126 1127static C_umask g_umask; 1128 1129// #define PRF(x) x; 1130#define PRF(x) 1131 1132#define TRACE_SetFileAttrib(msg) \ 1133 PRF(printf("\nSetFileAttrib(%s, %x) : %s\n", (const char *)path, attrib, msg);) 1134 1135#define TRACE_chmod(s, mode) \ 1136 PRF(printf("\n chmod(%s, %o)\n", (const char *)path, (unsigned)(mode));) 1137 1138int my_chown(CFSTR path, uid_t owner, gid_t group) 1139{ 1140 return chown(path, owner, group); 1141} 1142 1143bool SetFileAttrib_PosixHighDetect(CFSTR path, DWORD attrib) 1144{ 1145 TRACE_SetFileAttrib("") 1146 1147 struct stat st; 1148 1149 bool use_lstat = true; 1150 if (use_lstat) 1151 { 1152 if (lstat(path, &st) != 0) 1153 { 1154 TRACE_SetFileAttrib("bad lstat()") 1155 return false; 1156 } 1157 // TRACE_chmod("lstat", st.st_mode); 1158 } 1159 else 1160 { 1161 if (stat(path, &st) != 0) 1162 { 1163 TRACE_SetFileAttrib("bad stat()") 1164 return false; 1165 } 1166 } 1167 1168 if (attrib & FILE_ATTRIBUTE_UNIX_EXTENSION) 1169 { 1170 TRACE_SetFileAttrib("attrib & FILE_ATTRIBUTE_UNIX_EXTENSION") 1171 st.st_mode = attrib >> 16; 1172 if (S_ISDIR(st.st_mode)) 1173 { 1174 // user/7z must be able to create files in this directory 1175 st.st_mode |= (S_IRUSR | S_IWUSR | S_IXUSR); 1176 } 1177 else if (!S_ISREG(st.st_mode)) 1178 return true; 1179 } 1180 else if (S_ISLNK(st.st_mode)) 1181 { 1182 /* for most systems: permissions for symlinks are fixed to rwxrwxrwx. 1183 so we don't need chmod() for symlinks. */ 1184 return true; 1185 // SetLastError(ENOSYS); 1186 // return false; 1187 } 1188 else 1189 { 1190 TRACE_SetFileAttrib("Only Windows Attributes") 1191 // Only Windows Attributes 1192 if (S_ISDIR(st.st_mode) 1193 || (attrib & FILE_ATTRIBUTE_READONLY) == 0) 1194 return true; 1195 st.st_mode &= ~(mode_t)(S_IWUSR | S_IWGRP | S_IWOTH); // octal: ~0222; // disable write permissions 1196 } 1197 1198 int res; 1199 /* 1200 if (S_ISLNK(st.st_mode)) 1201 { 1202 printf("\nfchmodat()\n"); 1203 TRACE_chmod(path, (st.st_mode) & g_umask.mask) 1204 // AT_SYMLINK_NOFOLLOW is not implemted still in Linux. 1205 res = fchmodat(AT_FDCWD, path, (st.st_mode) & g_umask.mask, 1206 S_ISLNK(st.st_mode) ? AT_SYMLINK_NOFOLLOW : 0); 1207 } 1208 else 1209 */ 1210 { 1211 TRACE_chmod(path, (st.st_mode) & g_umask.mask) 1212 res = chmod(path, (st.st_mode) & g_umask.mask); 1213 } 1214 // TRACE_SetFileAttrib("End") 1215 return (res == 0); 1216} 1217 1218 1219bool MyCreateHardLink(CFSTR newFileName, CFSTR existFileName) 1220{ 1221 PRF(printf("\nhard link() %s -> %s\n", newFileName, existFileName);) 1222 return (link(existFileName, newFileName) == 0); 1223} 1224 1225#endif // !_WIN32 1226 1227// #endif 1228 1229}}} 1230