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