tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ArchiveExtractCallback.cpp (43891B)


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