• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // ArchiveExtractCallback.cpp
2 
3 #include "StdAfx.h"
4 
5 #undef sprintf
6 #undef printf
7 
8 #include "../../../../C/Alloc.h"
9 
10 #include "../../../Common/ComTry.h"
11 #include "../../../Common/IntToString.h"
12 #include "../../../Common/StringConvert.h"
13 #include "../../../Common/Wildcard.h"
14 
15 #include "../../../Windows/ErrorMsg.h"
16 #include "../../../Windows/FileDir.h"
17 #include "../../../Windows/FileFind.h"
18 #include "../../../Windows/FileName.h"
19 #include "../../../Windows/PropVariant.h"
20 #include "../../../Windows/PropVariantConv.h"
21 
22 #if defined(_WIN32) && !defined(UNDER_CE)  && !defined(_SFX)
23 #define _USE_SECURITY_CODE
24 #include "../../../Windows/SecurityUtils.h"
25 #endif
26 
27 #include "../../Common/FilePathAutoRename.h"
28 // #include "../../Common/StreamUtils.h"
29 
30 #include "../Common/ExtractingFilePath.h"
31 #include "../Common/PropIDUtils.h"
32 
33 #include "ArchiveExtractCallback.h"
34 
35 using namespace NWindows;
36 using namespace NFile;
37 using namespace NDir;
38 
39 static const char *kCantAutoRename = "Can not create file with auto name";
40 static const char *kCantRenameFile = "Can not rename existing file";
41 static const char *kCantDeleteOutputFile = "Can not delete output file";
42 static const char *kCantDeleteOutputDir = "Can not delete output folder";
43 
44 
45 #ifndef _SFX
46 
Write(const void * data,UInt32 size,UInt32 * processedSize)47 STDMETHODIMP COutStreamWithHash::Write(const void *data, UInt32 size, UInt32 *processedSize)
48 {
49   HRESULT result = S_OK;
50   if (_stream)
51     result = _stream->Write(data, size, &size);
52   if (_calculate)
53     _hash->Update(data, size);
54   _size += size;
55   if (processedSize)
56     *processedSize = size;
57   return result;
58 }
59 
60 #endif
61 
62 #ifdef _USE_SECURITY_CODE
InitLocalPrivileges()63 bool InitLocalPrivileges()
64 {
65   NSecurity::CAccessToken token;
66   if (!token.OpenProcessToken(GetCurrentProcess(),
67       TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES))
68     return false;
69 
70   TOKEN_PRIVILEGES tp;
71 
72   tp.PrivilegeCount = 1;
73   tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
74 
75   if  (!::LookupPrivilegeValue(NULL, SE_SECURITY_NAME, &tp.Privileges[0].Luid))
76     return false;
77   if (!token.AdjustPrivileges(&tp))
78     return false;
79   return (GetLastError() == ERROR_SUCCESS);
80 }
81 #endif
82 
83 #ifdef SUPPORT_LINKS
84 
Compare(const CHardLinkNode & a) const85 int CHardLinkNode::Compare(const CHardLinkNode &a) const
86 {
87   if (StreamId < a.StreamId) return -1;
88   if (StreamId > a.StreamId) return 1;
89   return MyCompare(INode, a.INode);
90 }
91 
Archive_Get_HardLinkNode(IInArchive * archive,UInt32 index,CHardLinkNode & h,bool & defined)92 static HRESULT Archive_Get_HardLinkNode(IInArchive *archive, UInt32 index, CHardLinkNode &h, bool &defined)
93 {
94   h.INode = 0;
95   h.StreamId = (UInt64)(Int64)-1;
96   defined = false;
97   {
98     NCOM::CPropVariant prop;
99     RINOK(archive->GetProperty(index, kpidINode, &prop));
100     if (!ConvertPropVariantToUInt64(prop, h.INode))
101       return S_OK;
102   }
103   {
104     NCOM::CPropVariant prop;
105     RINOK(archive->GetProperty(index, kpidStreamId, &prop));
106     ConvertPropVariantToUInt64(prop, h.StreamId);
107   }
108   defined = true;
109   return S_OK;
110 }
111 
112 
PrepareHardLinks(const CRecordVector<UInt32> * realIndices)113 HRESULT CArchiveExtractCallback::PrepareHardLinks(const CRecordVector<UInt32> *realIndices)
114 {
115   _hardLinks.Clear();
116 
117   if (!_arc->Ask_INode)
118     return S_OK;
119 
120   IInArchive *archive = _arc->Archive;
121   CRecordVector<CHardLinkNode> &hardIDs = _hardLinks.IDs;
122 
123   {
124     UInt32 numItems;
125     if (realIndices)
126       numItems = realIndices->Size();
127     else
128     {
129       RINOK(archive->GetNumberOfItems(&numItems));
130     }
131 
132     for (UInt32 i = 0; i < numItems; i++)
133     {
134       CHardLinkNode h;
135       bool defined;
136       UInt32 realIndex = realIndices ? (*realIndices)[i] : i;
137 
138       RINOK(Archive_Get_HardLinkNode(archive, realIndex, h, defined));
139       if (defined)
140       {
141         bool isAltStream = false;
142         RINOK(Archive_IsItem_AltStream(archive, realIndex, isAltStream));
143         if (!isAltStream)
144           hardIDs.Add(h);
145       }
146     }
147   }
148 
149   hardIDs.Sort2();
150 
151   {
152     // wee keep only items that have 2 or more items
153     unsigned k = 0;
154     unsigned numSame = 1;
155     for (unsigned i = 1; i < hardIDs.Size(); i++)
156     {
157       if (hardIDs[i].Compare(hardIDs[i - 1]) != 0)
158         numSame = 1;
159       else if (++numSame == 2)
160       {
161         if (i - 1 != k)
162           hardIDs[k] = hardIDs[i - 1];
163         k++;
164       }
165     }
166     hardIDs.DeleteFrom(k);
167   }
168 
169   _hardLinks.PrepareLinks();
170   return S_OK;
171 }
172 
173 #endif
174 
CArchiveExtractCallback()175 CArchiveExtractCallback::CArchiveExtractCallback():
176     WriteCTime(true),
177     WriteATime(true),
178     WriteMTime(true),
179     _multiArchives(false)
180 {
181   LocalProgressSpec = new CLocalProgress();
182   _localProgress = LocalProgressSpec;
183 
184   #ifdef _USE_SECURITY_CODE
185   _saclEnabled = InitLocalPrivileges();
186   #endif
187 }
188 
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)189 void CArchiveExtractCallback::Init(
190     const CExtractNtOptions &ntOptions,
191     const NWildcard::CCensorNode *wildcardCensor,
192     const CArc *arc,
193     IFolderArchiveExtractCallback *extractCallback2,
194     bool stdOutMode, bool testMode,
195     const FString &directoryPath,
196     const UStringVector &removePathParts, bool removePartsForAltStreams,
197     UInt64 packSize)
198 {
199   _extractedFolderPaths.Clear();
200   _extractedFolderIndices.Clear();
201 
202   #ifdef SUPPORT_LINKS
203   _hardLinks.Clear();
204   #endif
205 
206   #ifdef SUPPORT_ALT_STREAMS
207   _renamedFiles.Clear();
208   #endif
209 
210   _ntOptions = ntOptions;
211   _wildcardCensor = wildcardCensor;
212 
213   _stdOutMode = stdOutMode;
214   _testMode = testMode;
215 
216   // _progressTotal = 0;
217   // _progressTotal_Defined = false;
218 
219   _packTotal = packSize;
220   _progressTotal = packSize;
221   _progressTotal_Defined = true;
222 
223   _extractCallback2 = extractCallback2;
224   _compressProgress.Release();
225   _extractCallback2.QueryInterface(IID_ICompressProgressInfo, &_compressProgress);
226   _extractCallback2.QueryInterface(IID_IArchiveExtractCallbackMessage, &_callbackMessage);
227   _extractCallback2.QueryInterface(IID_IFolderArchiveExtractCallback2, &_folderArchiveExtractCallback2);
228 
229   #ifndef _SFX
230 
231   _extractCallback2.QueryInterface(IID_IFolderExtractToStreamCallback, &ExtractToStreamCallback);
232   if (ExtractToStreamCallback)
233   {
234     Int32 useStreams = 0;
235     if (ExtractToStreamCallback->UseExtractToStream(&useStreams) != S_OK)
236       useStreams = 0;
237     if (useStreams == 0)
238       ExtractToStreamCallback.Release();
239   }
240 
241   #endif
242 
243   LocalProgressSpec->Init(extractCallback2, true);
244   LocalProgressSpec->SendProgress = false;
245 
246   _removePathParts = removePathParts;
247   _removePartsForAltStreams = removePartsForAltStreams;
248 
249   #ifndef _SFX
250   _baseParentFolder = (UInt32)(Int32)-1;
251   _use_baseParentFolder_mode = false;
252   #endif
253 
254   _arc = arc;
255   _dirPathPrefix = directoryPath;
256   _dirPathPrefix_Full = directoryPath;
257   #if defined(_WIN32) && !defined(UNDER_CE)
258   if (!NName::IsAltPathPrefix(_dirPathPrefix))
259   #endif
260   {
261     NName::NormalizeDirPathPrefix(_dirPathPrefix);
262     NDir::MyGetFullPathName(directoryPath, _dirPathPrefix_Full);
263     NName::NormalizeDirPathPrefix(_dirPathPrefix_Full);
264   }
265 }
266 
SetTotal(UInt64 size)267 STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 size)
268 {
269   COM_TRY_BEGIN
270   _progressTotal = size;
271   _progressTotal_Defined = true;
272   if (!_multiArchives && _extractCallback2)
273     return _extractCallback2->SetTotal(size);
274   return S_OK;
275   COM_TRY_END
276 }
277 
NormalizeVals(UInt64 & v1,UInt64 & v2)278 static void NormalizeVals(UInt64 &v1, UInt64 &v2)
279 {
280   const UInt64 kMax = (UInt64)1 << 31;
281   while (v1 > kMax)
282   {
283     v1 >>= 1;
284     v2 >>= 1;
285   }
286 }
287 
MyMultDiv64(UInt64 unpCur,UInt64 unpTotal,UInt64 packTotal)288 static UInt64 MyMultDiv64(UInt64 unpCur, UInt64 unpTotal, UInt64 packTotal)
289 {
290   NormalizeVals(packTotal, unpTotal);
291   NormalizeVals(unpCur, unpTotal);
292   if (unpTotal == 0)
293     unpTotal = 1;
294   return unpCur * packTotal / unpTotal;
295 }
296 
SetCompleted(const UInt64 * completeValue)297 STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 *completeValue)
298 {
299   COM_TRY_BEGIN
300 
301   if (!_extractCallback2)
302     return S_OK;
303 
304   UInt64 packCur;
305   if (_multiArchives)
306   {
307     packCur = LocalProgressSpec->InSize;
308     if (completeValue && _progressTotal_Defined)
309       packCur += MyMultDiv64(*completeValue, _progressTotal, _packTotal);
310     completeValue = &packCur;
311   }
312   return _extractCallback2->SetCompleted(completeValue);
313 
314   COM_TRY_END
315 }
316 
SetRatioInfo(const UInt64 * inSize,const UInt64 * outSize)317 STDMETHODIMP CArchiveExtractCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)
318 {
319   COM_TRY_BEGIN
320   return _localProgress->SetRatioInfo(inSize, outSize);
321   COM_TRY_END
322 }
323 
CreateComplexDirectory(const UStringVector & dirPathParts,FString & fullPath)324 void CArchiveExtractCallback::CreateComplexDirectory(const UStringVector &dirPathParts, FString &fullPath)
325 {
326   bool isAbsPath = false;
327 
328   if (!dirPathParts.IsEmpty())
329   {
330     const UString &s = dirPathParts[0];
331     if (s.IsEmpty())
332       isAbsPath = true;
333     #if defined(_WIN32) && !defined(UNDER_CE)
334     else
335     {
336       if (NName::IsDrivePath2(s))
337         isAbsPath = true;
338     }
339     #endif
340   }
341 
342   if (_pathMode == NExtract::NPathMode::kAbsPaths && isAbsPath)
343     fullPath.Empty();
344   else
345     fullPath = _dirPathPrefix;
346 
347   FOR_VECTOR (i, dirPathParts)
348   {
349     if (i != 0)
350       fullPath.Add_PathSepar();
351     const UString &s = dirPathParts[i];
352     fullPath += us2fs(s);
353     #if defined(_WIN32) && !defined(UNDER_CE)
354     if (_pathMode == NExtract::NPathMode::kAbsPaths)
355       if (i == 0 && s.Len() == 2 && NName::IsDrivePath2(s))
356         continue;
357     #endif
358     CreateDir(fullPath);
359   }
360 }
361 
GetTime(int index,PROPID propID,FILETIME & filetime,bool & filetimeIsDefined)362 HRESULT CArchiveExtractCallback::GetTime(int index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined)
363 {
364   filetimeIsDefined = false;
365   NCOM::CPropVariant prop;
366   RINOK(_arc->Archive->GetProperty(index, propID, &prop));
367   if (prop.vt == VT_FILETIME)
368   {
369     filetime = prop.filetime;
370     filetimeIsDefined = (filetime.dwHighDateTime != 0 || filetime.dwLowDateTime != 0);
371   }
372   else if (prop.vt != VT_EMPTY)
373     return E_FAIL;
374   return S_OK;
375 }
376 
GetUnpackSize()377 HRESULT CArchiveExtractCallback::GetUnpackSize()
378 {
379   return _arc->GetItemSize(_index, _curSize, _curSizeDefined);
380 }
381 
AddPathToMessage(UString & s,const FString & path)382 static void AddPathToMessage(UString &s, const FString &path)
383 {
384   s.AddAscii(" : ");
385   s += fs2us(path);
386 }
387 
SendMessageError(const char * message,const FString & path)388 HRESULT CArchiveExtractCallback::SendMessageError(const char *message, const FString &path)
389 {
390   UString s;
391   s.AddAscii(message);
392   AddPathToMessage(s, path);
393   return _extractCallback2->MessageError(s);
394 }
395 
SendMessageError_with_LastError(const char * message,const FString & path)396 HRESULT CArchiveExtractCallback::SendMessageError_with_LastError(const char *message, const FString &path)
397 {
398   DWORD errorCode = GetLastError();
399   UString s;
400   s.AddAscii(message);
401   if (errorCode != 0)
402   {
403     s.AddAscii(" : ");
404     s += NError::MyFormatMessage(errorCode);
405   }
406   AddPathToMessage(s, path);
407   return _extractCallback2->MessageError(s);
408 }
409 
SendMessageError2(const char * message,const FString & path1,const FString & path2)410 HRESULT CArchiveExtractCallback::SendMessageError2(const char *message, const FString &path1, const FString &path2)
411 {
412   UString s;
413   s.AddAscii(message);
414   AddPathToMessage(s, path1);
415   AddPathToMessage(s, path2);
416   return _extractCallback2->MessageError(s);
417 }
418 
419 #ifndef _SFX
420 
GetProp(PROPID propID,PROPVARIANT * value)421 STDMETHODIMP CGetProp::GetProp(PROPID propID, PROPVARIANT *value)
422 {
423   /*
424   if (propID == kpidName)
425   {
426     COM_TRY_BEGIN
427     NCOM::CPropVariant prop = Name;
428     prop.Detach(value);
429     return S_OK;
430     COM_TRY_END
431   }
432   */
433   return Arc->Archive->GetProperty(IndexInArc, propID, value);
434 }
435 
436 #endif
437 
438 
439 #ifdef SUPPORT_LINKS
440 
GetDirPrefixOf(const UString & src)441 static UString GetDirPrefixOf(const UString &src)
442 {
443   UString s = src;
444   if (!s.IsEmpty())
445   {
446     if (IsPathSepar(s.Back()))
447       s.DeleteBack();
448     int pos = s.ReverseFind_PathSepar();
449     s.DeleteFrom(pos + 1);
450   }
451   return s;
452 }
453 
454 #endif
455 
456 
IsSafePath(const UString & path)457 bool IsSafePath(const UString &path)
458 {
459   if (NName::IsAbsolutePath(path))
460     return false;
461 
462   UStringVector parts;
463   SplitPathToParts(path, parts);
464   unsigned level = 0;
465 
466   FOR_VECTOR (i, parts)
467   {
468     const UString &s = parts[i];
469     if (s.IsEmpty())
470     {
471       if (i == 0)
472         return false;
473       continue;
474     }
475     if (s == L".")
476       continue;
477     if (s == L"..")
478     {
479       if (level == 0)
480         return false;
481       level--;
482     }
483     else
484       level++;
485   }
486 
487   return level > 0;
488 }
489 
490 
CensorNode_CheckPath2(const NWildcard::CCensorNode & node,const CReadArcItem & item,bool & include)491 bool CensorNode_CheckPath2(const NWildcard::CCensorNode &node, const CReadArcItem &item, bool &include)
492 {
493   bool found = false;
494 
495   if (node.CheckPathVect(item.PathParts, !item.MainIsDir, include))
496   {
497     if (!include)
498       return true;
499 
500     #ifdef SUPPORT_ALT_STREAMS
501     if (!item.IsAltStream)
502       return true;
503     #endif
504 
505     found = true;
506   }
507 
508   #ifdef SUPPORT_ALT_STREAMS
509 
510   if (!item.IsAltStream)
511     return false;
512 
513   UStringVector pathParts2 = item.PathParts;
514   if (pathParts2.IsEmpty())
515     pathParts2.AddNew();
516   UString &back = pathParts2.Back();
517   back += L':';
518   back += item.AltStreamName;
519   bool include2;
520 
521   if (node.CheckPathVect(pathParts2,
522       true, // isFile,
523       include2))
524   {
525     include = include2;
526     return true;
527   }
528 
529   #endif
530 
531   return found;
532 }
533 
CensorNode_CheckPath(const NWildcard::CCensorNode & node,const CReadArcItem & item)534 bool CensorNode_CheckPath(const NWildcard::CCensorNode &node, const CReadArcItem &item)
535 {
536   bool include;
537   if (CensorNode_CheckPath2(node, item, include))
538     return include;
539   return false;
540 }
541 
MakePath_from_2_Parts(const FString & prefix,const FString & path)542 static FString MakePath_from_2_Parts(const FString &prefix, const FString &path)
543 {
544   FString s = prefix;
545   #if defined(_WIN32) && !defined(UNDER_CE)
546   if (!path.IsEmpty() && path[0] == ':' && !prefix.IsEmpty() && IsPathSepar(prefix.Back()))
547   {
548     if (!NName::IsDriveRootPath_SuperAllowed(prefix))
549       s.DeleteBack();
550   }
551   #endif
552   s += path;
553   return s;
554 }
555 
556 
557 /*
558 #ifdef SUPPORT_LINKS
559 
560 struct CTempMidBuffer
561 {
562   void *Buf;
563 
564   CTempMidBuffer(size_t size): Buf(NULL) { Buf = ::MidAlloc(size); }
565   ~CTempMidBuffer() { ::MidFree(Buf); }
566 };
567 
568 HRESULT CArchiveExtractCallback::MyCopyFile(ISequentialOutStream *outStream)
569 {
570   const size_t kBufSize = 1 << 16;
571   CTempMidBuffer buf(kBufSize);
572   if (!buf.Buf)
573     return E_OUTOFMEMORY;
574 
575   NIO::CInFile inFile;
576   NIO::COutFile outFile;
577 
578   if (!inFile.Open(_CopyFile_Path))
579     return SendMessageError_with_LastError("Open error", _CopyFile_Path);
580 
581   for (;;)
582   {
583     UInt32 num;
584 
585     if (!inFile.Read(buf.Buf, kBufSize, num))
586       return SendMessageError_with_LastError("Read error", _CopyFile_Path);
587 
588     if (num == 0)
589       return S_OK;
590 
591 
592     RINOK(WriteStream(outStream, buf.Buf, num));
593   }
594 }
595 
596 #endif
597 */
598 
GetStream(UInt32 index,ISequentialOutStream ** outStream,Int32 askExtractMode)599 STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode)
600 {
601   COM_TRY_BEGIN
602 
603   *outStream = NULL;
604 
605   #ifndef _SFX
606   if (_hashStream)
607     _hashStreamSpec->ReleaseStream();
608   _hashStreamWasUsed = false;
609   #endif
610 
611   _outFileStream.Release();
612 
613   _encrypted = false;
614   _position = 0;
615   _isSplit = false;
616 
617   _curSize = 0;
618   _curSizeDefined = false;
619   _index = index;
620 
621   _diskFilePath.Empty();
622 
623   // _fi.Clear();
624 
625   #ifdef SUPPORT_LINKS
626   // _CopyFile_Path.Empty();
627   linkPath.Empty();
628   #endif
629 
630   IInArchive *archive = _arc->Archive;
631 
632   #ifndef _SFX
633   _item._use_baseParentFolder_mode = _use_baseParentFolder_mode;
634   if (_use_baseParentFolder_mode)
635   {
636     _item._baseParentFolder = _baseParentFolder;
637     if (_pathMode == NExtract::NPathMode::kFullPaths ||
638         _pathMode == NExtract::NPathMode::kAbsPaths)
639       _item._baseParentFolder = -1;
640   }
641   #endif
642 
643   #ifdef SUPPORT_ALT_STREAMS
644   _item.WriteToAltStreamIfColon = _ntOptions.WriteToAltStreamIfColon;
645   #endif
646 
647   RINOK(_arc->GetItem(index, _item));
648 
649   {
650     NCOM::CPropVariant prop;
651     RINOK(archive->GetProperty(index, kpidPosition, &prop));
652     if (prop.vt != VT_EMPTY)
653     {
654       if (prop.vt != VT_UI8)
655         return E_FAIL;
656       _position = prop.uhVal.QuadPart;
657       _isSplit = true;
658     }
659   }
660 
661   #ifdef SUPPORT_LINKS
662 
663   // bool isCopyLink = false;
664   bool isHardLink = false;
665   bool isJunction = false;
666   bool isRelative = false;
667 
668   {
669     NCOM::CPropVariant prop;
670     RINOK(archive->GetProperty(index, kpidHardLink, &prop));
671     if (prop.vt == VT_BSTR)
672     {
673       isHardLink = true;
674       // isCopyLink = false;
675       isRelative = false; // RAR5, TAR: hard links are from root folder of archive
676       linkPath.SetFromBstr(prop.bstrVal);
677     }
678     else if (prop.vt != VT_EMPTY)
679       return E_FAIL;
680   }
681 
682   /*
683   {
684     NCOM::CPropVariant prop;
685     RINOK(archive->GetProperty(index, kpidCopyLink, &prop));
686     if (prop.vt == VT_BSTR)
687     {
688       isHardLink = false;
689       isCopyLink = true;
690       isRelative = false; // RAR5: copy links are from root folder of archive
691       linkPath.SetFromBstr(prop.bstrVal);
692     }
693     else if (prop.vt != VT_EMPTY)
694       return E_FAIL;
695   }
696   */
697 
698   {
699     NCOM::CPropVariant prop;
700     RINOK(archive->GetProperty(index, kpidSymLink, &prop));
701     if (prop.vt == VT_BSTR)
702     {
703       isHardLink = false;
704       // isCopyLink = false;
705       isRelative = true; // RAR5, TAR: symbolic links can be relative
706       linkPath.SetFromBstr(prop.bstrVal);
707     }
708     else if (prop.vt != VT_EMPTY)
709       return E_FAIL;
710   }
711 
712 
713   bool isOkReparse = false;
714 
715   if (linkPath.IsEmpty() && _arc->GetRawProps)
716   {
717     const void *data;
718     UInt32 dataSize;
719     UInt32 propType;
720 
721     _arc->GetRawProps->GetRawProp(_index, kpidNtReparse, &data, &dataSize, &propType);
722 
723     if (dataSize != 0)
724     {
725       if (propType != NPropDataType::kRaw)
726         return E_FAIL;
727       UString s;
728       CReparseAttr reparse;
729       isOkReparse = reparse.Parse((const Byte *)data, dataSize);
730       if (isOkReparse)
731       {
732         isHardLink = false;
733         // isCopyLink = false;
734         linkPath = reparse.GetPath();
735         isJunction = reparse.IsMountPoint();
736         isRelative = reparse.IsRelative();
737         #ifndef _WIN32
738         linkPath.Replace(L'\\', WCHAR_PATH_SEPARATOR);
739         #endif
740       }
741     }
742   }
743 
744   if (!linkPath.IsEmpty())
745   {
746     #ifdef _WIN32
747     linkPath.Replace(L'/', WCHAR_PATH_SEPARATOR);
748     #endif
749 
750     // rar5 uses "\??\" prefix for absolute links
751     if (linkPath.IsPrefixedBy(WSTRING_PATH_SEPARATOR L"??" WSTRING_PATH_SEPARATOR))
752     {
753       isRelative = false;
754       linkPath.DeleteFrontal(4);
755     }
756 
757     for (;;)
758     // while (NName::IsAbsolutePath(linkPath))
759     {
760       unsigned n = NName::GetRootPrefixSize(linkPath);
761       if (n == 0)
762         break;
763       isRelative = false;
764       linkPath.DeleteFrontal(n);
765     }
766   }
767 
768   if (!linkPath.IsEmpty() && !isRelative && _removePathParts.Size() != 0)
769   {
770     UStringVector pathParts;
771     SplitPathToParts(linkPath, pathParts);
772     bool badPrefix = false;
773     FOR_VECTOR (i, _removePathParts)
774     {
775       if (CompareFileNames(_removePathParts[i], pathParts[i]) != 0)
776       {
777         badPrefix = true;
778         break;
779       }
780     }
781     if (!badPrefix)
782       pathParts.DeleteFrontal(_removePathParts.Size());
783     linkPath = MakePathFromParts(pathParts);
784   }
785 
786   #endif
787 
788   RINOK(Archive_GetItemBoolProp(archive, index, kpidEncrypted, _encrypted));
789 
790   RINOK(GetUnpackSize());
791 
792   #ifdef SUPPORT_ALT_STREAMS
793 
794   if (!_ntOptions.AltStreams.Val && _item.IsAltStream)
795     return S_OK;
796 
797   #endif
798 
799 
800   UStringVector &pathParts = _item.PathParts;
801 
802   if (_wildcardCensor)
803   {
804     if (!CensorNode_CheckPath(*_wildcardCensor, _item))
805       return S_OK;
806   }
807 
808   #ifndef _SFX
809   if (_use_baseParentFolder_mode)
810   {
811     if (!pathParts.IsEmpty())
812     {
813       unsigned numRemovePathParts = 0;
814 
815       #ifdef SUPPORT_ALT_STREAMS
816       if (_pathMode == NExtract::NPathMode::kNoPathsAlt && _item.IsAltStream)
817         numRemovePathParts = pathParts.Size();
818       else
819       #endif
820       if (_pathMode == NExtract::NPathMode::kNoPaths ||
821           _pathMode == NExtract::NPathMode::kNoPathsAlt)
822         numRemovePathParts = pathParts.Size() - 1;
823       pathParts.DeleteFrontal(numRemovePathParts);
824     }
825   }
826   else
827   #endif
828   {
829     if (pathParts.IsEmpty())
830     {
831       if (_item.IsDir)
832         return S_OK;
833       /*
834       #ifdef SUPPORT_ALT_STREAMS
835       if (!_item.IsAltStream)
836       #endif
837         return E_FAIL;
838       */
839     }
840 
841     unsigned numRemovePathParts = 0;
842 
843     switch (_pathMode)
844     {
845       case NExtract::NPathMode::kFullPaths:
846       case NExtract::NPathMode::kCurPaths:
847       {
848         if (_removePathParts.IsEmpty())
849           break;
850         bool badPrefix = false;
851 
852         if (pathParts.Size() < _removePathParts.Size())
853           badPrefix = true;
854         else
855         {
856           if (pathParts.Size() == _removePathParts.Size())
857           {
858             if (_removePartsForAltStreams)
859             {
860               #ifdef SUPPORT_ALT_STREAMS
861               if (!_item.IsAltStream)
862               #endif
863                 badPrefix = true;
864             }
865             else
866             {
867               if (!_item.MainIsDir)
868                 badPrefix = true;
869             }
870           }
871 
872           if (!badPrefix)
873           FOR_VECTOR (i, _removePathParts)
874           {
875             if (CompareFileNames(_removePathParts[i], pathParts[i]) != 0)
876             {
877               badPrefix = true;
878               break;
879             }
880           }
881         }
882 
883         if (badPrefix)
884         {
885           if (askExtractMode == NArchive::NExtract::NAskMode::kExtract && !_testMode)
886             return E_FAIL;
887         }
888         else
889           numRemovePathParts = _removePathParts.Size();
890         break;
891       }
892 
893       case NExtract::NPathMode::kNoPaths:
894       {
895         if (!pathParts.IsEmpty())
896           numRemovePathParts = pathParts.Size() - 1;
897         break;
898       }
899       case NExtract::NPathMode::kNoPathsAlt:
900       {
901         #ifdef SUPPORT_ALT_STREAMS
902         if (_item.IsAltStream)
903           numRemovePathParts = pathParts.Size();
904         else
905         #endif
906         if (!pathParts.IsEmpty())
907           numRemovePathParts = pathParts.Size() - 1;
908         break;
909       }
910       /*
911       case NExtract::NPathMode::kFullPaths:
912       case NExtract::NPathMode::kAbsPaths:
913         break;
914       */
915     }
916 
917     pathParts.DeleteFrontal(numRemovePathParts);
918   }
919 
920   #ifndef _SFX
921 
922   if (ExtractToStreamCallback)
923   {
924     if (!GetProp)
925     {
926       GetProp_Spec = new CGetProp;
927       GetProp = GetProp_Spec;
928     }
929     GetProp_Spec->Arc = _arc;
930     GetProp_Spec->IndexInArc = index;
931     UString name = MakePathFromParts(pathParts);
932 
933     #ifdef SUPPORT_ALT_STREAMS
934     if (_item.IsAltStream)
935     {
936       if (!pathParts.IsEmpty() || (!_removePartsForAltStreams && _pathMode != NExtract::NPathMode::kNoPathsAlt))
937         name += L':';
938       name += _item.AltStreamName;
939     }
940     #endif
941 
942     return ExtractToStreamCallback->GetStream7(name, BoolToInt(_item.IsDir), outStream, askExtractMode, GetProp);
943   }
944 
945   #endif
946 
947   CMyComPtr<ISequentialOutStream> outStreamLoc;
948 
949 if (askExtractMode == NArchive::NExtract::NAskMode::kExtract && !_testMode)
950 {
951   if (_stdOutMode)
952   {
953     outStreamLoc = new CStdOutFileStream;
954   }
955   else
956   {
957     {
958       NCOM::CPropVariant prop;
959       RINOK(archive->GetProperty(index, kpidAttrib, &prop));
960       if (prop.vt == VT_UI4)
961       {
962         _fi.Attrib = prop.ulVal;
963         _fi.AttribDefined = true;
964       }
965       else if (prop.vt == VT_EMPTY)
966         _fi.AttribDefined = false;
967       else
968         return E_FAIL;
969     }
970 
971     RINOK(GetTime(index, kpidCTime, _fi.CTime, _fi.CTimeDefined));
972     RINOK(GetTime(index, kpidATime, _fi.ATime, _fi.ATimeDefined));
973     RINOK(GetTime(index, kpidMTime, _fi.MTime, _fi.MTimeDefined));
974 
975     bool isAnti = false;
976     RINOK(_arc->IsItemAnti(index, isAnti));
977 
978     #ifdef SUPPORT_ALT_STREAMS
979     if (!_item.IsAltStream
980         || !pathParts.IsEmpty()
981         || !(_removePartsForAltStreams || _pathMode == NExtract::NPathMode::kNoPathsAlt))
982     #endif
983       Correct_FsPath(_pathMode == NExtract::NPathMode::kAbsPaths, pathParts, _item.MainIsDir);
984 
985     #ifdef SUPPORT_ALT_STREAMS
986 
987     if (_item.IsAltStream)
988     {
989       UString s = _item.AltStreamName;
990       Correct_AltStream_Name(s);
991       bool needColon = true;
992 
993       if (pathParts.IsEmpty())
994       {
995         pathParts.AddNew();
996         if (_removePartsForAltStreams || _pathMode == NExtract::NPathMode::kNoPathsAlt)
997           needColon = false;
998       }
999       else if (_pathMode == NExtract::NPathMode::kAbsPaths &&
1000           NWildcard::GetNumPrefixParts_if_DrivePath(pathParts) == pathParts.Size())
1001         pathParts.AddNew();
1002 
1003       UString &name = pathParts.Back();
1004       if (needColon)
1005         name += (wchar_t)(_ntOptions.ReplaceColonForAltStream ? L'_' : L':');
1006       name += s;
1007     }
1008 
1009     #endif
1010 
1011     UString processedPath = MakePathFromParts(pathParts);
1012 
1013     if (!isAnti)
1014     {
1015       if (!_item.IsDir)
1016       {
1017         if (!pathParts.IsEmpty())
1018           pathParts.DeleteBack();
1019       }
1020 
1021       if (!pathParts.IsEmpty())
1022       {
1023         FString fullPathNew;
1024         CreateComplexDirectory(pathParts, fullPathNew);
1025         if (_item.IsDir)
1026         {
1027           _extractedFolderPaths.Add(fullPathNew);
1028           _extractedFolderIndices.Add(index);
1029           SetDirTime(fullPathNew,
1030             (WriteCTime && _fi.CTimeDefined) ? &_fi.CTime : NULL,
1031             (WriteATime && _fi.ATimeDefined) ? &_fi.ATime : NULL,
1032             (WriteMTime && _fi.MTimeDefined) ? &_fi.MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL));
1033         }
1034       }
1035     }
1036 
1037 
1038     FString fullProcessedPath = us2fs(processedPath);
1039     if (_pathMode != NExtract::NPathMode::kAbsPaths
1040         || !NName::IsAbsolutePath(processedPath))
1041     {
1042        fullProcessedPath = MakePath_from_2_Parts(_dirPathPrefix, fullProcessedPath);
1043     }
1044 
1045     #ifdef SUPPORT_ALT_STREAMS
1046 
1047     if (_item.IsAltStream && _item.ParentIndex != (UInt32)(Int32)-1)
1048     {
1049       int renIndex = _renamedFiles.FindInSorted(CIndexToPathPair(_item.ParentIndex));
1050       if (renIndex >= 0)
1051       {
1052         const CIndexToPathPair &pair = _renamedFiles[renIndex];
1053         fullProcessedPath = pair.Path;
1054         fullProcessedPath += (FChar)':';
1055         UString s = _item.AltStreamName;
1056         Correct_AltStream_Name(s);
1057         fullProcessedPath += us2fs(s);
1058       }
1059     }
1060 
1061     #endif
1062 
1063     bool isRenamed = false;
1064 
1065     if (_item.IsDir)
1066     {
1067       _diskFilePath = fullProcessedPath;
1068       if (isAnti)
1069         RemoveDir(_diskFilePath);
1070       #ifdef SUPPORT_LINKS
1071       if (linkPath.IsEmpty())
1072       #endif
1073         return S_OK;
1074     }
1075     else if (!_isSplit)
1076     {
1077 
1078     // ----- Is file (not split) -----
1079     NFind::CFileInfo fileInfo;
1080     if (fileInfo.Find(fullProcessedPath))
1081     {
1082       switch (_overwriteMode)
1083       {
1084         case NExtract::NOverwriteMode::kSkip:
1085           return S_OK;
1086         case NExtract::NOverwriteMode::kAsk:
1087         {
1088           int slashPos = fullProcessedPath.ReverseFind_PathSepar();
1089           FString realFullProcessedPath = fullProcessedPath.Left(slashPos + 1) + fileInfo.Name;
1090 
1091           Int32 overwriteResult;
1092           RINOK(_extractCallback2->AskOverwrite(
1093               fs2us(realFullProcessedPath), &fileInfo.MTime, &fileInfo.Size, _item.Path,
1094               _fi.MTimeDefined ? &_fi.MTime : NULL,
1095               _curSizeDefined ? &_curSize : NULL,
1096               &overwriteResult))
1097 
1098           switch (overwriteResult)
1099           {
1100             case NOverwriteAnswer::kCancel: return E_ABORT;
1101             case NOverwriteAnswer::kNo: return S_OK;
1102             case NOverwriteAnswer::kNoToAll: _overwriteMode = NExtract::NOverwriteMode::kSkip; return S_OK;
1103             case NOverwriteAnswer::kYes: break;
1104             case NOverwriteAnswer::kYesToAll: _overwriteMode = NExtract::NOverwriteMode::kOverwrite; break;
1105             case NOverwriteAnswer::kAutoRename: _overwriteMode = NExtract::NOverwriteMode::kRename; break;
1106             default:
1107               return E_FAIL;
1108           }
1109         }
1110       }
1111       if (_overwriteMode == NExtract::NOverwriteMode::kRename)
1112       {
1113         if (!AutoRenamePath(fullProcessedPath))
1114         {
1115           RINOK(SendMessageError(kCantAutoRename, fullProcessedPath));
1116           return E_FAIL;
1117         }
1118         isRenamed = true;
1119       }
1120       else if (_overwriteMode == NExtract::NOverwriteMode::kRenameExisting)
1121       {
1122         FString existPath = fullProcessedPath;
1123         if (!AutoRenamePath(existPath))
1124         {
1125           RINOK(SendMessageError(kCantAutoRename, fullProcessedPath));
1126           return E_FAIL;
1127         }
1128         // MyMoveFile can raname folders. So it's OK to use it for folders too
1129         if (!MyMoveFile(fullProcessedPath, existPath))
1130         {
1131           RINOK(SendMessageError2(kCantRenameFile, existPath, fullProcessedPath));
1132           return E_FAIL;
1133         }
1134       }
1135       else
1136       {
1137         if (fileInfo.IsDir())
1138         {
1139           // do we need to delete all files in folder?
1140           if (!RemoveDir(fullProcessedPath))
1141           {
1142             RINOK(SendMessageError_with_LastError(kCantDeleteOutputDir, fullProcessedPath));
1143             return S_OK;
1144           }
1145         }
1146         else
1147         {
1148           bool needDelete = true;
1149           if (needDelete)
1150           {
1151             if (!DeleteFileAlways(fullProcessedPath))
1152             {
1153               RINOK(SendMessageError_with_LastError(kCantDeleteOutputFile, fullProcessedPath));
1154               return S_OK;
1155               // return E_FAIL;
1156             }
1157           }
1158         }
1159       }
1160     }
1161     else // not Find(fullProcessedPath)
1162     {
1163       // we need to clear READ-ONLY of parent before creating alt stream
1164       #if defined(_WIN32) && !defined(UNDER_CE)
1165       int colonPos = NName::FindAltStreamColon(fullProcessedPath);
1166       if (colonPos >= 0 && fullProcessedPath[(unsigned)colonPos + 1] != 0)
1167       {
1168         FString parentFsPath = fullProcessedPath;
1169         parentFsPath.DeleteFrom(colonPos);
1170         NFind::CFileInfo parentFi;
1171         if (parentFi.Find(parentFsPath))
1172         {
1173           if (parentFi.IsReadOnly())
1174             SetFileAttrib(parentFsPath, parentFi.Attrib & ~FILE_ATTRIBUTE_READONLY);
1175         }
1176       }
1177       #endif
1178     }
1179     // ----- END of code for    Is file (not split) -----
1180 
1181     }
1182     _diskFilePath = fullProcessedPath;
1183 
1184 
1185     if (!isAnti)
1186     {
1187       #ifdef SUPPORT_LINKS
1188 
1189       if (!linkPath.IsEmpty())
1190       {
1191         #ifndef UNDER_CE
1192 
1193         UString relatPath;
1194         if (isRelative)
1195           relatPath = GetDirPrefixOf(_item.Path);
1196         relatPath += linkPath;
1197 
1198         if (!IsSafePath(relatPath))
1199         {
1200           RINOK(SendMessageError("Dangerous link path was ignored", us2fs(relatPath)));
1201         }
1202         else
1203         {
1204           FString existPath;
1205           if (isHardLink /* || isCopyLink */ || !isRelative)
1206           {
1207             if (!NName::GetFullPath(_dirPathPrefix_Full, us2fs(relatPath), existPath))
1208             {
1209               RINOK(SendMessageError("Incorrect path", us2fs(relatPath)));
1210             }
1211           }
1212           else
1213           {
1214             existPath = us2fs(linkPath);
1215           }
1216 
1217           if (!existPath.IsEmpty())
1218           {
1219             if (isHardLink /* || isCopyLink */)
1220             {
1221               // if (isHardLink)
1222               {
1223                 if (!MyCreateHardLink(fullProcessedPath, existPath))
1224                 {
1225                   RINOK(SendMessageError2("Can not create hard link", fullProcessedPath, existPath));
1226                   // return S_OK;
1227                 }
1228               }
1229               /*
1230               else
1231               {
1232                 NFind::CFileInfo fi;
1233                 if (!fi.Find(existPath))
1234                 {
1235                   RINOK(SendMessageError2("Can not find the file for copying", existPath, fullProcessedPath));
1236                 }
1237                 else
1238                 {
1239                   if (_curSizeDefined && _curSize == fi.Size)
1240                     _CopyFile_Path = existPath;
1241                   else
1242                   {
1243                     RINOK(SendMessageError2("File size collision for file copying", existPath, fullProcessedPath));
1244                   }
1245 
1246                   // RINOK(MyCopyFile(existPath, fullProcessedPath));
1247                 }
1248               }
1249               */
1250             }
1251             else if (_ntOptions.SymLinks.Val)
1252             {
1253               // bool isSymLink = true; // = false for junction
1254               if (_item.IsDir && !isRelative)
1255               {
1256                 // if it's before Vista we use Junction Point
1257                 // isJunction = true;
1258                 // convertToAbs = true;
1259               }
1260 
1261               CByteBuffer data;
1262               if (FillLinkData(data, fs2us(existPath), !isJunction))
1263               {
1264                 CReparseAttr attr;
1265                 if (!attr.Parse(data, data.Size()))
1266                 {
1267                   RINOK(SendMessageError("Internal error for symbolic link file", us2fs(_item.Path)));
1268                   // return E_FAIL;
1269                 }
1270                 else
1271                 if (!NFile::NIO::SetReparseData(fullProcessedPath, _item.IsDir, data, (DWORD)data.Size()))
1272                 {
1273                   RINOK(SendMessageError_with_LastError("Can not create symbolic link", fullProcessedPath));
1274                 }
1275               }
1276             }
1277           }
1278         }
1279 
1280         #endif
1281       }
1282 
1283       if (linkPath.IsEmpty() /* || !_CopyFile_Path.IsEmpty() */)
1284       #endif // SUPPORT_LINKS
1285       {
1286         bool needWriteFile = true;
1287 
1288         #ifdef SUPPORT_LINKS
1289         if (!_hardLinks.IDs.IsEmpty() && !_item.IsAltStream)
1290         {
1291           CHardLinkNode h;
1292           bool defined;
1293           RINOK(Archive_Get_HardLinkNode(archive, index, h, defined));
1294           if (defined)
1295           {
1296             {
1297               int linkIndex = _hardLinks.IDs.FindInSorted2(h);
1298               if (linkIndex >= 0)
1299               {
1300                 FString &hl = _hardLinks.Links[linkIndex];
1301                 if (hl.IsEmpty())
1302                   hl = fullProcessedPath;
1303                 else
1304                 {
1305                   if (!MyCreateHardLink(fullProcessedPath, hl))
1306                   {
1307                     RINOK(SendMessageError2("Can not create hard link", fullProcessedPath, hl));
1308                     return S_OK;
1309                   }
1310                   needWriteFile = false;
1311                 }
1312               }
1313             }
1314           }
1315         }
1316         #endif
1317 
1318         if (needWriteFile)
1319         {
1320           _outFileStreamSpec = new COutFileStream;
1321           CMyComPtr<ISequentialOutStream> outStreamLoc2(_outFileStreamSpec);
1322           if (!_outFileStreamSpec->Open(fullProcessedPath, _isSplit ? OPEN_ALWAYS: CREATE_ALWAYS))
1323           {
1324             // if (::GetLastError() != ERROR_FILE_EXISTS || !isSplit)
1325             {
1326               RINOK(SendMessageError_with_LastError("Can not open output file", fullProcessedPath));
1327               return S_OK;
1328             }
1329           }
1330 
1331 
1332           #ifdef SUPPORT_ALT_STREAMS
1333           if (isRenamed && !_item.IsAltStream)
1334           {
1335             CIndexToPathPair pair(index, fullProcessedPath);
1336             unsigned oldSize = _renamedFiles.Size();
1337             unsigned insertIndex = _renamedFiles.AddToUniqueSorted(pair);
1338             if (oldSize == _renamedFiles.Size())
1339               _renamedFiles[insertIndex].Path = fullProcessedPath;
1340           }
1341           #endif
1342 
1343           if (_isSplit)
1344           {
1345             RINOK(_outFileStreamSpec->Seek(_position, STREAM_SEEK_SET, NULL));
1346           }
1347 
1348           _outFileStream = outStreamLoc2;
1349         }
1350       }
1351     }
1352 
1353     outStreamLoc = _outFileStream;
1354   }
1355 }
1356 
1357   #ifndef _SFX
1358 
1359   if (_hashStream)
1360   {
1361     if (askExtractMode == NArchive::NExtract::NAskMode::kExtract ||
1362         askExtractMode == NArchive::NExtract::NAskMode::kTest)
1363     {
1364       _hashStreamSpec->SetStream(outStreamLoc);
1365       outStreamLoc = _hashStream;
1366       _hashStreamSpec->Init(true);
1367       _hashStreamWasUsed = true;
1368     }
1369   }
1370 
1371   #endif
1372 
1373 
1374   if (outStreamLoc)
1375   {
1376     /*
1377     #ifdef SUPPORT_LINKS
1378 
1379     if (!_CopyFile_Path.IsEmpty())
1380     {
1381       RINOK(PrepareOperation(askExtractMode));
1382       RINOK(MyCopyFile(outStreamLoc));
1383       return SetOperationResult(NArchive::NExtract::NOperationResult::kOK);
1384     }
1385 
1386     if (isCopyLink && _testMode)
1387       return S_OK;
1388 
1389     #endif
1390     */
1391 
1392     *outStream = outStreamLoc.Detach();
1393   }
1394 
1395   return S_OK;
1396 
1397   COM_TRY_END
1398 }
1399 
1400 
PrepareOperation(Int32 askExtractMode)1401 STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode)
1402 {
1403   COM_TRY_BEGIN
1404 
1405   #ifndef _SFX
1406   if (ExtractToStreamCallback)
1407     return ExtractToStreamCallback->PrepareOperation7(askExtractMode);
1408   #endif
1409 
1410   _extractMode = false;
1411 
1412   switch (askExtractMode)
1413   {
1414     case NArchive::NExtract::NAskMode::kExtract:
1415       if (_testMode)
1416         askExtractMode = NArchive::NExtract::NAskMode::kTest;
1417       else
1418         _extractMode = true;
1419       break;
1420   };
1421 
1422   return _extractCallback2->PrepareOperation(_item.Path, BoolToInt(_item.IsDir),
1423       askExtractMode, _isSplit ? &_position: 0);
1424 
1425   COM_TRY_END
1426 }
1427 
1428 
SetOperationResult(Int32 opRes)1429 STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 opRes)
1430 {
1431   COM_TRY_BEGIN
1432 
1433   #ifndef _SFX
1434   if (ExtractToStreamCallback)
1435     return ExtractToStreamCallback->SetOperationResult7(opRes, BoolToInt(_encrypted));
1436   #endif
1437 
1438   #ifndef _SFX
1439 
1440   if (_hashStreamWasUsed)
1441   {
1442     _hashStreamSpec->_hash->Final(_item.IsDir,
1443         #ifdef SUPPORT_ALT_STREAMS
1444           _item.IsAltStream
1445         #else
1446           false
1447         #endif
1448         , _item.Path);
1449     _curSize = _hashStreamSpec->GetSize();
1450     _curSizeDefined = true;
1451     _hashStreamSpec->ReleaseStream();
1452     _hashStreamWasUsed = false;
1453   }
1454 
1455   #endif
1456 
1457   if (_outFileStream)
1458   {
1459     _outFileStreamSpec->SetTime(
1460         (WriteCTime && _fi.CTimeDefined) ? &_fi.CTime : NULL,
1461         (WriteATime && _fi.ATimeDefined) ? &_fi.ATime : NULL,
1462         (WriteMTime && _fi.MTimeDefined) ? &_fi.MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL));
1463     _curSize = _outFileStreamSpec->ProcessedSize;
1464     _curSizeDefined = true;
1465     RINOK(_outFileStreamSpec->Close());
1466     _outFileStream.Release();
1467   }
1468 
1469   #ifdef _USE_SECURITY_CODE
1470   if (!_stdOutMode && _extractMode && _ntOptions.NtSecurity.Val && _arc->GetRawProps)
1471   {
1472     const void *data;
1473     UInt32 dataSize;
1474     UInt32 propType;
1475     _arc->GetRawProps->GetRawProp(_index, kpidNtSecure, &data, &dataSize, &propType);
1476     if (dataSize != 0)
1477     {
1478       if (propType != NPropDataType::kRaw)
1479         return E_FAIL;
1480       if (CheckNtSecure((const Byte *)data, dataSize))
1481       {
1482         SECURITY_INFORMATION securInfo = DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION;
1483         if (_saclEnabled)
1484           securInfo |= SACL_SECURITY_INFORMATION;
1485         ::SetFileSecurityW(fs2us(_diskFilePath), securInfo, (PSECURITY_DESCRIPTOR)(void *)data);
1486       }
1487     }
1488   }
1489   #endif
1490 
1491   if (!_curSizeDefined)
1492     GetUnpackSize();
1493 
1494   if (_curSizeDefined)
1495   {
1496     #ifdef SUPPORT_ALT_STREAMS
1497     if (_item.IsAltStream)
1498       AltStreams_UnpackSize += _curSize;
1499     else
1500     #endif
1501       UnpackSize += _curSize;
1502   }
1503 
1504   if (_item.IsDir)
1505     NumFolders++;
1506   #ifdef SUPPORT_ALT_STREAMS
1507   else if (_item.IsAltStream)
1508     NumAltStreams++;
1509   #endif
1510   else
1511     NumFiles++;
1512 
1513   if (!_stdOutMode && _extractMode && _fi.AttribDefined)
1514     SetFileAttrib(_diskFilePath, _fi.Attrib);
1515 
1516   RINOK(_extractCallback2->SetOperationResult(opRes, BoolToInt(_encrypted)));
1517 
1518   return S_OK;
1519 
1520   COM_TRY_END
1521 }
1522 
ReportExtractResult(UInt32 indexType,UInt32 index,Int32 opRes)1523 STDMETHODIMP CArchiveExtractCallback::ReportExtractResult(UInt32 indexType, UInt32 index, Int32 opRes)
1524 {
1525   if (_folderArchiveExtractCallback2)
1526   {
1527     bool isEncrypted = false;
1528     wchar_t temp[16];
1529     UString s2;
1530     const wchar_t *s = NULL;
1531 
1532     if (indexType == NArchive::NEventIndexType::kInArcIndex && index != (UInt32)(Int32)-1)
1533     {
1534       CReadArcItem item;
1535       RINOK(_arc->GetItem(index, item));
1536       s2 = item.Path;
1537       s = s2;
1538       RINOK(Archive_GetItemBoolProp(_arc->Archive, index, kpidEncrypted, isEncrypted));
1539     }
1540     else
1541     {
1542       temp[0] = '#';
1543       ConvertUInt32ToString(index, temp + 1);
1544       s = temp;
1545       // if (indexType == NArchive::NEventIndexType::kBlockIndex) {}
1546     }
1547 
1548     return _folderArchiveExtractCallback2->ReportExtractResult(opRes, isEncrypted, s);
1549   }
1550 
1551   return S_OK;
1552 }
1553 
1554 
CryptoGetTextPassword(BSTR * password)1555 STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password)
1556 {
1557   COM_TRY_BEGIN
1558   if (!_cryptoGetTextPassword)
1559   {
1560     RINOK(_extractCallback2.QueryInterface(IID_ICryptoGetTextPassword,
1561         &_cryptoGetTextPassword));
1562   }
1563   return _cryptoGetTextPassword->CryptoGetTextPassword(password);
1564   COM_TRY_END
1565 }
1566 
1567 
1568 struct CExtrRefSortPair
1569 {
1570   unsigned Len;
1571   unsigned Index;
1572 
1573   int Compare(const CExtrRefSortPair &a) const;
1574 };
1575 
1576 #define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; }
1577 
Compare(const CExtrRefSortPair & a) const1578 int CExtrRefSortPair::Compare(const CExtrRefSortPair &a) const
1579 {
1580   RINOZ(-MyCompare(Len, a.Len));
1581   return MyCompare(Index, a.Index);
1582 }
1583 
GetNumSlashes(const FChar * s)1584 static unsigned GetNumSlashes(const FChar *s)
1585 {
1586   for (unsigned numSlashes = 0;;)
1587   {
1588     FChar c = *s++;
1589     if (c == 0)
1590       return numSlashes;
1591     if (IS_PATH_SEPAR(c))
1592       numSlashes++;
1593   }
1594 }
1595 
SetDirsTimes()1596 HRESULT CArchiveExtractCallback::SetDirsTimes()
1597 {
1598   CRecordVector<CExtrRefSortPair> pairs;
1599   pairs.ClearAndSetSize(_extractedFolderPaths.Size());
1600   unsigned i;
1601 
1602   for (i = 0; i < _extractedFolderPaths.Size(); i++)
1603   {
1604     CExtrRefSortPair &pair = pairs[i];
1605     pair.Index = i;
1606     pair.Len = GetNumSlashes(_extractedFolderPaths[i]);
1607   }
1608 
1609   pairs.Sort2();
1610 
1611   for (i = 0; i < pairs.Size(); i++)
1612   {
1613     int pairIndex = pairs[i].Index;
1614     int index = _extractedFolderIndices[pairIndex];
1615 
1616     FILETIME CTime;
1617     FILETIME ATime;
1618     FILETIME MTime;
1619 
1620     bool CTimeDefined;
1621     bool ATimeDefined;
1622     bool MTimeDefined;
1623 
1624     RINOK(GetTime(index, kpidCTime, CTime, CTimeDefined));
1625     RINOK(GetTime(index, kpidATime, ATime, ATimeDefined));
1626     RINOK(GetTime(index, kpidMTime, MTime, MTimeDefined));
1627 
1628     // printf("\n%S", _extractedFolderPaths[pairIndex]);
1629     SetDirTime(_extractedFolderPaths[pairIndex],
1630       (WriteCTime && CTimeDefined) ? &CTime : NULL,
1631       (WriteATime && ATimeDefined) ? &ATime : NULL,
1632       (WriteMTime && MTimeDefined) ? &MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL));
1633   }
1634   return S_OK;
1635 }
1636