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/UTFConvert.h"
19 #include "../../../Common/Wildcard.h"
20
21 #include "../../../Windows/ErrorMsg.h"
22 #include "../../../Windows/FileDir.h"
23 #include "../../../Windows/FileFind.h"
24 #include "../../../Windows/FileName.h"
25 #include "../../../Windows/PropVariant.h"
26 #include "../../../Windows/PropVariantConv.h"
27
28 #if defined(_WIN32) && !defined(UNDER_CE) && !defined(_SFX)
29 #define _USE_SECURITY_CODE
30 #include "../../../Windows/SecurityUtils.h"
31 #endif
32
33 #include "../../Common/FilePathAutoRename.h"
34 #include "../../Common/StreamUtils.h"
35
36 #include "../Common/ExtractingFilePath.h"
37 #include "../Common/PropIDUtils.h"
38
39 #include "ArchiveExtractCallback.h"
40
41 using namespace NWindows;
42 using namespace NFile;
43 using namespace NDir;
44
45 static const char * const kCantAutoRename = "Cannot create file with auto name";
46 static const char * const kCantRenameFile = "Cannot rename existing file";
47 static const char * const kCantDeleteOutputFile = "Cannot delete output file";
48 static const char * const kCantDeleteOutputDir = "Cannot delete output folder";
49 static const char * const kCantOpenOutFile = "Cannot open output file";
50 static const char * const kCantOpenInFile = "Cannot open input file";
51 static const char * const kCantSetFileLen = "Cannot set length for output file";
52 #ifdef SUPPORT_LINKS
53 static const char * const kCantCreateHardLink = "Cannot create hard link";
54 static const char * const kCantCreateSymLink = "Cannot create symbolic link";
55 #endif
56
57 #ifndef _SFX
58
Write(const void * data,UInt32 size,UInt32 * processedSize)59 STDMETHODIMP COutStreamWithHash::Write(const void *data, UInt32 size, UInt32 *processedSize)
60 {
61 HRESULT result = S_OK;
62 if (_stream)
63 result = _stream->Write(data, size, &size);
64 if (_calculate)
65 _hash->Update(data, size);
66 _size += size;
67 if (processedSize)
68 *processedSize = size;
69 return result;
70 }
71
72 #endif // _SFX
73
74
75 #ifdef _USE_SECURITY_CODE
76 bool InitLocalPrivileges();
InitLocalPrivileges()77 bool InitLocalPrivileges()
78 {
79 NSecurity::CAccessToken token;
80 if (!token.OpenProcessToken(GetCurrentProcess(),
81 TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES))
82 return false;
83
84 TOKEN_PRIVILEGES tp;
85
86 tp.PrivilegeCount = 1;
87 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
88
89 if (!::LookupPrivilegeValue(NULL, SE_SECURITY_NAME, &tp.Privileges[0].Luid))
90 return false;
91 if (!token.AdjustPrivileges(&tp))
92 return false;
93 return (GetLastError() == ERROR_SUCCESS);
94 }
95 #endif // _USE_SECURITY_CODE
96
97
98
99 #if defined(_WIN32) && !defined(UNDER_CE) && !defined(_SFX)
100
101 static const char * const kOfficeExtensions =
102 " doc dot wbk"
103 " docx docm dotx dotm docb wll wwl"
104 " xls xlt xlm"
105 " xlsx xlsm xltx xltm xlsb xla xlam"
106 " ppt pot pps ppa ppam"
107 " pptx pptm potx potm ppam ppsx ppsm sldx sldm"
108 " ";
109
FindExt2(const char * p,const UString & name)110 static bool FindExt2(const char *p, const UString &name)
111 {
112 const int pathPos = name.ReverseFind_PathSepar();
113 const int dotPos = name.ReverseFind_Dot();
114 if (dotPos < 0
115 || dotPos < pathPos
116 || dotPos == (int)name.Len() - 1)
117 return false;
118
119 AString s;
120 for (unsigned pos = dotPos + 1;; pos++)
121 {
122 const wchar_t c = name[pos];
123 if (c <= 0)
124 break;
125 if (c >= 0x80)
126 return false;
127 s += (char)MyCharLower_Ascii((char)c);
128 }
129 for (unsigned i = 0; p[i] != 0;)
130 {
131 unsigned j;
132 for (j = i; p[j] != ' '; j++);
133 if (s.Len() == j - i && memcmp(p + i, (const char *)s, s.Len()) == 0)
134 return true;
135 i = j + 1;
136 }
137 return false;
138 }
139
140
141 static const FChar * const k_ZoneId_StreamName = FTEXT(":Zone.Identifier");
142
ReadZoneFile_Of_BaseFile(CFSTR fileName2,CByteBuffer & buf)143 void ReadZoneFile_Of_BaseFile(CFSTR fileName2, CByteBuffer &buf)
144 {
145 FString fileName = fileName2;
146 fileName += k_ZoneId_StreamName;
147
148 buf.Free();
149 NIO::CInFile file;
150 if (!file.Open(fileName))
151 return;
152 UInt64 fileSize;
153 if (!file.GetLength(fileSize))
154 return;
155 if (fileSize == 0 || fileSize >= ((UInt32)1 << 16))
156 return;
157 buf.Alloc((size_t)fileSize);
158 size_t processed;
159 if (file.ReadFull(buf, (size_t)fileSize, processed) && processed == fileSize)
160 return;
161 buf.Free();
162 }
163
WriteZoneFile(CFSTR fileName,const CByteBuffer & buf)164 static bool WriteZoneFile(CFSTR fileName, const CByteBuffer &buf)
165 {
166 NIO::COutFile file;
167 if (!file.Create(fileName, true))
168 return false;
169 return file.WriteFull(buf, buf.Size());
170 }
171
172 #endif
173
174
175 #ifdef SUPPORT_LINKS
176
Compare(const CHardLinkNode & a) const177 int CHardLinkNode::Compare(const CHardLinkNode &a) const
178 {
179 if (StreamId < a.StreamId) return -1;
180 if (StreamId > a.StreamId) return 1;
181 return MyCompare(INode, a.INode);
182 }
183
Archive_Get_HardLinkNode(IInArchive * archive,UInt32 index,CHardLinkNode & h,bool & defined)184 static HRESULT Archive_Get_HardLinkNode(IInArchive *archive, UInt32 index, CHardLinkNode &h, bool &defined)
185 {
186 h.INode = 0;
187 h.StreamId = (UInt64)(Int64)-1;
188 defined = false;
189 {
190 NCOM::CPropVariant prop;
191 RINOK(archive->GetProperty(index, kpidINode, &prop));
192 if (!ConvertPropVariantToUInt64(prop, h.INode))
193 return S_OK;
194 }
195 {
196 NCOM::CPropVariant prop;
197 RINOK(archive->GetProperty(index, kpidStreamId, &prop));
198 ConvertPropVariantToUInt64(prop, h.StreamId);
199 }
200 defined = true;
201 return S_OK;
202 }
203
204
PrepareHardLinks(const CRecordVector<UInt32> * realIndices)205 HRESULT CArchiveExtractCallback::PrepareHardLinks(const CRecordVector<UInt32> *realIndices)
206 {
207 _hardLinks.Clear();
208
209 if (!_arc->Ask_INode)
210 return S_OK;
211
212 IInArchive *archive = _arc->Archive;
213 CRecordVector<CHardLinkNode> &hardIDs = _hardLinks.IDs;
214
215 {
216 UInt32 numItems;
217 if (realIndices)
218 numItems = realIndices->Size();
219 else
220 {
221 RINOK(archive->GetNumberOfItems(&numItems));
222 }
223
224 for (UInt32 i = 0; i < numItems; i++)
225 {
226 CHardLinkNode h;
227 bool defined;
228 UInt32 realIndex = realIndices ? (*realIndices)[i] : i;
229
230 RINOK(Archive_Get_HardLinkNode(archive, realIndex, h, defined));
231 if (defined)
232 {
233 bool isAltStream = false;
234 RINOK(Archive_IsItem_AltStream(archive, realIndex, isAltStream));
235 if (!isAltStream)
236 hardIDs.Add(h);
237 }
238 }
239 }
240
241 hardIDs.Sort2();
242
243 {
244 // we keep only items that have 2 or more items
245 unsigned k = 0;
246 unsigned numSame = 1;
247 for (unsigned i = 1; i < hardIDs.Size(); i++)
248 {
249 if (hardIDs[i].Compare(hardIDs[i - 1]) != 0)
250 numSame = 1;
251 else if (++numSame == 2)
252 {
253 if (i - 1 != k)
254 hardIDs[k] = hardIDs[i - 1];
255 k++;
256 }
257 }
258 hardIDs.DeleteFrom(k);
259 }
260
261 _hardLinks.PrepareLinks();
262 return S_OK;
263 }
264
265 #endif // SUPPORT_LINKS
266
267
CArchiveExtractCallback()268 CArchiveExtractCallback::CArchiveExtractCallback():
269 _arc(NULL),
270 Write_CTime(true),
271 Write_ATime(true),
272 Write_MTime(true),
273 _multiArchives(false)
274 {
275 LocalProgressSpec = new CLocalProgress();
276 _localProgress = LocalProgressSpec;
277
278 #ifdef _USE_SECURITY_CODE
279 _saclEnabled = InitLocalPrivileges();
280 #endif
281 }
282
283
InitBeforeNewArchive()284 void CArchiveExtractCallback::InitBeforeNewArchive()
285 {
286 #if defined(_WIN32) && !defined(UNDER_CE)
287 ZoneBuf.Free();
288 #endif
289 }
290
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)291 void CArchiveExtractCallback::Init(
292 const CExtractNtOptions &ntOptions,
293 const NWildcard::CCensorNode *wildcardCensor,
294 const CArc *arc,
295 IFolderArchiveExtractCallback *extractCallback2,
296 bool stdOutMode, bool testMode,
297 const FString &directoryPath,
298 const UStringVector &removePathParts, bool removePartsForAltStreams,
299 UInt64 packSize)
300 {
301 ClearExtractedDirsInfo();
302 _outFileStream.Release();
303 _bufPtrSeqOutStream.Release();
304
305 #ifdef SUPPORT_LINKS
306 _hardLinks.Clear();
307 #endif
308
309 #ifdef SUPPORT_ALT_STREAMS
310 _renamedFiles.Clear();
311 #endif
312
313 _ntOptions = ntOptions;
314 _wildcardCensor = wildcardCensor;
315
316 _stdOutMode = stdOutMode;
317 _testMode = testMode;
318
319 // _progressTotal = 0;
320 // _progressTotal_Defined = false;
321
322 _packTotal = packSize;
323 _progressTotal = packSize;
324 _progressTotal_Defined = true;
325
326 _extractCallback2 = extractCallback2;
327
328 _compressProgress.Release();
329 _extractCallback2.QueryInterface(IID_ICompressProgressInfo, &_compressProgress);
330
331 _callbackMessage.Release();
332 _extractCallback2.QueryInterface(IID_IArchiveExtractCallbackMessage, &_callbackMessage);
333
334 _folderArchiveExtractCallback2.Release();
335 _extractCallback2.QueryInterface(IID_IFolderArchiveExtractCallback2, &_folderArchiveExtractCallback2);
336
337 #ifndef _SFX
338
339 ExtractToStreamCallback.Release();
340 _extractCallback2.QueryInterface(IID_IFolderExtractToStreamCallback, &ExtractToStreamCallback);
341 if (ExtractToStreamCallback)
342 {
343 Int32 useStreams = 0;
344 if (ExtractToStreamCallback->UseExtractToStream(&useStreams) != S_OK)
345 useStreams = 0;
346 if (useStreams == 0)
347 ExtractToStreamCallback.Release();
348 }
349
350 #endif
351
352 LocalProgressSpec->Init(extractCallback2, true);
353 LocalProgressSpec->SendProgress = false;
354
355 _removePathParts = removePathParts;
356 _removePartsForAltStreams = removePartsForAltStreams;
357
358 #ifndef _SFX
359 _baseParentFolder = (UInt32)(Int32)-1;
360 _use_baseParentFolder_mode = false;
361 #endif
362
363 _arc = arc;
364 _dirPathPrefix = directoryPath;
365 _dirPathPrefix_Full = directoryPath;
366 #if defined(_WIN32) && !defined(UNDER_CE)
367 if (!NName::IsAltPathPrefix(_dirPathPrefix))
368 #endif
369 {
370 NName::NormalizeDirPathPrefix(_dirPathPrefix);
371 NDir::MyGetFullPathName(directoryPath, _dirPathPrefix_Full);
372 NName::NormalizeDirPathPrefix(_dirPathPrefix_Full);
373 }
374 }
375
376
SetTotal(UInt64 size)377 STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 size)
378 {
379 COM_TRY_BEGIN
380 _progressTotal = size;
381 _progressTotal_Defined = true;
382 if (!_multiArchives && _extractCallback2)
383 return _extractCallback2->SetTotal(size);
384 return S_OK;
385 COM_TRY_END
386 }
387
388
NormalizeVals(UInt64 & v1,UInt64 & v2)389 static void NormalizeVals(UInt64 &v1, UInt64 &v2)
390 {
391 const UInt64 kMax = (UInt64)1 << 31;
392 while (v1 > kMax)
393 {
394 v1 >>= 1;
395 v2 >>= 1;
396 }
397 }
398
399
MyMultDiv64(UInt64 unpCur,UInt64 unpTotal,UInt64 packTotal)400 static UInt64 MyMultDiv64(UInt64 unpCur, UInt64 unpTotal, UInt64 packTotal)
401 {
402 NormalizeVals(packTotal, unpTotal);
403 NormalizeVals(unpCur, unpTotal);
404 if (unpTotal == 0)
405 unpTotal = 1;
406 return unpCur * packTotal / unpTotal;
407 }
408
409
SetCompleted(const UInt64 * completeValue)410 STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 *completeValue)
411 {
412 COM_TRY_BEGIN
413
414 if (!_extractCallback2)
415 return S_OK;
416
417 UInt64 packCur;
418 if (_multiArchives)
419 {
420 packCur = LocalProgressSpec->InSize;
421 if (completeValue && _progressTotal_Defined)
422 packCur += MyMultDiv64(*completeValue, _progressTotal, _packTotal);
423 completeValue = &packCur;
424 }
425 return _extractCallback2->SetCompleted(completeValue);
426
427 COM_TRY_END
428 }
429
430
SetRatioInfo(const UInt64 * inSize,const UInt64 * outSize)431 STDMETHODIMP CArchiveExtractCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)
432 {
433 COM_TRY_BEGIN
434 return _localProgress->SetRatioInfo(inSize, outSize);
435 COM_TRY_END
436 }
437
438
CreateComplexDirectory(const UStringVector & dirPathParts,FString & fullPath)439 void CArchiveExtractCallback::CreateComplexDirectory(const UStringVector &dirPathParts, FString &fullPath)
440 {
441 // we use (_item.IsDir) in this function
442
443 bool isAbsPath = false;
444
445 if (!dirPathParts.IsEmpty())
446 {
447 const UString &s = dirPathParts[0];
448 if (s.IsEmpty())
449 isAbsPath = true;
450 #if defined(_WIN32) && !defined(UNDER_CE)
451 else
452 {
453 if (NName::IsDrivePath2(s))
454 isAbsPath = true;
455 }
456 #endif
457 }
458
459 if (_pathMode == NExtract::NPathMode::kAbsPaths && isAbsPath)
460 fullPath.Empty();
461 else
462 fullPath = _dirPathPrefix;
463
464 FOR_VECTOR (i, dirPathParts)
465 {
466 if (i != 0)
467 fullPath.Add_PathSepar();
468 const UString &s = dirPathParts[i];
469 fullPath += us2fs(s);
470
471 const bool isFinalDir = (i == dirPathParts.Size() - 1 && _item.IsDir);
472
473 if (fullPath.IsEmpty())
474 {
475 if (isFinalDir)
476 _itemFailure = true;
477 continue;
478 }
479
480 #if defined(_WIN32) && !defined(UNDER_CE)
481 if (_pathMode == NExtract::NPathMode::kAbsPaths)
482 if (i == 0 && s.Len() == 2 && NName::IsDrivePath2(s))
483 {
484 if (isFinalDir)
485 {
486 // we don't want to call SetAttrib() for root drive path
487 _itemFailure = true;
488 }
489 continue;
490 }
491 #endif
492
493 // bool res =
494 CreateDir(fullPath);
495 // if (!res)
496 if (isFinalDir)
497 {
498 if (!NFile::NFind::DoesDirExist(fullPath))
499 {
500 _itemFailure = true;
501 SendMessageError("Cannot create folder", fullPath);
502 // SendMessageError_with_LastError()
503 }
504 }
505 }
506 }
507
508
GetTime(UInt32 index,PROPID propID,CArcTime & ft)509 HRESULT CArchiveExtractCallback::GetTime(UInt32 index, PROPID propID, CArcTime &ft)
510 {
511 ft.Clear();
512 NCOM::CPropVariant prop;
513 RINOK(_arc->Archive->GetProperty(index, propID, &prop));
514 if (prop.vt == VT_FILETIME)
515 ft.Set_From_Prop(prop);
516 else if (prop.vt != VT_EMPTY)
517 return E_FAIL;
518 return S_OK;
519 }
520
521
GetUnpackSize()522 HRESULT CArchiveExtractCallback::GetUnpackSize()
523 {
524 return _arc->GetItem_Size(_index, _curSize, _curSizeDefined);
525 }
526
AddPathToMessage(UString & s,const FString & path)527 static void AddPathToMessage(UString &s, const FString &path)
528 {
529 s += " : ";
530 s += fs2us(path);
531 }
532
SendMessageError(const char * message,const FString & path)533 HRESULT CArchiveExtractCallback::SendMessageError(const char *message, const FString &path)
534 {
535 UString s (message);
536 AddPathToMessage(s, path);
537 return _extractCallback2->MessageError(s);
538 }
539
SendMessageError_with_LastError(const char * message,const FString & path)540 HRESULT CArchiveExtractCallback::SendMessageError_with_LastError(const char *message, const FString &path)
541 {
542 DWORD errorCode = GetLastError();
543 if (errorCode == 0)
544 errorCode = (DWORD)E_FAIL;
545 UString s (message);
546 {
547 s += " : ";
548 s += NError::MyFormatMessage(errorCode);
549 }
550 AddPathToMessage(s, path);
551 return _extractCallback2->MessageError(s);
552 }
553
SendMessageError2(HRESULT errorCode,const char * message,const FString & path1,const FString & path2)554 HRESULT CArchiveExtractCallback::SendMessageError2(HRESULT errorCode, const char *message, const FString &path1, const FString &path2)
555 {
556 UString s (message);
557 if (errorCode != 0)
558 {
559 s += " : ";
560 s += NError::MyFormatMessage(errorCode);
561 }
562 AddPathToMessage(s, path1);
563 AddPathToMessage(s, path2);
564 return _extractCallback2->MessageError(s);
565 }
566
567 #ifndef _SFX
568
GetProp(PROPID propID,PROPVARIANT * value)569 STDMETHODIMP CGetProp::GetProp(PROPID propID, PROPVARIANT *value)
570 {
571 /*
572 if (propID == kpidName)
573 {
574 COM_TRY_BEGIN
575 NCOM::CPropVariant prop = Name;
576 prop.Detach(value);
577 return S_OK;
578 COM_TRY_END
579 }
580 */
581 return Arc->Archive->GetProperty(IndexInArc, propID, value);
582 }
583
584 #endif // _SFX
585
586
587 #ifdef SUPPORT_LINKS
588
GetDirPrefixOf(const UString & src)589 static UString GetDirPrefixOf(const UString &src)
590 {
591 UString s (src);
592 if (!s.IsEmpty())
593 {
594 if (IsPathSepar(s.Back()))
595 s.DeleteBack();
596 int pos = s.ReverseFind_PathSepar();
597 s.DeleteFrom((unsigned)(pos + 1));
598 }
599 return s;
600 }
601
602 #endif // SUPPORT_LINKS
603
604 struct CLinkLevelsInfo
605 {
606 bool IsAbsolute;
607 int LowLevel;
608 int FinalLevel;
609
610 void Parse(const UString &path);
611 };
612
Parse(const UString & path)613 void CLinkLevelsInfo::Parse(const UString &path)
614 {
615 IsAbsolute = NName::IsAbsolutePath(path);
616
617 LowLevel = 0;
618 FinalLevel = 0;
619
620 UStringVector parts;
621 SplitPathToParts(path, parts);
622 int level = 0;
623
624 FOR_VECTOR (i, parts)
625 {
626 const UString &s = parts[i];
627 if (s.IsEmpty())
628 {
629 if (i == 0)
630 IsAbsolute = true;
631 continue;
632 }
633 if (s == L".")
634 continue;
635 if (s == L"..")
636 {
637 level--;
638 if (LowLevel > level)
639 LowLevel = level;
640 }
641 else
642 level++;
643 }
644
645 FinalLevel = level;
646 }
647
648
649 bool IsSafePath(const UString &path);
IsSafePath(const UString & path)650 bool IsSafePath(const UString &path)
651 {
652 CLinkLevelsInfo levelsInfo;
653 levelsInfo.Parse(path);
654 return !levelsInfo.IsAbsolute
655 && levelsInfo.LowLevel >= 0
656 && levelsInfo.FinalLevel > 0;
657 }
658
659
660 bool CensorNode_CheckPath2(const NWildcard::CCensorNode &node, const CReadArcItem &item, bool &include);
CensorNode_CheckPath2(const NWildcard::CCensorNode & node,const CReadArcItem & item,bool & include)661 bool CensorNode_CheckPath2(const NWildcard::CCensorNode &node, const CReadArcItem &item, bool &include)
662 {
663 bool found = false;
664
665 // CheckPathVect() doesn't check path to Parent nodes
666 if (node.CheckPathVect(item.PathParts, !item.MainIsDir, include))
667 {
668 if (!include)
669 return true;
670
671 #ifdef SUPPORT_ALT_STREAMS
672 if (!item.IsAltStream)
673 return true;
674 #endif
675
676 found = true;
677 }
678
679 #ifdef SUPPORT_ALT_STREAMS
680
681 if (!item.IsAltStream)
682 return false;
683
684 UStringVector pathParts2 = item.PathParts;
685 if (pathParts2.IsEmpty())
686 pathParts2.AddNew();
687 UString &back = pathParts2.Back();
688 back += ':';
689 back += item.AltStreamName;
690 bool include2;
691
692 if (node.CheckPathVect(pathParts2,
693 true, // isFile,
694 include2))
695 {
696 include = include2;
697 return true;
698 }
699
700 #endif // SUPPORT_ALT_STREAMS
701
702 return found;
703 }
704
705
CensorNode_CheckPath(const NWildcard::CCensorNode & node,const CReadArcItem & item)706 bool CensorNode_CheckPath(const NWildcard::CCensorNode &node, const CReadArcItem &item)
707 {
708 bool include;
709 if (CensorNode_CheckPath2(node, item, include))
710 return include;
711 return false;
712 }
713
714
MakePath_from_2_Parts(const FString & prefix,const FString & path)715 static FString MakePath_from_2_Parts(const FString &prefix, const FString &path)
716 {
717 FString s (prefix);
718 #if defined(_WIN32) && !defined(UNDER_CE)
719 if (!path.IsEmpty() && path[0] == ':' && !prefix.IsEmpty() && IsPathSepar(prefix.Back()))
720 {
721 if (!NName::IsDriveRootPath_SuperAllowed(prefix))
722 s.DeleteBack();
723 }
724 #endif
725 s += path;
726 return s;
727 }
728
729
730
731 #ifdef SUPPORT_LINKS
732
733 /*
734 struct CTempMidBuffer
735 {
736 void *Buf;
737
738 CTempMidBuffer(size_t size): Buf(NULL) { Buf = ::MidAlloc(size); }
739 ~CTempMidBuffer() { ::MidFree(Buf); }
740 };
741
742 HRESULT CArchiveExtractCallback::MyCopyFile(ISequentialOutStream *outStream)
743 {
744 const size_t kBufSize = 1 << 16;
745 CTempMidBuffer buf(kBufSize);
746 if (!buf.Buf)
747 return E_OUTOFMEMORY;
748
749 NIO::CInFile inFile;
750 NIO::COutFile outFile;
751
752 if (!inFile.Open(_CopyFile_Path))
753 return SendMessageError_with_LastError("Open error", _CopyFile_Path);
754
755 for (;;)
756 {
757 UInt32 num;
758
759 if (!inFile.Read(buf.Buf, kBufSize, num))
760 return SendMessageError_with_LastError("Read error", _CopyFile_Path);
761
762 if (num == 0)
763 return S_OK;
764
765
766 RINOK(WriteStream(outStream, buf.Buf, num));
767 }
768 }
769 */
770
771
ReadLink()772 HRESULT CArchiveExtractCallback::ReadLink()
773 {
774 IInArchive *archive = _arc->Archive;
775 const UInt32 index = _index;
776 _link.Clear();
777
778 {
779 NCOM::CPropVariant prop;
780 RINOK(archive->GetProperty(index, kpidHardLink, &prop));
781 if (prop.vt == VT_BSTR)
782 {
783 _link.isHardLink = true;
784 // _link.isCopyLink = false;
785 _link.isRelative = false; // RAR5, TAR: hard links are from root folder of archive
786 _link.linkPath.SetFromBstr(prop.bstrVal);
787 }
788 else if (prop.vt != VT_EMPTY)
789 return E_FAIL;
790 }
791
792 /*
793 {
794 NCOM::CPropVariant prop;
795 RINOK(archive->GetProperty(index, kpidCopyLink, &prop));
796 if (prop.vt == VT_BSTR)
797 {
798 _link.isHardLink = false;
799 _link.isCopyLink = true;
800 _link.isRelative = false; // RAR5: copy links are from root folder of archive
801 _link.linkPath.SetFromBstr(prop.bstrVal);
802 }
803 else if (prop.vt != VT_EMPTY)
804 return E_FAIL;
805 }
806 */
807
808 {
809 NCOM::CPropVariant prop;
810 RINOK(archive->GetProperty(index, kpidSymLink, &prop));
811 if (prop.vt == VT_BSTR)
812 {
813 _link.isHardLink = false;
814 // _link.isCopyLink = false;
815 _link.isRelative = true; // RAR5, TAR: symbolic links can be relative
816 _link.linkPath.SetFromBstr(prop.bstrVal);
817 }
818 else if (prop.vt != VT_EMPTY)
819 return E_FAIL;
820 }
821
822 NtReparse_Data = NULL;
823 NtReparse_Size = 0;
824
825 if (_link.linkPath.IsEmpty() && _arc->GetRawProps)
826 {
827 const void *data;
828 UInt32 dataSize;
829 UInt32 propType;
830
831 _arc->GetRawProps->GetRawProp(_index, kpidNtReparse, &data, &dataSize, &propType);
832
833 // if (dataSize == 1234567) // for debug: unpacking without reparse
834 if (dataSize != 0)
835 {
836 if (propType != NPropDataType::kRaw)
837 return E_FAIL;
838
839 // 21.06: we need kpidNtReparse in linux for wim archives created in Windows
840 // #ifdef _WIN32
841
842 NtReparse_Data = data;
843 NtReparse_Size = dataSize;
844
845 CReparseAttr reparse;
846 bool isOkReparse = reparse.Parse((const Byte *)data, dataSize);
847 if (isOkReparse)
848 {
849 _link.isHardLink = false;
850 // _link.isCopyLink = false;
851 _link.linkPath = reparse.GetPath();
852 _link.isJunction = reparse.IsMountPoint();
853
854 if (reparse.IsSymLink_WSL())
855 {
856 _link.isWSL = true;
857 _link.isRelative = reparse.IsRelative_WSL();
858 }
859 else
860 _link.isRelative = reparse.IsRelative_Win();
861
862 // const AString s = GetAnsiString(_link.linkPath);
863 // printf("\n_link.linkPath: %s\n", s.Ptr());
864
865 #ifndef _WIN32
866 _link.linkPath.Replace(L'\\', WCHAR_PATH_SEPARATOR);
867 #endif
868 }
869 // #endif
870 }
871 }
872
873 if (_link.linkPath.IsEmpty())
874 return S_OK;
875
876 {
877 #ifdef _WIN32
878 _link.linkPath.Replace(L'/', WCHAR_PATH_SEPARATOR);
879 #endif
880
881 // rar5 uses "\??\" prefix for absolute links
882 if (_link.linkPath.IsPrefixedBy(WSTRING_PATH_SEPARATOR L"??" WSTRING_PATH_SEPARATOR))
883 {
884 _link.isRelative = false;
885 _link.linkPath.DeleteFrontal(4);
886 }
887
888 for (;;)
889 // while (NName::IsAbsolutePath(linkPath))
890 {
891 unsigned n = NName::GetRootPrefixSize(_link.linkPath);
892 if (n == 0)
893 break;
894 _link.isRelative = false;
895 _link.linkPath.DeleteFrontal(n);
896 }
897 }
898
899 if (_link.linkPath.IsEmpty())
900 return S_OK;
901
902 if (!_link.isRelative && _removePathParts.Size() != 0)
903 {
904 UStringVector pathParts;
905 SplitPathToParts(_link.linkPath, pathParts);
906 bool badPrefix = false;
907 FOR_VECTOR (i, _removePathParts)
908 {
909 if (CompareFileNames(_removePathParts[i], pathParts[i]) != 0)
910 {
911 badPrefix = true;
912 break;
913 }
914 }
915 if (!badPrefix)
916 pathParts.DeleteFrontal(_removePathParts.Size());
917 _link.linkPath = MakePathFromParts(pathParts);
918 }
919
920 /*
921 if (!_link.linkPath.IsEmpty())
922 {
923 printf("\n_link %s to -> %s\n", GetOemString(_item.Path).Ptr(), GetOemString(_link.linkPath).Ptr());
924 }
925 */
926
927 return S_OK;
928 }
929
930 #endif // SUPPORT_LINKS
931
932
933 #ifndef _WIN32
934
GetOwner(IInArchive * archive,UInt32 index,UInt32 pidName,UInt32 pidId,COwnerInfo & res)935 static HRESULT GetOwner(IInArchive *archive,
936 UInt32 index, UInt32 pidName, UInt32 pidId, COwnerInfo &res)
937 {
938 {
939 NWindows::NCOM::CPropVariant prop;
940 RINOK(archive->GetProperty(index, pidId, &prop));
941 if (prop.vt == VT_UI4)
942 {
943 res.Id_Defined = true;
944 res.Id = prop.ulVal; // for debug
945 // res.Id++; // for debug
946 // if (pidId == kpidGroupId) res.Id += 7; // for debug
947 // res.Id = 0; // for debug
948 }
949 else if (prop.vt != VT_EMPTY)
950 return E_INVALIDARG;
951 }
952 {
953 NWindows::NCOM::CPropVariant prop;
954 RINOK(archive->GetProperty(index, pidName, &prop));
955 if (prop.vt == VT_BSTR)
956 {
957 const UString s = prop.bstrVal;
958 ConvertUnicodeToUTF8(s, res.Name);
959 }
960 else if (prop.vt == VT_UI4)
961 {
962 res.Id_Defined = true;
963 res.Id = prop.ulVal;
964 }
965 else if (prop.vt != VT_EMPTY)
966 return E_INVALIDARG;
967 }
968 return S_OK;
969 }
970
971 #endif
972
973
Read_fi_Props()974 HRESULT CArchiveExtractCallback::Read_fi_Props()
975 {
976 IInArchive *archive = _arc->Archive;
977 const UInt32 index = _index;
978
979 _fi.Attrib_Defined = false;
980
981 #ifndef _WIN32
982 _fi.Owner.Clear();
983 _fi.Group.Clear();
984 #endif
985
986 {
987 NCOM::CPropVariant prop;
988 RINOK(archive->GetProperty(index, kpidPosixAttrib, &prop));
989 if (prop.vt == VT_UI4)
990 {
991 _fi.SetFromPosixAttrib(prop.ulVal);
992 }
993 else if (prop.vt != VT_EMPTY)
994 return E_FAIL;
995 }
996
997 {
998 NCOM::CPropVariant prop;
999 RINOK(archive->GetProperty(index, kpidAttrib, &prop));
1000 if (prop.vt == VT_UI4)
1001 {
1002 _fi.Attrib = prop.ulVal;
1003 _fi.Attrib_Defined = true;
1004 }
1005 else if (prop.vt != VT_EMPTY)
1006 return E_FAIL;
1007 }
1008
1009 RINOK(GetTime(index, kpidCTime, _fi.CTime));
1010 RINOK(GetTime(index, kpidATime, _fi.ATime));
1011 RINOK(GetTime(index, kpidMTime, _fi.MTime));
1012
1013 #ifndef _WIN32
1014 if (_ntOptions.ExtractOwner)
1015 {
1016 // SendMessageError_with_LastError("_ntOptions.ExtractOwner", _diskFilePath);
1017 GetOwner(archive, index, kpidUser, kpidUserId, _fi.Owner);
1018 GetOwner(archive, index, kpidGroup, kpidGroupId, _fi.Group);
1019 }
1020 #endif
1021
1022 return S_OK;
1023 }
1024
1025
1026
CorrectPathParts()1027 void CArchiveExtractCallback::CorrectPathParts()
1028 {
1029 UStringVector &pathParts = _item.PathParts;
1030
1031 #ifdef SUPPORT_ALT_STREAMS
1032 if (!_item.IsAltStream
1033 || !pathParts.IsEmpty()
1034 || !(_removePartsForAltStreams || _pathMode == NExtract::NPathMode::kNoPathsAlt))
1035 #endif
1036 Correct_FsPath(_pathMode == NExtract::NPathMode::kAbsPaths, _keepAndReplaceEmptyDirPrefixes, pathParts, _item.MainIsDir);
1037
1038 #ifdef SUPPORT_ALT_STREAMS
1039
1040 if (_item.IsAltStream)
1041 {
1042 UString s (_item.AltStreamName);
1043 Correct_AltStream_Name(s);
1044 bool needColon = true;
1045
1046 if (pathParts.IsEmpty())
1047 {
1048 pathParts.AddNew();
1049 if (_removePartsForAltStreams || _pathMode == NExtract::NPathMode::kNoPathsAlt)
1050 needColon = false;
1051 }
1052 #ifdef _WIN32
1053 else if (_pathMode == NExtract::NPathMode::kAbsPaths &&
1054 NWildcard::GetNumPrefixParts_if_DrivePath(pathParts) == pathParts.Size())
1055 pathParts.AddNew();
1056 #endif
1057
1058 UString &name = pathParts.Back();
1059 if (needColon)
1060 name += (char)(_ntOptions.ReplaceColonForAltStream ? '_' : ':');
1061 name += s;
1062 }
1063
1064 #endif // SUPPORT_ALT_STREAMS
1065 }
1066
1067
GetFiTimesCAM(CFiTimesCAM & pt)1068 void CArchiveExtractCallback::GetFiTimesCAM(CFiTimesCAM &pt)
1069 {
1070 pt.CTime_Defined = false;
1071 pt.ATime_Defined = false;
1072 pt.MTime_Defined = false;
1073
1074 if (Write_MTime)
1075 {
1076 if (_fi.MTime.Def)
1077 {
1078 _fi.MTime.Write_To_FiTime(pt.MTime);
1079 pt.MTime_Defined = true;
1080 }
1081 else if (_arc->MTime.Def)
1082 {
1083 _arc->MTime.Write_To_FiTime(pt.MTime);
1084 pt.MTime_Defined = true;
1085 }
1086 }
1087
1088 if (Write_CTime && _fi.CTime.Def)
1089 {
1090 _fi.CTime.Write_To_FiTime(pt.CTime);
1091 pt.CTime_Defined = true;
1092 }
1093
1094 if (Write_ATime && _fi.ATime.Def)
1095 {
1096 _fi.ATime.Write_To_FiTime(pt.ATime);
1097 pt.ATime_Defined = true;
1098 }
1099 }
1100
1101
CreateFolders()1102 void CArchiveExtractCallback::CreateFolders()
1103 {
1104 // 21.04 : we don't change original (_item.PathParts) here
1105 UStringVector pathParts = _item.PathParts;
1106
1107 if (!_item.IsDir)
1108 {
1109 if (!pathParts.IsEmpty())
1110 pathParts.DeleteBack();
1111 }
1112
1113 if (pathParts.IsEmpty())
1114 return;
1115
1116 FString fullPathNew;
1117 CreateComplexDirectory(pathParts, fullPathNew);
1118
1119 if (!_item.IsDir)
1120 return;
1121
1122 if (_itemFailure)
1123 return;
1124
1125 CDirPathTime pt;
1126 GetFiTimesCAM(pt);
1127
1128 if (pt.IsSomeTimeDefined())
1129 {
1130 pt.Path = fullPathNew;
1131 pt.SetDirTime();
1132 _extractedFolders.Add(pt);
1133 }
1134 }
1135
1136
1137
1138 /*
1139 CheckExistFile(fullProcessedPath)
1140 it can change: fullProcessedPath, _isRenamed, _overwriteMode
1141 (needExit = true) means that we must exit GetStream() even for S_OK result.
1142 */
1143
CheckExistFile(FString & fullProcessedPath,bool & needExit)1144 HRESULT CArchiveExtractCallback::CheckExistFile(FString &fullProcessedPath, bool &needExit)
1145 {
1146 needExit = true; // it was set already before
1147
1148 NFind::CFileInfo fileInfo;
1149
1150 if (fileInfo.Find(fullProcessedPath))
1151 {
1152 if (_overwriteMode == NExtract::NOverwriteMode::kSkip)
1153 return S_OK;
1154
1155 if (_overwriteMode == NExtract::NOverwriteMode::kAsk)
1156 {
1157 const int slashPos = fullProcessedPath.ReverseFind_PathSepar();
1158 const FString realFullProcessedPath = fullProcessedPath.Left((unsigned)(slashPos + 1)) + fileInfo.Name;
1159
1160 /* (fileInfo) can be symbolic link.
1161 we can show final file properties here. */
1162
1163 FILETIME ft1;
1164 FiTime_To_FILETIME(fileInfo.MTime, ft1);
1165
1166 Int32 overwriteResult;
1167 RINOK(_extractCallback2->AskOverwrite(
1168 fs2us(realFullProcessedPath), &ft1, &fileInfo.Size, _item.Path,
1169 _fi.MTime.Def ? &_fi.MTime.FT : NULL,
1170 _curSizeDefined ? &_curSize : NULL,
1171 &overwriteResult))
1172
1173 switch (overwriteResult)
1174 {
1175 case NOverwriteAnswer::kCancel:
1176 return E_ABORT;
1177 case NOverwriteAnswer::kNo:
1178 return S_OK;
1179 case NOverwriteAnswer::kNoToAll:
1180 _overwriteMode = NExtract::NOverwriteMode::kSkip;
1181 return S_OK;
1182
1183 case NOverwriteAnswer::kYes:
1184 break;
1185 case NOverwriteAnswer::kYesToAll:
1186 _overwriteMode = NExtract::NOverwriteMode::kOverwrite;
1187 break;
1188 case NOverwriteAnswer::kAutoRename:
1189 _overwriteMode = NExtract::NOverwriteMode::kRename;
1190 break;
1191 default:
1192 return E_FAIL;
1193 }
1194 } // NExtract::NOverwriteMode::kAsk
1195
1196 if (_overwriteMode == NExtract::NOverwriteMode::kRename)
1197 {
1198 if (!AutoRenamePath(fullProcessedPath))
1199 {
1200 RINOK(SendMessageError(kCantAutoRename, fullProcessedPath));
1201 return E_FAIL;
1202 }
1203 _isRenamed = true;
1204 }
1205 else if (_overwriteMode == NExtract::NOverwriteMode::kRenameExisting)
1206 {
1207 FString existPath (fullProcessedPath);
1208 if (!AutoRenamePath(existPath))
1209 {
1210 RINOK(SendMessageError(kCantAutoRename, fullProcessedPath));
1211 return E_FAIL;
1212 }
1213 // MyMoveFile can rename folders. So it's OK to use it for folders too
1214 if (!MyMoveFile(fullProcessedPath, existPath))
1215 {
1216 HRESULT errorCode = GetLastError_noZero_HRESULT();
1217 RINOK(SendMessageError2(errorCode, kCantRenameFile, existPath, fullProcessedPath));
1218 return E_FAIL;
1219 }
1220 }
1221 else // not Rename*
1222 {
1223 if (fileInfo.IsDir())
1224 {
1225 // do we need to delete all files in folder?
1226 if (!RemoveDir(fullProcessedPath))
1227 {
1228 RINOK(SendMessageError_with_LastError(kCantDeleteOutputDir, fullProcessedPath));
1229 return S_OK;
1230 }
1231 }
1232 else // fileInfo is not Dir
1233 {
1234 if (NFind::DoesFileExist_Raw(fullProcessedPath))
1235 if (!DeleteFileAlways(fullProcessedPath))
1236 if (GetLastError() != ERROR_FILE_NOT_FOUND) // check it in linux
1237 {
1238 RINOK(SendMessageError_with_LastError(kCantDeleteOutputFile, fullProcessedPath));
1239 return S_OK;
1240 // return E_FAIL;
1241 }
1242 } // fileInfo is not Dir
1243 } // not Rename*
1244 }
1245 else // not Find(fullProcessedPath)
1246 {
1247 #if defined(_WIN32) && !defined(UNDER_CE)
1248 // we need to clear READ-ONLY of parent before creating alt stream
1249 int colonPos = NName::FindAltStreamColon(fullProcessedPath);
1250 if (colonPos >= 0 && fullProcessedPath[(unsigned)colonPos + 1] != 0)
1251 {
1252 FString parentFsPath (fullProcessedPath);
1253 parentFsPath.DeleteFrom((unsigned)colonPos);
1254 NFind::CFileInfo parentFi;
1255 if (parentFi.Find(parentFsPath))
1256 {
1257 if (parentFi.IsReadOnly())
1258 SetFileAttrib(parentFsPath, parentFi.Attrib & ~(DWORD)FILE_ATTRIBUTE_READONLY);
1259 }
1260 }
1261 #endif // defined(_WIN32) && !defined(UNDER_CE)
1262 }
1263
1264 needExit = false;
1265 return S_OK;
1266 }
1267
1268
1269
1270
1271
1272
GetExtractStream(CMyComPtr<ISequentialOutStream> & outStreamLoc,bool & needExit)1273 HRESULT CArchiveExtractCallback::GetExtractStream(CMyComPtr<ISequentialOutStream> &outStreamLoc, bool &needExit)
1274 {
1275 needExit = true;
1276
1277 RINOK(Read_fi_Props());
1278
1279 #ifdef SUPPORT_LINKS
1280 IInArchive *archive = _arc->Archive;
1281 #endif
1282
1283 const UInt32 index = _index;
1284
1285 bool isAnti = false;
1286 RINOK(_arc->IsItem_Anti(index, isAnti));
1287
1288 CorrectPathParts();
1289 UString processedPath (MakePathFromParts(_item.PathParts));
1290
1291 if (!isAnti)
1292 {
1293 // 21.04: CreateFolders doesn't change (_item.PathParts)
1294 CreateFolders();
1295 }
1296
1297 FString fullProcessedPath (us2fs(processedPath));
1298 if (_pathMode != NExtract::NPathMode::kAbsPaths
1299 || !NName::IsAbsolutePath(processedPath))
1300 {
1301 fullProcessedPath = MakePath_from_2_Parts(_dirPathPrefix, fullProcessedPath);
1302 }
1303
1304 #ifdef SUPPORT_ALT_STREAMS
1305 if (_item.IsAltStream && _item.ParentIndex != (UInt32)(Int32)-1)
1306 {
1307 const int renIndex = _renamedFiles.FindInSorted(CIndexToPathPair(_item.ParentIndex));
1308 if (renIndex != -1)
1309 {
1310 const CIndexToPathPair &pair = _renamedFiles[(unsigned)renIndex];
1311 fullProcessedPath = pair.Path;
1312 fullProcessedPath += ':';
1313 UString s (_item.AltStreamName);
1314 Correct_AltStream_Name(s);
1315 fullProcessedPath += us2fs(s);
1316 }
1317 }
1318 #endif // SUPPORT_ALT_STREAMS
1319
1320 if (_item.IsDir)
1321 {
1322 _diskFilePath = fullProcessedPath;
1323 if (isAnti)
1324 RemoveDir(_diskFilePath);
1325 #ifdef SUPPORT_LINKS
1326 if (_link.linkPath.IsEmpty())
1327 #endif
1328 {
1329 if (!isAnti)
1330 SetAttrib();
1331 return S_OK;
1332 }
1333 }
1334 else if (!_isSplit)
1335 {
1336 RINOK(CheckExistFile(fullProcessedPath, needExit));
1337 if (needExit)
1338 return S_OK;
1339 needExit = true;
1340 }
1341
1342 _diskFilePath = fullProcessedPath;
1343
1344
1345 if (isAnti)
1346 {
1347 needExit = false;
1348 return S_OK;
1349 }
1350
1351 // not anti
1352
1353 #ifdef SUPPORT_LINKS
1354
1355 if (!_link.linkPath.IsEmpty())
1356 {
1357 #ifndef UNDER_CE
1358 {
1359 bool linkWasSet = false;
1360 RINOK(SetFromLinkPath(fullProcessedPath, _link, linkWasSet));
1361 if (linkWasSet)
1362 {
1363 _isSymLinkCreated = _link.IsSymLink();
1364 SetAttrib();
1365 // printf("\nlinkWasSet %s\n", GetAnsiString(_diskFilePath));
1366 }
1367 }
1368 #endif // UNDER_CE
1369
1370 // if (_CopyFile_Path.IsEmpty())
1371 {
1372 needExit = false;
1373 return S_OK;
1374 }
1375 }
1376
1377 if (!_hardLinks.IDs.IsEmpty() && !_item.IsAltStream)
1378 {
1379 CHardLinkNode h;
1380 bool defined;
1381 RINOK(Archive_Get_HardLinkNode(archive, index, h, defined));
1382 if (defined)
1383 {
1384 const int linkIndex = _hardLinks.IDs.FindInSorted2(h);
1385 if (linkIndex != -1)
1386 {
1387 FString &hl = _hardLinks.Links[(unsigned)linkIndex];
1388 if (hl.IsEmpty())
1389 hl = fullProcessedPath;
1390 else
1391 {
1392 if (!MyCreateHardLink(fullProcessedPath, hl))
1393 {
1394 HRESULT errorCode = GetLastError_noZero_HRESULT();
1395 RINOK(SendMessageError2(errorCode, kCantCreateHardLink, fullProcessedPath, hl));
1396 return S_OK;
1397 }
1398
1399 // printf("\nHard linkWasSet Archive_Get_HardLinkNode %s\n", GetAnsiString(_diskFilePath));
1400 // _needSetAttrib = true; // do we need to set attribute ?
1401 SetAttrib();
1402 needExit = false;
1403 return S_OK;
1404 }
1405 }
1406 }
1407 }
1408
1409 #endif // SUPPORT_LINKS
1410
1411
1412 // ---------- CREATE WRITE FILE -----
1413
1414 _outFileStreamSpec = new COutFileStream;
1415 CMyComPtr<ISequentialOutStream> outFileStream_Loc(_outFileStreamSpec);
1416
1417 if (!_outFileStreamSpec->Open(fullProcessedPath, _isSplit ? OPEN_ALWAYS: CREATE_ALWAYS))
1418 {
1419 // if (::GetLastError() != ERROR_FILE_EXISTS || !isSplit)
1420 {
1421 RINOK(SendMessageError_with_LastError(kCantOpenOutFile, fullProcessedPath));
1422 return S_OK;
1423 }
1424 }
1425
1426 _needSetAttrib = true;
1427
1428 bool is_SymLink_in_Data = false;
1429
1430 if (_curSizeDefined && _curSize > 0 && _curSize < (1 << 12))
1431 {
1432 if (_fi.IsLinuxSymLink())
1433 {
1434 is_SymLink_in_Data = true;
1435 _is_SymLink_in_Data_Linux = true;
1436 }
1437 else if (_fi.IsReparse())
1438 {
1439 is_SymLink_in_Data = true;
1440 _is_SymLink_in_Data_Linux = false;
1441 }
1442 }
1443
1444 if (is_SymLink_in_Data)
1445 {
1446 _outMemBuf.Alloc((size_t)_curSize);
1447 _bufPtrSeqOutStream_Spec = new CBufPtrSeqOutStream;
1448 _bufPtrSeqOutStream = _bufPtrSeqOutStream_Spec;
1449 _bufPtrSeqOutStream_Spec->Init(_outMemBuf, _outMemBuf.Size());
1450 outStreamLoc = _bufPtrSeqOutStream;
1451 }
1452 else // not reprase
1453 {
1454 if (_ntOptions.PreAllocateOutFile && !_isSplit && _curSizeDefined && _curSize > (1 << 12))
1455 {
1456 // UInt64 ticks = GetCpuTicks();
1457 _fileLength_that_WasSet = _curSize;
1458 bool res = _outFileStreamSpec->File.SetLength(_curSize);
1459 _fileLengthWasSet = res;
1460
1461 // ticks = GetCpuTicks() - ticks;
1462 // printf("\nticks = %10d\n", (unsigned)ticks);
1463 if (!res)
1464 {
1465 RINOK(SendMessageError_with_LastError(kCantSetFileLen, fullProcessedPath));
1466 }
1467
1468 /*
1469 _outFileStreamSpec->File.Close();
1470 ticks = GetCpuTicks() - ticks;
1471 printf("\nticks = %10d\n", (unsigned)ticks);
1472 return S_FALSE;
1473 */
1474
1475 /*
1476 File.SetLength() on FAT (xp64): is fast, but then File.Close() can be slow,
1477 if we don't write any data.
1478 File.SetLength() for remote share file (exFAT) can be slow in some cases,
1479 and the Windows can return "network error" after 1 minute,
1480 while remote file still can grow.
1481 We need some way to detect such bad cases and disable PreAllocateOutFile mode.
1482 */
1483
1484 res = _outFileStreamSpec->SeekToBegin_bool();
1485 if (!res)
1486 {
1487 RINOK(SendMessageError_with_LastError("Cannot seek to begin of file", fullProcessedPath));
1488 }
1489 } // PreAllocateOutFile
1490
1491 #ifdef SUPPORT_ALT_STREAMS
1492 if (_isRenamed && !_item.IsAltStream)
1493 {
1494 CIndexToPathPair pair(index, fullProcessedPath);
1495 unsigned oldSize = _renamedFiles.Size();
1496 unsigned insertIndex = _renamedFiles.AddToUniqueSorted(pair);
1497 if (oldSize == _renamedFiles.Size())
1498 _renamedFiles[insertIndex].Path = fullProcessedPath;
1499 }
1500 #endif // SUPPORT_ALT_STREAMS
1501
1502 if (_isSplit)
1503 {
1504 RINOK(_outFileStreamSpec->Seek((Int64)_position, STREAM_SEEK_SET, NULL));
1505 }
1506 outStreamLoc = outFileStream_Loc;
1507 } // if not reprase
1508
1509 _outFileStream = outFileStream_Loc;
1510
1511 needExit = false;
1512 return S_OK;
1513 }
1514
1515
1516
GetItem(UInt32 index)1517 HRESULT CArchiveExtractCallback::GetItem(UInt32 index)
1518 {
1519 #ifndef _SFX
1520 _item._use_baseParentFolder_mode = _use_baseParentFolder_mode;
1521 if (_use_baseParentFolder_mode)
1522 {
1523 _item._baseParentFolder = (int)_baseParentFolder;
1524 if (_pathMode == NExtract::NPathMode::kFullPaths ||
1525 _pathMode == NExtract::NPathMode::kAbsPaths)
1526 _item._baseParentFolder = -1;
1527 }
1528 #endif // _SFX
1529
1530 #ifdef SUPPORT_ALT_STREAMS
1531 _item.WriteToAltStreamIfColon = _ntOptions.WriteToAltStreamIfColon;
1532 #endif
1533
1534 return _arc->GetItem(index, _item);
1535 }
1536
1537
GetStream(UInt32 index,ISequentialOutStream ** outStream,Int32 askExtractMode)1538 STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode)
1539 {
1540 COM_TRY_BEGIN
1541
1542 *outStream = NULL;
1543
1544 #ifndef _SFX
1545 if (_hashStream)
1546 _hashStreamSpec->ReleaseStream();
1547 _hashStreamWasUsed = false;
1548 #endif
1549
1550 _outFileStream.Release();
1551 _bufPtrSeqOutStream.Release();
1552
1553 _encrypted = false;
1554 _position = 0;
1555 _isSplit = false;
1556
1557 _curSize = 0;
1558 _curSizeDefined = false;
1559 _fileLengthWasSet = false;
1560 _fileLength_that_WasSet = 0;
1561 _index = index;
1562
1563 _diskFilePath.Empty();
1564
1565 _isRenamed = false;
1566
1567 // _fi.Clear();
1568
1569 // _is_SymLink_in_Data = false;
1570 _is_SymLink_in_Data_Linux = false;
1571
1572 _needSetAttrib = false;
1573 _isSymLinkCreated = false;
1574 _itemFailure = false;
1575
1576 #ifdef SUPPORT_LINKS
1577 // _CopyFile_Path.Empty();
1578 _link.Clear();
1579 #endif
1580
1581 _extractMode = false;
1582
1583 switch (askExtractMode)
1584 {
1585 case NArchive::NExtract::NAskMode::kExtract:
1586 if (_testMode)
1587 {
1588 // askExtractMode = NArchive::NExtract::NAskMode::kTest;
1589 }
1590 else
1591 _extractMode = true;
1592 break;
1593 };
1594
1595
1596 IInArchive *archive = _arc->Archive;
1597
1598 RINOK(GetItem(index));
1599
1600 {
1601 NCOM::CPropVariant prop;
1602 RINOK(archive->GetProperty(index, kpidPosition, &prop));
1603 if (prop.vt != VT_EMPTY)
1604 {
1605 if (prop.vt != VT_UI8)
1606 return E_FAIL;
1607 _position = prop.uhVal.QuadPart;
1608 _isSplit = true;
1609 }
1610 }
1611
1612 #ifdef SUPPORT_LINKS
1613 RINOK(ReadLink());
1614 #endif // SUPPORT_LINKS
1615
1616
1617 RINOK(Archive_GetItemBoolProp(archive, index, kpidEncrypted, _encrypted));
1618
1619 RINOK(GetUnpackSize());
1620
1621 #ifdef SUPPORT_ALT_STREAMS
1622 if (!_ntOptions.AltStreams.Val && _item.IsAltStream)
1623 return S_OK;
1624 #endif // SUPPORT_ALT_STREAMS
1625
1626 // we can change (_item.PathParts) in this function
1627 UStringVector &pathParts = _item.PathParts;
1628
1629 if (_wildcardCensor)
1630 {
1631 if (!CensorNode_CheckPath(*_wildcardCensor, _item))
1632 return S_OK;
1633 }
1634
1635 #ifndef _SFX
1636 if (_use_baseParentFolder_mode)
1637 {
1638 if (!pathParts.IsEmpty())
1639 {
1640 unsigned numRemovePathParts = 0;
1641
1642 #ifdef SUPPORT_ALT_STREAMS
1643 if (_pathMode == NExtract::NPathMode::kNoPathsAlt && _item.IsAltStream)
1644 numRemovePathParts = pathParts.Size();
1645 else
1646 #endif
1647 if (_pathMode == NExtract::NPathMode::kNoPaths ||
1648 _pathMode == NExtract::NPathMode::kNoPathsAlt)
1649 numRemovePathParts = pathParts.Size() - 1;
1650 pathParts.DeleteFrontal(numRemovePathParts);
1651 }
1652 }
1653 else
1654 #endif // _SFX
1655 {
1656 if (pathParts.IsEmpty())
1657 {
1658 if (_item.IsDir)
1659 return S_OK;
1660 /*
1661 #ifdef SUPPORT_ALT_STREAMS
1662 if (!_item.IsAltStream)
1663 #endif
1664 return E_FAIL;
1665 */
1666 }
1667
1668 unsigned numRemovePathParts = 0;
1669
1670 switch (_pathMode)
1671 {
1672 case NExtract::NPathMode::kFullPaths:
1673 case NExtract::NPathMode::kCurPaths:
1674 {
1675 if (_removePathParts.IsEmpty())
1676 break;
1677 bool badPrefix = false;
1678
1679 if (pathParts.Size() < _removePathParts.Size())
1680 badPrefix = true;
1681 else
1682 {
1683 if (pathParts.Size() == _removePathParts.Size())
1684 {
1685 if (_removePartsForAltStreams)
1686 {
1687 #ifdef SUPPORT_ALT_STREAMS
1688 if (!_item.IsAltStream)
1689 #endif
1690 badPrefix = true;
1691 }
1692 else
1693 {
1694 if (!_item.MainIsDir)
1695 badPrefix = true;
1696 }
1697 }
1698
1699 if (!badPrefix)
1700 FOR_VECTOR (i, _removePathParts)
1701 {
1702 if (CompareFileNames(_removePathParts[i], pathParts[i]) != 0)
1703 {
1704 badPrefix = true;
1705 break;
1706 }
1707 }
1708 }
1709
1710 if (badPrefix)
1711 {
1712 if (askExtractMode == NArchive::NExtract::NAskMode::kExtract && !_testMode)
1713 return E_FAIL;
1714 }
1715 else
1716 numRemovePathParts = _removePathParts.Size();
1717 break;
1718 }
1719
1720 case NExtract::NPathMode::kNoPaths:
1721 {
1722 if (!pathParts.IsEmpty())
1723 numRemovePathParts = pathParts.Size() - 1;
1724 break;
1725 }
1726 case NExtract::NPathMode::kNoPathsAlt:
1727 {
1728 #ifdef SUPPORT_ALT_STREAMS
1729 if (_item.IsAltStream)
1730 numRemovePathParts = pathParts.Size();
1731 else
1732 #endif
1733 if (!pathParts.IsEmpty())
1734 numRemovePathParts = pathParts.Size() - 1;
1735 break;
1736 }
1737 /*
1738 case NExtract::NPathMode::kFullPaths:
1739 case NExtract::NPathMode::kAbsPaths:
1740 break;
1741 */
1742 default:
1743 break;
1744 }
1745
1746 pathParts.DeleteFrontal(numRemovePathParts);
1747 }
1748
1749
1750 #ifndef _SFX
1751
1752 if (ExtractToStreamCallback)
1753 {
1754 if (!GetProp)
1755 {
1756 GetProp_Spec = new CGetProp;
1757 GetProp = GetProp_Spec;
1758 }
1759 GetProp_Spec->Arc = _arc;
1760 GetProp_Spec->IndexInArc = index;
1761 UString name (MakePathFromParts(pathParts));
1762
1763 #ifdef SUPPORT_ALT_STREAMS
1764 if (_item.IsAltStream)
1765 {
1766 if (!pathParts.IsEmpty() || (!_removePartsForAltStreams && _pathMode != NExtract::NPathMode::kNoPathsAlt))
1767 name += ':';
1768 name += _item.AltStreamName;
1769 }
1770 #endif
1771
1772 return ExtractToStreamCallback->GetStream7(name, BoolToInt(_item.IsDir), outStream, askExtractMode, GetProp);
1773 }
1774
1775 #endif // _SFX
1776
1777
1778 CMyComPtr<ISequentialOutStream> outStreamLoc;
1779
1780 if (askExtractMode == NArchive::NExtract::NAskMode::kExtract && !_testMode)
1781 {
1782 if (_stdOutMode)
1783 outStreamLoc = new CStdOutFileStream;
1784 else
1785 {
1786 bool needExit = true;
1787 RINOK(GetExtractStream(outStreamLoc, needExit));
1788 if (needExit)
1789 return S_OK;
1790 }
1791 }
1792
1793 #ifndef _SFX
1794 if (_hashStream)
1795 {
1796 if (askExtractMode == NArchive::NExtract::NAskMode::kExtract ||
1797 askExtractMode == NArchive::NExtract::NAskMode::kTest)
1798 {
1799 _hashStreamSpec->SetStream(outStreamLoc);
1800 outStreamLoc = _hashStream;
1801 _hashStreamSpec->Init(true);
1802 _hashStreamWasUsed = true;
1803 }
1804 }
1805 #endif // _SFX
1806
1807 if (outStreamLoc)
1808 {
1809 /*
1810 #ifdef SUPPORT_LINKS
1811 if (!_CopyFile_Path.IsEmpty())
1812 {
1813 RINOK(PrepareOperation(askExtractMode));
1814 RINOK(MyCopyFile(outStreamLoc));
1815 return SetOperationResult(NArchive::NExtract::NOperationResult::kOK);
1816 }
1817 if (_link.isCopyLink && _testMode)
1818 return S_OK;
1819 #endif
1820 */
1821 *outStream = outStreamLoc.Detach();
1822 }
1823
1824 return S_OK;
1825
1826 COM_TRY_END
1827 }
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
PrepareOperation(Int32 askExtractMode)1839 STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode)
1840 {
1841 COM_TRY_BEGIN
1842
1843 #ifndef _SFX
1844 if (ExtractToStreamCallback)
1845 return ExtractToStreamCallback->PrepareOperation7(askExtractMode);
1846 #endif
1847
1848 _extractMode = false;
1849
1850 switch (askExtractMode)
1851 {
1852 case NArchive::NExtract::NAskMode::kExtract:
1853 if (_testMode)
1854 askExtractMode = NArchive::NExtract::NAskMode::kTest;
1855 else
1856 _extractMode = true;
1857 break;
1858 };
1859
1860 return _extractCallback2->PrepareOperation(_item.Path, BoolToInt(_item.IsDir),
1861 askExtractMode, _isSplit ? &_position: 0);
1862
1863 COM_TRY_END
1864 }
1865
1866
1867
1868
1869
CloseFile()1870 HRESULT CArchiveExtractCallback::CloseFile()
1871 {
1872 if (!_outFileStream)
1873 return S_OK;
1874
1875 HRESULT hres = S_OK;
1876
1877 const UInt64 processedSize = _outFileStreamSpec->ProcessedSize;
1878 if (_fileLengthWasSet && _fileLength_that_WasSet > processedSize)
1879 {
1880 bool res = _outFileStreamSpec->File.SetLength(processedSize);
1881 _fileLengthWasSet = res;
1882 if (!res)
1883 {
1884 HRESULT hres2 = SendMessageError_with_LastError(kCantSetFileLen, us2fs(_item.Path));
1885 if (hres == S_OK)
1886 hres = hres2;
1887 }
1888 }
1889
1890 _curSize = processedSize;
1891 _curSizeDefined = true;
1892
1893 #if defined(_WIN32) && !defined(UNDER_CE) && !defined(_SFX)
1894 if (ZoneBuf.Size() != 0
1895 && !_item.IsAltStream)
1896 {
1897 // if (NFind::DoesFileExist_Raw(tempFilePath))
1898 if (ZoneMode != NExtract::NZoneIdMode::kOffice ||
1899 FindExt2(kOfficeExtensions, _diskFilePath))
1900 {
1901 // we must write zone file before setting of timestamps
1902 const FString path = _diskFilePath + k_ZoneId_StreamName;
1903 if (!WriteZoneFile(path, ZoneBuf))
1904 {
1905 // we can't write it in FAT
1906 // SendMessageError_with_LastError("Can't write Zone.Identifier stream", path);
1907 }
1908 }
1909 }
1910 #endif
1911
1912 CFiTimesCAM t;
1913 GetFiTimesCAM(t);
1914
1915 // #ifdef _WIN32
1916 if (t.IsSomeTimeDefined())
1917 _outFileStreamSpec->SetTime(
1918 t.CTime_Defined ? &t.CTime : NULL,
1919 t.ATime_Defined ? &t.ATime : NULL,
1920 t.MTime_Defined ? &t.MTime : NULL);
1921 // #endif
1922
1923 RINOK(_outFileStreamSpec->Close());
1924 _outFileStream.Release();
1925 return hres;
1926 }
1927
1928
1929 #ifdef SUPPORT_LINKS
1930
1931
SetFromLinkPath(const FString & fullProcessedPath,const CLinkInfo & linkInfo,bool & linkWasSet)1932 HRESULT CArchiveExtractCallback::SetFromLinkPath(
1933 const FString &fullProcessedPath,
1934 const CLinkInfo &linkInfo,
1935 bool &linkWasSet)
1936 {
1937 linkWasSet = false;
1938 if (!_ntOptions.SymLinks.Val && !linkInfo.isHardLink)
1939 return S_OK;
1940
1941 UString relatPath;
1942
1943 /* if (linkInfo.isRelative)
1944 linkInfo.linkPath is final link path that must be stored to file link field
1945 else
1946 linkInfo.linkPath is path from root of archive. So we must add _dirPathPrefix_Full before linkPath.
1947 */
1948
1949 if (linkInfo.isRelative)
1950 relatPath = GetDirPrefixOf(_item.Path);
1951 relatPath += linkInfo.linkPath;
1952
1953 if (!IsSafePath(relatPath))
1954 {
1955 return SendMessageError2(
1956 0, // errorCode
1957 "Dangerous link path was ignored",
1958 us2fs(_item.Path),
1959 us2fs(linkInfo.linkPath)); // us2fs(relatPath)
1960 }
1961
1962 FString existPath;
1963 if (linkInfo.isHardLink /* || linkInfo.IsCopyLink */ || !linkInfo.isRelative)
1964 {
1965 if (!NName::GetFullPath(_dirPathPrefix_Full, us2fs(relatPath), existPath))
1966 {
1967 RINOK(SendMessageError("Incorrect path", us2fs(relatPath)));
1968 }
1969 }
1970 else
1971 {
1972 existPath = us2fs(linkInfo.linkPath);
1973 // printf("\nlinkPath = : %s\n", GetOemString(linkInfo.linkPath).Ptr());
1974 }
1975
1976 if (existPath.IsEmpty())
1977 return SendMessageError("Empty link", fullProcessedPath);
1978
1979 if (linkInfo.isHardLink /* || linkInfo.IsCopyLink */)
1980 {
1981 // if (linkInfo.isHardLink)
1982 {
1983 if (!MyCreateHardLink(fullProcessedPath, existPath))
1984 {
1985 HRESULT errorCode = GetLastError_noZero_HRESULT();
1986 RINOK(SendMessageError2(errorCode, kCantCreateHardLink, fullProcessedPath, existPath));
1987 }
1988 linkWasSet = true;
1989 return S_OK;
1990 }
1991 /*
1992 // IsCopyLink
1993 {
1994 NFind::CFileInfo fi;
1995 if (!fi.Find(existPath))
1996 {
1997 RINOK(SendMessageError2("Cannot find the file for copying", existPath, fullProcessedPath));
1998 }
1999 else
2000 {
2001 if (_curSizeDefined && _curSize == fi.Size)
2002 _CopyFile_Path = existPath;
2003 else
2004 {
2005 RINOK(SendMessageError2("File size collision for file copying", existPath, fullProcessedPath));
2006 }
2007 // RINOK(MyCopyFile(existPath, fullProcessedPath));
2008 }
2009 }
2010 */
2011 }
2012
2013 // is Symbolic link
2014
2015 /*
2016 if (_item.IsDir && !isRelative)
2017 {
2018 // Windows before Vista doesn't support symbolic links.
2019 // we could convert such symbolic links to Junction Points
2020 // isJunction = true;
2021 // convertToAbs = true;
2022 }
2023 */
2024
2025 if (!_ntOptions.SymLinks_AllowDangerous.Val)
2026 {
2027 #ifdef _WIN32
2028 if (_item.IsDir)
2029 #endif
2030 if (linkInfo.isRelative)
2031 {
2032 CLinkLevelsInfo levelsInfo;
2033 levelsInfo.Parse(linkInfo.linkPath);
2034 if (levelsInfo.FinalLevel < 1 || levelsInfo.IsAbsolute)
2035 {
2036 return SendMessageError2(
2037 0, // errorCode
2038 "Dangerous symbolic link path was ignored",
2039 us2fs(_item.Path),
2040 us2fs(linkInfo.linkPath));
2041 }
2042 }
2043 }
2044
2045
2046 #ifdef _WIN32
2047
2048 CByteBuffer data;
2049 // printf("\nFillLinkData(): %s\n", GetOemString(existPath).Ptr());
2050 if (!FillLinkData(data, fs2us(existPath), !linkInfo.isJunction, linkInfo.isWSL))
2051 return SendMessageError("Cannot fill link data", us2fs(_item.Path));
2052
2053 /*
2054 if (NtReparse_Size != data.Size() || memcmp(NtReparse_Data, data, data.Size()) != 0)
2055 {
2056 SendMessageError("reconstructed Reparse is different", fs2us(existPath));
2057 }
2058 */
2059
2060 CReparseAttr attr;
2061 if (!attr.Parse(data, data.Size()))
2062 {
2063 RINOK(SendMessageError("Internal error for symbolic link file", us2fs(_item.Path)));
2064 return S_OK;
2065 }
2066 if (!NFile::NIO::SetReparseData(fullProcessedPath, _item.IsDir, data, (DWORD)data.Size()))
2067 {
2068 RINOK(SendMessageError_with_LastError(kCantCreateSymLink, fullProcessedPath));
2069 return S_OK;
2070 }
2071 linkWasSet = true;
2072
2073 return S_OK;
2074
2075
2076 #else // ! _WIN32
2077
2078 if (!NFile::NIO::SetSymLink(fullProcessedPath, existPath))
2079 {
2080 RINOK(SendMessageError_with_LastError(kCantCreateSymLink, fullProcessedPath));
2081 return S_OK;
2082 }
2083 linkWasSet = true;
2084
2085 return S_OK;
2086
2087 #endif // ! _WIN32
2088 }
2089
2090
Parse(const Byte * data,size_t dataSize,bool isLinuxData)2091 bool CLinkInfo::Parse(const Byte *data, size_t dataSize, bool isLinuxData)
2092 {
2093 Clear();
2094 // this->isLinux = isLinuxData;
2095
2096 if (isLinuxData)
2097 {
2098 isJunction = false;
2099 isHardLink = false;
2100 AString utf;
2101 if (dataSize >= (1 << 12))
2102 return false;
2103 utf.SetFrom_CalcLen((const char *)data, (unsigned)dataSize);
2104 UString u;
2105 if (!ConvertUTF8ToUnicode(utf, u))
2106 return false;
2107 linkPath = u;
2108
2109 // in linux symbolic data: we expect that linux separator '/' is used
2110 // if windows link was created, then we also must use linux separator
2111 if (u.IsEmpty())
2112 return false;
2113 wchar_t c = u[0];
2114 isRelative = !IS_PATH_SEPAR(c);
2115 return true;
2116 }
2117
2118 CReparseAttr reparse;
2119 if (!reparse.Parse(data, dataSize))
2120 return false;
2121 isHardLink = false;
2122 // isCopyLink = false;
2123 linkPath = reparse.GetPath();
2124 isJunction = reparse.IsMountPoint();
2125
2126 if (reparse.IsSymLink_WSL())
2127 {
2128 isWSL = true;
2129 isRelative = reparse.IsRelative_WSL();
2130 }
2131 else
2132 isRelative = reparse.IsRelative_Win();
2133
2134 // FIXME !!!
2135 #ifndef _WIN32
2136 linkPath.Replace(L'\\', WCHAR_PATH_SEPARATOR);
2137 #endif
2138
2139 return true;
2140 }
2141
2142 #endif // SUPPORT_LINKS
2143
2144
CloseReparseAndFile()2145 HRESULT CArchiveExtractCallback::CloseReparseAndFile()
2146 {
2147 HRESULT res = S_OK;
2148
2149 #ifdef SUPPORT_LINKS
2150
2151 size_t reparseSize = 0;
2152 bool repraseMode = false;
2153 bool needSetReparse = false;
2154 CLinkInfo linkInfo;
2155
2156 if (_bufPtrSeqOutStream)
2157 {
2158 repraseMode = true;
2159 reparseSize = _bufPtrSeqOutStream_Spec->GetPos();
2160 if (_curSizeDefined && reparseSize == _outMemBuf.Size())
2161 {
2162 /*
2163 CReparseAttr reparse;
2164 DWORD errorCode = 0;
2165 needSetReparse = reparse.Parse(_outMemBuf, reparseSize, errorCode);
2166 if (needSetReparse)
2167 {
2168 UString linkPath = reparse.GetPath();
2169 #ifndef _WIN32
2170 linkPath.Replace(L'\\', WCHAR_PATH_SEPARATOR);
2171 #endif
2172 }
2173 */
2174 needSetReparse = linkInfo.Parse(_outMemBuf, reparseSize, _is_SymLink_in_Data_Linux);
2175 if (!needSetReparse)
2176 res = SendMessageError_with_LastError("Incorrect reparse stream", us2fs(_item.Path));
2177 }
2178 else
2179 {
2180 res = SendMessageError_with_LastError("Unknown reparse stream", us2fs(_item.Path));
2181 }
2182 if (!needSetReparse && _outFileStream)
2183 {
2184 HRESULT res2 = WriteStream(_outFileStream, _outMemBuf, reparseSize);
2185 if (res == S_OK)
2186 res = res2;
2187 }
2188 _bufPtrSeqOutStream.Release();
2189 }
2190
2191 #endif // SUPPORT_LINKS
2192
2193
2194 HRESULT res2 = CloseFile();
2195
2196 if (res == S_OK)
2197 res = res2;
2198
2199 RINOK(res);
2200
2201 #ifdef SUPPORT_LINKS
2202 if (repraseMode)
2203 {
2204 _curSize = reparseSize;
2205 _curSizeDefined = true;
2206
2207 #ifdef SUPPORT_LINKS
2208 if (needSetReparse)
2209 {
2210 // in Linux : we must delete empty file before symbolic link creation
2211 // in Windows : we can create symbolic link even without file deleting
2212 if (!DeleteFileAlways(_diskFilePath))
2213 {
2214 RINOK(SendMessageError_with_LastError("can't delete file", _diskFilePath));
2215 }
2216 {
2217 /*
2218 // for DEBUG ONLY: we can extract sym links as WSL links
2219 // to elimanate (non-admin) errors for sym links.
2220 #ifdef _WIN32
2221 if (!linkInfo.isHardLink && !linkInfo.isJunction)
2222 linkInfo.isWSL = true;
2223 #endif
2224 */
2225 bool linkWasSet = false;
2226 RINOK(SetFromLinkPath(_diskFilePath, linkInfo, linkWasSet));
2227 if (linkWasSet)
2228 _isSymLinkCreated = linkInfo.IsSymLink();
2229 else
2230 _needSetAttrib = false;
2231 }
2232 /*
2233 if (!NFile::NIO::SetReparseData(_diskFilePath, _item.IsDir, ))
2234 {
2235 res = SendMessageError_with_LastError(kCantCreateSymLink, _diskFilePath);
2236 }
2237 */
2238 }
2239 #endif
2240 }
2241 #endif
2242 return res;
2243 }
2244
2245
SetAttrib()2246 void CArchiveExtractCallback::SetAttrib()
2247 {
2248 #ifndef _WIN32
2249 // Linux now doesn't support permissions for symlinks
2250 if (_isSymLinkCreated)
2251 return;
2252 #endif
2253
2254 if (_itemFailure
2255 || _diskFilePath.IsEmpty()
2256 || _stdOutMode
2257 || !_extractMode)
2258 return;
2259
2260 #ifndef _WIN32
2261 if (_fi.Owner.Id_Defined &&
2262 _fi.Group.Id_Defined)
2263 {
2264 if (my_chown(_diskFilePath, _fi.Owner.Id, _fi.Group.Id) != 0)
2265 {
2266 SendMessageError_with_LastError("Cannot set owner", _diskFilePath);
2267 }
2268 }
2269 #endif
2270
2271 if (_fi.Attrib_Defined)
2272 {
2273 // const AString s = GetAnsiString(_diskFilePath);
2274 // printf("\nSetFileAttrib_PosixHighDetect: %s: hex:%x\n", s.Ptr(), _fi.Attrib);
2275 bool res = SetFileAttrib_PosixHighDetect(_diskFilePath, _fi.Attrib);
2276 if (!res)
2277 {
2278 // do we need error message here in Windows and in posix?
2279 SendMessageError_with_LastError("Cannot set file attribute", _diskFilePath);
2280 }
2281 }
2282 }
2283
2284
SetOperationResult(Int32 opRes)2285 STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 opRes)
2286 {
2287 COM_TRY_BEGIN
2288
2289 // printf("\nCArchiveExtractCallback::SetOperationResult: %d %s\n", opRes, GetAnsiString(_diskFilePath));
2290
2291 #ifndef _SFX
2292 if (ExtractToStreamCallback)
2293 {
2294 GetUnpackSize();
2295 return ExtractToStreamCallback->SetOperationResult8(opRes, BoolToInt(_encrypted), _curSize);
2296 }
2297 #endif
2298
2299 #ifndef _SFX
2300
2301 if (_hashStreamWasUsed)
2302 {
2303 _hashStreamSpec->_hash->Final(_item.IsDir,
2304 #ifdef SUPPORT_ALT_STREAMS
2305 _item.IsAltStream
2306 #else
2307 false
2308 #endif
2309 , _item.Path);
2310 _curSize = _hashStreamSpec->GetSize();
2311 _curSizeDefined = true;
2312 _hashStreamSpec->ReleaseStream();
2313 _hashStreamWasUsed = false;
2314 }
2315
2316 #endif // _SFX
2317
2318 RINOK(CloseReparseAndFile());
2319
2320 #ifdef _USE_SECURITY_CODE
2321 if (!_stdOutMode && _extractMode && _ntOptions.NtSecurity.Val && _arc->GetRawProps)
2322 {
2323 const void *data;
2324 UInt32 dataSize;
2325 UInt32 propType;
2326 _arc->GetRawProps->GetRawProp(_index, kpidNtSecure, &data, &dataSize, &propType);
2327 if (dataSize != 0)
2328 {
2329 if (propType != NPropDataType::kRaw)
2330 return E_FAIL;
2331 if (CheckNtSecure((const Byte *)data, dataSize))
2332 {
2333 SECURITY_INFORMATION securInfo = DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION;
2334 if (_saclEnabled)
2335 securInfo |= SACL_SECURITY_INFORMATION;
2336 ::SetFileSecurityW(fs2us(_diskFilePath), securInfo, (PSECURITY_DESCRIPTOR)(void *)(const Byte *)(data));
2337 }
2338 }
2339 }
2340 #endif // _USE_SECURITY_CODE
2341
2342 if (!_curSizeDefined)
2343 GetUnpackSize();
2344
2345 if (_curSizeDefined)
2346 {
2347 #ifdef SUPPORT_ALT_STREAMS
2348 if (_item.IsAltStream)
2349 AltStreams_UnpackSize += _curSize;
2350 else
2351 #endif
2352 UnpackSize += _curSize;
2353 }
2354
2355 if (_item.IsDir)
2356 NumFolders++;
2357 #ifdef SUPPORT_ALT_STREAMS
2358 else if (_item.IsAltStream)
2359 NumAltStreams++;
2360 #endif
2361 else
2362 NumFiles++;
2363
2364 if (_needSetAttrib)
2365 SetAttrib();
2366
2367 RINOK(_extractCallback2->SetOperationResult(opRes, BoolToInt(_encrypted)));
2368
2369 return S_OK;
2370
2371 COM_TRY_END
2372 }
2373
2374
2375
ReportExtractResult(UInt32 indexType,UInt32 index,Int32 opRes)2376 STDMETHODIMP CArchiveExtractCallback::ReportExtractResult(UInt32 indexType, UInt32 index, Int32 opRes)
2377 {
2378 if (_folderArchiveExtractCallback2)
2379 {
2380 bool isEncrypted = false;
2381 UString s;
2382
2383 if (indexType == NArchive::NEventIndexType::kInArcIndex && index != (UInt32)(Int32)-1)
2384 {
2385 CReadArcItem item;
2386 RINOK(_arc->GetItem(index, item));
2387 s = item.Path;
2388 RINOK(Archive_GetItemBoolProp(_arc->Archive, index, kpidEncrypted, isEncrypted));
2389 }
2390 else
2391 {
2392 s = '#';
2393 s.Add_UInt32(index);
2394 // if (indexType == NArchive::NEventIndexType::kBlockIndex) {}
2395 }
2396
2397 return _folderArchiveExtractCallback2->ReportExtractResult(opRes, isEncrypted, s);
2398 }
2399
2400 return S_OK;
2401 }
2402
2403
CryptoGetTextPassword(BSTR * password)2404 STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password)
2405 {
2406 COM_TRY_BEGIN
2407 if (!_cryptoGetTextPassword)
2408 {
2409 RINOK(_extractCallback2.QueryInterface(IID_ICryptoGetTextPassword,
2410 &_cryptoGetTextPassword));
2411 }
2412 return _cryptoGetTextPassword->CryptoGetTextPassword(password);
2413 COM_TRY_END
2414 }
2415
2416
2417 // ---------- HASH functions ----------
2418
Hash_GetFullFilePath()2419 FString CArchiveExtractCallback::Hash_GetFullFilePath()
2420 {
2421 // this function changes _item.PathParts.
2422 CorrectPathParts();
2423 const UStringVector &pathParts = _item.PathParts;
2424 const UString processedPath (MakePathFromParts(pathParts));
2425 FString fullProcessedPath (us2fs(processedPath));
2426 if (_pathMode != NExtract::NPathMode::kAbsPaths
2427 || !NName::IsAbsolutePath(processedPath))
2428 {
2429 fullProcessedPath = MakePath_from_2_Parts(
2430 DirPathPrefix_for_HashFiles,
2431 // _dirPathPrefix,
2432 fullProcessedPath);
2433 }
2434 return fullProcessedPath;
2435 }
2436
2437
GetDiskProperty(UInt32 index,PROPID propID,PROPVARIANT * value)2438 STDMETHODIMP CArchiveExtractCallback::GetDiskProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
2439 {
2440 COM_TRY_BEGIN
2441 NCOM::CPropVariant prop;
2442 if (propID == kpidSize)
2443 {
2444 RINOK(GetItem(index));
2445 const FString fullProcessedPath = Hash_GetFullFilePath();
2446 NFile::NFind::CFileInfo fi;
2447 if (fi.Find_FollowLink(fullProcessedPath))
2448 if (!fi.IsDir())
2449 prop = (UInt64)fi.Size;
2450 }
2451 prop.Detach(value);
2452 return S_OK;
2453 COM_TRY_END
2454 }
2455
2456
GetStream2(UInt32 index,ISequentialInStream ** inStream,UInt32 mode)2457 STDMETHODIMP CArchiveExtractCallback::GetStream2(UInt32 index, ISequentialInStream **inStream, UInt32 mode)
2458 {
2459 COM_TRY_BEGIN
2460 *inStream = NULL;
2461 // if (index != _index) return E_FAIL;
2462 if (mode != NUpdateNotifyOp::kHashRead)
2463 return E_FAIL;
2464
2465 RINOK(GetItem(index));
2466 const FString fullProcessedPath = Hash_GetFullFilePath();
2467
2468 CInFileStream *inStreamSpec = new CInFileStream;
2469 CMyComPtr<ISequentialInStream> inStreamRef = inStreamSpec;
2470 inStreamSpec->Set_PreserveATime(_ntOptions.PreserveATime);
2471 if (!inStreamSpec->OpenShared(fullProcessedPath, _ntOptions.OpenShareForWrite))
2472 {
2473 RINOK(SendMessageError_with_LastError(kCantOpenInFile, fullProcessedPath));
2474 return S_OK;
2475 }
2476 *inStream = inStreamRef.Detach();
2477 return S_OK;
2478 COM_TRY_END
2479 }
2480
2481
ReportOperation(UInt32,UInt32,UInt32)2482 STDMETHODIMP CArchiveExtractCallback::ReportOperation(
2483 UInt32 /* indexType */, UInt32 /* index */, UInt32 /* op */)
2484 {
2485 // COM_TRY_BEGIN
2486 return S_OK;
2487 // COM_TRY_END
2488 }
2489
2490
2491 // ------------ After Extracting functions ------------
2492
SetNumSlashes(const FChar * s)2493 void CDirPathSortPair::SetNumSlashes(const FChar *s)
2494 {
2495 for (unsigned numSlashes = 0;;)
2496 {
2497 FChar c = *s++;
2498 if (c == 0)
2499 {
2500 Len = numSlashes;
2501 return;
2502 }
2503 if (IS_PATH_SEPAR(c))
2504 numSlashes++;
2505 }
2506 }
2507
2508
SetDirTime() const2509 bool CDirPathTime::SetDirTime() const
2510 {
2511 return NDir::SetDirTime(Path,
2512 CTime_Defined ? &CTime : NULL,
2513 ATime_Defined ? &ATime : NULL,
2514 MTime_Defined ? &MTime : NULL);
2515 }
2516
2517
SetDirsTimes()2518 HRESULT CArchiveExtractCallback::SetDirsTimes()
2519 {
2520 if (!_arc)
2521 return S_OK;
2522
2523 CRecordVector<CDirPathSortPair> pairs;
2524 pairs.ClearAndSetSize(_extractedFolders.Size());
2525 unsigned i;
2526
2527 for (i = 0; i < _extractedFolders.Size(); i++)
2528 {
2529 CDirPathSortPair &pair = pairs[i];
2530 pair.Index = i;
2531 pair.SetNumSlashes(_extractedFolders[i].Path);
2532 }
2533
2534 pairs.Sort2();
2535
2536 HRESULT res = S_OK;
2537
2538 for (i = 0; i < pairs.Size(); i++)
2539 {
2540 const CDirPathTime &dpt = _extractedFolders[pairs[i].Index];
2541 if (!dpt.SetDirTime())
2542 {
2543 // result = E_FAIL;
2544 // do we need error message here in Windows and in posix?
2545 // SendMessageError_with_LastError("Cannot set directory time", dpt.Path);
2546 }
2547 }
2548
2549 /*
2550 #ifndef _WIN32
2551 for (i = 0; i < _delayedSymLinks.Size(); i++)
2552 {
2553 const CDelayedSymLink &link = _delayedSymLinks[i];
2554 if (!link.Create())
2555 {
2556 if (res == S_OK)
2557 res = GetLastError_noZero_HRESULT();
2558 // res = E_FAIL;
2559 // do we need error message here in Windows and in posix?
2560 SendMessageError_with_LastError("Cannot create Symbolic Link", link._source);
2561 }
2562 }
2563 #endif // _WIN32
2564 */
2565
2566 ClearExtractedDirsInfo();
2567 return res;
2568 }
2569
2570
CloseArc()2571 HRESULT CArchiveExtractCallback::CloseArc()
2572 {
2573 HRESULT res = CloseReparseAndFile();
2574 HRESULT res2 = SetDirsTimes();
2575 if (res == S_OK)
2576 res = res2;
2577 _arc = NULL;
2578 return res;
2579 }
2580