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