1// ArchiveExtractCallback.cpp 2 3#include "StdAfx.h" 4 5#undef sprintf 6#undef printf 7 8// #include <stdio.h> 9// #include "../../../../C/CpuTicks.h" 10 11#include "../../../../C/Alloc.h" 12#include "../../../../C/CpuArch.h" 13 14 15#include "../../../Common/ComTry.h" 16#include "../../../Common/IntToString.h" 17#include "../../../Common/StringConvert.h" 18#include "../../../Common/UTFConvert.h" 19#include "../../../Common/Wildcard.h" 20 21#include "../../../Windows/ErrorMsg.h" 22#include "../../../Windows/FileDir.h" 23#include "../../../Windows/FileFind.h" 24#include "../../../Windows/FileName.h" 25#include "../../../Windows/PropVariant.h" 26#include "../../../Windows/PropVariantConv.h" 27 28#if defined(_WIN32) && !defined(UNDER_CE) && !defined(Z7_SFX) 29#define Z7_USE_SECURITY_CODE 30#include "../../../Windows/SecurityUtils.h" 31#endif 32 33#include "../../Common/FilePathAutoRename.h" 34#include "../../Common/StreamUtils.h" 35 36#include "../Common/ExtractingFilePath.h" 37#include "../Common/PropIDUtils.h" 38 39#include "ArchiveExtractCallback.h" 40 41using namespace NWindows; 42using namespace NFile; 43using namespace NDir; 44 45static const char * const kCantAutoRename = "Cannot create file with auto name"; 46static const char * const kCantRenameFile = "Cannot rename existing file"; 47static const char * const kCantDeleteOutputFile = "Cannot delete output file"; 48static const char * const kCantDeleteOutputDir = "Cannot delete output folder"; 49static const char * const kCantOpenOutFile = "Cannot open output file"; 50static const char * const kCantOpenInFile = "Cannot open input file"; 51static const char * const kCantSetFileLen = "Cannot set length for output file"; 52#ifdef SUPPORT_LINKS 53static const char * const kCantCreateHardLink = "Cannot create hard link"; 54static const char * const kCantCreateSymLink = "Cannot create symbolic link"; 55#endif 56 57#ifndef Z7_SFX 58 59Z7_COM7F_IMF(COutStreamWithHash::Write(const void *data, UInt32 size, UInt32 *processedSize)) 60{ 61 HRESULT result = S_OK; 62 if (_stream) 63 result = _stream->Write(data, size, &size); 64 if (_calculate) 65 _hash->Update(data, size); 66 _size += size; 67 if (processedSize) 68 *processedSize = size; 69 return result; 70} 71 72#endif // Z7_SFX 73 74 75#ifdef Z7_USE_SECURITY_CODE 76bool InitLocalPrivileges(); 77bool InitLocalPrivileges() 78{ 79 NSecurity::CAccessToken token; 80 if (!token.OpenProcessToken(GetCurrentProcess(), 81 TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES)) 82 return false; 83 84 TOKEN_PRIVILEGES tp; 85 86 tp.PrivilegeCount = 1; 87 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 88 89 if (!::LookupPrivilegeValue(NULL, SE_SECURITY_NAME, &tp.Privileges[0].Luid)) 90 return false; 91 if (!token.AdjustPrivileges(&tp)) 92 return false; 93 return (GetLastError() == ERROR_SUCCESS); 94} 95#endif // Z7_USE_SECURITY_CODE 96 97 98 99#if defined(_WIN32) && !defined(UNDER_CE) && !defined(Z7_SFX) 100 101static const char * const kOfficeExtensions = 102 " doc dot wbk" 103 " docx docm dotx dotm docb wll wwl" 104 " xls xlt xlm" 105 " xlsx xlsm xltx xltm xlsb xla xlam" 106 " ppt pot pps ppa ppam" 107 " pptx pptm potx potm ppam ppsx ppsm sldx sldm" 108 " "; 109 110static bool FindExt2(const char *p, const UString &name) 111{ 112 const int pathPos = name.ReverseFind_PathSepar(); 113 const int dotPos = name.ReverseFind_Dot(); 114 if (dotPos < 0 115 || dotPos < pathPos 116 || dotPos == (int)name.Len() - 1) 117 return false; 118 119 AString s; 120 for (unsigned pos = (unsigned)(dotPos + 1);; pos++) 121 { 122 const wchar_t c = name[pos]; 123 if (c <= 0) 124 break; 125 if (c >= 0x80) 126 return false; 127 s += (char)MyCharLower_Ascii((char)c); 128 } 129 for (unsigned i = 0; p[i] != 0;) 130 { 131 unsigned j; 132 for (j = i; p[j] != ' '; j++); 133 if (s.Len() == j - i && memcmp(p + i, (const char *)s, s.Len()) == 0) 134 return true; 135 i = j + 1; 136 } 137 return false; 138} 139 140 141static const FChar * const k_ZoneId_StreamName = FTEXT(":Zone.Identifier"); 142 143void ReadZoneFile_Of_BaseFile(CFSTR fileName2, CByteBuffer &buf) 144{ 145 FString fileName (fileName2); 146 fileName += k_ZoneId_StreamName; 147 148 buf.Free(); 149 NIO::CInFile file; 150 if (!file.Open(fileName)) 151 return; 152 UInt64 fileSize; 153 if (!file.GetLength(fileSize)) 154 return; 155 if (fileSize == 0 || fileSize >= ((UInt32)1 << 16)) 156 return; 157 buf.Alloc((size_t)fileSize); 158 size_t processed; 159 if (file.ReadFull(buf, (size_t)fileSize, processed) && processed == fileSize) 160 return; 161 buf.Free(); 162} 163 164static bool WriteZoneFile(CFSTR fileName, const CByteBuffer &buf) 165{ 166 NIO::COutFile file; 167 if (!file.Create(fileName, true)) 168 return false; 169 return file.WriteFull(buf, buf.Size()); 170} 171 172#endif 173 174 175#ifdef SUPPORT_LINKS 176 177int CHardLinkNode::Compare(const CHardLinkNode &a) const 178{ 179 if (StreamId < a.StreamId) return -1; 180 if (StreamId > a.StreamId) return 1; 181 return MyCompare(INode, a.INode); 182} 183 184static HRESULT Archive_Get_HardLinkNode(IInArchive *archive, UInt32 index, CHardLinkNode &h, bool &defined) 185{ 186 h.INode = 0; 187 h.StreamId = (UInt64)(Int64)-1; 188 defined = false; 189 { 190 NCOM::CPropVariant prop; 191 RINOK(archive->GetProperty(index, kpidINode, &prop)) 192 if (!ConvertPropVariantToUInt64(prop, h.INode)) 193 return S_OK; 194 } 195 { 196 NCOM::CPropVariant prop; 197 RINOK(archive->GetProperty(index, kpidStreamId, &prop)) 198 ConvertPropVariantToUInt64(prop, h.StreamId); 199 } 200 defined = true; 201 return S_OK; 202} 203 204 205HRESULT CArchiveExtractCallback::PrepareHardLinks(const CRecordVector<UInt32> *realIndices) 206{ 207 _hardLinks.Clear(); 208 209 if (!_arc->Ask_INode) 210 return S_OK; 211 212 IInArchive *archive = _arc->Archive; 213 CRecordVector<CHardLinkNode> &hardIDs = _hardLinks.IDs; 214 215 { 216 UInt32 numItems; 217 if (realIndices) 218 numItems = realIndices->Size(); 219 else 220 { 221 RINOK(archive->GetNumberOfItems(&numItems)) 222 } 223 224 for (UInt32 i = 0; i < numItems; i++) 225 { 226 CHardLinkNode h; 227 bool defined; 228 const UInt32 realIndex = realIndices ? (*realIndices)[i] : i; 229 230 RINOK(Archive_Get_HardLinkNode(archive, realIndex, h, defined)) 231 if (defined) 232 { 233 bool isAltStream = false; 234 RINOK(Archive_IsItem_AltStream(archive, realIndex, isAltStream)) 235 if (!isAltStream) 236 { 237 bool isDir = false; 238 RINOK(Archive_IsItem_Dir(archive, realIndex, isDir)) 239 if (!isDir) 240 hardIDs.Add(h); 241 } 242 } 243 } 244 } 245 246 hardIDs.Sort2(); 247 248 { 249 // we keep only items that have 2 or more items 250 unsigned k = 0; 251 unsigned numSame = 1; 252 for (unsigned i = 1; i < hardIDs.Size(); i++) 253 { 254 if (hardIDs[i].Compare(hardIDs[i - 1]) != 0) 255 numSame = 1; 256 else if (++numSame == 2) 257 { 258 if (i - 1 != k) 259 hardIDs[k] = hardIDs[i - 1]; 260 k++; 261 } 262 } 263 hardIDs.DeleteFrom(k); 264 } 265 266 _hardLinks.PrepareLinks(); 267 return S_OK; 268} 269 270#endif // SUPPORT_LINKS 271 272 273CArchiveExtractCallback::CArchiveExtractCallback(): 274 _arc(NULL), 275 Write_CTime(true), 276 Write_ATime(true), 277 Write_MTime(true), 278 _multiArchives(false) 279{ 280 LocalProgressSpec = new CLocalProgress(); 281 _localProgress = LocalProgressSpec; 282 283 #ifdef Z7_USE_SECURITY_CODE 284 _saclEnabled = InitLocalPrivileges(); 285 #endif 286} 287 288 289void CArchiveExtractCallback::InitBeforeNewArchive() 290{ 291 #if defined(_WIN32) && !defined(UNDER_CE) 292 ZoneBuf.Free(); 293 #endif 294} 295 296void CArchiveExtractCallback::Init( 297 const CExtractNtOptions &ntOptions, 298 const NWildcard::CCensorNode *wildcardCensor, 299 const CArc *arc, 300 IFolderArchiveExtractCallback *extractCallback2, 301 bool stdOutMode, bool testMode, 302 const FString &directoryPath, 303 const UStringVector &removePathParts, bool removePartsForAltStreams, 304 UInt64 packSize) 305{ 306 ClearExtractedDirsInfo(); 307 _outFileStream.Release(); 308 _bufPtrSeqOutStream.Release(); 309 310 #ifdef SUPPORT_LINKS 311 _hardLinks.Clear(); 312 #endif 313 314 #ifdef SUPPORT_ALT_STREAMS 315 _renamedFiles.Clear(); 316 #endif 317 318 _ntOptions = ntOptions; 319 _wildcardCensor = wildcardCensor; 320 321 _stdOutMode = stdOutMode; 322 _testMode = testMode; 323 324 // _progressTotal = 0; 325 // _progressTotal_Defined = false; 326 327 _packTotal = packSize; 328 _progressTotal = packSize; 329 _progressTotal_Defined = true; 330 331 _extractCallback2 = extractCallback2; 332 333 /* 334 _compressProgress.Release(); 335 _extractCallback2.QueryInterface(IID_ICompressProgressInfo, &_compressProgress); 336 337 _callbackMessage.Release(); 338 _extractCallback2.QueryInterface(IID_IArchiveExtractCallbackMessage2, &_callbackMessage); 339 */ 340 341 _folderArchiveExtractCallback2.Release(); 342 _extractCallback2.QueryInterface(IID_IFolderArchiveExtractCallback2, &_folderArchiveExtractCallback2); 343 344 #ifndef Z7_SFX 345 346 ExtractToStreamCallback.Release(); 347 _extractCallback2.QueryInterface(IID_IFolderExtractToStreamCallback, &ExtractToStreamCallback); 348 if (ExtractToStreamCallback) 349 { 350 Int32 useStreams = 0; 351 if (ExtractToStreamCallback->UseExtractToStream(&useStreams) != S_OK) 352 useStreams = 0; 353 if (useStreams == 0) 354 ExtractToStreamCallback.Release(); 355 } 356 357 #endif 358 359 LocalProgressSpec->Init(extractCallback2, true); 360 LocalProgressSpec->SendProgress = false; 361 362 _removePathParts = removePathParts; 363 _removePartsForAltStreams = removePartsForAltStreams; 364 365 #ifndef Z7_SFX 366 _baseParentFolder = (UInt32)(Int32)-1; 367 _use_baseParentFolder_mode = false; 368 #endif 369 370 _arc = arc; 371 _dirPathPrefix = directoryPath; 372 _dirPathPrefix_Full = directoryPath; 373 #if defined(_WIN32) && !defined(UNDER_CE) 374 if (!NName::IsAltPathPrefix(_dirPathPrefix)) 375 #endif 376 { 377 NName::NormalizeDirPathPrefix(_dirPathPrefix); 378 NDir::MyGetFullPathName(directoryPath, _dirPathPrefix_Full); 379 NName::NormalizeDirPathPrefix(_dirPathPrefix_Full); 380 } 381} 382 383 384Z7_COM7F_IMF(CArchiveExtractCallback::SetTotal(UInt64 size)) 385{ 386 COM_TRY_BEGIN 387 _progressTotal = size; 388 _progressTotal_Defined = true; 389 if (!_multiArchives && _extractCallback2) 390 return _extractCallback2->SetTotal(size); 391 return S_OK; 392 COM_TRY_END 393} 394 395 396static void NormalizeVals(UInt64 &v1, UInt64 &v2) 397{ 398 const UInt64 kMax = (UInt64)1 << 31; 399 while (v1 > kMax) 400 { 401 v1 >>= 1; 402 v2 >>= 1; 403 } 404} 405 406 407static UInt64 MyMultDiv64(UInt64 unpCur, UInt64 unpTotal, UInt64 packTotal) 408{ 409 NormalizeVals(packTotal, unpTotal); 410 NormalizeVals(unpCur, unpTotal); 411 if (unpTotal == 0) 412 unpTotal = 1; 413 return unpCur * packTotal / unpTotal; 414} 415 416 417Z7_COM7F_IMF(CArchiveExtractCallback::SetCompleted(const UInt64 *completeValue)) 418{ 419 COM_TRY_BEGIN 420 421 if (!_extractCallback2) 422 return S_OK; 423 424 UInt64 packCur; 425 if (_multiArchives) 426 { 427 packCur = LocalProgressSpec->InSize; 428 if (completeValue && _progressTotal_Defined) 429 packCur += MyMultDiv64(*completeValue, _progressTotal, _packTotal); 430 completeValue = &packCur; 431 } 432 return _extractCallback2->SetCompleted(completeValue); 433 434 COM_TRY_END 435} 436 437 438Z7_COM7F_IMF(CArchiveExtractCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)) 439{ 440 COM_TRY_BEGIN 441 return _localProgress->SetRatioInfo(inSize, outSize); 442 COM_TRY_END 443} 444 445 446void CArchiveExtractCallback::CreateComplexDirectory(const UStringVector &dirPathParts, FString &fullPath) 447{ 448 // we use (_item.IsDir) in this function 449 450 bool isAbsPath = false; 451 452 if (!dirPathParts.IsEmpty()) 453 { 454 const UString &s = dirPathParts[0]; 455 if (s.IsEmpty()) 456 isAbsPath = true; 457 #if defined(_WIN32) && !defined(UNDER_CE) 458 else 459 { 460 if (NName::IsDrivePath2(s)) 461 isAbsPath = true; 462 } 463 #endif 464 } 465 466 if (_pathMode == NExtract::NPathMode::kAbsPaths && isAbsPath) 467 fullPath.Empty(); 468 else 469 fullPath = _dirPathPrefix; 470 471 FOR_VECTOR (i, dirPathParts) 472 { 473 if (i != 0) 474 fullPath.Add_PathSepar(); 475 const UString &s = dirPathParts[i]; 476 fullPath += us2fs(s); 477 478 const bool isFinalDir = (i == dirPathParts.Size() - 1 && _item.IsDir); 479 480 if (fullPath.IsEmpty()) 481 { 482 if (isFinalDir) 483 _itemFailure = true; 484 continue; 485 } 486 487 #if defined(_WIN32) && !defined(UNDER_CE) 488 if (_pathMode == NExtract::NPathMode::kAbsPaths) 489 if (i == 0 && s.Len() == 2 && NName::IsDrivePath2(s)) 490 { 491 if (isFinalDir) 492 { 493 // we don't want to call SetAttrib() for root drive path 494 _itemFailure = true; 495 } 496 continue; 497 } 498 #endif 499 500 // bool res = 501 CreateDir(fullPath); 502 // if (!res) 503 if (isFinalDir) 504 { 505 if (!NFile::NFind::DoesDirExist(fullPath)) 506 { 507 _itemFailure = true; 508 SendMessageError("Cannot create folder", fullPath); 509 // SendMessageError_with_LastError() 510 } 511 } 512 } 513} 514 515 516HRESULT CArchiveExtractCallback::GetTime(UInt32 index, PROPID propID, CArcTime &ft) 517{ 518 ft.Clear(); 519 NCOM::CPropVariant prop; 520 RINOK(_arc->Archive->GetProperty(index, propID, &prop)) 521 if (prop.vt == VT_FILETIME) 522 ft.Set_From_Prop(prop); 523 else if (prop.vt != VT_EMPTY) 524 return E_FAIL; 525 return S_OK; 526} 527 528 529HRESULT CArchiveExtractCallback::GetUnpackSize() 530{ 531 return _arc->GetItem_Size(_index, _curSize, _curSize_Defined); 532} 533 534static void AddPathToMessage(UString &s, const FString &path) 535{ 536 s += " : "; 537 s += fs2us(path); 538} 539 540HRESULT CArchiveExtractCallback::SendMessageError(const char *message, const FString &path) 541{ 542 UString s (message); 543 AddPathToMessage(s, path); 544 return _extractCallback2->MessageError(s); 545} 546 547HRESULT CArchiveExtractCallback::SendMessageError_with_LastError(const char *message, const FString &path) 548{ 549 DWORD errorCode = GetLastError(); 550 if (errorCode == 0) 551 errorCode = (DWORD)E_FAIL; 552 UString s (message); 553 { 554 s += " : "; 555 s += NError::MyFormatMessage(errorCode); 556 } 557 AddPathToMessage(s, path); 558 return _extractCallback2->MessageError(s); 559} 560 561HRESULT CArchiveExtractCallback::SendMessageError2(HRESULT errorCode, const char *message, const FString &path1, const FString &path2) 562{ 563 UString s (message); 564 if (errorCode != 0) 565 { 566 s += " : "; 567 s += NError::MyFormatMessage(errorCode); 568 } 569 AddPathToMessage(s, path1); 570 AddPathToMessage(s, path2); 571 return _extractCallback2->MessageError(s); 572} 573 574#ifndef Z7_SFX 575 576Z7_COM7F_IMF(CGetProp::GetProp(PROPID propID, PROPVARIANT *value)) 577{ 578 /* 579 if (propID == kpidName) 580 { 581 COM_TRY_BEGIN 582 NCOM::CPropVariant prop = Name; 583 prop.Detach(value); 584 return S_OK; 585 COM_TRY_END 586 } 587 */ 588 return Arc->Archive->GetProperty(IndexInArc, propID, value); 589} 590 591#endif // Z7_SFX 592 593 594#ifdef SUPPORT_LINKS 595 596static UString GetDirPrefixOf(const UString &src) 597{ 598 UString s (src); 599 if (!s.IsEmpty()) 600 { 601 if (IsPathSepar(s.Back())) 602 s.DeleteBack(); 603 int pos = s.ReverseFind_PathSepar(); 604 s.DeleteFrom((unsigned)(pos + 1)); 605 } 606 return s; 607} 608 609#endif // SUPPORT_LINKS 610 611struct CLinkLevelsInfo 612{ 613 bool IsAbsolute; 614 int LowLevel; 615 int FinalLevel; 616 617 void Parse(const UString &path); 618}; 619 620void CLinkLevelsInfo::Parse(const UString &path) 621{ 622 IsAbsolute = NName::IsAbsolutePath(path); 623 624 LowLevel = 0; 625 FinalLevel = 0; 626 627 UStringVector parts; 628 SplitPathToParts(path, parts); 629 int level = 0; 630 631 FOR_VECTOR (i, parts) 632 { 633 const UString &s = parts[i]; 634 if (s.IsEmpty()) 635 { 636 if (i == 0) 637 IsAbsolute = true; 638 continue; 639 } 640 if (s == L".") 641 continue; 642 if (s == L"..") 643 { 644 level--; 645 if (LowLevel > level) 646 LowLevel = level; 647 } 648 else 649 level++; 650 } 651 652 FinalLevel = level; 653} 654 655 656bool IsSafePath(const UString &path); 657bool IsSafePath(const UString &path) 658{ 659 CLinkLevelsInfo levelsInfo; 660 levelsInfo.Parse(path); 661 return !levelsInfo.IsAbsolute 662 && levelsInfo.LowLevel >= 0 663 && levelsInfo.FinalLevel > 0; 664} 665 666 667bool CensorNode_CheckPath2(const NWildcard::CCensorNode &node, const CReadArcItem &item, bool &include); 668bool CensorNode_CheckPath2(const NWildcard::CCensorNode &node, const CReadArcItem &item, bool &include) 669{ 670 bool found = false; 671 672 // CheckPathVect() doesn't check path to Parent nodes 673 if (node.CheckPathVect(item.PathParts, !item.MainIsDir, include)) 674 { 675 if (!include) 676 return true; 677 678 #ifdef SUPPORT_ALT_STREAMS 679 if (!item.IsAltStream) 680 return true; 681 #endif 682 683 found = true; 684 } 685 686 #ifdef SUPPORT_ALT_STREAMS 687 688 if (!item.IsAltStream) 689 return false; 690 691 UStringVector pathParts2 = item.PathParts; 692 if (pathParts2.IsEmpty()) 693 pathParts2.AddNew(); 694 UString &back = pathParts2.Back(); 695 back += ':'; 696 back += item.AltStreamName; 697 bool include2; 698 699 if (node.CheckPathVect(pathParts2, 700 true, // isFile, 701 include2)) 702 { 703 include = include2; 704 return true; 705 } 706 707 #endif // SUPPORT_ALT_STREAMS 708 709 return found; 710} 711 712 713bool CensorNode_CheckPath(const NWildcard::CCensorNode &node, const CReadArcItem &item) 714{ 715 bool include; 716 if (CensorNode_CheckPath2(node, item, include)) 717 return include; 718 return false; 719} 720 721 722static FString MakePath_from_2_Parts(const FString &prefix, const FString &path) 723{ 724 FString s (prefix); 725 #if defined(_WIN32) && !defined(UNDER_CE) 726 if (!path.IsEmpty() && path[0] == ':' && !prefix.IsEmpty() && IsPathSepar(prefix.Back())) 727 { 728 if (!NName::IsDriveRootPath_SuperAllowed(prefix)) 729 s.DeleteBack(); 730 } 731 #endif 732 s += path; 733 return s; 734} 735 736 737 738#ifdef SUPPORT_LINKS 739 740/* 741struct CTempMidBuffer 742{ 743 void *Buf; 744 745 CTempMidBuffer(size_t size): Buf(NULL) { Buf = ::MidAlloc(size); } 746 ~CTempMidBuffer() { ::MidFree(Buf); } 747}; 748 749HRESULT CArchiveExtractCallback::MyCopyFile(ISequentialOutStream *outStream) 750{ 751 const size_t kBufSize = 1 << 16; 752 CTempMidBuffer buf(kBufSize); 753 if (!buf.Buf) 754 return E_OUTOFMEMORY; 755 756 NIO::CInFile inFile; 757 NIO::COutFile outFile; 758 759 if (!inFile.Open(_copyFile_Path)) 760 return SendMessageError_with_LastError("Open error", _copyFile_Path); 761 762 for (;;) 763 { 764 UInt32 num; 765 766 if (!inFile.Read(buf.Buf, kBufSize, num)) 767 return SendMessageError_with_LastError("Read error", _copyFile_Path); 768 769 if (num == 0) 770 return S_OK; 771 772 773 RINOK(WriteStream(outStream, buf.Buf, num)); 774 } 775} 776*/ 777 778 779HRESULT CArchiveExtractCallback::ReadLink() 780{ 781 IInArchive *archive = _arc->Archive; 782 const UInt32 index = _index; 783 _link.Clear(); 784 785 { 786 NCOM::CPropVariant prop; 787 RINOK(archive->GetProperty(index, kpidHardLink, &prop)) 788 if (prop.vt == VT_BSTR) 789 { 790 _link.isHardLink = true; 791 // _link.isCopyLink = false; 792 _link.isRelative = false; // RAR5, TAR: hard links are from root folder of archive 793 _link.linkPath.SetFromBstr(prop.bstrVal); 794 } 795 else if (prop.vt != VT_EMPTY) 796 return E_FAIL; 797 } 798 799 /* 800 { 801 NCOM::CPropVariant prop; 802 RINOK(archive->GetProperty(index, kpidCopyLink, &prop)); 803 if (prop.vt == VT_BSTR) 804 { 805 _link.isHardLink = false; 806 _link.isCopyLink = true; 807 _link.isRelative = false; // RAR5: copy links are from root folder of archive 808 _link.linkPath.SetFromBstr(prop.bstrVal); 809 } 810 else if (prop.vt != VT_EMPTY) 811 return E_FAIL; 812 } 813 */ 814 815 { 816 NCOM::CPropVariant prop; 817 RINOK(archive->GetProperty(index, kpidSymLink, &prop)) 818 if (prop.vt == VT_BSTR) 819 { 820 _link.isHardLink = false; 821 // _link.isCopyLink = false; 822 _link.isRelative = true; // RAR5, TAR: symbolic links can be relative 823 _link.linkPath.SetFromBstr(prop.bstrVal); 824 } 825 else if (prop.vt != VT_EMPTY) 826 return E_FAIL; 827 } 828 829 NtReparse_Data = NULL; 830 NtReparse_Size = 0; 831 832 if (_link.linkPath.IsEmpty() && _arc->GetRawProps) 833 { 834 const void *data; 835 UInt32 dataSize; 836 UInt32 propType; 837 838 _arc->GetRawProps->GetRawProp(_index, kpidNtReparse, &data, &dataSize, &propType); 839 840 // if (dataSize == 1234567) // for debug: unpacking without reparse 841 if (dataSize != 0) 842 { 843 if (propType != NPropDataType::kRaw) 844 return E_FAIL; 845 846 // 21.06: we need kpidNtReparse in linux for wim archives created in Windows 847 // #ifdef _WIN32 848 849 NtReparse_Data = data; 850 NtReparse_Size = dataSize; 851 852 CReparseAttr reparse; 853 bool isOkReparse = reparse.Parse((const Byte *)data, dataSize); 854 if (isOkReparse) 855 { 856 _link.isHardLink = false; 857 // _link.isCopyLink = false; 858 _link.linkPath = reparse.GetPath(); 859 _link.isJunction = reparse.IsMountPoint(); 860 861 if (reparse.IsSymLink_WSL()) 862 { 863 _link.isWSL = true; 864 _link.isRelative = reparse.IsRelative_WSL(); 865 } 866 else 867 _link.isRelative = reparse.IsRelative_Win(); 868 869 // const AString s = GetAnsiString(_link.linkPath); 870 // printf("\n_link.linkPath: %s\n", s.Ptr()); 871 872 #ifndef _WIN32 873 _link.linkPath.Replace(L'\\', WCHAR_PATH_SEPARATOR); 874 #endif 875 } 876 // #endif 877 } 878 } 879 880 if (_link.linkPath.IsEmpty()) 881 return S_OK; 882 883 { 884 #ifdef _WIN32 885 _link.linkPath.Replace(L'/', WCHAR_PATH_SEPARATOR); 886 #endif 887 888 // rar5 uses "\??\" prefix for absolute links 889 if (_link.linkPath.IsPrefixedBy(WSTRING_PATH_SEPARATOR L"??" WSTRING_PATH_SEPARATOR)) 890 { 891 _link.isRelative = false; 892 _link.linkPath.DeleteFrontal(4); 893 } 894 895 for (;;) 896 // while (NName::IsAbsolutePath(linkPath)) 897 { 898 unsigned n = NName::GetRootPrefixSize(_link.linkPath); 899 if (n == 0) 900 break; 901 _link.isRelative = false; 902 _link.linkPath.DeleteFrontal(n); 903 } 904 } 905 906 if (_link.linkPath.IsEmpty()) 907 return S_OK; 908 909 if (!_link.isRelative && _removePathParts.Size() != 0) 910 { 911 UStringVector pathParts; 912 SplitPathToParts(_link.linkPath, pathParts); 913 bool badPrefix = false; 914 FOR_VECTOR (i, _removePathParts) 915 { 916 if (CompareFileNames(_removePathParts[i], pathParts[i]) != 0) 917 { 918 badPrefix = true; 919 break; 920 } 921 } 922 if (!badPrefix) 923 pathParts.DeleteFrontal(_removePathParts.Size()); 924 _link.linkPath = MakePathFromParts(pathParts); 925 } 926 927 /* 928 if (!_link.linkPath.IsEmpty()) 929 { 930 printf("\n_link %s to -> %s\n", GetOemString(_item.Path).Ptr(), GetOemString(_link.linkPath).Ptr()); 931 } 932 */ 933 934 return S_OK; 935} 936 937#endif // SUPPORT_LINKS 938 939 940#ifndef _WIN32 941 942static HRESULT GetOwner(IInArchive *archive, 943 UInt32 index, UInt32 pidName, UInt32 pidId, COwnerInfo &res) 944{ 945 { 946 NWindows::NCOM::CPropVariant prop; 947 RINOK(archive->GetProperty(index, pidId, &prop)) 948 if (prop.vt == VT_UI4) 949 { 950 res.Id_Defined = true; 951 res.Id = prop.ulVal; // for debug 952 // res.Id++; // for debug 953 // if (pidId == kpidGroupId) res.Id += 7; // for debug 954 // res.Id = 0; // for debug 955 } 956 else if (prop.vt != VT_EMPTY) 957 return E_INVALIDARG; 958 } 959 { 960 NWindows::NCOM::CPropVariant prop; 961 RINOK(archive->GetProperty(index, pidName, &prop)) 962 if (prop.vt == VT_BSTR) 963 { 964 const UString s = prop.bstrVal; 965 ConvertUnicodeToUTF8(s, res.Name); 966 } 967 else if (prop.vt == VT_UI4) 968 { 969 res.Id_Defined = true; 970 res.Id = prop.ulVal; 971 } 972 else if (prop.vt != VT_EMPTY) 973 return E_INVALIDARG; 974 } 975 return S_OK; 976} 977 978#endif 979 980 981HRESULT CArchiveExtractCallback::Read_fi_Props() 982{ 983 IInArchive *archive = _arc->Archive; 984 const UInt32 index = _index; 985 986 _fi.Attrib_Defined = false; 987 988 #ifndef _WIN32 989 _fi.Owner.Clear(); 990 _fi.Group.Clear(); 991 #endif 992 993 { 994 NCOM::CPropVariant prop; 995 RINOK(archive->GetProperty(index, kpidPosixAttrib, &prop)) 996 if (prop.vt == VT_UI4) 997 { 998 _fi.SetFromPosixAttrib(prop.ulVal); 999 } 1000 else if (prop.vt != VT_EMPTY) 1001 return E_FAIL; 1002 } 1003 1004 { 1005 NCOM::CPropVariant prop; 1006 RINOK(archive->GetProperty(index, kpidAttrib, &prop)) 1007 if (prop.vt == VT_UI4) 1008 { 1009 _fi.Attrib = prop.ulVal; 1010 _fi.Attrib_Defined = true; 1011 } 1012 else if (prop.vt != VT_EMPTY) 1013 return E_FAIL; 1014 } 1015 1016 RINOK(GetTime(index, kpidCTime, _fi.CTime)) 1017 RINOK(GetTime(index, kpidATime, _fi.ATime)) 1018 RINOK(GetTime(index, kpidMTime, _fi.MTime)) 1019 1020 #ifndef _WIN32 1021 if (_ntOptions.ExtractOwner) 1022 { 1023 // SendMessageError_with_LastError("_ntOptions.ExtractOwner", _diskFilePath); 1024 GetOwner(archive, index, kpidUser, kpidUserId, _fi.Owner); 1025 GetOwner(archive, index, kpidGroup, kpidGroupId, _fi.Group); 1026 } 1027 #endif 1028 1029 return S_OK; 1030} 1031 1032 1033 1034void CArchiveExtractCallback::CorrectPathParts() 1035{ 1036 UStringVector &pathParts = _item.PathParts; 1037 1038 #ifdef SUPPORT_ALT_STREAMS 1039 if (!_item.IsAltStream 1040 || !pathParts.IsEmpty() 1041 || !(_removePartsForAltStreams || _pathMode == NExtract::NPathMode::kNoPathsAlt)) 1042 #endif 1043 Correct_FsPath(_pathMode == NExtract::NPathMode::kAbsPaths, _keepAndReplaceEmptyDirPrefixes, pathParts, _item.MainIsDir); 1044 1045 #ifdef SUPPORT_ALT_STREAMS 1046 1047 if (_item.IsAltStream) 1048 { 1049 UString s (_item.AltStreamName); 1050 Correct_AltStream_Name(s); 1051 bool needColon = true; 1052 1053 if (pathParts.IsEmpty()) 1054 { 1055 pathParts.AddNew(); 1056 if (_removePartsForAltStreams || _pathMode == NExtract::NPathMode::kNoPathsAlt) 1057 needColon = false; 1058 } 1059 #ifdef _WIN32 1060 else if (_pathMode == NExtract::NPathMode::kAbsPaths && 1061 NWildcard::GetNumPrefixParts_if_DrivePath(pathParts) == pathParts.Size()) 1062 pathParts.AddNew(); 1063 #endif 1064 1065 UString &name = pathParts.Back(); 1066 if (needColon) 1067 name += (char)(_ntOptions.ReplaceColonForAltStream ? '_' : ':'); 1068 name += s; 1069 } 1070 1071 #endif // SUPPORT_ALT_STREAMS 1072} 1073 1074 1075void CArchiveExtractCallback::GetFiTimesCAM(CFiTimesCAM &pt) 1076{ 1077 pt.CTime_Defined = false; 1078 pt.ATime_Defined = false; 1079 pt.MTime_Defined = false; 1080 1081 if (Write_MTime) 1082 { 1083 if (_fi.MTime.Def) 1084 { 1085 _fi.MTime.Write_To_FiTime(pt.MTime); 1086 pt.MTime_Defined = true; 1087 } 1088 else if (_arc->MTime.Def) 1089 { 1090 _arc->MTime.Write_To_FiTime(pt.MTime); 1091 pt.MTime_Defined = true; 1092 } 1093 } 1094 1095 if (Write_CTime && _fi.CTime.Def) 1096 { 1097 _fi.CTime.Write_To_FiTime(pt.CTime); 1098 pt.CTime_Defined = true; 1099 } 1100 1101 if (Write_ATime && _fi.ATime.Def) 1102 { 1103 _fi.ATime.Write_To_FiTime(pt.ATime); 1104 pt.ATime_Defined = true; 1105 } 1106} 1107 1108 1109void CArchiveExtractCallback::CreateFolders() 1110{ 1111 // 21.04 : we don't change original (_item.PathParts) here 1112 UStringVector pathParts = _item.PathParts; 1113 1114 if (!pathParts.IsEmpty()) 1115 { 1116 /* v23: if we extract symlink, and we know that it links to dir: 1117 Linux: we don't create dir item (symlink_from_path) here. 1118 Windows: SetReparseData() will create dir item, if it doesn't exist, 1119 but if we create dir item here, it's not problem. */ 1120 if (!_item.IsDir 1121 #ifdef SUPPORT_LINKS 1122 #ifndef WIN32 1123 || !_link.linkPath.IsEmpty() 1124 #endif 1125 #endif 1126 ) 1127 pathParts.DeleteBack(); 1128 } 1129 1130 if (pathParts.IsEmpty()) 1131 return; 1132 1133 FString fullPathNew; 1134 CreateComplexDirectory(pathParts, fullPathNew); 1135 1136 if (!_item.IsDir) 1137 return; 1138 1139 if (_itemFailure) 1140 return; 1141 1142 CDirPathTime pt; 1143 GetFiTimesCAM(pt); 1144 1145 if (pt.IsSomeTimeDefined()) 1146 { 1147 pt.Path = fullPathNew; 1148 pt.SetDirTime(); 1149 _extractedFolders.Add(pt); 1150 } 1151} 1152 1153 1154 1155/* 1156 CheckExistFile(fullProcessedPath) 1157 it can change: fullProcessedPath, _isRenamed, _overwriteMode 1158 (needExit = true) means that we must exit GetStream() even for S_OK result. 1159*/ 1160 1161HRESULT CArchiveExtractCallback::CheckExistFile(FString &fullProcessedPath, bool &needExit) 1162{ 1163 needExit = true; // it was set already before 1164 1165 NFind::CFileInfo fileInfo; 1166 1167 if (fileInfo.Find(fullProcessedPath)) 1168 { 1169 if (_overwriteMode == NExtract::NOverwriteMode::kSkip) 1170 return S_OK; 1171 1172 if (_overwriteMode == NExtract::NOverwriteMode::kAsk) 1173 { 1174 const int slashPos = fullProcessedPath.ReverseFind_PathSepar(); 1175 const FString realFullProcessedPath = fullProcessedPath.Left((unsigned)(slashPos + 1)) + fileInfo.Name; 1176 1177 /* (fileInfo) can be symbolic link. 1178 we can show final file properties here. */ 1179 1180 FILETIME ft1; 1181 FiTime_To_FILETIME(fileInfo.MTime, ft1); 1182 1183 Int32 overwriteResult; 1184 RINOK(_extractCallback2->AskOverwrite( 1185 fs2us(realFullProcessedPath), &ft1, &fileInfo.Size, _item.Path, 1186 _fi.MTime.Def ? &_fi.MTime.FT : NULL, 1187 _curSize_Defined ? &_curSize : NULL, 1188 &overwriteResult)) 1189 1190 switch (overwriteResult) 1191 { 1192 case NOverwriteAnswer::kCancel: 1193 return E_ABORT; 1194 case NOverwriteAnswer::kNo: 1195 return S_OK; 1196 case NOverwriteAnswer::kNoToAll: 1197 _overwriteMode = NExtract::NOverwriteMode::kSkip; 1198 return S_OK; 1199 1200 case NOverwriteAnswer::kYes: 1201 break; 1202 case NOverwriteAnswer::kYesToAll: 1203 _overwriteMode = NExtract::NOverwriteMode::kOverwrite; 1204 break; 1205 case NOverwriteAnswer::kAutoRename: 1206 _overwriteMode = NExtract::NOverwriteMode::kRename; 1207 break; 1208 default: 1209 return E_FAIL; 1210 } 1211 } // NExtract::NOverwriteMode::kAsk 1212 1213 if (_overwriteMode == NExtract::NOverwriteMode::kRename) 1214 { 1215 if (!AutoRenamePath(fullProcessedPath)) 1216 { 1217 RINOK(SendMessageError(kCantAutoRename, fullProcessedPath)) 1218 return E_FAIL; 1219 } 1220 _isRenamed = true; 1221 } 1222 else if (_overwriteMode == NExtract::NOverwriteMode::kRenameExisting) 1223 { 1224 FString existPath (fullProcessedPath); 1225 if (!AutoRenamePath(existPath)) 1226 { 1227 RINOK(SendMessageError(kCantAutoRename, fullProcessedPath)) 1228 return E_FAIL; 1229 } 1230 // MyMoveFile can rename folders. So it's OK to use it for folders too 1231 if (!MyMoveFile(fullProcessedPath, existPath)) 1232 { 1233 HRESULT errorCode = GetLastError_noZero_HRESULT(); 1234 RINOK(SendMessageError2(errorCode, kCantRenameFile, existPath, fullProcessedPath)) 1235 return E_FAIL; 1236 } 1237 } 1238 else // not Rename* 1239 { 1240 if (fileInfo.IsDir()) 1241 { 1242 // do we need to delete all files in folder? 1243 if (!RemoveDir(fullProcessedPath)) 1244 { 1245 RINOK(SendMessageError_with_LastError(kCantDeleteOutputDir, fullProcessedPath)) 1246 return S_OK; 1247 } 1248 } 1249 else // fileInfo is not Dir 1250 { 1251 if (NFind::DoesFileExist_Raw(fullProcessedPath)) 1252 if (!DeleteFileAlways(fullProcessedPath)) 1253 if (GetLastError() != ERROR_FILE_NOT_FOUND) // check it in linux 1254 { 1255 RINOK(SendMessageError_with_LastError(kCantDeleteOutputFile, fullProcessedPath)) 1256 return S_OK; 1257 // return E_FAIL; 1258 } 1259 } // fileInfo is not Dir 1260 } // not Rename* 1261 } 1262 else // not Find(fullProcessedPath) 1263 { 1264 #if defined(_WIN32) && !defined(UNDER_CE) 1265 // we need to clear READ-ONLY of parent before creating alt stream 1266 int colonPos = NName::FindAltStreamColon(fullProcessedPath); 1267 if (colonPos >= 0 && fullProcessedPath[(unsigned)colonPos + 1] != 0) 1268 { 1269 FString parentFsPath (fullProcessedPath); 1270 parentFsPath.DeleteFrom((unsigned)colonPos); 1271 NFind::CFileInfo parentFi; 1272 if (parentFi.Find(parentFsPath)) 1273 { 1274 if (parentFi.IsReadOnly()) 1275 SetFileAttrib(parentFsPath, parentFi.Attrib & ~(DWORD)FILE_ATTRIBUTE_READONLY); 1276 } 1277 } 1278 #endif // defined(_WIN32) && !defined(UNDER_CE) 1279 } 1280 1281 needExit = false; 1282 return S_OK; 1283} 1284 1285 1286 1287 1288 1289 1290HRESULT CArchiveExtractCallback::GetExtractStream(CMyComPtr<ISequentialOutStream> &outStreamLoc, bool &needExit) 1291{ 1292 needExit = true; 1293 1294 RINOK(Read_fi_Props()) 1295 1296 #ifdef SUPPORT_LINKS 1297 IInArchive *archive = _arc->Archive; 1298 #endif 1299 1300 const UInt32 index = _index; 1301 1302 bool isAnti = false; 1303 RINOK(_arc->IsItem_Anti(index, isAnti)) 1304 1305 CorrectPathParts(); 1306 UString processedPath (MakePathFromParts(_item.PathParts)); 1307 1308 if (!isAnti) 1309 { 1310 // 21.04: CreateFolders doesn't change (_item.PathParts) 1311 CreateFolders(); 1312 } 1313 1314 FString fullProcessedPath (us2fs(processedPath)); 1315 if (_pathMode != NExtract::NPathMode::kAbsPaths 1316 || !NName::IsAbsolutePath(processedPath)) 1317 { 1318 fullProcessedPath = MakePath_from_2_Parts(_dirPathPrefix, fullProcessedPath); 1319 } 1320 1321 #ifdef SUPPORT_ALT_STREAMS 1322 if (_item.IsAltStream && _item.ParentIndex != (UInt32)(Int32)-1) 1323 { 1324 const int renIndex = _renamedFiles.FindInSorted(CIndexToPathPair(_item.ParentIndex)); 1325 if (renIndex != -1) 1326 { 1327 const CIndexToPathPair &pair = _renamedFiles[(unsigned)renIndex]; 1328 fullProcessedPath = pair.Path; 1329 fullProcessedPath += ':'; 1330 UString s (_item.AltStreamName); 1331 Correct_AltStream_Name(s); 1332 fullProcessedPath += us2fs(s); 1333 } 1334 } 1335 #endif // SUPPORT_ALT_STREAMS 1336 1337 if (_item.IsDir) 1338 { 1339 _diskFilePath = fullProcessedPath; 1340 if (isAnti) 1341 RemoveDir(_diskFilePath); 1342 #ifdef SUPPORT_LINKS 1343 if (_link.linkPath.IsEmpty()) 1344 #endif 1345 { 1346 if (!isAnti) 1347 SetAttrib(); 1348 return S_OK; 1349 } 1350 } 1351 else if (!_isSplit) 1352 { 1353 RINOK(CheckExistFile(fullProcessedPath, needExit)) 1354 if (needExit) 1355 return S_OK; 1356 needExit = true; 1357 } 1358 1359 _diskFilePath = fullProcessedPath; 1360 1361 1362 if (isAnti) 1363 { 1364 needExit = false; 1365 return S_OK; 1366 } 1367 1368 // not anti 1369 1370 #ifdef SUPPORT_LINKS 1371 1372 if (!_link.linkPath.IsEmpty()) 1373 { 1374 #ifndef UNDER_CE 1375 { 1376 bool linkWasSet = false; 1377 RINOK(SetFromLinkPath(fullProcessedPath, _link, linkWasSet)) 1378 if (linkWasSet) 1379 { 1380 _isSymLinkCreated = _link.IsSymLink(); 1381 SetAttrib(); 1382 // printf("\nlinkWasSet %s\n", GetAnsiString(_diskFilePath)); 1383 } 1384 } 1385 #endif // UNDER_CE 1386 1387 // if (_copyFile_Path.IsEmpty()) 1388 { 1389 needExit = false; 1390 return S_OK; 1391 } 1392 } 1393 1394 if (!_hardLinks.IDs.IsEmpty() && !_item.IsAltStream && !_item.IsDir) 1395 { 1396 CHardLinkNode h; 1397 bool defined; 1398 RINOK(Archive_Get_HardLinkNode(archive, index, h, defined)) 1399 if (defined) 1400 { 1401 const int linkIndex = _hardLinks.IDs.FindInSorted2(h); 1402 if (linkIndex != -1) 1403 { 1404 FString &hl = _hardLinks.Links[(unsigned)linkIndex]; 1405 if (hl.IsEmpty()) 1406 hl = fullProcessedPath; 1407 else 1408 { 1409 if (!MyCreateHardLink(fullProcessedPath, hl)) 1410 { 1411 HRESULT errorCode = GetLastError_noZero_HRESULT(); 1412 RINOK(SendMessageError2(errorCode, kCantCreateHardLink, fullProcessedPath, hl)) 1413 return S_OK; 1414 } 1415 1416 // printf("\nHard linkWasSet Archive_Get_HardLinkNode %s\n", GetAnsiString(_diskFilePath)); 1417 // _needSetAttrib = true; // do we need to set attribute ? 1418 SetAttrib(); 1419 needExit = false; 1420 return S_OK; 1421 } 1422 } 1423 } 1424 } 1425 1426 #endif // SUPPORT_LINKS 1427 1428 1429 // ---------- CREATE WRITE FILE ----- 1430 1431 _outFileStreamSpec = new COutFileStream; 1432 CMyComPtr<IOutStream> outFileStream_Loc(_outFileStreamSpec); 1433 1434 if (!_outFileStreamSpec->Open(fullProcessedPath, _isSplit ? OPEN_ALWAYS: CREATE_ALWAYS)) 1435 { 1436 // if (::GetLastError() != ERROR_FILE_EXISTS || !isSplit) 1437 { 1438 RINOK(SendMessageError_with_LastError(kCantOpenOutFile, fullProcessedPath)) 1439 return S_OK; 1440 } 1441 } 1442 1443 _needSetAttrib = true; 1444 1445 bool is_SymLink_in_Data = false; 1446 1447 if (_curSize_Defined && _curSize > 0 && _curSize < (1 << 12)) 1448 { 1449 if (_fi.IsLinuxSymLink()) 1450 { 1451 is_SymLink_in_Data = true; 1452 _is_SymLink_in_Data_Linux = true; 1453 } 1454 else if (_fi.IsReparse()) 1455 { 1456 is_SymLink_in_Data = true; 1457 _is_SymLink_in_Data_Linux = false; 1458 } 1459 } 1460 1461 if (is_SymLink_in_Data) 1462 { 1463 _outMemBuf.Alloc((size_t)_curSize); 1464 _bufPtrSeqOutStream_Spec = new CBufPtrSeqOutStream; 1465 _bufPtrSeqOutStream = _bufPtrSeqOutStream_Spec; 1466 _bufPtrSeqOutStream_Spec->Init(_outMemBuf, _outMemBuf.Size()); 1467 outStreamLoc = _bufPtrSeqOutStream; 1468 } 1469 else // not reprase 1470 { 1471 if (_ntOptions.PreAllocateOutFile && !_isSplit && _curSize_Defined && _curSize > (1 << 12)) 1472 { 1473 // UInt64 ticks = GetCpuTicks(); 1474 _fileLength_that_WasSet = _curSize; 1475 bool res = _outFileStreamSpec->File.SetLength(_curSize); 1476 _fileLength_WasSet = res; 1477 1478 // ticks = GetCpuTicks() - ticks; 1479 // printf("\nticks = %10d\n", (unsigned)ticks); 1480 if (!res) 1481 { 1482 RINOK(SendMessageError_with_LastError(kCantSetFileLen, fullProcessedPath)) 1483 } 1484 1485 /* 1486 _outFileStreamSpec->File.Close(); 1487 ticks = GetCpuTicks() - ticks; 1488 printf("\nticks = %10d\n", (unsigned)ticks); 1489 return S_FALSE; 1490 */ 1491 1492 /* 1493 File.SetLength() on FAT (xp64): is fast, but then File.Close() can be slow, 1494 if we don't write any data. 1495 File.SetLength() for remote share file (exFAT) can be slow in some cases, 1496 and the Windows can return "network error" after 1 minute, 1497 while remote file still can grow. 1498 We need some way to detect such bad cases and disable PreAllocateOutFile mode. 1499 */ 1500 1501 res = _outFileStreamSpec->SeekToBegin_bool(); 1502 if (!res) 1503 { 1504 RINOK(SendMessageError_with_LastError("Cannot seek to begin of file", fullProcessedPath)) 1505 } 1506 } // PreAllocateOutFile 1507 1508 #ifdef SUPPORT_ALT_STREAMS 1509 if (_isRenamed && !_item.IsAltStream) 1510 { 1511 CIndexToPathPair pair(index, fullProcessedPath); 1512 unsigned oldSize = _renamedFiles.Size(); 1513 unsigned insertIndex = _renamedFiles.AddToUniqueSorted(pair); 1514 if (oldSize == _renamedFiles.Size()) 1515 _renamedFiles[insertIndex].Path = fullProcessedPath; 1516 } 1517 #endif // SUPPORT_ALT_STREAMS 1518 1519 if (_isSplit) 1520 { 1521 RINOK(outFileStream_Loc->Seek((Int64)_position, STREAM_SEEK_SET, NULL)) 1522 } 1523 outStreamLoc = outFileStream_Loc; 1524 } // if not reprase 1525 1526 _outFileStream = outFileStream_Loc; 1527 1528 needExit = false; 1529 return S_OK; 1530} 1531 1532 1533 1534HRESULT CArchiveExtractCallback::GetItem(UInt32 index) 1535{ 1536 #ifndef Z7_SFX 1537 _item._use_baseParentFolder_mode = _use_baseParentFolder_mode; 1538 if (_use_baseParentFolder_mode) 1539 { 1540 _item._baseParentFolder = (int)_baseParentFolder; 1541 if (_pathMode == NExtract::NPathMode::kFullPaths || 1542 _pathMode == NExtract::NPathMode::kAbsPaths) 1543 _item._baseParentFolder = -1; 1544 } 1545 #endif // Z7_SFX 1546 1547 #ifdef SUPPORT_ALT_STREAMS 1548 _item.WriteToAltStreamIfColon = _ntOptions.WriteToAltStreamIfColon; 1549 #endif 1550 1551 return _arc->GetItem(index, _item); 1552} 1553 1554 1555Z7_COM7F_IMF(CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode)) 1556{ 1557 COM_TRY_BEGIN 1558 1559 *outStream = NULL; 1560 1561 #ifndef Z7_SFX 1562 if (_hashStream) 1563 _hashStreamSpec->ReleaseStream(); 1564 _hashStreamWasUsed = false; 1565 #endif 1566 1567 _outFileStream.Release(); 1568 _bufPtrSeqOutStream.Release(); 1569 1570 _encrypted = false; 1571 _position = 0; 1572 _isSplit = false; 1573 1574 _curSize = 0; 1575 _curSize_Defined = false; 1576 _fileLength_WasSet = false; 1577 _fileLength_that_WasSet = 0; 1578 _index = index; 1579 1580 _diskFilePath.Empty(); 1581 1582 _isRenamed = false; 1583 1584 // _fi.Clear(); 1585 1586 // _is_SymLink_in_Data = false; 1587 _is_SymLink_in_Data_Linux = false; 1588 1589 _needSetAttrib = false; 1590 _isSymLinkCreated = false; 1591 _itemFailure = false; 1592 1593 #ifdef SUPPORT_LINKS 1594 // _copyFile_Path.Empty(); 1595 _link.Clear(); 1596 #endif 1597 1598 _extractMode = false; 1599 1600 switch (askExtractMode) 1601 { 1602 case NArchive::NExtract::NAskMode::kExtract: 1603 if (_testMode) 1604 { 1605 // askExtractMode = NArchive::NExtract::NAskMode::kTest; 1606 } 1607 else 1608 _extractMode = true; 1609 break; 1610 } 1611 1612 1613 IInArchive *archive = _arc->Archive; 1614 1615 RINOK(GetItem(index)) 1616 1617 { 1618 NCOM::CPropVariant prop; 1619 RINOK(archive->GetProperty(index, kpidPosition, &prop)) 1620 if (prop.vt != VT_EMPTY) 1621 { 1622 if (prop.vt != VT_UI8) 1623 return E_FAIL; 1624 _position = prop.uhVal.QuadPart; 1625 _isSplit = true; 1626 } 1627 } 1628 1629 #ifdef SUPPORT_LINKS 1630 RINOK(ReadLink()) 1631 #endif // SUPPORT_LINKS 1632 1633 1634 RINOK(Archive_GetItemBoolProp(archive, index, kpidEncrypted, _encrypted)) 1635 1636 RINOK(GetUnpackSize()) 1637 1638 #ifdef SUPPORT_ALT_STREAMS 1639 if (!_ntOptions.AltStreams.Val && _item.IsAltStream) 1640 return S_OK; 1641 #endif // SUPPORT_ALT_STREAMS 1642 1643 // we can change (_item.PathParts) in this function 1644 UStringVector &pathParts = _item.PathParts; 1645 1646 if (_wildcardCensor) 1647 { 1648 if (!CensorNode_CheckPath(*_wildcardCensor, _item)) 1649 return S_OK; 1650 } 1651 1652 #ifndef Z7_SFX 1653 if (_use_baseParentFolder_mode) 1654 { 1655 if (!pathParts.IsEmpty()) 1656 { 1657 unsigned numRemovePathParts = 0; 1658 1659 #ifdef SUPPORT_ALT_STREAMS 1660 if (_pathMode == NExtract::NPathMode::kNoPathsAlt && _item.IsAltStream) 1661 numRemovePathParts = pathParts.Size(); 1662 else 1663 #endif 1664 if (_pathMode == NExtract::NPathMode::kNoPaths || 1665 _pathMode == NExtract::NPathMode::kNoPathsAlt) 1666 numRemovePathParts = pathParts.Size() - 1; 1667 pathParts.DeleteFrontal(numRemovePathParts); 1668 } 1669 } 1670 else 1671 #endif // Z7_SFX 1672 { 1673 if (pathParts.IsEmpty()) 1674 { 1675 if (_item.IsDir) 1676 return S_OK; 1677 /* 1678 #ifdef SUPPORT_ALT_STREAMS 1679 if (!_item.IsAltStream) 1680 #endif 1681 return E_FAIL; 1682 */ 1683 } 1684 1685 unsigned numRemovePathParts = 0; 1686 1687 switch (_pathMode) 1688 { 1689 case NExtract::NPathMode::kFullPaths: 1690 case NExtract::NPathMode::kCurPaths: 1691 { 1692 if (_removePathParts.IsEmpty()) 1693 break; 1694 bool badPrefix = false; 1695 1696 if (pathParts.Size() < _removePathParts.Size()) 1697 badPrefix = true; 1698 else 1699 { 1700 if (pathParts.Size() == _removePathParts.Size()) 1701 { 1702 if (_removePartsForAltStreams) 1703 { 1704 #ifdef SUPPORT_ALT_STREAMS 1705 if (!_item.IsAltStream) 1706 #endif 1707 badPrefix = true; 1708 } 1709 else 1710 { 1711 if (!_item.MainIsDir) 1712 badPrefix = true; 1713 } 1714 } 1715 1716 if (!badPrefix) 1717 FOR_VECTOR (i, _removePathParts) 1718 { 1719 if (CompareFileNames(_removePathParts[i], pathParts[i]) != 0) 1720 { 1721 badPrefix = true; 1722 break; 1723 } 1724 } 1725 } 1726 1727 if (badPrefix) 1728 { 1729 if (askExtractMode == NArchive::NExtract::NAskMode::kExtract && !_testMode) 1730 return E_FAIL; 1731 } 1732 else 1733 numRemovePathParts = _removePathParts.Size(); 1734 break; 1735 } 1736 1737 case NExtract::NPathMode::kNoPaths: 1738 { 1739 if (!pathParts.IsEmpty()) 1740 numRemovePathParts = pathParts.Size() - 1; 1741 break; 1742 } 1743 case NExtract::NPathMode::kNoPathsAlt: 1744 { 1745 #ifdef SUPPORT_ALT_STREAMS 1746 if (_item.IsAltStream) 1747 numRemovePathParts = pathParts.Size(); 1748 else 1749 #endif 1750 if (!pathParts.IsEmpty()) 1751 numRemovePathParts = pathParts.Size() - 1; 1752 break; 1753 } 1754 case NExtract::NPathMode::kAbsPaths: 1755 // default: 1756 break; 1757 } 1758 1759 pathParts.DeleteFrontal(numRemovePathParts); 1760 } 1761 1762 1763 #ifndef Z7_SFX 1764 1765 if (ExtractToStreamCallback) 1766 { 1767 if (!GetProp) 1768 { 1769 GetProp_Spec = new CGetProp; 1770 GetProp = GetProp_Spec; 1771 } 1772 GetProp_Spec->Arc = _arc; 1773 GetProp_Spec->IndexInArc = index; 1774 UString name (MakePathFromParts(pathParts)); 1775 1776 #ifdef SUPPORT_ALT_STREAMS 1777 if (_item.IsAltStream) 1778 { 1779 if (!pathParts.IsEmpty() || (!_removePartsForAltStreams && _pathMode != NExtract::NPathMode::kNoPathsAlt)) 1780 name += ':'; 1781 name += _item.AltStreamName; 1782 } 1783 #endif 1784 1785 return ExtractToStreamCallback->GetStream7(name, BoolToInt(_item.IsDir), outStream, askExtractMode, GetProp); 1786 } 1787 1788 #endif // Z7_SFX 1789 1790 1791 CMyComPtr<ISequentialOutStream> outStreamLoc; 1792 1793 if (askExtractMode == NArchive::NExtract::NAskMode::kExtract && !_testMode) 1794 { 1795 if (_stdOutMode) 1796 outStreamLoc = new CStdOutFileStream; 1797 else 1798 { 1799 bool needExit = true; 1800 RINOK(GetExtractStream(outStreamLoc, needExit)) 1801 if (needExit) 1802 return S_OK; 1803 } 1804 } 1805 1806 #ifndef Z7_SFX 1807 if (_hashStream) 1808 { 1809 if (askExtractMode == NArchive::NExtract::NAskMode::kExtract || 1810 askExtractMode == NArchive::NExtract::NAskMode::kTest) 1811 { 1812 _hashStreamSpec->SetStream(outStreamLoc); 1813 outStreamLoc = _hashStream; 1814 _hashStreamSpec->Init(true); 1815 _hashStreamWasUsed = true; 1816 } 1817 } 1818 #endif // Z7_SFX 1819 1820 if (outStreamLoc) 1821 { 1822 /* 1823 #ifdef SUPPORT_LINKS 1824 if (!_copyFile_Path.IsEmpty()) 1825 { 1826 RINOK(PrepareOperation(askExtractMode)); 1827 RINOK(MyCopyFile(outStreamLoc)); 1828 return SetOperationResult(NArchive::NExtract::NOperationResult::kOK); 1829 } 1830 if (_link.isCopyLink && _testMode) 1831 return S_OK; 1832 #endif 1833 */ 1834 *outStream = outStreamLoc.Detach(); 1835 } 1836 1837 return S_OK; 1838 1839 COM_TRY_END 1840} 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852Z7_COM7F_IMF(CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode)) 1853{ 1854 COM_TRY_BEGIN 1855 1856 #ifndef Z7_SFX 1857 if (ExtractToStreamCallback) 1858 return ExtractToStreamCallback->PrepareOperation7(askExtractMode); 1859 #endif 1860 1861 _extractMode = false; 1862 1863 switch (askExtractMode) 1864 { 1865 case NArchive::NExtract::NAskMode::kExtract: 1866 if (_testMode) 1867 askExtractMode = NArchive::NExtract::NAskMode::kTest; 1868 else 1869 _extractMode = true; 1870 break; 1871 } 1872 1873 return _extractCallback2->PrepareOperation(_item.Path, BoolToInt(_item.IsDir), 1874 askExtractMode, _isSplit ? &_position: NULL); 1875 1876 COM_TRY_END 1877} 1878 1879 1880 1881 1882 1883HRESULT CArchiveExtractCallback::CloseFile() 1884{ 1885 if (!_outFileStream) 1886 return S_OK; 1887 1888 HRESULT hres = S_OK; 1889 1890 const UInt64 processedSize = _outFileStreamSpec->ProcessedSize; 1891 if (_fileLength_WasSet && _fileLength_that_WasSet > processedSize) 1892 { 1893 const bool res = _outFileStreamSpec->File.SetLength(processedSize); 1894 _fileLength_WasSet = res; 1895 if (!res) 1896 { 1897 const HRESULT hres2 = SendMessageError_with_LastError(kCantSetFileLen, us2fs(_item.Path)); 1898 if (hres == S_OK) 1899 hres = hres2; 1900 } 1901 } 1902 1903 _curSize = processedSize; 1904 _curSize_Defined = true; 1905 1906 #if defined(_WIN32) && !defined(UNDER_CE) && !defined(Z7_SFX) 1907 if (ZoneBuf.Size() != 0 1908 && !_item.IsAltStream) 1909 { 1910 // if (NFind::DoesFileExist_Raw(tempFilePath)) 1911 if (ZoneMode != NExtract::NZoneIdMode::kOffice || 1912 FindExt2(kOfficeExtensions, fs2us(_diskFilePath))) 1913 { 1914 // we must write zone file before setting of timestamps 1915 const FString path = _diskFilePath + k_ZoneId_StreamName; 1916 if (!WriteZoneFile(path, ZoneBuf)) 1917 { 1918 // we can't write it in FAT 1919 // SendMessageError_with_LastError("Can't write Zone.Identifier stream", path); 1920 } 1921 } 1922 } 1923 #endif 1924 1925 CFiTimesCAM t; 1926 GetFiTimesCAM(t); 1927 1928 // #ifdef _WIN32 1929 if (t.IsSomeTimeDefined()) 1930 _outFileStreamSpec->SetTime( 1931 t.CTime_Defined ? &t.CTime : NULL, 1932 t.ATime_Defined ? &t.ATime : NULL, 1933 t.MTime_Defined ? &t.MTime : NULL); 1934 // #endif 1935 1936 RINOK(_outFileStreamSpec->Close()) 1937 _outFileStream.Release(); 1938 return hres; 1939} 1940 1941 1942#ifdef SUPPORT_LINKS 1943 1944 1945HRESULT CArchiveExtractCallback::SetFromLinkPath( 1946 const FString &fullProcessedPath, 1947 const CLinkInfo &linkInfo, 1948 bool &linkWasSet) 1949{ 1950 linkWasSet = false; 1951 if (!_ntOptions.SymLinks.Val && !linkInfo.isHardLink) 1952 return S_OK; 1953 1954 UString relatPath; 1955 1956 /* if (linkInfo.isRelative) 1957 linkInfo.linkPath is final link path that must be stored to file link field 1958 else 1959 linkInfo.linkPath is path from root of archive. So we must add _dirPathPrefix_Full before linkPath. 1960 */ 1961 1962 if (linkInfo.isRelative) 1963 relatPath = GetDirPrefixOf(_item.Path); 1964 relatPath += linkInfo.linkPath; 1965 1966 if (!IsSafePath(relatPath)) 1967 { 1968 return SendMessageError2( 1969 0, // errorCode 1970 "Dangerous link path was ignored", 1971 us2fs(_item.Path), 1972 us2fs(linkInfo.linkPath)); // us2fs(relatPath) 1973 } 1974 1975 FString existPath; 1976 if (linkInfo.isHardLink /* || linkInfo.IsCopyLink */ || !linkInfo.isRelative) 1977 { 1978 if (!NName::GetFullPath(_dirPathPrefix_Full, us2fs(relatPath), existPath)) 1979 { 1980 RINOK(SendMessageError("Incorrect path", us2fs(relatPath))) 1981 } 1982 } 1983 else 1984 { 1985 existPath = us2fs(linkInfo.linkPath); 1986 // printf("\nlinkPath = : %s\n", GetOemString(linkInfo.linkPath).Ptr()); 1987 } 1988 1989 if (existPath.IsEmpty()) 1990 return SendMessageError("Empty link", fullProcessedPath); 1991 1992 if (linkInfo.isHardLink /* || linkInfo.IsCopyLink */) 1993 { 1994 // if (linkInfo.isHardLink) 1995 { 1996 if (!MyCreateHardLink(fullProcessedPath, existPath)) 1997 { 1998 const HRESULT errorCode = GetLastError_noZero_HRESULT(); 1999 RINOK(SendMessageError2(errorCode, kCantCreateHardLink, fullProcessedPath, existPath)) 2000 } 2001 linkWasSet = true; 2002 return S_OK; 2003 } 2004 /* 2005 // IsCopyLink 2006 { 2007 NFind::CFileInfo fi; 2008 if (!fi.Find(existPath)) 2009 { 2010 RINOK(SendMessageError2("Cannot find the file for copying", existPath, fullProcessedPath)); 2011 } 2012 else 2013 { 2014 if (_curSize_Defined && _curSize == fi.Size) 2015 _copyFile_Path = existPath; 2016 else 2017 { 2018 RINOK(SendMessageError2("File size collision for file copying", existPath, fullProcessedPath)); 2019 } 2020 // RINOK(MyCopyFile(existPath, fullProcessedPath)); 2021 } 2022 } 2023 */ 2024 } 2025 2026 // is Symbolic link 2027 2028 /* 2029 if (_item.IsDir && !isRelative) 2030 { 2031 // Windows before Vista doesn't support symbolic links. 2032 // we could convert such symbolic links to Junction Points 2033 // isJunction = true; 2034 // convertToAbs = true; 2035 } 2036 */ 2037 2038 if (!_ntOptions.SymLinks_AllowDangerous.Val) 2039 { 2040 #ifdef _WIN32 2041 if (_item.IsDir) 2042 #endif 2043 if (linkInfo.isRelative) 2044 { 2045 CLinkLevelsInfo levelsInfo; 2046 levelsInfo.Parse(linkInfo.linkPath); 2047 if (levelsInfo.FinalLevel < 1 || levelsInfo.IsAbsolute) 2048 { 2049 return SendMessageError2( 2050 0, // errorCode 2051 "Dangerous symbolic link path was ignored", 2052 us2fs(_item.Path), 2053 us2fs(linkInfo.linkPath)); 2054 } 2055 } 2056 } 2057 2058 2059 #ifdef _WIN32 2060 2061 CByteBuffer data; 2062 // printf("\nFillLinkData(): %s\n", GetOemString(existPath).Ptr()); 2063 if (!FillLinkData(data, fs2us(existPath), !linkInfo.isJunction, linkInfo.isWSL)) 2064 return SendMessageError("Cannot fill link data", us2fs(_item.Path)); 2065 2066 /* 2067 if (NtReparse_Size != data.Size() || memcmp(NtReparse_Data, data, data.Size()) != 0) 2068 { 2069 SendMessageError("reconstructed Reparse is different", fs2us(existPath)); 2070 } 2071 */ 2072 2073 CReparseAttr attr; 2074 if (!attr.Parse(data, data.Size())) 2075 { 2076 RINOK(SendMessageError("Internal error for symbolic link file", us2fs(_item.Path))) 2077 return S_OK; 2078 } 2079 if (!NFile::NIO::SetReparseData(fullProcessedPath, _item.IsDir, data, (DWORD)data.Size())) 2080 { 2081 RINOK(SendMessageError_with_LastError(kCantCreateSymLink, fullProcessedPath)) 2082 return S_OK; 2083 } 2084 linkWasSet = true; 2085 2086 return S_OK; 2087 2088 2089 #else // ! _WIN32 2090 2091 if (!NFile::NIO::SetSymLink(fullProcessedPath, existPath)) 2092 { 2093 RINOK(SendMessageError_with_LastError(kCantCreateSymLink, fullProcessedPath)) 2094 return S_OK; 2095 } 2096 linkWasSet = true; 2097 2098 return S_OK; 2099 2100 #endif // ! _WIN32 2101} 2102 2103 2104bool CLinkInfo::Parse(const Byte *data, size_t dataSize, bool isLinuxData) 2105{ 2106 Clear(); 2107 // this->isLinux = isLinuxData; 2108 2109 if (isLinuxData) 2110 { 2111 isJunction = false; 2112 isHardLink = false; 2113 AString utf; 2114 if (dataSize >= (1 << 12)) 2115 return false; 2116 utf.SetFrom_CalcLen((const char *)data, (unsigned)dataSize); 2117 UString u; 2118 if (!ConvertUTF8ToUnicode(utf, u)) 2119 return false; 2120 linkPath = u; 2121 2122 // in linux symbolic data: we expect that linux separator '/' is used 2123 // if windows link was created, then we also must use linux separator 2124 if (u.IsEmpty()) 2125 return false; 2126 const wchar_t c = u[0]; 2127 isRelative = !IS_PATH_SEPAR(c); 2128 return true; 2129 } 2130 2131 CReparseAttr reparse; 2132 if (!reparse.Parse(data, dataSize)) 2133 return false; 2134 isHardLink = false; 2135 // isCopyLink = false; 2136 linkPath = reparse.GetPath(); 2137 isJunction = reparse.IsMountPoint(); 2138 2139 if (reparse.IsSymLink_WSL()) 2140 { 2141 isWSL = true; 2142 isRelative = reparse.IsRelative_WSL(); 2143 } 2144 else 2145 isRelative = reparse.IsRelative_Win(); 2146 2147 // FIXME !!! 2148 #ifndef _WIN32 2149 linkPath.Replace(L'\\', WCHAR_PATH_SEPARATOR); 2150 #endif 2151 2152 return true; 2153} 2154 2155#endif // SUPPORT_LINKS 2156 2157 2158HRESULT CArchiveExtractCallback::CloseReparseAndFile() 2159{ 2160 HRESULT res = S_OK; 2161 2162 #ifdef SUPPORT_LINKS 2163 2164 size_t reparseSize = 0; 2165 bool repraseMode = false; 2166 bool needSetReparse = false; 2167 CLinkInfo linkInfo; 2168 2169 if (_bufPtrSeqOutStream) 2170 { 2171 repraseMode = true; 2172 reparseSize = _bufPtrSeqOutStream_Spec->GetPos(); 2173 if (_curSize_Defined && reparseSize == _outMemBuf.Size()) 2174 { 2175 /* 2176 CReparseAttr reparse; 2177 DWORD errorCode = 0; 2178 needSetReparse = reparse.Parse(_outMemBuf, reparseSize, errorCode); 2179 if (needSetReparse) 2180 { 2181 UString linkPath = reparse.GetPath(); 2182 #ifndef _WIN32 2183 linkPath.Replace(L'\\', WCHAR_PATH_SEPARATOR); 2184 #endif 2185 } 2186 */ 2187 needSetReparse = linkInfo.Parse(_outMemBuf, reparseSize, _is_SymLink_in_Data_Linux); 2188 if (!needSetReparse) 2189 res = SendMessageError_with_LastError("Incorrect reparse stream", us2fs(_item.Path)); 2190 } 2191 else 2192 { 2193 res = SendMessageError_with_LastError("Unknown reparse stream", us2fs(_item.Path)); 2194 } 2195 if (!needSetReparse && _outFileStream) 2196 { 2197 const HRESULT res2 = WriteStream(_outFileStream, _outMemBuf, reparseSize); 2198 if (res == S_OK) 2199 res = res2; 2200 } 2201 _bufPtrSeqOutStream.Release(); 2202 } 2203 2204 #endif // SUPPORT_LINKS 2205 2206 2207 const HRESULT res2 = CloseFile(); 2208 2209 if (res == S_OK) 2210 res = res2; 2211 2212 RINOK(res) 2213 2214 #ifdef SUPPORT_LINKS 2215 if (repraseMode) 2216 { 2217 _curSize = reparseSize; 2218 _curSize_Defined = true; 2219 2220 #ifdef SUPPORT_LINKS 2221 if (needSetReparse) 2222 { 2223 // in Linux : we must delete empty file before symbolic link creation 2224 // in Windows : we can create symbolic link even without file deleting 2225 if (!DeleteFileAlways(_diskFilePath)) 2226 { 2227 RINOK(SendMessageError_with_LastError("can't delete file", _diskFilePath)) 2228 } 2229 { 2230 /* 2231 // for DEBUG ONLY: we can extract sym links as WSL links 2232 // to eliminate (non-admin) errors for sym links. 2233 #ifdef _WIN32 2234 if (!linkInfo.isHardLink && !linkInfo.isJunction) 2235 linkInfo.isWSL = true; 2236 #endif 2237 */ 2238 bool linkWasSet = false; 2239 RINOK(SetFromLinkPath(_diskFilePath, linkInfo, linkWasSet)) 2240 if (linkWasSet) 2241 _isSymLinkCreated = linkInfo.IsSymLink(); 2242 else 2243 _needSetAttrib = false; 2244 } 2245 /* 2246 if (!NFile::NIO::SetReparseData(_diskFilePath, _item.IsDir, )) 2247 { 2248 res = SendMessageError_with_LastError(kCantCreateSymLink, _diskFilePath); 2249 } 2250 */ 2251 } 2252 #endif 2253 } 2254 #endif 2255 return res; 2256} 2257 2258 2259void CArchiveExtractCallback::SetAttrib() 2260{ 2261 #ifndef _WIN32 2262 // Linux now doesn't support permissions for symlinks 2263 if (_isSymLinkCreated) 2264 return; 2265 #endif 2266 2267 if (_itemFailure 2268 || _diskFilePath.IsEmpty() 2269 || _stdOutMode 2270 || !_extractMode) 2271 return; 2272 2273 #ifndef _WIN32 2274 if (_fi.Owner.Id_Defined && 2275 _fi.Group.Id_Defined) 2276 { 2277 if (my_chown(_diskFilePath, _fi.Owner.Id, _fi.Group.Id) != 0) 2278 { 2279 SendMessageError_with_LastError("Cannot set owner", _diskFilePath); 2280 } 2281 } 2282 #endif 2283 2284 if (_fi.Attrib_Defined) 2285 { 2286 // const AString s = GetAnsiString(_diskFilePath); 2287 // printf("\nSetFileAttrib_PosixHighDetect: %s: hex:%x\n", s.Ptr(), _fi.Attrib); 2288 bool res = SetFileAttrib_PosixHighDetect(_diskFilePath, _fi.Attrib); 2289 if (!res) 2290 { 2291 // do we need error message here in Windows and in posix? 2292 SendMessageError_with_LastError("Cannot set file attribute", _diskFilePath); 2293 } 2294 } 2295} 2296 2297 2298Z7_COM7F_IMF(CArchiveExtractCallback::SetOperationResult(Int32 opRes)) 2299{ 2300 COM_TRY_BEGIN 2301 2302 // printf("\nCArchiveExtractCallback::SetOperationResult: %d %s\n", opRes, GetAnsiString(_diskFilePath)); 2303 2304 #ifndef Z7_SFX 2305 if (ExtractToStreamCallback) 2306 { 2307 GetUnpackSize(); 2308 return ExtractToStreamCallback->SetOperationResult8(opRes, BoolToInt(_encrypted), _curSize); 2309 } 2310 #endif 2311 2312 #ifndef Z7_SFX 2313 2314 if (_hashStreamWasUsed) 2315 { 2316 _hashStreamSpec->_hash->Final(_item.IsDir, 2317 #ifdef SUPPORT_ALT_STREAMS 2318 _item.IsAltStream 2319 #else 2320 false 2321 #endif 2322 , _item.Path); 2323 _curSize = _hashStreamSpec->GetSize(); 2324 _curSize_Defined = true; 2325 _hashStreamSpec->ReleaseStream(); 2326 _hashStreamWasUsed = false; 2327 } 2328 2329 #endif // Z7_SFX 2330 2331 RINOK(CloseReparseAndFile()) 2332 2333 #ifdef Z7_USE_SECURITY_CODE 2334 if (!_stdOutMode && _extractMode && _ntOptions.NtSecurity.Val && _arc->GetRawProps) 2335 { 2336 const void *data; 2337 UInt32 dataSize; 2338 UInt32 propType; 2339 _arc->GetRawProps->GetRawProp(_index, kpidNtSecure, &data, &dataSize, &propType); 2340 if (dataSize != 0) 2341 { 2342 if (propType != NPropDataType::kRaw) 2343 return E_FAIL; 2344 if (CheckNtSecure((const Byte *)data, dataSize)) 2345 { 2346 SECURITY_INFORMATION securInfo = DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION; 2347 if (_saclEnabled) 2348 securInfo |= SACL_SECURITY_INFORMATION; 2349 ::SetFileSecurityW(fs2us(_diskFilePath), securInfo, (PSECURITY_DESCRIPTOR)(void *)(const Byte *)(data)); 2350 } 2351 } 2352 } 2353 #endif // Z7_USE_SECURITY_CODE 2354 2355 if (!_curSize_Defined) 2356 GetUnpackSize(); 2357 2358 if (_curSize_Defined) 2359 { 2360 #ifdef SUPPORT_ALT_STREAMS 2361 if (_item.IsAltStream) 2362 AltStreams_UnpackSize += _curSize; 2363 else 2364 #endif 2365 UnpackSize += _curSize; 2366 } 2367 2368 if (_item.IsDir) 2369 NumFolders++; 2370 #ifdef SUPPORT_ALT_STREAMS 2371 else if (_item.IsAltStream) 2372 NumAltStreams++; 2373 #endif 2374 else 2375 NumFiles++; 2376 2377 if (_needSetAttrib) 2378 SetAttrib(); 2379 2380 RINOK(_extractCallback2->SetOperationResult(opRes, BoolToInt(_encrypted))) 2381 2382 return S_OK; 2383 2384 COM_TRY_END 2385} 2386 2387 2388 2389Z7_COM7F_IMF(CArchiveExtractCallback::ReportExtractResult(UInt32 indexType, UInt32 index, Int32 opRes)) 2390{ 2391 if (_folderArchiveExtractCallback2) 2392 { 2393 bool isEncrypted = false; 2394 UString s; 2395 2396 if (indexType == NArchive::NEventIndexType::kInArcIndex && index != (UInt32)(Int32)-1) 2397 { 2398 CReadArcItem item; 2399 RINOK(_arc->GetItem(index, item)) 2400 s = item.Path; 2401 RINOK(Archive_GetItemBoolProp(_arc->Archive, index, kpidEncrypted, isEncrypted)) 2402 } 2403 else 2404 { 2405 s = '#'; 2406 s.Add_UInt32(index); 2407 // if (indexType == NArchive::NEventIndexType::kBlockIndex) {} 2408 } 2409 2410 return _folderArchiveExtractCallback2->ReportExtractResult(opRes, isEncrypted, s); 2411 } 2412 2413 return S_OK; 2414} 2415 2416 2417Z7_COM7F_IMF(CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password)) 2418{ 2419 COM_TRY_BEGIN 2420 if (!_cryptoGetTextPassword) 2421 { 2422 RINOK(_extractCallback2.QueryInterface(IID_ICryptoGetTextPassword, 2423 &_cryptoGetTextPassword)) 2424 } 2425 return _cryptoGetTextPassword->CryptoGetTextPassword(password); 2426 COM_TRY_END 2427} 2428 2429 2430// ---------- HASH functions ---------- 2431 2432FString CArchiveExtractCallback::Hash_GetFullFilePath() 2433{ 2434 // this function changes _item.PathParts. 2435 CorrectPathParts(); 2436 const UStringVector &pathParts = _item.PathParts; 2437 const UString processedPath (MakePathFromParts(pathParts)); 2438 FString fullProcessedPath (us2fs(processedPath)); 2439 if (_pathMode != NExtract::NPathMode::kAbsPaths 2440 || !NName::IsAbsolutePath(processedPath)) 2441 { 2442 fullProcessedPath = MakePath_from_2_Parts( 2443 DirPathPrefix_for_HashFiles, 2444 // _dirPathPrefix, 2445 fullProcessedPath); 2446 } 2447 return fullProcessedPath; 2448} 2449 2450 2451Z7_COM7F_IMF(CArchiveExtractCallback::GetDiskProperty(UInt32 index, PROPID propID, PROPVARIANT *value)) 2452{ 2453 COM_TRY_BEGIN 2454 NCOM::CPropVariant prop; 2455 if (propID == kpidSize) 2456 { 2457 RINOK(GetItem(index)) 2458 const FString fullProcessedPath = Hash_GetFullFilePath(); 2459 NFile::NFind::CFileInfo fi; 2460 if (fi.Find_FollowLink(fullProcessedPath)) 2461 if (!fi.IsDir()) 2462 prop = (UInt64)fi.Size; 2463 } 2464 prop.Detach(value); 2465 return S_OK; 2466 COM_TRY_END 2467} 2468 2469 2470Z7_COM7F_IMF(CArchiveExtractCallback::GetStream2(UInt32 index, ISequentialInStream **inStream, UInt32 mode)) 2471{ 2472 COM_TRY_BEGIN 2473 *inStream = NULL; 2474 // if (index != _index) return E_FAIL; 2475 if (mode != NUpdateNotifyOp::kHashRead) 2476 return E_FAIL; 2477 2478 RINOK(GetItem(index)) 2479 const FString fullProcessedPath = Hash_GetFullFilePath(); 2480 2481 CInFileStream *inStreamSpec = new CInFileStream; 2482 CMyComPtr<ISequentialInStream> inStreamRef = inStreamSpec; 2483 inStreamSpec->Set_PreserveATime(_ntOptions.PreserveATime); 2484 if (!inStreamSpec->OpenShared(fullProcessedPath, _ntOptions.OpenShareForWrite)) 2485 { 2486 RINOK(SendMessageError_with_LastError(kCantOpenInFile, fullProcessedPath)) 2487 return S_OK; 2488 } 2489 *inStream = inStreamRef.Detach(); 2490 return S_OK; 2491 COM_TRY_END 2492} 2493 2494 2495Z7_COM7F_IMF(CArchiveExtractCallback::ReportOperation( 2496 UInt32 /* indexType */, UInt32 /* index */, UInt32 /* op */)) 2497{ 2498 // COM_TRY_BEGIN 2499 return S_OK; 2500 // COM_TRY_END 2501} 2502 2503 2504// ------------ After Extracting functions ------------ 2505 2506void CDirPathSortPair::SetNumSlashes(const FChar *s) 2507{ 2508 for (unsigned numSlashes = 0;;) 2509 { 2510 FChar c = *s++; 2511 if (c == 0) 2512 { 2513 Len = numSlashes; 2514 return; 2515 } 2516 if (IS_PATH_SEPAR(c)) 2517 numSlashes++; 2518 } 2519} 2520 2521 2522bool CDirPathTime::SetDirTime() const 2523{ 2524 return NDir::SetDirTime(Path, 2525 CTime_Defined ? &CTime : NULL, 2526 ATime_Defined ? &ATime : NULL, 2527 MTime_Defined ? &MTime : NULL); 2528} 2529 2530 2531HRESULT CArchiveExtractCallback::SetDirsTimes() 2532{ 2533 if (!_arc) 2534 return S_OK; 2535 2536 CRecordVector<CDirPathSortPair> pairs; 2537 pairs.ClearAndSetSize(_extractedFolders.Size()); 2538 unsigned i; 2539 2540 for (i = 0; i < _extractedFolders.Size(); i++) 2541 { 2542 CDirPathSortPair &pair = pairs[i]; 2543 pair.Index = i; 2544 pair.SetNumSlashes(_extractedFolders[i].Path); 2545 } 2546 2547 pairs.Sort2(); 2548 2549 HRESULT res = S_OK; 2550 2551 for (i = 0; i < pairs.Size(); i++) 2552 { 2553 const CDirPathTime &dpt = _extractedFolders[pairs[i].Index]; 2554 if (!dpt.SetDirTime()) 2555 { 2556 // result = E_FAIL; 2557 // do we need error message here in Windows and in posix? 2558 // SendMessageError_with_LastError("Cannot set directory time", dpt.Path); 2559 } 2560 } 2561 2562 /* 2563 #ifndef _WIN32 2564 for (i = 0; i < _delayedSymLinks.Size(); i++) 2565 { 2566 const CDelayedSymLink &link = _delayedSymLinks[i]; 2567 if (!link.Create()) 2568 { 2569 if (res == S_OK) 2570 res = GetLastError_noZero_HRESULT(); 2571 // res = E_FAIL; 2572 // do we need error message here in Windows and in posix? 2573 SendMessageError_with_LastError("Cannot create Symbolic Link", link._source); 2574 } 2575 } 2576 #endif // _WIN32 2577 */ 2578 2579 ClearExtractedDirsInfo(); 2580 return res; 2581} 2582 2583 2584HRESULT CArchiveExtractCallback::CloseArc() 2585{ 2586 HRESULT res = CloseReparseAndFile(); 2587 const HRESULT res2 = SetDirsTimes(); 2588 if (res == S_OK) 2589 res = res2; 2590 _arc = NULL; 2591 return res; 2592} 2593