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