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