1 // ArchiveExtractCallback.cpp 2 3 #include "StdAfx.h" 4 5 #undef sprintf 6 #undef printf 7 8 #include "../../../Common/ComTry.h" 9 #include "../../../Common/StringConvert.h" 10 #include "../../../Common/Wildcard.h" 11 12 #include "../../../Windows/FileDir.h" 13 #include "../../../Windows/FileFind.h" 14 #include "../../../Windows/FileName.h" 15 #include "../../../Windows/PropVariant.h" 16 #include "../../../Windows/PropVariantConv.h" 17 18 #if defined(_WIN32) && !defined(UNDER_CE) && !defined(_SFX) 19 #define _USE_SECURITY_CODE 20 #include "../../../Windows/SecurityUtils.h" 21 #endif 22 23 #include "../../Common/FilePathAutoRename.h" 24 25 #include "../Common/ExtractingFilePath.h" 26 #include "../Common/PropIDUtils.h" 27 28 #include "ArchiveExtractCallback.h" 29 30 using namespace NWindows; 31 using namespace NFile; 32 using namespace NDir; 33 34 static const char *kCantAutoRename = "Can not create file with auto name"; 35 static const char *kCantRenameFile = "Can not rename existing file"; 36 static const char *kCantDeleteOutputFile = "Can not delete output file"; 37 static const char *kCantDeleteOutputDir = "Can not delete output folder"; 38 39 40 #ifndef _SFX 41 42 STDMETHODIMP COutStreamWithHash::Write(const void *data, UInt32 size, UInt32 *processedSize) 43 { 44 HRESULT result = S_OK; 45 if (_stream) 46 result = _stream->Write(data, size, &size); 47 if (_calculate) 48 _hash->Update(data, size); 49 _size += size; 50 if (processedSize) 51 *processedSize = size; 52 return result; 53 } 54 55 #endif 56 57 #ifdef _USE_SECURITY_CODE 58 bool InitLocalPrivileges() 59 { 60 NSecurity::CAccessToken token; 61 if (!token.OpenProcessToken(GetCurrentProcess(), 62 TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES)) 63 return false; 64 65 TOKEN_PRIVILEGES tp; 66 67 tp.PrivilegeCount = 1; 68 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 69 70 if (!::LookupPrivilegeValue(NULL, SE_SECURITY_NAME, &tp.Privileges[0].Luid)) 71 return false; 72 if (!token.AdjustPrivileges(&tp)) 73 return false; 74 return (GetLastError() == ERROR_SUCCESS); 75 } 76 #endif 77 78 #ifdef SUPPORT_LINKS 79 80 int CHardLinkNode::Compare(const CHardLinkNode &a) const 81 { 82 if (StreamId < a.StreamId) return -1; 83 if (StreamId > a.StreamId) return 1; 84 return MyCompare(INode, a.INode); 85 } 86 87 HRESULT Archive_Get_HardLinkNode(IInArchive *archive, UInt32 index, CHardLinkNode &h, bool &defined) 88 { 89 h.INode = 0; 90 h.StreamId = (UInt64)(Int64)-1; 91 defined = false; 92 { 93 NCOM::CPropVariant prop; 94 RINOK(archive->GetProperty(index, kpidINode, &prop)); 95 if (!ConvertPropVariantToUInt64(prop, h.INode)) 96 return S_OK; 97 } 98 { 99 NCOM::CPropVariant prop; 100 RINOK(archive->GetProperty(index, kpidStreamId, &prop)); 101 ConvertPropVariantToUInt64(prop, h.StreamId); 102 } 103 defined = true; 104 return S_OK; 105 } 106 107 108 HRESULT CArchiveExtractCallback::PrepareHardLinks(const CRecordVector<UInt32> *realIndices) 109 { 110 _hardLinks.Clear(); 111 112 if (!_arc->Ask_INode) 113 return S_OK; 114 115 IInArchive *archive = _arc->Archive; 116 CRecordVector<CHardLinkNode> &hardIDs = _hardLinks.IDs; 117 118 { 119 UInt32 numItems; 120 if (realIndices) 121 numItems = realIndices->Size(); 122 else 123 { 124 RINOK(archive->GetNumberOfItems(&numItems)); 125 } 126 127 for (UInt32 i = 0; i < numItems; i++) 128 { 129 CHardLinkNode h; 130 bool defined; 131 RINOK(Archive_Get_HardLinkNode(archive, realIndices ? (*realIndices)[i] : i, h, defined)); 132 if (defined) 133 hardIDs.Add(h); 134 } 135 } 136 137 hardIDs.Sort2(); 138 139 { 140 // wee keep only items that have 2 or more items 141 unsigned k = 0; 142 unsigned numSame = 1; 143 for (unsigned i = 1; i < hardIDs.Size(); i++) 144 { 145 if (hardIDs[i].Compare(hardIDs[i - 1]) != 0) 146 numSame = 1; 147 else if (++numSame == 2) 148 { 149 if (i - 1 != k) 150 hardIDs[k] = hardIDs[i - 1]; 151 k++; 152 } 153 } 154 hardIDs.DeleteFrom(k); 155 } 156 157 _hardLinks.PrepareLinks(); 158 return S_OK; 159 } 160 161 #endif 162 163 CArchiveExtractCallback::CArchiveExtractCallback(): 164 WriteCTime(true), 165 WriteATime(true), 166 WriteMTime(true), 167 _multiArchives(false) 168 { 169 LocalProgressSpec = new CLocalProgress(); 170 _localProgress = LocalProgressSpec; 171 172 #ifdef _USE_SECURITY_CODE 173 _saclEnabled = InitLocalPrivileges(); 174 #endif 175 } 176 177 void CArchiveExtractCallback::Init( 178 const CExtractNtOptions &ntOptions, 179 const NWildcard::CCensorNode *wildcardCensor, 180 const CArc *arc, 181 IFolderArchiveExtractCallback *extractCallback2, 182 bool stdOutMode, bool testMode, 183 const FString &directoryPath, 184 const UStringVector &removePathParts, 185 UInt64 packSize) 186 { 187 _extractedFolderPaths.Clear(); 188 _extractedFolderIndices.Clear(); 189 190 #ifdef SUPPORT_LINKS 191 _hardLinks.Clear(); 192 #endif 193 194 _ntOptions = ntOptions; 195 _wildcardCensor = wildcardCensor; 196 197 _stdOutMode = stdOutMode; 198 _testMode = testMode; 199 _unpTotal = 1; 200 _packTotal = packSize; 201 202 _extractCallback2 = extractCallback2; 203 _compressProgress.Release(); 204 _extractCallback2.QueryInterface(IID_ICompressProgressInfo, &_compressProgress); 205 206 #ifndef _SFX 207 208 _extractCallback2.QueryInterface(IID_IFolderExtractToStreamCallback, &ExtractToStreamCallback); 209 if (ExtractToStreamCallback) 210 { 211 Int32 useStreams = 0; 212 if (ExtractToStreamCallback->UseExtractToStream(&useStreams) != S_OK) 213 useStreams = 0; 214 if (useStreams == 0) 215 ExtractToStreamCallback.Release(); 216 } 217 218 #endif 219 220 LocalProgressSpec->Init(extractCallback2, true); 221 LocalProgressSpec->SendProgress = false; 222 223 _removePathParts = removePathParts; 224 _baseParentFolder = (UInt32)(Int32)-1; 225 _use_baseParentFolder_mode = false; 226 227 _arc = arc; 228 _directoryPath = directoryPath; 229 NName::NormalizeDirPathPrefix(_directoryPath); 230 NDir::MyGetFullPathName(directoryPath, _directoryPathFull); 231 } 232 233 STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 size) 234 { 235 COM_TRY_BEGIN 236 _unpTotal = size; 237 if (!_multiArchives && _extractCallback2) 238 return _extractCallback2->SetTotal(size); 239 return S_OK; 240 COM_TRY_END 241 } 242 243 static void NormalizeVals(UInt64 &v1, UInt64 &v2) 244 { 245 const UInt64 kMax = (UInt64)1 << 31; 246 while (v1 > kMax) 247 { 248 v1 >>= 1; 249 v2 >>= 1; 250 } 251 } 252 253 static UInt64 MyMultDiv64(UInt64 unpCur, UInt64 unpTotal, UInt64 packTotal) 254 { 255 NormalizeVals(packTotal, unpTotal); 256 NormalizeVals(unpCur, unpTotal); 257 if (unpTotal == 0) 258 unpTotal = 1; 259 return unpCur * packTotal / unpTotal; 260 } 261 262 STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 *completeValue) 263 { 264 COM_TRY_BEGIN 265 if (!_extractCallback2) 266 return S_OK; 267 268 if (_multiArchives) 269 { 270 if (completeValue != NULL) 271 { 272 UInt64 packCur = LocalProgressSpec->InSize + MyMultDiv64(*completeValue, _unpTotal, _packTotal); 273 return _extractCallback2->SetCompleted(&packCur); 274 } 275 } 276 return _extractCallback2->SetCompleted(completeValue); 277 COM_TRY_END 278 } 279 280 STDMETHODIMP CArchiveExtractCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) 281 { 282 COM_TRY_BEGIN 283 return _localProgress->SetRatioInfo(inSize, outSize); 284 COM_TRY_END 285 } 286 287 #define IS_LETTER_CHAR(c) ((c) >= 'a' && (c) <= 'z' || (c) >= 'A' && (c) <= 'Z') 288 289 static inline bool IsDriveName(const UString &s) 290 { 291 return s.Len() == 2 && s[1] == ':' && IS_LETTER_CHAR(s[0]); 292 } 293 294 void CArchiveExtractCallback::CreateComplexDirectory(const UStringVector &dirPathParts, FString &fullPath) 295 { 296 bool isAbsPath = false; 297 298 if (!dirPathParts.IsEmpty()) 299 { 300 const UString &s = dirPathParts[0]; 301 if (s.IsEmpty()) 302 isAbsPath = true; 303 #ifdef _WIN32 304 else 305 { 306 if (dirPathParts.Size() > 1 && IsDriveName(s)) 307 isAbsPath = true; 308 } 309 #endif 310 } 311 312 if (_pathMode == NExtract::NPathMode::kAbsPaths && isAbsPath) 313 fullPath.Empty(); 314 else 315 fullPath = _directoryPath; 316 317 FOR_VECTOR (i, dirPathParts) 318 { 319 if (i > 0) 320 fullPath += FCHAR_PATH_SEPARATOR; 321 const UString &s = dirPathParts[i]; 322 fullPath += us2fs(s); 323 #ifdef _WIN32 324 if (_pathMode == NExtract::NPathMode::kAbsPaths) 325 if (i == 0 && IsDriveName(s)) 326 continue; 327 #endif 328 CreateDir(fullPath); 329 } 330 } 331 332 HRESULT CArchiveExtractCallback::GetTime(int index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined) 333 { 334 filetimeIsDefined = false; 335 NCOM::CPropVariant prop; 336 RINOK(_arc->Archive->GetProperty(index, propID, &prop)); 337 if (prop.vt == VT_FILETIME) 338 { 339 filetime = prop.filetime; 340 filetimeIsDefined = (filetime.dwHighDateTime != 0 || filetime.dwLowDateTime != 0); 341 } 342 else if (prop.vt != VT_EMPTY) 343 return E_FAIL; 344 return S_OK; 345 } 346 347 HRESULT CArchiveExtractCallback::GetUnpackSize() 348 { 349 return _arc->GetItemSize(_index, _curSize, _curSizeDefined); 350 } 351 352 HRESULT CArchiveExtractCallback::SendMessageError(const char *message, const FString &path) 353 { 354 return _extractCallback2->MessageError( 355 UString(L"ERROR: ") + 356 GetUnicodeString(message) + L": " + fs2us(path)); 357 } 358 359 HRESULT CArchiveExtractCallback::SendMessageError2(const char *message, const FString &path1, const FString &path2) 360 { 361 return _extractCallback2->MessageError( 362 UString(L"ERROR: ") + 363 GetUnicodeString(message) + UString(L": ") + fs2us(path1) + UString(L" : ") + fs2us(path2)); 364 } 365 366 #ifndef _SFX 367 368 STDMETHODIMP CGetProp::GetProp(PROPID propID, PROPVARIANT *value) 369 { 370 if (propID == kpidName) 371 { 372 COM_TRY_BEGIN 373 NCOM::CPropVariant prop = Name.Ptr(); 374 prop.Detach(value); 375 return S_OK; 376 COM_TRY_END 377 } 378 return Arc->Archive->GetProperty(IndexInArc, propID, value); 379 } 380 381 #endif 382 383 384 #ifdef SUPPORT_LINKS 385 386 static UString GetDirPrefixOf(const UString &src) 387 { 388 UString s = src; 389 if (!s.IsEmpty()) 390 { 391 if (s.Back() == WCHAR_PATH_SEPARATOR) 392 s.DeleteBack(); 393 int pos = s.ReverseFind(WCHAR_PATH_SEPARATOR); 394 s.DeleteFrom(pos + 1); 395 } 396 return s; 397 } 398 399 static bool IsSafePath(const UString &path) 400 { 401 UStringVector parts; 402 SplitPathToParts(path, parts); 403 int level = 0; 404 FOR_VECTOR(i, parts) 405 { 406 const UString &s = parts[i]; 407 if (s.IsEmpty()) 408 continue; 409 if (s == L".") 410 continue; 411 if (s == L"..") 412 { 413 if (level <= 0) 414 return false; 415 level--; 416 } 417 else 418 level++; 419 } 420 return level > 0; 421 } 422 423 #endif 424 425 426 STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode) 427 { 428 COM_TRY_BEGIN 429 430 *outStream = 0; 431 432 #ifndef _SFX 433 if (_hashStream) 434 _hashStreamSpec->ReleaseStream(); 435 _hashStreamWasUsed = false; 436 #endif 437 438 _outFileStream.Release(); 439 440 _encrypted = false; 441 _isSplit = false; 442 _isAltStream = false; 443 _curSize = 0; 444 _curSizeDefined = false; 445 _index = index; 446 447 UString fullPath; 448 449 IInArchive *archive = _arc->Archive; 450 RINOK(_arc->GetItemPath(index, fullPath)); 451 RINOK(Archive_IsItem_Folder(archive, index, _fi.IsDir)); 452 453 _filePath = fullPath; 454 455 { 456 NCOM::CPropVariant prop; 457 RINOK(archive->GetProperty(index, kpidPosition, &prop)); 458 if (prop.vt != VT_EMPTY) 459 { 460 if (prop.vt != VT_UI8) 461 return E_FAIL; 462 _position = prop.uhVal.QuadPart; 463 _isSplit = true; 464 } 465 } 466 467 #ifdef SUPPORT_LINKS 468 469 bool isHardLink = false; 470 bool isJunction = false; 471 bool isRelative = false; 472 473 UString linkPath; 474 // RINOK(Archive_GetItemBoolProp(archive, index, kpidIsHardLink, isHardLink)); 475 // if (isHardLink) 476 { 477 NCOM::CPropVariant prop; 478 RINOK(archive->GetProperty(index, kpidHardLink, &prop)); 479 if (prop.vt == VT_BSTR) 480 { 481 isHardLink = true; 482 linkPath = prop.bstrVal; 483 isRelative = false; // TAR: hard links are from root folder of archive 484 } 485 else if (prop.vt == VT_EMPTY) 486 { 487 // linkPath.Empty(); 488 } 489 else 490 return E_FAIL; 491 } 492 { 493 NCOM::CPropVariant prop; 494 RINOK(archive->GetProperty(index, kpidSymLink, &prop)); 495 if (prop.vt == VT_BSTR) 496 { 497 isHardLink = false; 498 linkPath = prop.bstrVal; 499 isRelative = true; // TAR: symbolic links are relative 500 } 501 else if (prop.vt == VT_EMPTY) 502 { 503 // linkPath.Empty(); 504 } 505 else 506 return E_FAIL; 507 } 508 509 bool isOkReparse = false; 510 511 if (linkPath.IsEmpty() && _arc->GetRawProps) 512 { 513 const void *data; 514 UInt32 dataSize; 515 UInt32 propType; 516 _arc->GetRawProps->GetRawProp(_index, kpidNtReparse, &data, &dataSize, &propType); 517 if (dataSize != 0) 518 { 519 if (propType != NPropDataType::kRaw) 520 return E_FAIL; 521 UString s; 522 CReparseAttr reparse; 523 isOkReparse = reparse.Parse((const Byte *)data, dataSize); 524 if (isOkReparse) 525 { 526 isHardLink = false; 527 linkPath = reparse.GetPath(); 528 isJunction = reparse.IsMountPoint(); 529 isRelative = reparse.IsRelative(); 530 #ifndef _WIN32 531 linkPath.Replace(WCHAR_PATH_SEPARATOR, '/', ); 532 #endif 533 } 534 } 535 } 536 537 if (!linkPath.IsEmpty()) 538 { 539 #ifdef _WIN32 540 linkPath.Replace('/', WCHAR_PATH_SEPARATOR); 541 #endif 542 543 for (;;) 544 // while (NName::IsAbsolutePath(linkPath)) 545 { 546 unsigned n = NName::GetRootPrefixSize(linkPath); 547 if (n == 0) 548 break; 549 isRelative = false; 550 linkPath.DeleteFrontal(n); 551 } 552 } 553 554 if (!linkPath.IsEmpty() && !isRelative && _removePathParts.Size() != 0) 555 { 556 UStringVector pathParts; 557 SplitPathToParts(linkPath, pathParts); 558 bool badPrefix = false; 559 FOR_VECTOR (i, _removePathParts) 560 { 561 if (CompareFileNames(_removePathParts[i], pathParts[i]) != 0) 562 { 563 badPrefix = true; 564 break; 565 } 566 } 567 if (!badPrefix) 568 pathParts.DeleteFrontal(_removePathParts.Size()); 569 linkPath = MakePathNameFromParts(pathParts); 570 } 571 572 #endif 573 574 RINOK(Archive_GetItemBoolProp(archive, index, kpidEncrypted, _encrypted)); 575 576 RINOK(GetUnpackSize()); 577 578 RINOK(Archive_IsItem_AltStream(archive, index, _isAltStream)); 579 580 if (!_ntOptions.AltStreams.Val && _isAltStream) 581 return S_OK; 582 583 if (_wildcardCensor) 584 { 585 if (!_wildcardCensor->CheckPath(_isAltStream, fullPath, !_fi.IsDir)) 586 return S_OK; 587 } 588 589 590 UStringVector pathParts; 591 592 if (_use_baseParentFolder_mode) 593 { 594 int baseParent = _baseParentFolder; 595 if (_pathMode == NExtract::NPathMode::kFullPaths || 596 _pathMode == NExtract::NPathMode::kAbsPaths) 597 baseParent = -1; 598 RINOK(_arc->GetItemPathToParent(index, baseParent, pathParts)); 599 if (_pathMode == NExtract::NPathMode::kNoPaths && !pathParts.IsEmpty()) 600 pathParts.DeleteFrontal(pathParts.Size() - 1); 601 } 602 else 603 { 604 SplitPathToParts(fullPath, pathParts); 605 606 if (pathParts.IsEmpty()) 607 return E_FAIL; 608 unsigned numRemovePathParts = 0; 609 610 switch (_pathMode) 611 { 612 case NExtract::NPathMode::kCurPaths: 613 { 614 bool badPrefix = false; 615 if (pathParts.Size() <= _removePathParts.Size()) 616 badPrefix = true; 617 else 618 { 619 FOR_VECTOR (i, _removePathParts) 620 { 621 if (!_removePathParts[i].IsEqualToNoCase(pathParts[i])) 622 { 623 badPrefix = true; 624 break; 625 } 626 } 627 } 628 if (badPrefix) 629 { 630 if (askExtractMode == NArchive::NExtract::NAskMode::kExtract && !_testMode) 631 return E_FAIL; 632 } 633 else 634 numRemovePathParts = _removePathParts.Size(); 635 break; 636 } 637 case NExtract::NPathMode::kNoPaths: 638 { 639 numRemovePathParts = pathParts.Size() - 1; 640 break; 641 } 642 /* 643 case NExtract::NPathMode::kFullPaths: 644 case NExtract::NPathMode::kAbsPaths: 645 break; 646 */ 647 } 648 649 pathParts.DeleteFrontal(numRemovePathParts); 650 } 651 652 #ifndef _SFX 653 654 if (ExtractToStreamCallback) 655 { 656 if (!GetProp) 657 { 658 GetProp_Spec = new CGetProp; 659 GetProp = GetProp_Spec; 660 } 661 GetProp_Spec->Arc = _arc; 662 GetProp_Spec->IndexInArc = index; 663 GetProp_Spec->Name = MakePathNameFromParts(pathParts); 664 665 return ExtractToStreamCallback->GetStream7(GetProp_Spec->Name, _fi.IsDir, outStream, askExtractMode, GetProp); 666 } 667 668 #endif 669 670 CMyComPtr<ISequentialOutStream> outStreamLoc; 671 672 if (askExtractMode == NArchive::NExtract::NAskMode::kExtract && !_testMode) 673 { 674 if (_stdOutMode) 675 { 676 outStreamLoc = new CStdOutFileStream; 677 } 678 else 679 { 680 { 681 NCOM::CPropVariant prop; 682 RINOK(archive->GetProperty(index, kpidAttrib, &prop)); 683 if (prop.vt == VT_UI4) 684 { 685 _fi.Attrib = prop.ulVal; 686 _fi.AttribDefined = true; 687 } 688 else if (prop.vt == VT_EMPTY) 689 _fi.AttribDefined = false; 690 else 691 return E_FAIL; 692 } 693 694 RINOK(GetTime(index, kpidCTime, _fi.CTime, _fi.CTimeDefined)); 695 RINOK(GetTime(index, kpidATime, _fi.ATime, _fi.ATimeDefined)); 696 RINOK(GetTime(index, kpidMTime, _fi.MTime, _fi.MTimeDefined)); 697 698 bool isAnti = false; 699 RINOK(_arc->IsItemAnti(index, isAnti)); 700 701 bool replace = _isAltStream ? 702 _ntOptions.ReplaceColonForAltStream : 703 !_ntOptions.WriteToAltStreamIfColon; 704 705 if (_pathMode != NExtract::NPathMode::kAbsPaths) 706 MakeCorrectPath(_directoryPath.IsEmpty(), pathParts, replace); 707 Correct_IfEmptyLastPart(pathParts); 708 UString processedPath = MakePathNameFromParts(pathParts); 709 710 if (!isAnti) 711 { 712 if (!_fi.IsDir) 713 { 714 if (!pathParts.IsEmpty()) 715 pathParts.DeleteBack(); 716 } 717 718 if (!pathParts.IsEmpty()) 719 { 720 FString fullPathNew; 721 CreateComplexDirectory(pathParts, fullPathNew); 722 if (_fi.IsDir) 723 { 724 _extractedFolderPaths.Add(fullPathNew); 725 _extractedFolderIndices.Add(index); 726 SetDirTime(fullPathNew, 727 (WriteCTime && _fi.CTimeDefined) ? &_fi.CTime : NULL, 728 (WriteATime && _fi.ATimeDefined) ? &_fi.ATime : NULL, 729 (WriteMTime && _fi.MTimeDefined) ? &_fi.MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL)); 730 } 731 } 732 } 733 734 735 FString fullProcessedPath = us2fs(processedPath); 736 if (_pathMode != NExtract::NPathMode::kAbsPaths || 737 !NName::IsAbsolutePath(processedPath)) 738 fullProcessedPath = _directoryPath + fullProcessedPath; 739 740 if (_fi.IsDir) 741 { 742 _diskFilePath = fullProcessedPath; 743 if (isAnti) 744 RemoveDir(_diskFilePath); 745 #ifdef SUPPORT_LINKS 746 if (linkPath.IsEmpty()) 747 #endif 748 return S_OK; 749 } 750 else if (!_isSplit) 751 { 752 NFind::CFileInfo fileInfo; 753 if (fileInfo.Find(fullProcessedPath)) 754 { 755 switch (_overwriteMode) 756 { 757 case NExtract::NOverwriteMode::kSkip: 758 return S_OK; 759 case NExtract::NOverwriteMode::kAsk: 760 { 761 int slashPos = fullProcessedPath.ReverseFind(FTEXT('/')); 762 #ifdef _WIN32 763 int slash1Pos = fullProcessedPath.ReverseFind(FTEXT('\\')); 764 slashPos = MyMax(slashPos, slash1Pos); 765 #endif 766 FString realFullProcessedPath = fullProcessedPath.Left(slashPos + 1) + fileInfo.Name; 767 768 Int32 overwiteResult; 769 RINOK(_extractCallback2->AskOverwrite( 770 fs2us(realFullProcessedPath), &fileInfo.MTime, &fileInfo.Size, fullPath, 771 _fi.MTimeDefined ? &_fi.MTime : NULL, 772 _curSizeDefined ? &_curSize : NULL, 773 &overwiteResult)) 774 775 switch (overwiteResult) 776 { 777 case NOverwriteAnswer::kCancel: return E_ABORT; 778 case NOverwriteAnswer::kNo: return S_OK; 779 case NOverwriteAnswer::kNoToAll: _overwriteMode = NExtract::NOverwriteMode::kSkip; return S_OK; 780 case NOverwriteAnswer::kYes: break; 781 case NOverwriteAnswer::kYesToAll: _overwriteMode = NExtract::NOverwriteMode::kOverwrite; break; 782 case NOverwriteAnswer::kAutoRename: _overwriteMode = NExtract::NOverwriteMode::kRename; break; 783 default: 784 return E_FAIL; 785 } 786 } 787 } 788 if (_overwriteMode == NExtract::NOverwriteMode::kRename) 789 { 790 if (!AutoRenamePath(fullProcessedPath)) 791 { 792 RINOK(SendMessageError(kCantAutoRename, fullProcessedPath)); 793 return E_FAIL; 794 } 795 } 796 else if (_overwriteMode == NExtract::NOverwriteMode::kRenameExisting) 797 { 798 FString existPath = fullProcessedPath; 799 if (!AutoRenamePath(existPath)) 800 { 801 RINOK(SendMessageError(kCantAutoRename, fullProcessedPath)); 802 return E_FAIL; 803 } 804 // MyMoveFile can raname folders. So it's OK to use it folders too 805 if (!MyMoveFile(fullProcessedPath, existPath)) 806 { 807 RINOK(SendMessageError(kCantRenameFile, fullProcessedPath)); 808 return E_FAIL; 809 } 810 } 811 else 812 { 813 if (fileInfo.IsDir()) 814 { 815 // do we need to delete all files in folder? 816 if (!RemoveDir(fullProcessedPath)) 817 { 818 RINOK(SendMessageError(kCantDeleteOutputDir, fullProcessedPath)); 819 return S_OK; 820 } 821 } 822 else if (!DeleteFileAlways(fullProcessedPath)) 823 { 824 RINOK(SendMessageError(kCantDeleteOutputFile, fullProcessedPath)); 825 return S_OK; 826 // return E_FAIL; 827 } 828 } 829 } 830 } 831 _diskFilePath = fullProcessedPath; 832 833 834 if (!isAnti) 835 { 836 #ifdef SUPPORT_LINKS 837 838 if (!linkPath.IsEmpty()) 839 { 840 #ifndef UNDER_CE 841 842 UString relatPath; 843 if (isRelative) 844 relatPath = GetDirPrefixOf(_filePath); 845 relatPath += linkPath; 846 847 if (!IsSafePath(relatPath)) 848 { 849 RINOK(SendMessageError("Dangerous link path was ignored", us2fs(relatPath))); 850 } 851 else 852 { 853 FString existPath; 854 if (isHardLink || !isRelative) 855 { 856 if (!NName::GetFullPath(_directoryPathFull, us2fs(relatPath), existPath)) 857 { 858 RINOK(SendMessageError("Incorrect path", us2fs(relatPath))); 859 } 860 } 861 else 862 { 863 existPath = us2fs(linkPath); 864 } 865 866 if (!existPath.IsEmpty()) 867 { 868 if (isHardLink) 869 { 870 if (!MyCreateHardLink(fullProcessedPath, existPath)) 871 { 872 RINOK(SendMessageError2("Can not create hard link", fullProcessedPath, existPath)); 873 // return S_OK; 874 } 875 } 876 else if (_ntOptions.SymLinks.Val) 877 { 878 // bool isSymLink = true; // = false for junction 879 if (_fi.IsDir && !isRelative) 880 { 881 // if it's before Vista we use Junction Point 882 // isJunction = true; 883 // convertToAbs = true; 884 } 885 886 CByteBuffer data; 887 if (FillLinkData(data, fs2us(existPath), !isJunction)) 888 { 889 CReparseAttr attr; 890 if (!attr.Parse(data, data.Size())) 891 { 892 return E_FAIL; // "Internal conversion error"; 893 } 894 895 if (!NFile::NIO::SetReparseData(fullProcessedPath, _fi.IsDir, data, (DWORD)data.Size())) 896 { 897 RINOK(SendMessageError("Can not set reparse data", fullProcessedPath)); 898 } 899 } 900 } 901 } 902 } 903 904 #endif 905 } 906 else 907 #endif // SUPPORT_LINKS 908 { 909 bool needWriteFile = true; 910 911 #ifdef SUPPORT_LINKS 912 if (!_hardLinks.IDs.IsEmpty()) 913 { 914 CHardLinkNode h; 915 bool defined; 916 RINOK(Archive_Get_HardLinkNode(archive, index, h, defined)); 917 if (defined) 918 { 919 { 920 int linkIndex = _hardLinks.IDs.FindInSorted2(h); 921 if (linkIndex >= 0) 922 { 923 FString &hl = _hardLinks.Links[linkIndex]; 924 if (hl.IsEmpty()) 925 hl = fullProcessedPath; 926 else 927 { 928 if (!MyCreateHardLink(fullProcessedPath, hl)) 929 { 930 RINOK(SendMessageError2("Can not create hard link", fullProcessedPath, hl)); 931 return S_OK; 932 } 933 needWriteFile = false; 934 } 935 } 936 } 937 } 938 } 939 #endif 940 941 if (needWriteFile) 942 { 943 _outFileStreamSpec = new COutFileStream; 944 CMyComPtr<ISequentialOutStream> outStreamLoc(_outFileStreamSpec); 945 if (!_outFileStreamSpec->Open(fullProcessedPath, _isSplit ? OPEN_ALWAYS: CREATE_ALWAYS)) 946 { 947 // if (::GetLastError() != ERROR_FILE_EXISTS || !isSplit) 948 { 949 RINOK(SendMessageError("Can not open output file ", fullProcessedPath)); 950 return S_OK; 951 } 952 } 953 if (_isSplit) 954 { 955 RINOK(_outFileStreamSpec->Seek(_position, STREAM_SEEK_SET, NULL)); 956 } 957 _outFileStream = outStreamLoc; 958 } 959 } 960 } 961 962 outStreamLoc = _outFileStream; 963 } 964 } 965 966 #ifndef _SFX 967 968 if (_hashStream) 969 { 970 if (askExtractMode == NArchive::NExtract::NAskMode::kExtract || 971 askExtractMode == NArchive::NExtract::NAskMode::kTest) 972 { 973 _hashStreamSpec->SetStream(outStreamLoc); 974 outStreamLoc = _hashStream; 975 _hashStreamSpec->Init(true); 976 _hashStreamWasUsed = true; 977 } 978 } 979 980 #endif 981 982 if (outStreamLoc) 983 *outStream = outStreamLoc.Detach(); 984 return S_OK; 985 COM_TRY_END 986 } 987 988 STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode) 989 { 990 COM_TRY_BEGIN 991 992 #ifndef _SFX 993 if (ExtractToStreamCallback) 994 return ExtractToStreamCallback->PrepareOperation7(askExtractMode); 995 #endif 996 997 _extractMode = false; 998 switch (askExtractMode) 999 { 1000 case NArchive::NExtract::NAskMode::kExtract: 1001 if (_testMode) 1002 askExtractMode = NArchive::NExtract::NAskMode::kTest; 1003 else 1004 _extractMode = true; 1005 break; 1006 }; 1007 return _extractCallback2->PrepareOperation(_filePath, _fi.IsDir, 1008 askExtractMode, _isSplit ? &_position: 0); 1009 COM_TRY_END 1010 } 1011 1012 STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult) 1013 { 1014 COM_TRY_BEGIN 1015 1016 #ifndef _SFX 1017 if (ExtractToStreamCallback) 1018 return ExtractToStreamCallback->SetOperationResult7(operationResult, _encrypted); 1019 #endif 1020 1021 #ifndef _SFX 1022 1023 if (_hashStreamWasUsed) 1024 { 1025 _hashStreamSpec->_hash->Final(_fi.IsDir, _isAltStream, _filePath); 1026 _curSize = _hashStreamSpec->GetSize(); 1027 _curSizeDefined = true; 1028 _hashStreamSpec->ReleaseStream(); 1029 _hashStreamWasUsed = false; 1030 } 1031 1032 #endif 1033 1034 if (_outFileStream) 1035 { 1036 _outFileStreamSpec->SetTime( 1037 (WriteCTime && _fi.CTimeDefined) ? &_fi.CTime : NULL, 1038 (WriteATime && _fi.ATimeDefined) ? &_fi.ATime : NULL, 1039 (WriteMTime && _fi.MTimeDefined) ? &_fi.MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL)); 1040 _curSize = _outFileStreamSpec->ProcessedSize; 1041 _curSizeDefined = true; 1042 RINOK(_outFileStreamSpec->Close()); 1043 _outFileStream.Release(); 1044 } 1045 1046 #ifdef _USE_SECURITY_CODE 1047 if (_ntOptions.NtSecurity.Val && _arc->GetRawProps) 1048 { 1049 const void *data; 1050 UInt32 dataSize; 1051 UInt32 propType; 1052 _arc->GetRawProps->GetRawProp(_index, kpidNtSecure, &data, &dataSize, &propType); 1053 if (dataSize != 0) 1054 { 1055 if (propType != NPropDataType::kRaw) 1056 return E_FAIL; 1057 if (CheckNtSecure((const Byte *)data, dataSize)) 1058 { 1059 SECURITY_INFORMATION securInfo = DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION; 1060 if (_saclEnabled) 1061 securInfo |= SACL_SECURITY_INFORMATION; 1062 ::SetFileSecurityW(fs2us(_diskFilePath), securInfo, (PSECURITY_DESCRIPTOR)(void *)data); 1063 } 1064 } 1065 } 1066 #endif 1067 1068 if (!_curSizeDefined) 1069 GetUnpackSize(); 1070 if (_curSizeDefined) 1071 { 1072 if (_isAltStream) 1073 AltStreams_UnpackSize += _curSize; 1074 else 1075 UnpackSize += _curSize; 1076 } 1077 1078 if (_fi.IsDir) 1079 NumFolders++; 1080 else if (_isAltStream) 1081 NumAltStreams++; 1082 else 1083 NumFiles++; 1084 1085 if (_extractMode && _fi.AttribDefined) 1086 SetFileAttrib(_diskFilePath, _fi.Attrib); 1087 RINOK(_extractCallback2->SetOperationResult(operationResult, _encrypted)); 1088 return S_OK; 1089 COM_TRY_END 1090 } 1091 1092 /* 1093 STDMETHODIMP CArchiveExtractCallback::GetInStream( 1094 const wchar_t *name, ISequentialInStream **inStream) 1095 { 1096 COM_TRY_BEGIN 1097 CInFileStream *inFile = new CInFileStream; 1098 CMyComPtr<ISequentialInStream> inStreamTemp = inFile; 1099 if (!inFile->Open(_srcDirectoryPrefix + name)) 1100 return ::GetLastError(); 1101 *inStream = inStreamTemp.Detach(); 1102 return S_OK; 1103 COM_TRY_END 1104 } 1105 */ 1106 1107 STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password) 1108 { 1109 COM_TRY_BEGIN 1110 if (!_cryptoGetTextPassword) 1111 { 1112 RINOK(_extractCallback2.QueryInterface(IID_ICryptoGetTextPassword, 1113 &_cryptoGetTextPassword)); 1114 } 1115 return _cryptoGetTextPassword->CryptoGetTextPassword(password); 1116 COM_TRY_END 1117 } 1118 1119 1120 struct CExtrRefSortPair 1121 { 1122 int Len; 1123 int Index; 1124 1125 int Compare(const CExtrRefSortPair &a) const; 1126 }; 1127 1128 #define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; } 1129 1130 int CExtrRefSortPair::Compare(const CExtrRefSortPair &a) const 1131 { 1132 RINOZ(-MyCompare(Len, a.Len)); 1133 return MyCompare(Index, a.Index); 1134 } 1135 1136 static int GetNumSlashes(const FChar *s) 1137 { 1138 for (int numSlashes = 0;;) 1139 { 1140 FChar c = *s++; 1141 if (c == 0) 1142 return numSlashes; 1143 if ( 1144 #ifdef _WIN32 1145 c == FTEXT('\\') || 1146 #endif 1147 c == FTEXT('/')) 1148 numSlashes++; 1149 } 1150 } 1151 1152 HRESULT CArchiveExtractCallback::SetDirsTimes() 1153 { 1154 CRecordVector<CExtrRefSortPair> pairs; 1155 pairs.ClearAndSetSize(_extractedFolderPaths.Size()); 1156 unsigned i; 1157 1158 for (i = 0; i < _extractedFolderPaths.Size(); i++) 1159 { 1160 CExtrRefSortPair &pair = pairs[i]; 1161 pair.Index = i; 1162 pair.Len = GetNumSlashes(_extractedFolderPaths[i]); 1163 } 1164 1165 pairs.Sort2(); 1166 1167 for (i = 0; i < pairs.Size(); i++) 1168 { 1169 int pairIndex = pairs[i].Index; 1170 int index = _extractedFolderIndices[pairIndex]; 1171 1172 FILETIME CTime; 1173 FILETIME ATime; 1174 FILETIME MTime; 1175 1176 bool CTimeDefined; 1177 bool ATimeDefined; 1178 bool MTimeDefined; 1179 1180 RINOK(GetTime(index, kpidCTime, CTime, CTimeDefined)); 1181 RINOK(GetTime(index, kpidATime, ATime, ATimeDefined)); 1182 RINOK(GetTime(index, kpidMTime, MTime, MTimeDefined)); 1183 1184 // printf("\n%S", _extractedFolderPaths[pairIndex]); 1185 SetDirTime(_extractedFolderPaths[pairIndex], 1186 (WriteCTime && CTimeDefined) ? &CTime : NULL, 1187 (WriteATime && ATimeDefined) ? &ATime : NULL, 1188 (WriteMTime && MTimeDefined) ? &MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL)); 1189 } 1190 return S_OK; 1191 } 1192