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