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