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