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