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