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