1 // ArchiveExtractCallback.cpp
2
3 #include "StdAfx.h"
4
5 #undef sprintf
6 #undef printf
7
8 #include "../../../Common/ComTry.h"
9 #include "../../../Common/StringConvert.h"
10 #include "../../../Common/Wildcard.h"
11
12 #include "../../../Windows/FileDir.h"
13 #include "../../../Windows/FileFind.h"
14 #include "../../../Windows/FileName.h"
15 #include "../../../Windows/PropVariant.h"
16 #include "../../../Windows/PropVariantConv.h"
17
18 #if defined(_WIN32) && !defined(UNDER_CE) && !defined(_SFX)
19 #define _USE_SECURITY_CODE
20 #include "../../../Windows/SecurityUtils.h"
21 #endif
22
23 #include "../../Common/FilePathAutoRename.h"
24
25 #include "../Common/ExtractingFilePath.h"
26 #include "../Common/PropIDUtils.h"
27
28 #include "ArchiveExtractCallback.h"
29
30 using namespace NWindows;
31 using namespace NFile;
32 using namespace NDir;
33
34 static const char *kCantAutoRename = "Can not create file with auto name";
35 static const char *kCantRenameFile = "Can not rename existing file";
36 static const char *kCantDeleteOutputFile = "Can not delete output file";
37 static const char *kCantDeleteOutputDir = "Can not delete output folder";
38
39
40 #ifndef _SFX
41
Write(const void * data,UInt32 size,UInt32 * processedSize)42 STDMETHODIMP COutStreamWithHash::Write(const void *data, UInt32 size, UInt32 *processedSize)
43 {
44 HRESULT result = S_OK;
45 if (_stream)
46 result = _stream->Write(data, size, &size);
47 if (_calculate)
48 _hash->Update(data, size);
49 _size += size;
50 if (processedSize)
51 *processedSize = size;
52 return result;
53 }
54
55 #endif
56
57 #ifdef _USE_SECURITY_CODE
InitLocalPrivileges()58 bool InitLocalPrivileges()
59 {
60 NSecurity::CAccessToken token;
61 if (!token.OpenProcessToken(GetCurrentProcess(),
62 TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES))
63 return false;
64
65 TOKEN_PRIVILEGES tp;
66
67 tp.PrivilegeCount = 1;
68 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
69
70 if (!::LookupPrivilegeValue(NULL, SE_SECURITY_NAME, &tp.Privileges[0].Luid))
71 return false;
72 if (!token.AdjustPrivileges(&tp))
73 return false;
74 return (GetLastError() == ERROR_SUCCESS);
75 }
76 #endif
77
78 #ifdef SUPPORT_LINKS
79
Compare(const CHardLinkNode & a) const80 int CHardLinkNode::Compare(const CHardLinkNode &a) const
81 {
82 if (StreamId < a.StreamId) return -1;
83 if (StreamId > a.StreamId) return 1;
84 return MyCompare(INode, a.INode);
85 }
86
Archive_Get_HardLinkNode(IInArchive * archive,UInt32 index,CHardLinkNode & h,bool & defined)87 HRESULT Archive_Get_HardLinkNode(IInArchive *archive, UInt32 index, CHardLinkNode &h, bool &defined)
88 {
89 h.INode = 0;
90 h.StreamId = (UInt64)(Int64)-1;
91 defined = false;
92 {
93 NCOM::CPropVariant prop;
94 RINOK(archive->GetProperty(index, kpidINode, &prop));
95 if (!ConvertPropVariantToUInt64(prop, h.INode))
96 return S_OK;
97 }
98 {
99 NCOM::CPropVariant prop;
100 RINOK(archive->GetProperty(index, kpidStreamId, &prop));
101 ConvertPropVariantToUInt64(prop, h.StreamId);
102 }
103 defined = true;
104 return S_OK;
105 }
106
107
PrepareHardLinks(const CRecordVector<UInt32> * realIndices)108 HRESULT CArchiveExtractCallback::PrepareHardLinks(const CRecordVector<UInt32> *realIndices)
109 {
110 _hardLinks.Clear();
111
112 if (!_arc->Ask_INode)
113 return S_OK;
114
115 IInArchive *archive = _arc->Archive;
116 CRecordVector<CHardLinkNode> &hardIDs = _hardLinks.IDs;
117
118 {
119 UInt32 numItems;
120 if (realIndices)
121 numItems = realIndices->Size();
122 else
123 {
124 RINOK(archive->GetNumberOfItems(&numItems));
125 }
126
127 for (UInt32 i = 0; i < numItems; i++)
128 {
129 CHardLinkNode h;
130 bool defined;
131 RINOK(Archive_Get_HardLinkNode(archive, realIndices ? (*realIndices)[i] : i, h, defined));
132 if (defined)
133 hardIDs.Add(h);
134 }
135 }
136
137 hardIDs.Sort2();
138
139 {
140 // wee keep only items that have 2 or more items
141 unsigned k = 0;
142 unsigned numSame = 1;
143 for (unsigned i = 1; i < hardIDs.Size(); i++)
144 {
145 if (hardIDs[i].Compare(hardIDs[i - 1]) != 0)
146 numSame = 1;
147 else if (++numSame == 2)
148 {
149 if (i - 1 != k)
150 hardIDs[k] = hardIDs[i - 1];
151 k++;
152 }
153 }
154 hardIDs.DeleteFrom(k);
155 }
156
157 _hardLinks.PrepareLinks();
158 return S_OK;
159 }
160
161 #endif
162
CArchiveExtractCallback()163 CArchiveExtractCallback::CArchiveExtractCallback():
164 WriteCTime(true),
165 WriteATime(true),
166 WriteMTime(true),
167 _multiArchives(false)
168 {
169 LocalProgressSpec = new CLocalProgress();
170 _localProgress = LocalProgressSpec;
171
172 #ifdef _USE_SECURITY_CODE
173 _saclEnabled = InitLocalPrivileges();
174 #endif
175 }
176
Init(const CExtractNtOptions & ntOptions,const NWildcard::CCensorNode * wildcardCensor,const CArc * arc,IFolderArchiveExtractCallback * extractCallback2,bool stdOutMode,bool testMode,const FString & directoryPath,const UStringVector & removePathParts,UInt64 packSize)177 void CArchiveExtractCallback::Init(
178 const CExtractNtOptions &ntOptions,
179 const NWildcard::CCensorNode *wildcardCensor,
180 const CArc *arc,
181 IFolderArchiveExtractCallback *extractCallback2,
182 bool stdOutMode, bool testMode,
183 const FString &directoryPath,
184 const UStringVector &removePathParts,
185 UInt64 packSize)
186 {
187 _extractedFolderPaths.Clear();
188 _extractedFolderIndices.Clear();
189
190 #ifdef SUPPORT_LINKS
191 _hardLinks.Clear();
192 #endif
193
194 _ntOptions = ntOptions;
195 _wildcardCensor = wildcardCensor;
196
197 _stdOutMode = stdOutMode;
198 _testMode = testMode;
199 _unpTotal = 1;
200 _packTotal = packSize;
201
202 _extractCallback2 = extractCallback2;
203 _compressProgress.Release();
204 _extractCallback2.QueryInterface(IID_ICompressProgressInfo, &_compressProgress);
205
206 #ifndef _SFX
207
208 _extractCallback2.QueryInterface(IID_IFolderExtractToStreamCallback, &ExtractToStreamCallback);
209 if (ExtractToStreamCallback)
210 {
211 Int32 useStreams = 0;
212 if (ExtractToStreamCallback->UseExtractToStream(&useStreams) != S_OK)
213 useStreams = 0;
214 if (useStreams == 0)
215 ExtractToStreamCallback.Release();
216 }
217
218 #endif
219
220 LocalProgressSpec->Init(extractCallback2, true);
221 LocalProgressSpec->SendProgress = false;
222
223 _removePathParts = removePathParts;
224 _baseParentFolder = (UInt32)(Int32)-1;
225 _use_baseParentFolder_mode = false;
226
227 _arc = arc;
228 _directoryPath = directoryPath;
229 NName::NormalizeDirPathPrefix(_directoryPath);
230 NDir::MyGetFullPathName(directoryPath, _directoryPathFull);
231 }
232
SetTotal(UInt64 size)233 STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 size)
234 {
235 COM_TRY_BEGIN
236 _unpTotal = size;
237 if (!_multiArchives && _extractCallback2)
238 return _extractCallback2->SetTotal(size);
239 return S_OK;
240 COM_TRY_END
241 }
242
NormalizeVals(UInt64 & v1,UInt64 & v2)243 static void NormalizeVals(UInt64 &v1, UInt64 &v2)
244 {
245 const UInt64 kMax = (UInt64)1 << 31;
246 while (v1 > kMax)
247 {
248 v1 >>= 1;
249 v2 >>= 1;
250 }
251 }
252
MyMultDiv64(UInt64 unpCur,UInt64 unpTotal,UInt64 packTotal)253 static UInt64 MyMultDiv64(UInt64 unpCur, UInt64 unpTotal, UInt64 packTotal)
254 {
255 NormalizeVals(packTotal, unpTotal);
256 NormalizeVals(unpCur, unpTotal);
257 if (unpTotal == 0)
258 unpTotal = 1;
259 return unpCur * packTotal / unpTotal;
260 }
261
SetCompleted(const UInt64 * completeValue)262 STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 *completeValue)
263 {
264 COM_TRY_BEGIN
265 if (!_extractCallback2)
266 return S_OK;
267
268 if (_multiArchives)
269 {
270 if (completeValue != NULL)
271 {
272 UInt64 packCur = LocalProgressSpec->InSize + MyMultDiv64(*completeValue, _unpTotal, _packTotal);
273 return _extractCallback2->SetCompleted(&packCur);
274 }
275 }
276 return _extractCallback2->SetCompleted(completeValue);
277 COM_TRY_END
278 }
279
SetRatioInfo(const UInt64 * inSize,const UInt64 * outSize)280 STDMETHODIMP CArchiveExtractCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)
281 {
282 COM_TRY_BEGIN
283 return _localProgress->SetRatioInfo(inSize, outSize);
284 COM_TRY_END
285 }
286
287 #define IS_LETTER_CHAR(c) ((c) >= 'a' && (c) <= 'z' || (c) >= 'A' && (c) <= 'Z')
288
IsDriveName(const UString & s)289 static inline bool IsDriveName(const UString &s)
290 {
291 return s.Len() == 2 && s[1] == ':' && IS_LETTER_CHAR(s[0]);
292 }
293
CreateComplexDirectory(const UStringVector & dirPathParts,FString & fullPath)294 void CArchiveExtractCallback::CreateComplexDirectory(const UStringVector &dirPathParts, FString &fullPath)
295 {
296 bool isAbsPath = false;
297
298 if (!dirPathParts.IsEmpty())
299 {
300 const UString &s = dirPathParts[0];
301 if (s.IsEmpty())
302 isAbsPath = true;
303 #ifdef _WIN32
304 else
305 {
306 if (dirPathParts.Size() > 1 && IsDriveName(s))
307 isAbsPath = true;
308 }
309 #endif
310 }
311
312 if (_pathMode == NExtract::NPathMode::kAbsPaths && isAbsPath)
313 fullPath.Empty();
314 else
315 fullPath = _directoryPath;
316
317 FOR_VECTOR (i, dirPathParts)
318 {
319 if (i > 0)
320 fullPath += FCHAR_PATH_SEPARATOR;
321 const UString &s = dirPathParts[i];
322 fullPath += us2fs(s);
323 #ifdef _WIN32
324 if (_pathMode == NExtract::NPathMode::kAbsPaths)
325 if (i == 0 && IsDriveName(s))
326 continue;
327 #endif
328 CreateDir(fullPath);
329 }
330 }
331
GetTime(int index,PROPID propID,FILETIME & filetime,bool & filetimeIsDefined)332 HRESULT CArchiveExtractCallback::GetTime(int index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined)
333 {
334 filetimeIsDefined = false;
335 NCOM::CPropVariant prop;
336 RINOK(_arc->Archive->GetProperty(index, propID, &prop));
337 if (prop.vt == VT_FILETIME)
338 {
339 filetime = prop.filetime;
340 filetimeIsDefined = (filetime.dwHighDateTime != 0 || filetime.dwLowDateTime != 0);
341 }
342 else if (prop.vt != VT_EMPTY)
343 return E_FAIL;
344 return S_OK;
345 }
346
GetUnpackSize()347 HRESULT CArchiveExtractCallback::GetUnpackSize()
348 {
349 return _arc->GetItemSize(_index, _curSize, _curSizeDefined);
350 }
351
SendMessageError(const char * message,const FString & path)352 HRESULT CArchiveExtractCallback::SendMessageError(const char *message, const FString &path)
353 {
354 return _extractCallback2->MessageError(
355 UString(L"ERROR: ") +
356 GetUnicodeString(message) + L": " + fs2us(path));
357 }
358
SendMessageError2(const char * message,const FString & path1,const FString & path2)359 HRESULT CArchiveExtractCallback::SendMessageError2(const char *message, const FString &path1, const FString &path2)
360 {
361 return _extractCallback2->MessageError(
362 UString(L"ERROR: ") +
363 GetUnicodeString(message) + UString(L": ") + fs2us(path1) + UString(L" : ") + fs2us(path2));
364 }
365
366 #ifndef _SFX
367
GetProp(PROPID propID,PROPVARIANT * value)368 STDMETHODIMP CGetProp::GetProp(PROPID propID, PROPVARIANT *value)
369 {
370 if (propID == kpidName)
371 {
372 COM_TRY_BEGIN
373 NCOM::CPropVariant prop = Name.Ptr();
374 prop.Detach(value);
375 return S_OK;
376 COM_TRY_END
377 }
378 return Arc->Archive->GetProperty(IndexInArc, propID, value);
379 }
380
381 #endif
382
383
384 #ifdef SUPPORT_LINKS
385
GetDirPrefixOf(const UString & src)386 static UString GetDirPrefixOf(const UString &src)
387 {
388 UString s = src;
389 if (!s.IsEmpty())
390 {
391 if (s.Back() == WCHAR_PATH_SEPARATOR)
392 s.DeleteBack();
393 int pos = s.ReverseFind(WCHAR_PATH_SEPARATOR);
394 s.DeleteFrom(pos + 1);
395 }
396 return s;
397 }
398
IsSafePath(const UString & path)399 static bool IsSafePath(const UString &path)
400 {
401 UStringVector parts;
402 SplitPathToParts(path, parts);
403 int level = 0;
404 FOR_VECTOR(i, parts)
405 {
406 const UString &s = parts[i];
407 if (s.IsEmpty())
408 continue;
409 if (s == L".")
410 continue;
411 if (s == L"..")
412 {
413 if (level <= 0)
414 return false;
415 level--;
416 }
417 else
418 level++;
419 }
420 return level > 0;
421 }
422
423 #endif
424
425
GetStream(UInt32 index,ISequentialOutStream ** outStream,Int32 askExtractMode)426 STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode)
427 {
428 COM_TRY_BEGIN
429
430 *outStream = 0;
431
432 #ifndef _SFX
433 if (_hashStream)
434 _hashStreamSpec->ReleaseStream();
435 _hashStreamWasUsed = false;
436 #endif
437
438 _outFileStream.Release();
439
440 _encrypted = false;
441 _isSplit = false;
442 _isAltStream = false;
443 _curSize = 0;
444 _curSizeDefined = false;
445 _index = index;
446
447 UString fullPath;
448
449 IInArchive *archive = _arc->Archive;
450 RINOK(_arc->GetItemPath(index, fullPath));
451 RINOK(Archive_IsItem_Folder(archive, index, _fi.IsDir));
452
453 _filePath = fullPath;
454
455 {
456 NCOM::CPropVariant prop;
457 RINOK(archive->GetProperty(index, kpidPosition, &prop));
458 if (prop.vt != VT_EMPTY)
459 {
460 if (prop.vt != VT_UI8)
461 return E_FAIL;
462 _position = prop.uhVal.QuadPart;
463 _isSplit = true;
464 }
465 }
466
467 #ifdef SUPPORT_LINKS
468
469 bool isHardLink = false;
470 bool isJunction = false;
471 bool isRelative = false;
472
473 UString linkPath;
474 // RINOK(Archive_GetItemBoolProp(archive, index, kpidIsHardLink, isHardLink));
475 // if (isHardLink)
476 {
477 NCOM::CPropVariant prop;
478 RINOK(archive->GetProperty(index, kpidHardLink, &prop));
479 if (prop.vt == VT_BSTR)
480 {
481 isHardLink = true;
482 linkPath = prop.bstrVal;
483 isRelative = false; // TAR: hard links are from root folder of archive
484 }
485 else if (prop.vt == VT_EMPTY)
486 {
487 // linkPath.Empty();
488 }
489 else
490 return E_FAIL;
491 }
492 {
493 NCOM::CPropVariant prop;
494 RINOK(archive->GetProperty(index, kpidSymLink, &prop));
495 if (prop.vt == VT_BSTR)
496 {
497 isHardLink = false;
498 linkPath = prop.bstrVal;
499 isRelative = true; // TAR: symbolic links are relative
500 }
501 else if (prop.vt == VT_EMPTY)
502 {
503 // linkPath.Empty();
504 }
505 else
506 return E_FAIL;
507 }
508
509 bool isOkReparse = false;
510
511 if (linkPath.IsEmpty() && _arc->GetRawProps)
512 {
513 const void *data;
514 UInt32 dataSize;
515 UInt32 propType;
516 _arc->GetRawProps->GetRawProp(_index, kpidNtReparse, &data, &dataSize, &propType);
517 if (dataSize != 0)
518 {
519 if (propType != NPropDataType::kRaw)
520 return E_FAIL;
521 UString s;
522 CReparseAttr reparse;
523 isOkReparse = reparse.Parse((const Byte *)data, dataSize);
524 if (isOkReparse)
525 {
526 isHardLink = false;
527 linkPath = reparse.GetPath();
528 isJunction = reparse.IsMountPoint();
529 isRelative = reparse.IsRelative();
530 #ifndef _WIN32
531 linkPath.Replace(WCHAR_PATH_SEPARATOR, '/', );
532 #endif
533 }
534 }
535 }
536
537 if (!linkPath.IsEmpty())
538 {
539 #ifdef _WIN32
540 linkPath.Replace('/', WCHAR_PATH_SEPARATOR);
541 #endif
542
543 for (;;)
544 // while (NName::IsAbsolutePath(linkPath))
545 {
546 unsigned n = NName::GetRootPrefixSize(linkPath);
547 if (n == 0)
548 break;
549 isRelative = false;
550 linkPath.DeleteFrontal(n);
551 }
552 }
553
554 if (!linkPath.IsEmpty() && !isRelative && _removePathParts.Size() != 0)
555 {
556 UStringVector pathParts;
557 SplitPathToParts(linkPath, pathParts);
558 bool badPrefix = false;
559 FOR_VECTOR (i, _removePathParts)
560 {
561 if (CompareFileNames(_removePathParts[i], pathParts[i]) != 0)
562 {
563 badPrefix = true;
564 break;
565 }
566 }
567 if (!badPrefix)
568 pathParts.DeleteFrontal(_removePathParts.Size());
569 linkPath = MakePathNameFromParts(pathParts);
570 }
571
572 #endif
573
574 RINOK(Archive_GetItemBoolProp(archive, index, kpidEncrypted, _encrypted));
575
576 RINOK(GetUnpackSize());
577
578 RINOK(Archive_IsItem_AltStream(archive, index, _isAltStream));
579
580 if (!_ntOptions.AltStreams.Val && _isAltStream)
581 return S_OK;
582
583 if (_wildcardCensor)
584 {
585 if (!_wildcardCensor->CheckPath(_isAltStream, fullPath, !_fi.IsDir))
586 return S_OK;
587 }
588
589
590 UStringVector pathParts;
591
592 if (_use_baseParentFolder_mode)
593 {
594 int baseParent = _baseParentFolder;
595 if (_pathMode == NExtract::NPathMode::kFullPaths ||
596 _pathMode == NExtract::NPathMode::kAbsPaths)
597 baseParent = -1;
598 RINOK(_arc->GetItemPathToParent(index, baseParent, pathParts));
599 if (_pathMode == NExtract::NPathMode::kNoPaths && !pathParts.IsEmpty())
600 pathParts.DeleteFrontal(pathParts.Size() - 1);
601 }
602 else
603 {
604 SplitPathToParts(fullPath, pathParts);
605
606 if (pathParts.IsEmpty())
607 return E_FAIL;
608 unsigned numRemovePathParts = 0;
609
610 switch (_pathMode)
611 {
612 case NExtract::NPathMode::kCurPaths:
613 {
614 bool badPrefix = false;
615 if (pathParts.Size() <= _removePathParts.Size())
616 badPrefix = true;
617 else
618 {
619 FOR_VECTOR (i, _removePathParts)
620 {
621 if (!_removePathParts[i].IsEqualToNoCase(pathParts[i]))
622 {
623 badPrefix = true;
624 break;
625 }
626 }
627 }
628 if (badPrefix)
629 {
630 if (askExtractMode == NArchive::NExtract::NAskMode::kExtract && !_testMode)
631 return E_FAIL;
632 }
633 else
634 numRemovePathParts = _removePathParts.Size();
635 break;
636 }
637 case NExtract::NPathMode::kNoPaths:
638 {
639 numRemovePathParts = pathParts.Size() - 1;
640 break;
641 }
642 /*
643 case NExtract::NPathMode::kFullPaths:
644 case NExtract::NPathMode::kAbsPaths:
645 break;
646 */
647 }
648
649 pathParts.DeleteFrontal(numRemovePathParts);
650 }
651
652 #ifndef _SFX
653
654 if (ExtractToStreamCallback)
655 {
656 if (!GetProp)
657 {
658 GetProp_Spec = new CGetProp;
659 GetProp = GetProp_Spec;
660 }
661 GetProp_Spec->Arc = _arc;
662 GetProp_Spec->IndexInArc = index;
663 GetProp_Spec->Name = MakePathNameFromParts(pathParts);
664
665 return ExtractToStreamCallback->GetStream7(GetProp_Spec->Name, _fi.IsDir, outStream, askExtractMode, GetProp);
666 }
667
668 #endif
669
670 CMyComPtr<ISequentialOutStream> outStreamLoc;
671
672 if (askExtractMode == NArchive::NExtract::NAskMode::kExtract && !_testMode)
673 {
674 if (_stdOutMode)
675 {
676 outStreamLoc = new CStdOutFileStream;
677 }
678 else
679 {
680 {
681 NCOM::CPropVariant prop;
682 RINOK(archive->GetProperty(index, kpidAttrib, &prop));
683 if (prop.vt == VT_UI4)
684 {
685 _fi.Attrib = prop.ulVal;
686 _fi.AttribDefined = true;
687 }
688 else if (prop.vt == VT_EMPTY)
689 _fi.AttribDefined = false;
690 else
691 return E_FAIL;
692 }
693
694 RINOK(GetTime(index, kpidCTime, _fi.CTime, _fi.CTimeDefined));
695 RINOK(GetTime(index, kpidATime, _fi.ATime, _fi.ATimeDefined));
696 RINOK(GetTime(index, kpidMTime, _fi.MTime, _fi.MTimeDefined));
697
698 bool isAnti = false;
699 RINOK(_arc->IsItemAnti(index, isAnti));
700
701 bool replace = _isAltStream ?
702 _ntOptions.ReplaceColonForAltStream :
703 !_ntOptions.WriteToAltStreamIfColon;
704
705 if (_pathMode != NExtract::NPathMode::kAbsPaths)
706 MakeCorrectPath(_directoryPath.IsEmpty(), pathParts, replace);
707 Correct_IfEmptyLastPart(pathParts);
708 UString processedPath = MakePathNameFromParts(pathParts);
709
710 if (!isAnti)
711 {
712 if (!_fi.IsDir)
713 {
714 if (!pathParts.IsEmpty())
715 pathParts.DeleteBack();
716 }
717
718 if (!pathParts.IsEmpty())
719 {
720 FString fullPathNew;
721 CreateComplexDirectory(pathParts, fullPathNew);
722 if (_fi.IsDir)
723 {
724 _extractedFolderPaths.Add(fullPathNew);
725 _extractedFolderIndices.Add(index);
726 SetDirTime(fullPathNew,
727 (WriteCTime && _fi.CTimeDefined) ? &_fi.CTime : NULL,
728 (WriteATime && _fi.ATimeDefined) ? &_fi.ATime : NULL,
729 (WriteMTime && _fi.MTimeDefined) ? &_fi.MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL));
730 }
731 }
732 }
733
734
735 FString fullProcessedPath = us2fs(processedPath);
736 if (_pathMode != NExtract::NPathMode::kAbsPaths ||
737 !NName::IsAbsolutePath(processedPath))
738 fullProcessedPath = _directoryPath + fullProcessedPath;
739
740 if (_fi.IsDir)
741 {
742 _diskFilePath = fullProcessedPath;
743 if (isAnti)
744 RemoveDir(_diskFilePath);
745 #ifdef SUPPORT_LINKS
746 if (linkPath.IsEmpty())
747 #endif
748 return S_OK;
749 }
750 else if (!_isSplit)
751 {
752 NFind::CFileInfo fileInfo;
753 if (fileInfo.Find(fullProcessedPath))
754 {
755 switch (_overwriteMode)
756 {
757 case NExtract::NOverwriteMode::kSkip:
758 return S_OK;
759 case NExtract::NOverwriteMode::kAsk:
760 {
761 int slashPos = fullProcessedPath.ReverseFind(FTEXT('/'));
762 #ifdef _WIN32
763 int slash1Pos = fullProcessedPath.ReverseFind(FTEXT('\\'));
764 slashPos = MyMax(slashPos, slash1Pos);
765 #endif
766 FString realFullProcessedPath = fullProcessedPath.Left(slashPos + 1) + fileInfo.Name;
767
768 Int32 overwiteResult;
769 RINOK(_extractCallback2->AskOverwrite(
770 fs2us(realFullProcessedPath), &fileInfo.MTime, &fileInfo.Size, fullPath,
771 _fi.MTimeDefined ? &_fi.MTime : NULL,
772 _curSizeDefined ? &_curSize : NULL,
773 &overwiteResult))
774
775 switch (overwiteResult)
776 {
777 case NOverwriteAnswer::kCancel: return E_ABORT;
778 case NOverwriteAnswer::kNo: return S_OK;
779 case NOverwriteAnswer::kNoToAll: _overwriteMode = NExtract::NOverwriteMode::kSkip; return S_OK;
780 case NOverwriteAnswer::kYes: break;
781 case NOverwriteAnswer::kYesToAll: _overwriteMode = NExtract::NOverwriteMode::kOverwrite; break;
782 case NOverwriteAnswer::kAutoRename: _overwriteMode = NExtract::NOverwriteMode::kRename; break;
783 default:
784 return E_FAIL;
785 }
786 }
787 }
788 if (_overwriteMode == NExtract::NOverwriteMode::kRename)
789 {
790 if (!AutoRenamePath(fullProcessedPath))
791 {
792 RINOK(SendMessageError(kCantAutoRename, fullProcessedPath));
793 return E_FAIL;
794 }
795 }
796 else if (_overwriteMode == NExtract::NOverwriteMode::kRenameExisting)
797 {
798 FString existPath = fullProcessedPath;
799 if (!AutoRenamePath(existPath))
800 {
801 RINOK(SendMessageError(kCantAutoRename, fullProcessedPath));
802 return E_FAIL;
803 }
804 // MyMoveFile can raname folders. So it's OK to use it folders too
805 if (!MyMoveFile(fullProcessedPath, existPath))
806 {
807 RINOK(SendMessageError(kCantRenameFile, fullProcessedPath));
808 return E_FAIL;
809 }
810 }
811 else
812 {
813 if (fileInfo.IsDir())
814 {
815 // do we need to delete all files in folder?
816 if (!RemoveDir(fullProcessedPath))
817 {
818 RINOK(SendMessageError(kCantDeleteOutputDir, fullProcessedPath));
819 return S_OK;
820 }
821 }
822 else if (!DeleteFileAlways(fullProcessedPath))
823 {
824 RINOK(SendMessageError(kCantDeleteOutputFile, fullProcessedPath));
825 return S_OK;
826 // return E_FAIL;
827 }
828 }
829 }
830 }
831 _diskFilePath = fullProcessedPath;
832
833
834 if (!isAnti)
835 {
836 #ifdef SUPPORT_LINKS
837
838 if (!linkPath.IsEmpty())
839 {
840 #ifndef UNDER_CE
841
842 UString relatPath;
843 if (isRelative)
844 relatPath = GetDirPrefixOf(_filePath);
845 relatPath += linkPath;
846
847 if (!IsSafePath(relatPath))
848 {
849 RINOK(SendMessageError("Dangerous link path was ignored", us2fs(relatPath)));
850 }
851 else
852 {
853 FString existPath;
854 if (isHardLink || !isRelative)
855 {
856 if (!NName::GetFullPath(_directoryPathFull, us2fs(relatPath), existPath))
857 {
858 RINOK(SendMessageError("Incorrect path", us2fs(relatPath)));
859 }
860 }
861 else
862 {
863 existPath = us2fs(linkPath);
864 }
865
866 if (!existPath.IsEmpty())
867 {
868 if (isHardLink)
869 {
870 if (!MyCreateHardLink(fullProcessedPath, existPath))
871 {
872 RINOK(SendMessageError2("Can not create hard link", fullProcessedPath, existPath));
873 // return S_OK;
874 }
875 }
876 else if (_ntOptions.SymLinks.Val)
877 {
878 // bool isSymLink = true; // = false for junction
879 if (_fi.IsDir && !isRelative)
880 {
881 // if it's before Vista we use Junction Point
882 // isJunction = true;
883 // convertToAbs = true;
884 }
885
886 CByteBuffer data;
887 if (FillLinkData(data, fs2us(existPath), !isJunction))
888 {
889 CReparseAttr attr;
890 if (!attr.Parse(data, data.Size()))
891 {
892 return E_FAIL; // "Internal conversion error";
893 }
894
895 if (!NFile::NIO::SetReparseData(fullProcessedPath, _fi.IsDir, data, (DWORD)data.Size()))
896 {
897 RINOK(SendMessageError("Can not set reparse data", fullProcessedPath));
898 }
899 }
900 }
901 }
902 }
903
904 #endif
905 }
906 else
907 #endif // SUPPORT_LINKS
908 {
909 bool needWriteFile = true;
910
911 #ifdef SUPPORT_LINKS
912 if (!_hardLinks.IDs.IsEmpty())
913 {
914 CHardLinkNode h;
915 bool defined;
916 RINOK(Archive_Get_HardLinkNode(archive, index, h, defined));
917 if (defined)
918 {
919 {
920 int linkIndex = _hardLinks.IDs.FindInSorted2(h);
921 if (linkIndex >= 0)
922 {
923 FString &hl = _hardLinks.Links[linkIndex];
924 if (hl.IsEmpty())
925 hl = fullProcessedPath;
926 else
927 {
928 if (!MyCreateHardLink(fullProcessedPath, hl))
929 {
930 RINOK(SendMessageError2("Can not create hard link", fullProcessedPath, hl));
931 return S_OK;
932 }
933 needWriteFile = false;
934 }
935 }
936 }
937 }
938 }
939 #endif
940
941 if (needWriteFile)
942 {
943 _outFileStreamSpec = new COutFileStream;
944 CMyComPtr<ISequentialOutStream> outStreamLoc(_outFileStreamSpec);
945 if (!_outFileStreamSpec->Open(fullProcessedPath, _isSplit ? OPEN_ALWAYS: CREATE_ALWAYS))
946 {
947 // if (::GetLastError() != ERROR_FILE_EXISTS || !isSplit)
948 {
949 RINOK(SendMessageError("Can not open output file ", fullProcessedPath));
950 return S_OK;
951 }
952 }
953 if (_isSplit)
954 {
955 RINOK(_outFileStreamSpec->Seek(_position, STREAM_SEEK_SET, NULL));
956 }
957 _outFileStream = outStreamLoc;
958 }
959 }
960 }
961
962 outStreamLoc = _outFileStream;
963 }
964 }
965
966 #ifndef _SFX
967
968 if (_hashStream)
969 {
970 if (askExtractMode == NArchive::NExtract::NAskMode::kExtract ||
971 askExtractMode == NArchive::NExtract::NAskMode::kTest)
972 {
973 _hashStreamSpec->SetStream(outStreamLoc);
974 outStreamLoc = _hashStream;
975 _hashStreamSpec->Init(true);
976 _hashStreamWasUsed = true;
977 }
978 }
979
980 #endif
981
982 if (outStreamLoc)
983 *outStream = outStreamLoc.Detach();
984 return S_OK;
985 COM_TRY_END
986 }
987
PrepareOperation(Int32 askExtractMode)988 STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode)
989 {
990 COM_TRY_BEGIN
991
992 #ifndef _SFX
993 if (ExtractToStreamCallback)
994 return ExtractToStreamCallback->PrepareOperation7(askExtractMode);
995 #endif
996
997 _extractMode = false;
998 switch (askExtractMode)
999 {
1000 case NArchive::NExtract::NAskMode::kExtract:
1001 if (_testMode)
1002 askExtractMode = NArchive::NExtract::NAskMode::kTest;
1003 else
1004 _extractMode = true;
1005 break;
1006 };
1007 return _extractCallback2->PrepareOperation(_filePath, _fi.IsDir,
1008 askExtractMode, _isSplit ? &_position: 0);
1009 COM_TRY_END
1010 }
1011
SetOperationResult(Int32 operationResult)1012 STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult)
1013 {
1014 COM_TRY_BEGIN
1015
1016 #ifndef _SFX
1017 if (ExtractToStreamCallback)
1018 return ExtractToStreamCallback->SetOperationResult7(operationResult, _encrypted);
1019 #endif
1020
1021 #ifndef _SFX
1022
1023 if (_hashStreamWasUsed)
1024 {
1025 _hashStreamSpec->_hash->Final(_fi.IsDir, _isAltStream, _filePath);
1026 _curSize = _hashStreamSpec->GetSize();
1027 _curSizeDefined = true;
1028 _hashStreamSpec->ReleaseStream();
1029 _hashStreamWasUsed = false;
1030 }
1031
1032 #endif
1033
1034 if (_outFileStream)
1035 {
1036 _outFileStreamSpec->SetTime(
1037 (WriteCTime && _fi.CTimeDefined) ? &_fi.CTime : NULL,
1038 (WriteATime && _fi.ATimeDefined) ? &_fi.ATime : NULL,
1039 (WriteMTime && _fi.MTimeDefined) ? &_fi.MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL));
1040 _curSize = _outFileStreamSpec->ProcessedSize;
1041 _curSizeDefined = true;
1042 RINOK(_outFileStreamSpec->Close());
1043 _outFileStream.Release();
1044 }
1045
1046 #ifdef _USE_SECURITY_CODE
1047 if (_ntOptions.NtSecurity.Val && _arc->GetRawProps)
1048 {
1049 const void *data;
1050 UInt32 dataSize;
1051 UInt32 propType;
1052 _arc->GetRawProps->GetRawProp(_index, kpidNtSecure, &data, &dataSize, &propType);
1053 if (dataSize != 0)
1054 {
1055 if (propType != NPropDataType::kRaw)
1056 return E_FAIL;
1057 if (CheckNtSecure((const Byte *)data, dataSize))
1058 {
1059 SECURITY_INFORMATION securInfo = DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION;
1060 if (_saclEnabled)
1061 securInfo |= SACL_SECURITY_INFORMATION;
1062 ::SetFileSecurityW(fs2us(_diskFilePath), securInfo, (PSECURITY_DESCRIPTOR)(void *)data);
1063 }
1064 }
1065 }
1066 #endif
1067
1068 if (!_curSizeDefined)
1069 GetUnpackSize();
1070 if (_curSizeDefined)
1071 {
1072 if (_isAltStream)
1073 AltStreams_UnpackSize += _curSize;
1074 else
1075 UnpackSize += _curSize;
1076 }
1077
1078 if (_fi.IsDir)
1079 NumFolders++;
1080 else if (_isAltStream)
1081 NumAltStreams++;
1082 else
1083 NumFiles++;
1084
1085 if (_extractMode && _fi.AttribDefined)
1086 SetFileAttrib(_diskFilePath, _fi.Attrib);
1087 RINOK(_extractCallback2->SetOperationResult(operationResult, _encrypted));
1088 return S_OK;
1089 COM_TRY_END
1090 }
1091
1092 /*
1093 STDMETHODIMP CArchiveExtractCallback::GetInStream(
1094 const wchar_t *name, ISequentialInStream **inStream)
1095 {
1096 COM_TRY_BEGIN
1097 CInFileStream *inFile = new CInFileStream;
1098 CMyComPtr<ISequentialInStream> inStreamTemp = inFile;
1099 if (!inFile->Open(_srcDirectoryPrefix + name))
1100 return ::GetLastError();
1101 *inStream = inStreamTemp.Detach();
1102 return S_OK;
1103 COM_TRY_END
1104 }
1105 */
1106
CryptoGetTextPassword(BSTR * password)1107 STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password)
1108 {
1109 COM_TRY_BEGIN
1110 if (!_cryptoGetTextPassword)
1111 {
1112 RINOK(_extractCallback2.QueryInterface(IID_ICryptoGetTextPassword,
1113 &_cryptoGetTextPassword));
1114 }
1115 return _cryptoGetTextPassword->CryptoGetTextPassword(password);
1116 COM_TRY_END
1117 }
1118
1119
1120 struct CExtrRefSortPair
1121 {
1122 int Len;
1123 int Index;
1124
1125 int Compare(const CExtrRefSortPair &a) const;
1126 };
1127
1128 #define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; }
1129
Compare(const CExtrRefSortPair & a) const1130 int CExtrRefSortPair::Compare(const CExtrRefSortPair &a) const
1131 {
1132 RINOZ(-MyCompare(Len, a.Len));
1133 return MyCompare(Index, a.Index);
1134 }
1135
GetNumSlashes(const FChar * s)1136 static int GetNumSlashes(const FChar *s)
1137 {
1138 for (int numSlashes = 0;;)
1139 {
1140 FChar c = *s++;
1141 if (c == 0)
1142 return numSlashes;
1143 if (
1144 #ifdef _WIN32
1145 c == FTEXT('\\') ||
1146 #endif
1147 c == FTEXT('/'))
1148 numSlashes++;
1149 }
1150 }
1151
SetDirsTimes()1152 HRESULT CArchiveExtractCallback::SetDirsTimes()
1153 {
1154 CRecordVector<CExtrRefSortPair> pairs;
1155 pairs.ClearAndSetSize(_extractedFolderPaths.Size());
1156 unsigned i;
1157
1158 for (i = 0; i < _extractedFolderPaths.Size(); i++)
1159 {
1160 CExtrRefSortPair &pair = pairs[i];
1161 pair.Index = i;
1162 pair.Len = GetNumSlashes(_extractedFolderPaths[i]);
1163 }
1164
1165 pairs.Sort2();
1166
1167 for (i = 0; i < pairs.Size(); i++)
1168 {
1169 int pairIndex = pairs[i].Index;
1170 int index = _extractedFolderIndices[pairIndex];
1171
1172 FILETIME CTime;
1173 FILETIME ATime;
1174 FILETIME MTime;
1175
1176 bool CTimeDefined;
1177 bool ATimeDefined;
1178 bool MTimeDefined;
1179
1180 RINOK(GetTime(index, kpidCTime, CTime, CTimeDefined));
1181 RINOK(GetTime(index, kpidATime, ATime, ATimeDefined));
1182 RINOK(GetTime(index, kpidMTime, MTime, MTimeDefined));
1183
1184 // printf("\n%S", _extractedFolderPaths[pairIndex]);
1185 SetDirTime(_extractedFolderPaths[pairIndex],
1186 (WriteCTime && CTimeDefined) ? &CTime : NULL,
1187 (WriteATime && ATimeDefined) ? &ATime : NULL,
1188 (WriteMTime && MTimeDefined) ? &MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL));
1189 }
1190 return S_OK;
1191 }
1192