Home | History | Annotate | Download | only in Common
      1 // ArchiveExtractCallback.cpp
      2 
      3 #include "StdAfx.h"
      4 
      5 #undef sprintf
      6 #undef printf
      7 
      8 #include "../../../../C/Alloc.h"
      9 
     10 #include "../../../Common/ComTry.h"
     11 #include "../../../Common/IntToString.h"
     12 #include "../../../Common/StringConvert.h"
     13 #include "../../../Common/Wildcard.h"
     14 
     15 #include "../../../Windows/ErrorMsg.h"
     16 #include "../../../Windows/FileDir.h"
     17 #include "../../../Windows/FileFind.h"
     18 #include "../../../Windows/FileName.h"
     19 #include "../../../Windows/PropVariant.h"
     20 #include "../../../Windows/PropVariantConv.h"
     21 
     22 #if defined(_WIN32) && !defined(UNDER_CE)  && !defined(_SFX)
     23 #define _USE_SECURITY_CODE
     24 #include "../../../Windows/SecurityUtils.h"
     25 #endif
     26 
     27 #include "../../Common/FilePathAutoRename.h"
     28 // #include "../../Common/StreamUtils.h"
     29 
     30 #include "../Common/ExtractingFilePath.h"
     31 #include "../Common/PropIDUtils.h"
     32 
     33 #include "ArchiveExtractCallback.h"
     34 
     35 using namespace NWindows;
     36 using namespace NFile;
     37 using namespace NDir;
     38 
     39 static const char *kCantAutoRename = "Can not create file with auto name";
     40 static const char *kCantRenameFile = "Can not rename existing file";
     41 static const char *kCantDeleteOutputFile = "Can not delete output file";
     42 static const char *kCantDeleteOutputDir = "Can not delete output folder";
     43 
     44 
     45 #ifndef _SFX
     46 
     47 STDMETHODIMP COutStreamWithHash::Write(const void *data, UInt32 size, UInt32 *processedSize)
     48 {
     49   HRESULT result = S_OK;
     50   if (_stream)
     51     result = _stream->Write(data, size, &size);
     52   if (_calculate)
     53     _hash->Update(data, size);
     54   _size += size;
     55   if (processedSize)
     56     *processedSize = size;
     57   return result;
     58 }
     59 
     60 #endif
     61 
     62 #ifdef _USE_SECURITY_CODE
     63 bool InitLocalPrivileges()
     64 {
     65   NSecurity::CAccessToken token;
     66   if (!token.OpenProcessToken(GetCurrentProcess(),
     67       TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES))
     68     return false;
     69 
     70   TOKEN_PRIVILEGES tp;
     71 
     72   tp.PrivilegeCount = 1;
     73   tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
     74 
     75   if  (!::LookupPrivilegeValue(NULL, SE_SECURITY_NAME, &tp.Privileges[0].Luid))
     76     return false;
     77   if (!token.AdjustPrivileges(&tp))
     78     return false;
     79   return (GetLastError() == ERROR_SUCCESS);
     80 }
     81 #endif
     82 
     83 #ifdef SUPPORT_LINKS
     84 
     85 int CHardLinkNode::Compare(const CHardLinkNode &a) const
     86 {
     87   if (StreamId < a.StreamId) return -1;
     88   if (StreamId > a.StreamId) return 1;
     89   return MyCompare(INode, a.INode);
     90 }
     91 
     92 static HRESULT Archive_Get_HardLinkNode(IInArchive *archive, UInt32 index, CHardLinkNode &h, bool &defined)
     93 {
     94   h.INode = 0;
     95   h.StreamId = (UInt64)(Int64)-1;
     96   defined = false;
     97   {
     98     NCOM::CPropVariant prop;
     99     RINOK(archive->GetProperty(index, kpidINode, &prop));
    100     if (!ConvertPropVariantToUInt64(prop, h.INode))
    101       return S_OK;
    102   }
    103   {
    104     NCOM::CPropVariant prop;
    105     RINOK(archive->GetProperty(index, kpidStreamId, &prop));
    106     ConvertPropVariantToUInt64(prop, h.StreamId);
    107   }
    108   defined = true;
    109   return S_OK;
    110 }
    111 
    112 
    113 HRESULT CArchiveExtractCallback::PrepareHardLinks(const CRecordVector<UInt32> *realIndices)
    114 {
    115   _hardLinks.Clear();
    116 
    117   if (!_arc->Ask_INode)
    118     return S_OK;
    119 
    120   IInArchive *archive = _arc->Archive;
    121   CRecordVector<CHardLinkNode> &hardIDs = _hardLinks.IDs;
    122 
    123   {
    124     UInt32 numItems;
    125     if (realIndices)
    126       numItems = realIndices->Size();
    127     else
    128     {
    129       RINOK(archive->GetNumberOfItems(&numItems));
    130     }
    131 
    132     for (UInt32 i = 0; i < numItems; i++)
    133     {
    134       CHardLinkNode h;
    135       bool defined;
    136       UInt32 realIndex = realIndices ? (*realIndices)[i] : i;
    137 
    138       RINOK(Archive_Get_HardLinkNode(archive, realIndex, h, defined));
    139       if (defined)
    140       {
    141         bool isAltStream = false;
    142         RINOK(Archive_IsItem_AltStream(archive, realIndex, isAltStream));
    143         if (!isAltStream)
    144           hardIDs.Add(h);
    145       }
    146     }
    147   }
    148 
    149   hardIDs.Sort2();
    150 
    151   {
    152     // wee keep only items that have 2 or more items
    153     unsigned k = 0;
    154     unsigned numSame = 1;
    155     for (unsigned i = 1; i < hardIDs.Size(); i++)
    156     {
    157       if (hardIDs[i].Compare(hardIDs[i - 1]) != 0)
    158         numSame = 1;
    159       else if (++numSame == 2)
    160       {
    161         if (i - 1 != k)
    162           hardIDs[k] = hardIDs[i - 1];
    163         k++;
    164       }
    165     }
    166     hardIDs.DeleteFrom(k);
    167   }
    168 
    169   _hardLinks.PrepareLinks();
    170   return S_OK;
    171 }
    172 
    173 #endif
    174 
    175 CArchiveExtractCallback::CArchiveExtractCallback():
    176     WriteCTime(true),
    177     WriteATime(true),
    178     WriteMTime(true),
    179     _multiArchives(false)
    180 {
    181   LocalProgressSpec = new CLocalProgress();
    182   _localProgress = LocalProgressSpec;
    183 
    184   #ifdef _USE_SECURITY_CODE
    185   _saclEnabled = InitLocalPrivileges();
    186   #endif
    187 }
    188 
    189 void CArchiveExtractCallback::Init(
    190     const CExtractNtOptions &ntOptions,
    191     const NWildcard::CCensorNode *wildcardCensor,
    192     const CArc *arc,
    193     IFolderArchiveExtractCallback *extractCallback2,
    194     bool stdOutMode, bool testMode,
    195     const FString &directoryPath,
    196     const UStringVector &removePathParts, bool removePartsForAltStreams,
    197     UInt64 packSize)
    198 {
    199   _extractedFolderPaths.Clear();
    200   _extractedFolderIndices.Clear();
    201 
    202   #ifdef SUPPORT_LINKS
    203   _hardLinks.Clear();
    204   #endif
    205 
    206   #ifdef SUPPORT_ALT_STREAMS
    207   _renamedFiles.Clear();
    208   #endif
    209 
    210   _ntOptions = ntOptions;
    211   _wildcardCensor = wildcardCensor;
    212 
    213   _stdOutMode = stdOutMode;
    214   _testMode = testMode;
    215 
    216   // _progressTotal = 0;
    217   // _progressTotal_Defined = false;
    218 
    219   _packTotal = packSize;
    220   _progressTotal = packSize;
    221   _progressTotal_Defined = true;
    222 
    223   _extractCallback2 = extractCallback2;
    224   _compressProgress.Release();
    225   _extractCallback2.QueryInterface(IID_ICompressProgressInfo, &_compressProgress);
    226   _extractCallback2.QueryInterface(IID_IArchiveExtractCallbackMessage, &_callbackMessage);
    227   _extractCallback2.QueryInterface(IID_IFolderArchiveExtractCallback2, &_folderArchiveExtractCallback2);
    228 
    229   #ifndef _SFX
    230 
    231   _extractCallback2.QueryInterface(IID_IFolderExtractToStreamCallback, &ExtractToStreamCallback);
    232   if (ExtractToStreamCallback)
    233   {
    234     Int32 useStreams = 0;
    235     if (ExtractToStreamCallback->UseExtractToStream(&useStreams) != S_OK)
    236       useStreams = 0;
    237     if (useStreams == 0)
    238       ExtractToStreamCallback.Release();
    239   }
    240 
    241   #endif
    242 
    243   LocalProgressSpec->Init(extractCallback2, true);
    244   LocalProgressSpec->SendProgress = false;
    245 
    246   _removePathParts = removePathParts;
    247   _removePartsForAltStreams = removePartsForAltStreams;
    248 
    249   #ifndef _SFX
    250   _baseParentFolder = (UInt32)(Int32)-1;
    251   _use_baseParentFolder_mode = false;
    252   #endif
    253 
    254   _arc = arc;
    255   _dirPathPrefix = directoryPath;
    256   _dirPathPrefix_Full = directoryPath;
    257   #if defined(_WIN32) && !defined(UNDER_CE)
    258   if (!NName::IsAltPathPrefix(_dirPathPrefix))
    259   #endif
    260   {
    261     NName::NormalizeDirPathPrefix(_dirPathPrefix);
    262     NDir::MyGetFullPathName(directoryPath, _dirPathPrefix_Full);
    263     NName::NormalizeDirPathPrefix(_dirPathPrefix_Full);
    264   }
    265 }
    266 
    267 STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 size)
    268 {
    269   COM_TRY_BEGIN
    270   _progressTotal = size;
    271   _progressTotal_Defined = true;
    272   if (!_multiArchives && _extractCallback2)
    273     return _extractCallback2->SetTotal(size);
    274   return S_OK;
    275   COM_TRY_END
    276 }
    277 
    278 static void NormalizeVals(UInt64 &v1, UInt64 &v2)
    279 {
    280   const UInt64 kMax = (UInt64)1 << 31;
    281   while (v1 > kMax)
    282   {
    283     v1 >>= 1;
    284     v2 >>= 1;
    285   }
    286 }
    287 
    288 static UInt64 MyMultDiv64(UInt64 unpCur, UInt64 unpTotal, UInt64 packTotal)
    289 {
    290   NormalizeVals(packTotal, unpTotal);
    291   NormalizeVals(unpCur, unpTotal);
    292   if (unpTotal == 0)
    293     unpTotal = 1;
    294   return unpCur * packTotal / unpTotal;
    295 }
    296 
    297 STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 *completeValue)
    298 {
    299   COM_TRY_BEGIN
    300 
    301   if (!_extractCallback2)
    302     return S_OK;
    303 
    304   UInt64 packCur;
    305   if (_multiArchives)
    306   {
    307     packCur = LocalProgressSpec->InSize;
    308     if (completeValue && _progressTotal_Defined)
    309       packCur += MyMultDiv64(*completeValue, _progressTotal, _packTotal);
    310     completeValue = &packCur;
    311   }
    312   return _extractCallback2->SetCompleted(completeValue);
    313 
    314   COM_TRY_END
    315 }
    316 
    317 STDMETHODIMP CArchiveExtractCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)
    318 {
    319   COM_TRY_BEGIN
    320   return _localProgress->SetRatioInfo(inSize, outSize);
    321   COM_TRY_END
    322 }
    323 
    324 void CArchiveExtractCallback::CreateComplexDirectory(const UStringVector &dirPathParts, FString &fullPath)
    325 {
    326   bool isAbsPath = false;
    327 
    328   if (!dirPathParts.IsEmpty())
    329   {
    330     const UString &s = dirPathParts[0];
    331     if (s.IsEmpty())
    332       isAbsPath = true;
    333     #if defined(_WIN32) && !defined(UNDER_CE)
    334     else
    335     {
    336       if (NName::IsDrivePath2(s))
    337         isAbsPath = true;
    338     }
    339     #endif
    340   }
    341 
    342   if (_pathMode == NExtract::NPathMode::kAbsPaths && isAbsPath)
    343     fullPath.Empty();
    344   else
    345     fullPath = _dirPathPrefix;
    346 
    347   FOR_VECTOR (i, dirPathParts)
    348   {
    349     if (i != 0)
    350       fullPath.Add_PathSepar();
    351     const UString &s = dirPathParts[i];
    352     fullPath += us2fs(s);
    353     #if defined(_WIN32) && !defined(UNDER_CE)
    354     if (_pathMode == NExtract::NPathMode::kAbsPaths)
    355       if (i == 0 && s.Len() == 2 && NName::IsDrivePath2(s))
    356         continue;
    357     #endif
    358     CreateDir(fullPath);
    359   }
    360 }
    361 
    362 HRESULT CArchiveExtractCallback::GetTime(int index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined)
    363 {
    364   filetimeIsDefined = false;
    365   NCOM::CPropVariant prop;
    366   RINOK(_arc->Archive->GetProperty(index, propID, &prop));
    367   if (prop.vt == VT_FILETIME)
    368   {
    369     filetime = prop.filetime;
    370     filetimeIsDefined = (filetime.dwHighDateTime != 0 || filetime.dwLowDateTime != 0);
    371   }
    372   else if (prop.vt != VT_EMPTY)
    373     return E_FAIL;
    374   return S_OK;
    375 }
    376 
    377 HRESULT CArchiveExtractCallback::GetUnpackSize()
    378 {
    379   return _arc->GetItemSize(_index, _curSize, _curSizeDefined);
    380 }
    381 
    382 static void AddPathToMessage(UString &s, const FString &path)
    383 {
    384   s.AddAscii(" : ");
    385   s += fs2us(path);
    386 }
    387 
    388 HRESULT CArchiveExtractCallback::SendMessageError(const char *message, const FString &path)
    389 {
    390   UString s;
    391   s.AddAscii(message);
    392   AddPathToMessage(s, path);
    393   return _extractCallback2->MessageError(s);
    394 }
    395 
    396 HRESULT CArchiveExtractCallback::SendMessageError_with_LastError(const char *message, const FString &path)
    397 {
    398   DWORD errorCode = GetLastError();
    399   UString s;
    400   s.AddAscii(message);
    401   if (errorCode != 0)
    402   {
    403     s.AddAscii(" : ");
    404     s += NError::MyFormatMessage(errorCode);
    405   }
    406   AddPathToMessage(s, path);
    407   return _extractCallback2->MessageError(s);
    408 }
    409 
    410 HRESULT CArchiveExtractCallback::SendMessageError2(const char *message, const FString &path1, const FString &path2)
    411 {
    412   UString s;
    413   s.AddAscii(message);
    414   AddPathToMessage(s, path1);
    415   AddPathToMessage(s, path2);
    416   return _extractCallback2->MessageError(s);
    417 }
    418 
    419 #ifndef _SFX
    420 
    421 STDMETHODIMP CGetProp::GetProp(PROPID propID, PROPVARIANT *value)
    422 {
    423   /*
    424   if (propID == kpidName)
    425   {
    426     COM_TRY_BEGIN
    427     NCOM::CPropVariant prop = Name;
    428     prop.Detach(value);
    429     return S_OK;
    430     COM_TRY_END
    431   }
    432   */
    433   return Arc->Archive->GetProperty(IndexInArc, propID, value);
    434 }
    435 
    436 #endif
    437 
    438 
    439 #ifdef SUPPORT_LINKS
    440 
    441 static UString GetDirPrefixOf(const UString &src)
    442 {
    443   UString s = src;
    444   if (!s.IsEmpty())
    445   {
    446     if (IsPathSepar(s.Back()))
    447       s.DeleteBack();
    448     int pos = s.ReverseFind_PathSepar();
    449     s.DeleteFrom(pos + 1);
    450   }
    451   return s;
    452 }
    453 
    454 #endif
    455 
    456 
    457 bool IsSafePath(const UString &path)
    458 {
    459   if (NName::IsAbsolutePath(path))
    460     return false;
    461 
    462   UStringVector parts;
    463   SplitPathToParts(path, parts);
    464   unsigned level = 0;
    465 
    466   FOR_VECTOR (i, parts)
    467   {
    468     const UString &s = parts[i];
    469     if (s.IsEmpty())
    470     {
    471       if (i == 0)
    472         return false;
    473       continue;
    474     }
    475     if (s == L".")
    476       continue;
    477     if (s == L"..")
    478     {
    479       if (level == 0)
    480         return false;
    481       level--;
    482     }
    483     else
    484       level++;
    485   }
    486 
    487   return level > 0;
    488 }
    489 
    490 
    491 bool CensorNode_CheckPath2(const NWildcard::CCensorNode &node, const CReadArcItem &item, bool &include)
    492 {
    493   bool found = false;
    494 
    495   if (node.CheckPathVect(item.PathParts, !item.MainIsDir, include))
    496   {
    497     if (!include)
    498       return true;
    499 
    500     #ifdef SUPPORT_ALT_STREAMS
    501     if (!item.IsAltStream)
    502       return true;
    503     #endif
    504 
    505     found = true;
    506   }
    507 
    508   #ifdef SUPPORT_ALT_STREAMS
    509 
    510   if (!item.IsAltStream)
    511     return false;
    512 
    513   UStringVector pathParts2 = item.PathParts;
    514   if (pathParts2.IsEmpty())
    515     pathParts2.AddNew();
    516   UString &back = pathParts2.Back();
    517   back += L':';
    518   back += item.AltStreamName;
    519   bool include2;
    520 
    521   if (node.CheckPathVect(pathParts2,
    522       true, // isFile,
    523       include2))
    524   {
    525     include = include2;
    526     return true;
    527   }
    528 
    529   #endif
    530 
    531   return found;
    532 }
    533 
    534 bool CensorNode_CheckPath(const NWildcard::CCensorNode &node, const CReadArcItem &item)
    535 {
    536   bool include;
    537   if (CensorNode_CheckPath2(node, item, include))
    538     return include;
    539   return false;
    540 }
    541 
    542 static FString MakePath_from_2_Parts(const FString &prefix, const FString &path)
    543 {
    544   FString s = prefix;
    545   #if defined(_WIN32) && !defined(UNDER_CE)
    546   if (!path.IsEmpty() && path[0] == ':' && !prefix.IsEmpty() && IsPathSepar(prefix.Back()))
    547   {
    548     if (!NName::IsDriveRootPath_SuperAllowed(prefix))
    549       s.DeleteBack();
    550   }
    551   #endif
    552   s += path;
    553   return s;
    554 }
    555 
    556 
    557 /*
    558 #ifdef SUPPORT_LINKS
    559 
    560 struct CTempMidBuffer
    561 {
    562   void *Buf;
    563 
    564   CTempMidBuffer(size_t size): Buf(NULL) { Buf = ::MidAlloc(size); }
    565   ~CTempMidBuffer() { ::MidFree(Buf); }
    566 };
    567 
    568 HRESULT CArchiveExtractCallback::MyCopyFile(ISequentialOutStream *outStream)
    569 {
    570   const size_t kBufSize = 1 << 16;
    571   CTempMidBuffer buf(kBufSize);
    572   if (!buf.Buf)
    573     return E_OUTOFMEMORY;
    574 
    575   NIO::CInFile inFile;
    576   NIO::COutFile outFile;
    577 
    578   if (!inFile.Open(_CopyFile_Path))
    579     return SendMessageError_with_LastError("Open error", _CopyFile_Path);
    580 
    581   for (;;)
    582   {
    583     UInt32 num;
    584 
    585     if (!inFile.Read(buf.Buf, kBufSize, num))
    586       return SendMessageError_with_LastError("Read error", _CopyFile_Path);
    587 
    588     if (num == 0)
    589       return S_OK;
    590 
    591 
    592     RINOK(WriteStream(outStream, buf.Buf, num));
    593   }
    594 }
    595 
    596 #endif
    597 */
    598 
    599 STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode)
    600 {
    601   COM_TRY_BEGIN
    602 
    603   *outStream = NULL;
    604 
    605   #ifndef _SFX
    606   if (_hashStream)
    607     _hashStreamSpec->ReleaseStream();
    608   _hashStreamWasUsed = false;
    609   #endif
    610 
    611   _outFileStream.Release();
    612 
    613   _encrypted = false;
    614   _position = 0;
    615   _isSplit = false;
    616 
    617   _curSize = 0;
    618   _curSizeDefined = false;
    619   _index = index;
    620 
    621   _diskFilePath.Empty();
    622 
    623   // _fi.Clear();
    624 
    625   #ifdef SUPPORT_LINKS
    626   // _CopyFile_Path.Empty();
    627   linkPath.Empty();
    628   #endif
    629 
    630   IInArchive *archive = _arc->Archive;
    631 
    632   #ifndef _SFX
    633   _item._use_baseParentFolder_mode = _use_baseParentFolder_mode;
    634   if (_use_baseParentFolder_mode)
    635   {
    636     _item._baseParentFolder = _baseParentFolder;
    637     if (_pathMode == NExtract::NPathMode::kFullPaths ||
    638         _pathMode == NExtract::NPathMode::kAbsPaths)
    639       _item._baseParentFolder = -1;
    640   }
    641   #endif
    642 
    643   #ifdef SUPPORT_ALT_STREAMS
    644   _item.WriteToAltStreamIfColon = _ntOptions.WriteToAltStreamIfColon;
    645   #endif
    646 
    647   RINOK(_arc->GetItem(index, _item));
    648 
    649   {
    650     NCOM::CPropVariant prop;
    651     RINOK(archive->GetProperty(index, kpidPosition, &prop));
    652     if (prop.vt != VT_EMPTY)
    653     {
    654       if (prop.vt != VT_UI8)
    655         return E_FAIL;
    656       _position = prop.uhVal.QuadPart;
    657       _isSplit = true;
    658     }
    659   }
    660 
    661   #ifdef SUPPORT_LINKS
    662 
    663   // bool isCopyLink = false;
    664   bool isHardLink = false;
    665   bool isJunction = false;
    666   bool isRelative = false;
    667 
    668   {
    669     NCOM::CPropVariant prop;
    670     RINOK(archive->GetProperty(index, kpidHardLink, &prop));
    671     if (prop.vt == VT_BSTR)
    672     {
    673       isHardLink = true;
    674       // isCopyLink = false;
    675       isRelative = false; // RAR5, TAR: hard links are from root folder of archive
    676       linkPath.SetFromBstr(prop.bstrVal);
    677     }
    678     else if (prop.vt != VT_EMPTY)
    679       return E_FAIL;
    680   }
    681 
    682   /*
    683   {
    684     NCOM::CPropVariant prop;
    685     RINOK(archive->GetProperty(index, kpidCopyLink, &prop));
    686     if (prop.vt == VT_BSTR)
    687     {
    688       isHardLink = false;
    689       isCopyLink = true;
    690       isRelative = false; // RAR5: copy links are from root folder of archive
    691       linkPath.SetFromBstr(prop.bstrVal);
    692     }
    693     else if (prop.vt != VT_EMPTY)
    694       return E_FAIL;
    695   }
    696   */
    697 
    698   {
    699     NCOM::CPropVariant prop;
    700     RINOK(archive->GetProperty(index, kpidSymLink, &prop));
    701     if (prop.vt == VT_BSTR)
    702     {
    703       isHardLink = false;
    704       // isCopyLink = false;
    705       isRelative = true; // RAR5, TAR: symbolic links can be relative
    706       linkPath.SetFromBstr(prop.bstrVal);
    707     }
    708     else if (prop.vt != VT_EMPTY)
    709       return E_FAIL;
    710   }
    711 
    712 
    713   bool isOkReparse = false;
    714 
    715   if (linkPath.IsEmpty() && _arc->GetRawProps)
    716   {
    717     const void *data;
    718     UInt32 dataSize;
    719     UInt32 propType;
    720 
    721     _arc->GetRawProps->GetRawProp(_index, kpidNtReparse, &data, &dataSize, &propType);
    722 
    723     if (dataSize != 0)
    724     {
    725       if (propType != NPropDataType::kRaw)
    726         return E_FAIL;
    727       UString s;
    728       CReparseAttr reparse;
    729       isOkReparse = reparse.Parse((const Byte *)data, dataSize);
    730       if (isOkReparse)
    731       {
    732         isHardLink = false;
    733         // isCopyLink = false;
    734         linkPath = reparse.GetPath();
    735         isJunction = reparse.IsMountPoint();
    736         isRelative = reparse.IsRelative();
    737         #ifndef _WIN32
    738         linkPath.Replace(L'\\', WCHAR_PATH_SEPARATOR);
    739         #endif
    740       }
    741     }
    742   }
    743 
    744   if (!linkPath.IsEmpty())
    745   {
    746     #ifdef _WIN32
    747     linkPath.Replace(L'/', WCHAR_PATH_SEPARATOR);
    748     #endif
    749 
    750     // rar5 uses "\??\" prefix for absolute links
    751     if (linkPath.IsPrefixedBy(WSTRING_PATH_SEPARATOR L"??" WSTRING_PATH_SEPARATOR))
    752     {
    753       isRelative = false;
    754       linkPath.DeleteFrontal(4);
    755     }
    756 
    757     for (;;)
    758     // while (NName::IsAbsolutePath(linkPath))
    759     {
    760       unsigned n = NName::GetRootPrefixSize(linkPath);
    761       if (n == 0)
    762         break;
    763       isRelative = false;
    764       linkPath.DeleteFrontal(n);
    765     }
    766   }
    767 
    768   if (!linkPath.IsEmpty() && !isRelative && _removePathParts.Size() != 0)
    769   {
    770     UStringVector pathParts;
    771     SplitPathToParts(linkPath, pathParts);
    772     bool badPrefix = false;
    773     FOR_VECTOR (i, _removePathParts)
    774     {
    775       if (CompareFileNames(_removePathParts[i], pathParts[i]) != 0)
    776       {
    777         badPrefix = true;
    778         break;
    779       }
    780     }
    781     if (!badPrefix)
    782       pathParts.DeleteFrontal(_removePathParts.Size());
    783     linkPath = MakePathFromParts(pathParts);
    784   }
    785 
    786   #endif
    787 
    788   RINOK(Archive_GetItemBoolProp(archive, index, kpidEncrypted, _encrypted));
    789 
    790   RINOK(GetUnpackSize());
    791 
    792   #ifdef SUPPORT_ALT_STREAMS
    793 
    794   if (!_ntOptions.AltStreams.Val && _item.IsAltStream)
    795     return S_OK;
    796 
    797   #endif
    798 
    799 
    800   UStringVector &pathParts = _item.PathParts;
    801 
    802   if (_wildcardCensor)
    803   {
    804     if (!CensorNode_CheckPath(*_wildcardCensor, _item))
    805       return S_OK;
    806   }
    807 
    808   #ifndef _SFX
    809   if (_use_baseParentFolder_mode)
    810   {
    811     if (!pathParts.IsEmpty())
    812     {
    813       unsigned numRemovePathParts = 0;
    814 
    815       #ifdef SUPPORT_ALT_STREAMS
    816       if (_pathMode == NExtract::NPathMode::kNoPathsAlt && _item.IsAltStream)
    817         numRemovePathParts = pathParts.Size();
    818       else
    819       #endif
    820       if (_pathMode == NExtract::NPathMode::kNoPaths ||
    821           _pathMode == NExtract::NPathMode::kNoPathsAlt)
    822         numRemovePathParts = pathParts.Size() - 1;
    823       pathParts.DeleteFrontal(numRemovePathParts);
    824     }
    825   }
    826   else
    827   #endif
    828   {
    829     if (pathParts.IsEmpty())
    830     {
    831       if (_item.IsDir)
    832         return S_OK;
    833       /*
    834       #ifdef SUPPORT_ALT_STREAMS
    835       if (!_item.IsAltStream)
    836       #endif
    837         return E_FAIL;
    838       */
    839     }
    840 
    841     unsigned numRemovePathParts = 0;
    842 
    843     switch (_pathMode)
    844     {
    845       case NExtract::NPathMode::kFullPaths:
    846       case NExtract::NPathMode::kCurPaths:
    847       {
    848         if (_removePathParts.IsEmpty())
    849           break;
    850         bool badPrefix = false;
    851 
    852         if (pathParts.Size() < _removePathParts.Size())
    853           badPrefix = true;
    854         else
    855         {
    856           if (pathParts.Size() == _removePathParts.Size())
    857           {
    858             if (_removePartsForAltStreams)
    859             {
    860               #ifdef SUPPORT_ALT_STREAMS
    861               if (!_item.IsAltStream)
    862               #endif
    863                 badPrefix = true;
    864             }
    865             else
    866             {
    867               if (!_item.MainIsDir)
    868                 badPrefix = true;
    869             }
    870           }
    871 
    872           if (!badPrefix)
    873           FOR_VECTOR (i, _removePathParts)
    874           {
    875             if (CompareFileNames(_removePathParts[i], pathParts[i]) != 0)
    876             {
    877               badPrefix = true;
    878               break;
    879             }
    880           }
    881         }
    882 
    883         if (badPrefix)
    884         {
    885           if (askExtractMode == NArchive::NExtract::NAskMode::kExtract && !_testMode)
    886             return E_FAIL;
    887         }
    888         else
    889           numRemovePathParts = _removePathParts.Size();
    890         break;
    891       }
    892 
    893       case NExtract::NPathMode::kNoPaths:
    894       {
    895         if (!pathParts.IsEmpty())
    896           numRemovePathParts = pathParts.Size() - 1;
    897         break;
    898       }
    899       case NExtract::NPathMode::kNoPathsAlt:
    900       {
    901         #ifdef SUPPORT_ALT_STREAMS
    902         if (_item.IsAltStream)
    903           numRemovePathParts = pathParts.Size();
    904         else
    905         #endif
    906         if (!pathParts.IsEmpty())
    907           numRemovePathParts = pathParts.Size() - 1;
    908         break;
    909       }
    910       /*
    911       case NExtract::NPathMode::kFullPaths:
    912       case NExtract::NPathMode::kAbsPaths:
    913         break;
    914       */
    915     }
    916 
    917     pathParts.DeleteFrontal(numRemovePathParts);
    918   }
    919 
    920   #ifndef _SFX
    921 
    922   if (ExtractToStreamCallback)
    923   {
    924     if (!GetProp)
    925     {
    926       GetProp_Spec = new CGetProp;
    927       GetProp = GetProp_Spec;
    928     }
    929     GetProp_Spec->Arc = _arc;
    930     GetProp_Spec->IndexInArc = index;
    931     UString name = MakePathFromParts(pathParts);
    932 
    933     #ifdef SUPPORT_ALT_STREAMS
    934     if (_item.IsAltStream)
    935     {
    936       if (!pathParts.IsEmpty() || (!_removePartsForAltStreams && _pathMode != NExtract::NPathMode::kNoPathsAlt))
    937         name += L':';
    938       name += _item.AltStreamName;
    939     }
    940     #endif
    941 
    942     return ExtractToStreamCallback->GetStream7(name, BoolToInt(_item.IsDir), outStream, askExtractMode, GetProp);
    943   }
    944 
    945   #endif
    946 
    947   CMyComPtr<ISequentialOutStream> outStreamLoc;
    948 
    949 if (askExtractMode == NArchive::NExtract::NAskMode::kExtract && !_testMode)
    950 {
    951   if (_stdOutMode)
    952   {
    953     outStreamLoc = new CStdOutFileStream;
    954   }
    955   else
    956   {
    957     {
    958       NCOM::CPropVariant prop;
    959       RINOK(archive->GetProperty(index, kpidAttrib, &prop));
    960       if (prop.vt == VT_UI4)
    961       {
    962         _fi.Attrib = prop.ulVal;
    963         _fi.AttribDefined = true;
    964       }
    965       else if (prop.vt == VT_EMPTY)
    966         _fi.AttribDefined = false;
    967       else
    968         return E_FAIL;
    969     }
    970 
    971     RINOK(GetTime(index, kpidCTime, _fi.CTime, _fi.CTimeDefined));
    972     RINOK(GetTime(index, kpidATime, _fi.ATime, _fi.ATimeDefined));
    973     RINOK(GetTime(index, kpidMTime, _fi.MTime, _fi.MTimeDefined));
    974 
    975     bool isAnti = false;
    976     RINOK(_arc->IsItemAnti(index, isAnti));
    977 
    978     #ifdef SUPPORT_ALT_STREAMS
    979     if (!_item.IsAltStream
    980         || !pathParts.IsEmpty()
    981         || !(_removePartsForAltStreams || _pathMode == NExtract::NPathMode::kNoPathsAlt))
    982     #endif
    983       Correct_FsPath(_pathMode == NExtract::NPathMode::kAbsPaths, pathParts, _item.MainIsDir);
    984 
    985     #ifdef SUPPORT_ALT_STREAMS
    986 
    987     if (_item.IsAltStream)
    988     {
    989       UString s = _item.AltStreamName;
    990       Correct_AltStream_Name(s);
    991       bool needColon = true;
    992 
    993       if (pathParts.IsEmpty())
    994       {
    995         pathParts.AddNew();
    996         if (_removePartsForAltStreams || _pathMode == NExtract::NPathMode::kNoPathsAlt)
    997           needColon = false;
    998       }
    999       else if (_pathMode == NExtract::NPathMode::kAbsPaths &&
   1000           NWildcard::GetNumPrefixParts_if_DrivePath(pathParts) == pathParts.Size())
   1001         pathParts.AddNew();
   1002 
   1003       UString &name = pathParts.Back();
   1004       if (needColon)
   1005         name += (wchar_t)(_ntOptions.ReplaceColonForAltStream ? L'_' : L':');
   1006       name += s;
   1007     }
   1008 
   1009     #endif
   1010 
   1011     UString processedPath = MakePathFromParts(pathParts);
   1012 
   1013     if (!isAnti)
   1014     {
   1015       if (!_item.IsDir)
   1016       {
   1017         if (!pathParts.IsEmpty())
   1018           pathParts.DeleteBack();
   1019       }
   1020 
   1021       if (!pathParts.IsEmpty())
   1022       {
   1023         FString fullPathNew;
   1024         CreateComplexDirectory(pathParts, fullPathNew);
   1025         if (_item.IsDir)
   1026         {
   1027           _extractedFolderPaths.Add(fullPathNew);
   1028           _extractedFolderIndices.Add(index);
   1029           SetDirTime(fullPathNew,
   1030             (WriteCTime && _fi.CTimeDefined) ? &_fi.CTime : NULL,
   1031             (WriteATime && _fi.ATimeDefined) ? &_fi.ATime : NULL,
   1032             (WriteMTime && _fi.MTimeDefined) ? &_fi.MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL));
   1033         }
   1034       }
   1035     }
   1036 
   1037 
   1038     FString fullProcessedPath = us2fs(processedPath);
   1039     if (_pathMode != NExtract::NPathMode::kAbsPaths
   1040         || !NName::IsAbsolutePath(processedPath))
   1041     {
   1042        fullProcessedPath = MakePath_from_2_Parts(_dirPathPrefix, fullProcessedPath);
   1043     }
   1044 
   1045     #ifdef SUPPORT_ALT_STREAMS
   1046 
   1047     if (_item.IsAltStream && _item.ParentIndex != (UInt32)(Int32)-1)
   1048     {
   1049       int renIndex = _renamedFiles.FindInSorted(CIndexToPathPair(_item.ParentIndex));
   1050       if (renIndex >= 0)
   1051       {
   1052         const CIndexToPathPair &pair = _renamedFiles[renIndex];
   1053         fullProcessedPath = pair.Path;
   1054         fullProcessedPath += (FChar)':';
   1055         UString s = _item.AltStreamName;
   1056         Correct_AltStream_Name(s);
   1057         fullProcessedPath += us2fs(s);
   1058       }
   1059     }
   1060 
   1061     #endif
   1062 
   1063     bool isRenamed = false;
   1064 
   1065     if (_item.IsDir)
   1066     {
   1067       _diskFilePath = fullProcessedPath;
   1068       if (isAnti)
   1069         RemoveDir(_diskFilePath);
   1070       #ifdef SUPPORT_LINKS
   1071       if (linkPath.IsEmpty())
   1072       #endif
   1073         return S_OK;
   1074     }
   1075     else if (!_isSplit)
   1076     {
   1077 
   1078     // ----- Is file (not split) -----
   1079     NFind::CFileInfo fileInfo;
   1080     if (fileInfo.Find(fullProcessedPath))
   1081     {
   1082       switch (_overwriteMode)
   1083       {
   1084         case NExtract::NOverwriteMode::kSkip:
   1085           return S_OK;
   1086         case NExtract::NOverwriteMode::kAsk:
   1087         {
   1088           int slashPos = fullProcessedPath.ReverseFind_PathSepar();
   1089           FString realFullProcessedPath = fullProcessedPath.Left(slashPos + 1) + fileInfo.Name;
   1090 
   1091           Int32 overwriteResult;
   1092           RINOK(_extractCallback2->AskOverwrite(
   1093               fs2us(realFullProcessedPath), &fileInfo.MTime, &fileInfo.Size, _item.Path,
   1094               _fi.MTimeDefined ? &_fi.MTime : NULL,
   1095               _curSizeDefined ? &_curSize : NULL,
   1096               &overwriteResult))
   1097 
   1098           switch (overwriteResult)
   1099           {
   1100             case NOverwriteAnswer::kCancel: return E_ABORT;
   1101             case NOverwriteAnswer::kNo: return S_OK;
   1102             case NOverwriteAnswer::kNoToAll: _overwriteMode = NExtract::NOverwriteMode::kSkip; return S_OK;
   1103             case NOverwriteAnswer::kYes: break;
   1104             case NOverwriteAnswer::kYesToAll: _overwriteMode = NExtract::NOverwriteMode::kOverwrite; break;
   1105             case NOverwriteAnswer::kAutoRename: _overwriteMode = NExtract::NOverwriteMode::kRename; break;
   1106             default:
   1107               return E_FAIL;
   1108           }
   1109         }
   1110       }
   1111       if (_overwriteMode == NExtract::NOverwriteMode::kRename)
   1112       {
   1113         if (!AutoRenamePath(fullProcessedPath))
   1114         {
   1115           RINOK(SendMessageError(kCantAutoRename, fullProcessedPath));
   1116           return E_FAIL;
   1117         }
   1118         isRenamed = true;
   1119       }
   1120       else if (_overwriteMode == NExtract::NOverwriteMode::kRenameExisting)
   1121       {
   1122         FString existPath = fullProcessedPath;
   1123         if (!AutoRenamePath(existPath))
   1124         {
   1125           RINOK(SendMessageError(kCantAutoRename, fullProcessedPath));
   1126           return E_FAIL;
   1127         }
   1128         // MyMoveFile can raname folders. So it's OK to use it for folders too
   1129         if (!MyMoveFile(fullProcessedPath, existPath))
   1130         {
   1131           RINOK(SendMessageError2(kCantRenameFile, existPath, fullProcessedPath));
   1132           return E_FAIL;
   1133         }
   1134       }
   1135       else
   1136       {
   1137         if (fileInfo.IsDir())
   1138         {
   1139           // do we need to delete all files in folder?
   1140           if (!RemoveDir(fullProcessedPath))
   1141           {
   1142             RINOK(SendMessageError_with_LastError(kCantDeleteOutputDir, fullProcessedPath));
   1143             return S_OK;
   1144           }
   1145         }
   1146         else
   1147         {
   1148           bool needDelete = true;
   1149           if (needDelete)
   1150           {
   1151             if (!DeleteFileAlways(fullProcessedPath))
   1152             {
   1153               RINOK(SendMessageError_with_LastError(kCantDeleteOutputFile, fullProcessedPath));
   1154               return S_OK;
   1155               // return E_FAIL;
   1156             }
   1157           }
   1158         }
   1159       }
   1160     }
   1161     else // not Find(fullProcessedPath)
   1162     {
   1163       // we need to clear READ-ONLY of parent before creating alt stream
   1164       #if defined(_WIN32) && !defined(UNDER_CE)
   1165       int colonPos = NName::FindAltStreamColon(fullProcessedPath);
   1166       if (colonPos >= 0 && fullProcessedPath[(unsigned)colonPos + 1] != 0)
   1167       {
   1168         FString parentFsPath = fullProcessedPath;
   1169         parentFsPath.DeleteFrom(colonPos);
   1170         NFind::CFileInfo parentFi;
   1171         if (parentFi.Find(parentFsPath))
   1172         {
   1173           if (parentFi.IsReadOnly())
   1174             SetFileAttrib(parentFsPath, parentFi.Attrib & ~FILE_ATTRIBUTE_READONLY);
   1175         }
   1176       }
   1177       #endif
   1178     }
   1179     // ----- END of code for    Is file (not split) -----
   1180 
   1181     }
   1182     _diskFilePath = fullProcessedPath;
   1183 
   1184 
   1185     if (!isAnti)
   1186     {
   1187       #ifdef SUPPORT_LINKS
   1188 
   1189       if (!linkPath.IsEmpty())
   1190       {
   1191         #ifndef UNDER_CE
   1192 
   1193         UString relatPath;
   1194         if (isRelative)
   1195           relatPath = GetDirPrefixOf(_item.Path);
   1196         relatPath += linkPath;
   1197 
   1198         if (!IsSafePath(relatPath))
   1199         {
   1200           RINOK(SendMessageError("Dangerous link path was ignored", us2fs(relatPath)));
   1201         }
   1202         else
   1203         {
   1204           FString existPath;
   1205           if (isHardLink /* || isCopyLink */ || !isRelative)
   1206           {
   1207             if (!NName::GetFullPath(_dirPathPrefix_Full, us2fs(relatPath), existPath))
   1208             {
   1209               RINOK(SendMessageError("Incorrect path", us2fs(relatPath)));
   1210             }
   1211           }
   1212           else
   1213           {
   1214             existPath = us2fs(linkPath);
   1215           }
   1216 
   1217           if (!existPath.IsEmpty())
   1218           {
   1219             if (isHardLink /* || isCopyLink */)
   1220             {
   1221               // if (isHardLink)
   1222               {
   1223                 if (!MyCreateHardLink(fullProcessedPath, existPath))
   1224                 {
   1225                   RINOK(SendMessageError2("Can not create hard link", fullProcessedPath, existPath));
   1226                   // return S_OK;
   1227                 }
   1228               }
   1229               /*
   1230               else
   1231               {
   1232                 NFind::CFileInfo fi;
   1233                 if (!fi.Find(existPath))
   1234                 {
   1235                   RINOK(SendMessageError2("Can not find the file for copying", existPath, fullProcessedPath));
   1236                 }
   1237                 else
   1238                 {
   1239                   if (_curSizeDefined && _curSize == fi.Size)
   1240                     _CopyFile_Path = existPath;
   1241                   else
   1242                   {
   1243                     RINOK(SendMessageError2("File size collision for file copying", existPath, fullProcessedPath));
   1244                   }
   1245 
   1246                   // RINOK(MyCopyFile(existPath, fullProcessedPath));
   1247                 }
   1248               }
   1249               */
   1250             }
   1251             else if (_ntOptions.SymLinks.Val)
   1252             {
   1253               // bool isSymLink = true; // = false for junction
   1254               if (_item.IsDir && !isRelative)
   1255               {
   1256                 // if it's before Vista we use Junction Point
   1257                 // isJunction = true;
   1258                 // convertToAbs = true;
   1259               }
   1260 
   1261               CByteBuffer data;
   1262               if (FillLinkData(data, fs2us(existPath), !isJunction))
   1263               {
   1264                 CReparseAttr attr;
   1265                 if (!attr.Parse(data, data.Size()))
   1266                 {
   1267                   RINOK(SendMessageError("Internal error for symbolic link file", us2fs(_item.Path)));
   1268                   // return E_FAIL;
   1269                 }
   1270                 else
   1271                 if (!NFile::NIO::SetReparseData(fullProcessedPath, _item.IsDir, data, (DWORD)data.Size()))
   1272                 {
   1273                   RINOK(SendMessageError_with_LastError("Can not create symbolic link", fullProcessedPath));
   1274                 }
   1275               }
   1276             }
   1277           }
   1278         }
   1279 
   1280         #endif
   1281       }
   1282 
   1283       if (linkPath.IsEmpty() /* || !_CopyFile_Path.IsEmpty() */)
   1284       #endif // SUPPORT_LINKS
   1285       {
   1286         bool needWriteFile = true;
   1287 
   1288         #ifdef SUPPORT_LINKS
   1289         if (!_hardLinks.IDs.IsEmpty() && !_item.IsAltStream)
   1290         {
   1291           CHardLinkNode h;
   1292           bool defined;
   1293           RINOK(Archive_Get_HardLinkNode(archive, index, h, defined));
   1294           if (defined)
   1295           {
   1296             {
   1297               int linkIndex = _hardLinks.IDs.FindInSorted2(h);
   1298               if (linkIndex >= 0)
   1299               {
   1300                 FString &hl = _hardLinks.Links[linkIndex];
   1301                 if (hl.IsEmpty())
   1302                   hl = fullProcessedPath;
   1303                 else
   1304                 {
   1305                   if (!MyCreateHardLink(fullProcessedPath, hl))
   1306                   {
   1307                     RINOK(SendMessageError2("Can not create hard link", fullProcessedPath, hl));
   1308                     return S_OK;
   1309                   }
   1310                   needWriteFile = false;
   1311                 }
   1312               }
   1313             }
   1314           }
   1315         }
   1316         #endif
   1317 
   1318         if (needWriteFile)
   1319         {
   1320           _outFileStreamSpec = new COutFileStream;
   1321           CMyComPtr<ISequentialOutStream> outStreamLoc2(_outFileStreamSpec);
   1322           if (!_outFileStreamSpec->Open(fullProcessedPath, _isSplit ? OPEN_ALWAYS: CREATE_ALWAYS))
   1323           {
   1324             // if (::GetLastError() != ERROR_FILE_EXISTS || !isSplit)
   1325             {
   1326               RINOK(SendMessageError_with_LastError("Can not open output file", fullProcessedPath));
   1327               return S_OK;
   1328             }
   1329           }
   1330 
   1331 
   1332           #ifdef SUPPORT_ALT_STREAMS
   1333           if (isRenamed && !_item.IsAltStream)
   1334           {
   1335             CIndexToPathPair pair(index, fullProcessedPath);
   1336             unsigned oldSize = _renamedFiles.Size();
   1337             unsigned insertIndex = _renamedFiles.AddToUniqueSorted(pair);
   1338             if (oldSize == _renamedFiles.Size())
   1339               _renamedFiles[insertIndex].Path = fullProcessedPath;
   1340           }
   1341           #endif
   1342 
   1343           if (_isSplit)
   1344           {
   1345             RINOK(_outFileStreamSpec->Seek(_position, STREAM_SEEK_SET, NULL));
   1346           }
   1347 
   1348           _outFileStream = outStreamLoc2;
   1349         }
   1350       }
   1351     }
   1352 
   1353     outStreamLoc = _outFileStream;
   1354   }
   1355 }
   1356 
   1357   #ifndef _SFX
   1358 
   1359   if (_hashStream)
   1360   {
   1361     if (askExtractMode == NArchive::NExtract::NAskMode::kExtract ||
   1362         askExtractMode == NArchive::NExtract::NAskMode::kTest)
   1363     {
   1364       _hashStreamSpec->SetStream(outStreamLoc);
   1365       outStreamLoc = _hashStream;
   1366       _hashStreamSpec->Init(true);
   1367       _hashStreamWasUsed = true;
   1368     }
   1369   }
   1370 
   1371   #endif
   1372 
   1373 
   1374   if (outStreamLoc)
   1375   {
   1376     /*
   1377     #ifdef SUPPORT_LINKS
   1378 
   1379     if (!_CopyFile_Path.IsEmpty())
   1380     {
   1381       RINOK(PrepareOperation(askExtractMode));
   1382       RINOK(MyCopyFile(outStreamLoc));
   1383       return SetOperationResult(NArchive::NExtract::NOperationResult::kOK);
   1384     }
   1385 
   1386     if (isCopyLink && _testMode)
   1387       return S_OK;
   1388 
   1389     #endif
   1390     */
   1391 
   1392     *outStream = outStreamLoc.Detach();
   1393   }
   1394 
   1395   return S_OK;
   1396 
   1397   COM_TRY_END
   1398 }
   1399 
   1400 
   1401 STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode)
   1402 {
   1403   COM_TRY_BEGIN
   1404 
   1405   #ifndef _SFX
   1406   if (ExtractToStreamCallback)
   1407     return ExtractToStreamCallback->PrepareOperation7(askExtractMode);
   1408   #endif
   1409 
   1410   _extractMode = false;
   1411 
   1412   switch (askExtractMode)
   1413   {
   1414     case NArchive::NExtract::NAskMode::kExtract:
   1415       if (_testMode)
   1416         askExtractMode = NArchive::NExtract::NAskMode::kTest;
   1417       else
   1418         _extractMode = true;
   1419       break;
   1420   };
   1421 
   1422   return _extractCallback2->PrepareOperation(_item.Path, BoolToInt(_item.IsDir),
   1423       askExtractMode, _isSplit ? &_position: 0);
   1424 
   1425   COM_TRY_END
   1426 }
   1427 
   1428 
   1429 STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 opRes)
   1430 {
   1431   COM_TRY_BEGIN
   1432 
   1433   #ifndef _SFX
   1434   if (ExtractToStreamCallback)
   1435     return ExtractToStreamCallback->SetOperationResult7(opRes, BoolToInt(_encrypted));
   1436   #endif
   1437 
   1438   #ifndef _SFX
   1439 
   1440   if (_hashStreamWasUsed)
   1441   {
   1442     _hashStreamSpec->_hash->Final(_item.IsDir,
   1443         #ifdef SUPPORT_ALT_STREAMS
   1444           _item.IsAltStream
   1445         #else
   1446           false
   1447         #endif
   1448         , _item.Path);
   1449     _curSize = _hashStreamSpec->GetSize();
   1450     _curSizeDefined = true;
   1451     _hashStreamSpec->ReleaseStream();
   1452     _hashStreamWasUsed = false;
   1453   }
   1454 
   1455   #endif
   1456 
   1457   if (_outFileStream)
   1458   {
   1459     _outFileStreamSpec->SetTime(
   1460         (WriteCTime && _fi.CTimeDefined) ? &_fi.CTime : NULL,
   1461         (WriteATime && _fi.ATimeDefined) ? &_fi.ATime : NULL,
   1462         (WriteMTime && _fi.MTimeDefined) ? &_fi.MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL));
   1463     _curSize = _outFileStreamSpec->ProcessedSize;
   1464     _curSizeDefined = true;
   1465     RINOK(_outFileStreamSpec->Close());
   1466     _outFileStream.Release();
   1467   }
   1468 
   1469   #ifdef _USE_SECURITY_CODE
   1470   if (!_stdOutMode && _extractMode && _ntOptions.NtSecurity.Val && _arc->GetRawProps)
   1471   {
   1472     const void *data;
   1473     UInt32 dataSize;
   1474     UInt32 propType;
   1475     _arc->GetRawProps->GetRawProp(_index, kpidNtSecure, &data, &dataSize, &propType);
   1476     if (dataSize != 0)
   1477     {
   1478       if (propType != NPropDataType::kRaw)
   1479         return E_FAIL;
   1480       if (CheckNtSecure((const Byte *)data, dataSize))
   1481       {
   1482         SECURITY_INFORMATION securInfo = DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION;
   1483         if (_saclEnabled)
   1484           securInfo |= SACL_SECURITY_INFORMATION;
   1485         ::SetFileSecurityW(fs2us(_diskFilePath), securInfo, (PSECURITY_DESCRIPTOR)(void *)data);
   1486       }
   1487     }
   1488   }
   1489   #endif
   1490 
   1491   if (!_curSizeDefined)
   1492     GetUnpackSize();
   1493 
   1494   if (_curSizeDefined)
   1495   {
   1496     #ifdef SUPPORT_ALT_STREAMS
   1497     if (_item.IsAltStream)
   1498       AltStreams_UnpackSize += _curSize;
   1499     else
   1500     #endif
   1501       UnpackSize += _curSize;
   1502   }
   1503 
   1504   if (_item.IsDir)
   1505     NumFolders++;
   1506   #ifdef SUPPORT_ALT_STREAMS
   1507   else if (_item.IsAltStream)
   1508     NumAltStreams++;
   1509   #endif
   1510   else
   1511     NumFiles++;
   1512 
   1513   if (!_stdOutMode && _extractMode && _fi.AttribDefined)
   1514     SetFileAttrib(_diskFilePath, _fi.Attrib);
   1515 
   1516   RINOK(_extractCallback2->SetOperationResult(opRes, BoolToInt(_encrypted)));
   1517 
   1518   return S_OK;
   1519 
   1520   COM_TRY_END
   1521 }
   1522 
   1523 STDMETHODIMP CArchiveExtractCallback::ReportExtractResult(UInt32 indexType, UInt32 index, Int32 opRes)
   1524 {
   1525   if (_folderArchiveExtractCallback2)
   1526   {
   1527     bool isEncrypted = false;
   1528     wchar_t temp[16];
   1529     UString s2;
   1530     const wchar_t *s = NULL;
   1531 
   1532     if (indexType == NArchive::NEventIndexType::kInArcIndex && index != (UInt32)(Int32)-1)
   1533     {
   1534       CReadArcItem item;
   1535       RINOK(_arc->GetItem(index, item));
   1536       s2 = item.Path;
   1537       s = s2;
   1538       RINOK(Archive_GetItemBoolProp(_arc->Archive, index, kpidEncrypted, isEncrypted));
   1539     }
   1540     else
   1541     {
   1542       temp[0] = '#';
   1543       ConvertUInt32ToString(index, temp + 1);
   1544       s = temp;
   1545       // if (indexType == NArchive::NEventIndexType::kBlockIndex) {}
   1546     }
   1547 
   1548     return _folderArchiveExtractCallback2->ReportExtractResult(opRes, isEncrypted, s);
   1549   }
   1550 
   1551   return S_OK;
   1552 }
   1553 
   1554 
   1555 STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password)
   1556 {
   1557   COM_TRY_BEGIN
   1558   if (!_cryptoGetTextPassword)
   1559   {
   1560     RINOK(_extractCallback2.QueryInterface(IID_ICryptoGetTextPassword,
   1561         &_cryptoGetTextPassword));
   1562   }
   1563   return _cryptoGetTextPassword->CryptoGetTextPassword(password);
   1564   COM_TRY_END
   1565 }
   1566 
   1567 
   1568 struct CExtrRefSortPair
   1569 {
   1570   unsigned Len;
   1571   unsigned Index;
   1572 
   1573   int Compare(const CExtrRefSortPair &a) const;
   1574 };
   1575 
   1576 #define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; }
   1577 
   1578 int CExtrRefSortPair::Compare(const CExtrRefSortPair &a) const
   1579 {
   1580   RINOZ(-MyCompare(Len, a.Len));
   1581   return MyCompare(Index, a.Index);
   1582 }
   1583 
   1584 static unsigned GetNumSlashes(const FChar *s)
   1585 {
   1586   for (unsigned numSlashes = 0;;)
   1587   {
   1588     FChar c = *s++;
   1589     if (c == 0)
   1590       return numSlashes;
   1591     if (IS_PATH_SEPAR(c))
   1592       numSlashes++;
   1593   }
   1594 }
   1595 
   1596 HRESULT CArchiveExtractCallback::SetDirsTimes()
   1597 {
   1598   CRecordVector<CExtrRefSortPair> pairs;
   1599   pairs.ClearAndSetSize(_extractedFolderPaths.Size());
   1600   unsigned i;
   1601 
   1602   for (i = 0; i < _extractedFolderPaths.Size(); i++)
   1603   {
   1604     CExtrRefSortPair &pair = pairs[i];
   1605     pair.Index = i;
   1606     pair.Len = GetNumSlashes(_extractedFolderPaths[i]);
   1607   }
   1608 
   1609   pairs.Sort2();
   1610 
   1611   for (i = 0; i < pairs.Size(); i++)
   1612   {
   1613     int pairIndex = pairs[i].Index;
   1614     int index = _extractedFolderIndices[pairIndex];
   1615 
   1616     FILETIME CTime;
   1617     FILETIME ATime;
   1618     FILETIME MTime;
   1619 
   1620     bool CTimeDefined;
   1621     bool ATimeDefined;
   1622     bool MTimeDefined;
   1623 
   1624     RINOK(GetTime(index, kpidCTime, CTime, CTimeDefined));
   1625     RINOK(GetTime(index, kpidATime, ATime, ATimeDefined));
   1626     RINOK(GetTime(index, kpidMTime, MTime, MTimeDefined));
   1627 
   1628     // printf("\n%S", _extractedFolderPaths[pairIndex]);
   1629     SetDirTime(_extractedFolderPaths[pairIndex],
   1630       (WriteCTime && CTimeDefined) ? &CTime : NULL,
   1631       (WriteATime && ATimeDefined) ? &ATime : NULL,
   1632       (WriteMTime && MTimeDefined) ? &MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL));
   1633   }
   1634   return S_OK;
   1635 }
   1636