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