aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--C/7zVersion.h6
-rw-r--r--C/LzFind.c2
-rw-r--r--CPP/7zip/Bundles/Alone/Alone.dsp20
-rw-r--r--CPP/7zip/Compress/BZip2Encoder.cpp23
-rw-r--r--CPP/7zip/UI/Agent/AgentProxy.cpp2
-rw-r--r--CPP/7zip/UI/Client7z/makefile.gcc2
-rw-r--r--CPP/7zip/UI/Common/ArchiveCommandLine.cpp20
-rw-r--r--CPP/7zip/UI/Common/ArchiveExtractCallback.cpp544
-rw-r--r--CPP/7zip/UI/Common/ArchiveExtractCallback.h169
-rw-r--r--CPP/Windows/FileDir.cpp44
-rw-r--r--CPP/Windows/FileDir.h13
-rw-r--r--DOC/7zip.wxs2
-rw-r--r--DOC/readme.txt2
-rw-r--r--DOC/src-history.txt10
14 files changed, 577 insertions, 282 deletions
diff --git a/C/7zVersion.h b/C/7zVersion.h
index 72733f7..b6142e9 100644
--- a/C/7zVersion.h
+++ b/C/7zVersion.h
@@ -1,7 +1,7 @@
1#define MY_VER_MAJOR 25 1#define MY_VER_MAJOR 25
2#define MY_VER_MINOR 0 2#define MY_VER_MINOR 1
3#define MY_VER_BUILD 0 3#define MY_VER_BUILD 0
4#define MY_VERSION_NUMBERS "25.00" 4#define MY_VERSION_NUMBERS "25.01"
5#define MY_VERSION MY_VERSION_NUMBERS 5#define MY_VERSION MY_VERSION_NUMBERS
6 6
7#ifdef MY_CPU_NAME 7#ifdef MY_CPU_NAME
@@ -10,7 +10,7 @@
10 #define MY_VERSION_CPU MY_VERSION 10 #define MY_VERSION_CPU MY_VERSION
11#endif 11#endif
12 12
13#define MY_DATE "2025-07-05" 13#define MY_DATE "2025-08-03"
14#undef MY_COPYRIGHT 14#undef MY_COPYRIGHT
15#undef MY_VERSION_COPYRIGHT_DATE 15#undef MY_VERSION_COPYRIGHT_DATE
16#define MY_AUTHOR_NAME "Igor Pavlov" 16#define MY_AUTHOR_NAME "Igor Pavlov"
diff --git a/C/LzFind.c b/C/LzFind.c
index 6aba919..330bc17 100644
--- a/C/LzFind.c
+++ b/C/LzFind.c
@@ -598,7 +598,7 @@ void MatchFinder_Init(void *_p)
598 598
599#ifdef MY_CPU_X86_OR_AMD64 599#ifdef MY_CPU_X86_OR_AMD64
600 #if defined(__clang__) && (__clang_major__ >= 4) \ 600 #if defined(__clang__) && (__clang_major__ >= 4) \
601 || defined(Z7_GCC_VERSION) && (Z7_GCC_VERSION >= 40701) 601 || defined(Z7_GCC_VERSION) && (Z7_GCC_VERSION >= 40900)
602 // || defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 1900) 602 // || defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 1900)
603 603
604 #define USE_LZFIND_SATUR_SUB_128 604 #define USE_LZFIND_SATUR_SUB_128
diff --git a/CPP/7zip/Bundles/Alone/Alone.dsp b/CPP/7zip/Bundles/Alone/Alone.dsp
index beed5a7..558c41e 100644
--- a/CPP/7zip/Bundles/Alone/Alone.dsp
+++ b/CPP/7zip/Bundles/Alone/Alone.dsp
@@ -1148,26 +1148,6 @@ SOURCE=..\..\Compress\PpmdZip.cpp
1148SOURCE=..\..\Compress\PpmdZip.h 1148SOURCE=..\..\Compress\PpmdZip.h
1149# End Source File 1149# End Source File
1150# End Group 1150# End Group
1151# Begin Group "RangeCoder"
1152
1153# PROP Default_Filter ""
1154# Begin Source File
1155
1156SOURCE=..\..\Compress\RangeCoder.h
1157# End Source File
1158# Begin Source File
1159
1160SOURCE=..\..\Compress\RangeCoderBit.h
1161# End Source File
1162# Begin Source File
1163
1164SOURCE=..\..\Compress\RangeCoderBitTree.h
1165# End Source File
1166# Begin Source File
1167
1168SOURCE=..\..\Compress\RangeCoderOpt.h
1169# End Source File
1170# End Group
1171# Begin Group "Shrink" 1151# Begin Group "Shrink"
1172 1152
1173# PROP Default_Filter "" 1153# PROP Default_Filter ""
diff --git a/CPP/7zip/Compress/BZip2Encoder.cpp b/CPP/7zip/Compress/BZip2Encoder.cpp
index f8ee0c9..af0b312 100644
--- a/CPP/7zip/Compress/BZip2Encoder.cpp
+++ b/CPP/7zip/Compress/BZip2Encoder.cpp
@@ -66,18 +66,14 @@ HRESULT CThreadInfo::Create()
66 if (wres == 0) { wres = CanWriteEvent.Create(); 66 if (wres == 0) { wres = CanWriteEvent.Create();
67 if (wres == 0) 67 if (wres == 0)
68 { 68 {
69 wres =
69#ifdef _WIN32 70#ifdef _WIN32
70 if (Encoder->_props.NumThreadGroups != 0) 71 Encoder->_props.NumThreadGroups > 1 ?
71 { 72 Thread.Create_With_Group(MFThread, this, ThreadNextGroup_GetNext(&Encoder->ThreadNextGroup), 0) : // affinity
72 const UInt32 group = ThreadNextGroup_GetNext(&Encoder->ThreadNextGroup);
73 wres = Thread.Create_With_Group(MFThread, this, group, 0); // affinity
74 }
75 else
76#endif 73#endif
77 if (Encoder->_props.Affinity != 0) 74 Encoder->_props.Affinity != 0 ?
78 wres = Thread.Create_With_Affinity(MFThread, this, (CAffinityMask)Encoder->_props.Affinity); 75 Thread.Create_With_Affinity(MFThread, this, (CAffinityMask)Encoder->_props.Affinity) :
79 else 76 Thread.Create(MFThread, this);
80 wres = Thread.Create(MFThread, this);
81 }}} 77 }}}
82 return HRESULT_FROM_WIN32(wres); 78 return HRESULT_FROM_WIN32(wres);
83} 79}
@@ -935,14 +931,13 @@ void CEncoder::WriteBytes(const Byte *data, UInt32 sizeInBits, unsigned lastByte
935HRESULT CEncoder::CodeReal(ISequentialInStream *inStream, ISequentialOutStream *outStream, 931HRESULT CEncoder::CodeReal(ISequentialInStream *inStream, ISequentialOutStream *outStream,
936 const UInt64 * /* inSize */, const UInt64 * /* outSize */, ICompressProgressInfo *progress) 932 const UInt64 * /* inSize */, const UInt64 * /* outSize */, ICompressProgressInfo *progress)
937{ 933{
938 ThreadNextGroup_Init(&ThreadNextGroup, _props.NumThreadGroups, 0); // startGroup
939
940 NumBlocks = 0; 934 NumBlocks = 0;
941 #ifndef Z7_ST 935#ifndef Z7_ST
942 Progress = progress; 936 Progress = progress;
937 ThreadNextGroup_Init(&ThreadNextGroup, _props.NumThreadGroups, 0); // startGroup
943 RINOK(Create()) 938 RINOK(Create())
944 for (UInt32 t = 0; t < NumThreads; t++) 939 for (UInt32 t = 0; t < NumThreads; t++)
945 #endif 940#endif
946 { 941 {
947 #ifndef Z7_ST 942 #ifndef Z7_ST
948 CThreadInfo &ti = ThreadsInfo[t]; 943 CThreadInfo &ti = ThreadsInfo[t];
diff --git a/CPP/7zip/UI/Agent/AgentProxy.cpp b/CPP/7zip/UI/Agent/AgentProxy.cpp
index 176f39b..d04ddab 100644
--- a/CPP/7zip/UI/Agent/AgentProxy.cpp
+++ b/CPP/7zip/UI/Agent/AgentProxy.cpp
@@ -636,7 +636,7 @@ HRESULT CProxyArc2::Load(const CArc &arc, IProgress *progress)
636 file.Name = (const wchar_t *)p; 636 file.Name = (const wchar_t *)p;
637 file.NameLen = 0; 637 file.NameLen = 0;
638 if (size >= sizeof(wchar_t)) 638 if (size >= sizeof(wchar_t))
639 file.NameLen = size / sizeof(wchar_t) - 1; 639 file.NameLen = size / (unsigned)sizeof(wchar_t) - 1;
640 } 640 }
641 else 641 else
642 #endif 642 #endif
diff --git a/CPP/7zip/UI/Client7z/makefile.gcc b/CPP/7zip/UI/Client7z/makefile.gcc
index fe27011..0f89cb0 100644
--- a/CPP/7zip/UI/Client7z/makefile.gcc
+++ b/CPP/7zip/UI/Client7z/makefile.gcc
@@ -24,7 +24,6 @@ else
24 24
25SYS_OBJS = \ 25SYS_OBJS = \
26 $O/MyWindows.o \ 26 $O/MyWindows.o \
27 $O/TimeUtils.o \
28 27
29endif 28endif
30 29
@@ -53,6 +52,7 @@ WIN_OBJS = \
53 $O/FileName.o \ 52 $O/FileName.o \
54 $O/PropVariant.o \ 53 $O/PropVariant.o \
55 $O/PropVariantConv.o \ 54 $O/PropVariantConv.o \
55 $O/TimeUtils.o \
56 56
577ZIP_COMMON_OBJS = \ 577ZIP_COMMON_OBJS = \
58 $O/FileStreams.o \ 58 $O/FileStreams.o \
diff --git a/CPP/7zip/UI/Common/ArchiveCommandLine.cpp b/CPP/7zip/UI/Common/ArchiveCommandLine.cpp
index de9f43e..7fe18fb 100644
--- a/CPP/7zip/UI/Common/ArchiveCommandLine.cpp
+++ b/CPP/7zip/UI/Common/ArchiveCommandLine.cpp
@@ -341,7 +341,7 @@ static const CSwitchForm kSwitchForms[] =
341 { "spf", SWFRM_STRING_SINGL(0) }, 341 { "spf", SWFRM_STRING_SINGL(0) },
342 342
343 { "snh", SWFRM_MINUS }, 343 { "snh", SWFRM_MINUS },
344 { "snld", SWFRM_MINUS }, 344 { "snld", SWFRM_STRING },
345 { "snl", SWFRM_MINUS }, 345 { "snl", SWFRM_MINUS },
346 { "sni", SWFRM_SIMPLE }, 346 { "sni", SWFRM_SIMPLE },
347 347
@@ -1479,14 +1479,8 @@ void CArcCmdLineParser::Parse2(CArcCmdLineOptions &options)
1479 1479
1480 SetBoolPair(parser, NKey::kStoreOwnerId, options.StoreOwnerId); 1480 SetBoolPair(parser, NKey::kStoreOwnerId, options.StoreOwnerId);
1481 SetBoolPair(parser, NKey::kStoreOwnerName, options.StoreOwnerName); 1481 SetBoolPair(parser, NKey::kStoreOwnerName, options.StoreOwnerName);
1482
1483 CBoolPair symLinks_AllowDangerous;
1484 SetBoolPair(parser, NKey::kSymLinks_AllowDangerous, symLinks_AllowDangerous);
1485
1486
1487 /* 1482 /*
1488 bool supportSymLink = options.SymLinks.Val; 1483 bool supportSymLink = options.SymLinks.Val;
1489
1490 if (!options.SymLinks.Def) 1484 if (!options.SymLinks.Def)
1491 { 1485 {
1492 if (isExtractOrList) 1486 if (isExtractOrList)
@@ -1494,7 +1488,6 @@ void CArcCmdLineParser::Parse2(CArcCmdLineOptions &options)
1494 else 1488 else
1495 supportSymLink = false; 1489 supportSymLink = false;
1496 } 1490 }
1497
1498 #ifdef ENV_HAVE_LSTAT 1491 #ifdef ENV_HAVE_LSTAT
1499 if (supportSymLink) 1492 if (supportSymLink)
1500 global_use_lstat = 1; 1493 global_use_lstat = 1;
@@ -1503,7 +1496,6 @@ void CArcCmdLineParser::Parse2(CArcCmdLineOptions &options)
1503 #endif 1496 #endif
1504 */ 1497 */
1505 1498
1506
1507 if (isExtractOrList) 1499 if (isExtractOrList)
1508 { 1500 {
1509 CExtractOptionsBase &eo = options.ExtractOptions; 1501 CExtractOptionsBase &eo = options.ExtractOptions;
@@ -1527,7 +1519,15 @@ void CArcCmdLineParser::Parse2(CArcCmdLineOptions &options)
1527 if (!options.SymLinks.Def) 1519 if (!options.SymLinks.Def)
1528 nt.SymLinks.Val = true; 1520 nt.SymLinks.Val = true;
1529 1521
1530 nt.SymLinks_AllowDangerous = symLinks_AllowDangerous; 1522 if (parser[NKey::kSymLinks_AllowDangerous].ThereIs)
1523 {
1524 const UString &s = parser[NKey::kSymLinks_AllowDangerous].PostStrings[0];
1525 UInt32 v = 9; // default value for "-snld" instead of default = 5 without "-snld".
1526 if (!s.IsEmpty())
1527 if (!StringToUInt32(s, v))
1528 throw CArcCmdLineException("Unsupported switch postfix -snld", s);
1529 nt.SymLinks_DangerousLevel = (unsigned)v;
1530 }
1531 1531
1532 nt.ReplaceColonForAltStream = parser[NKey::kReplaceColonForAltStream].ThereIs; 1532 nt.ReplaceColonForAltStream = parser[NKey::kReplaceColonForAltStream].ThereIs;
1533 nt.WriteToAltStreamIfColon = parser[NKey::kWriteToAltStreamIfColon].ThereIs; 1533 nt.WriteToAltStreamIfColon = parser[NKey::kWriteToAltStreamIfColon].ThereIs;
diff --git a/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp b/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp
index 3abcd2d..6631629 100644
--- a/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp
+++ b/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp
@@ -54,10 +54,14 @@ static const char * const kCantSetFileLen = "Cannot set length for output file";
54#ifdef SUPPORT_LINKS 54#ifdef SUPPORT_LINKS
55static const char * const kCantCreateHardLink = "Cannot create hard link"; 55static const char * const kCantCreateHardLink = "Cannot create hard link";
56static const char * const kCantCreateSymLink = "Cannot create symbolic link"; 56static const char * const kCantCreateSymLink = "Cannot create symbolic link";
57static const char * const k_HardLink_to_SymLink_Ignored = "Hard link to symbolic link was ignored";
58static const char * const k_CantDelete_File_for_SymLink = "Cannot delete file for symbolic link creation";
59static const char * const k_CantDelete_Dir_for_SymLink = "Cannot delete directory for symbolic link creation";
57#endif 60#endif
58 61
59static const unsigned k_LinkDataSize_LIMIT = 1 << 12; 62static const unsigned k_LinkDataSize_LIMIT = 1 << 12;
60 63
64#ifdef SUPPORT_LINKS
61#if WCHAR_PATH_SEPARATOR != L'/' 65#if WCHAR_PATH_SEPARATOR != L'/'
62 // we convert linux slashes to windows slashes for further processing. 66 // we convert linux slashes to windows slashes for further processing.
63 // also we convert linux backslashes to BackslashReplacement character. 67 // also we convert linux backslashes to BackslashReplacement character.
@@ -67,7 +71,7 @@ static const unsigned k_LinkDataSize_LIMIT = 1 << 12;
67#else 71#else
68 #define REPLACE_SLASHES_from_Linux_to_Sys(s) 72 #define REPLACE_SLASHES_from_Linux_to_Sys(s)
69#endif 73#endif
70 74#endif
71 75
72#ifndef Z7_SFX 76#ifndef Z7_SFX
73 77
@@ -326,13 +330,14 @@ void CArchiveExtractCallback::Init(
326 _outFileStream.Release(); 330 _outFileStream.Release();
327 _bufPtrSeqOutStream.Release(); 331 _bufPtrSeqOutStream.Release();
328 332
329 #ifdef SUPPORT_LINKS 333#ifdef SUPPORT_LINKS
330 _hardLinks.Clear(); 334 _hardLinks.Clear();
331 #endif 335 _postLinks.Clear();
336#endif
332 337
333 #ifdef SUPPORT_ALT_STREAMS 338#ifdef SUPPORT_ALT_STREAMS
334 _renamedFiles.Clear(); 339 _renamedFiles.Clear();
335 #endif 340#endif
336 341
337 _ntOptions = ntOptions; 342 _ntOptions = ntOptions;
338 _wildcardCensor = wildcardCensor; 343 _wildcardCensor = wildcardCensor;
@@ -455,7 +460,8 @@ Z7_COM7F_IMF(CArchiveExtractCallback::SetRatioInfo(const UInt64 *inSize, const U
455} 460}
456 461
457 462
458void CArchiveExtractCallback::CreateComplexDirectory(const UStringVector &dirPathParts, FString &fullPath) 463void CArchiveExtractCallback::CreateComplexDirectory(
464 const UStringVector &dirPathParts, bool isFinal, FString &fullPath)
459{ 465{
460 // we use (_item.IsDir) in this function 466 // we use (_item.IsDir) in this function
461 467
@@ -487,7 +493,7 @@ void CArchiveExtractCallback::CreateComplexDirectory(const UStringVector &dirPat
487 const UString &s = dirPathParts[i]; 493 const UString &s = dirPathParts[i];
488 fullPath += us2fs(s); 494 fullPath += us2fs(s);
489 495
490 const bool isFinalDir = (i == dirPathParts.Size() - 1 && _item.IsDir); 496 const bool isFinalDir = (i == dirPathParts.Size() - 1 && isFinal && _item.IsDir);
491 497
492 if (fullPath.IsEmpty()) 498 if (fullPath.IsEmpty())
493 { 499 {
@@ -548,7 +554,7 @@ static void AddPathToMessage(UString &s, const FString &path)
548 s += fs2us(path); 554 s += fs2us(path);
549} 555}
550 556
551HRESULT CArchiveExtractCallback::SendMessageError(const char *message, const FString &path) 557HRESULT CArchiveExtractCallback::SendMessageError(const char *message, const FString &path) const
552{ 558{
553 UString s (message); 559 UString s (message);
554 AddPathToMessage(s, path); 560 AddPathToMessage(s, path);
@@ -556,7 +562,7 @@ HRESULT CArchiveExtractCallback::SendMessageError(const char *message, const FSt
556} 562}
557 563
558 564
559HRESULT CArchiveExtractCallback::SendMessageError_with_Error(HRESULT errorCode, const char *message, const FString &path) 565HRESULT CArchiveExtractCallback::SendMessageError_with_Error(HRESULT errorCode, const char *message, const FString &path) const
560{ 566{
561 UString s (message); 567 UString s (message);
562 if (errorCode != S_OK) 568 if (errorCode != S_OK)
@@ -568,13 +574,13 @@ HRESULT CArchiveExtractCallback::SendMessageError_with_Error(HRESULT errorCode,
568 return _extractCallback2->MessageError(s); 574 return _extractCallback2->MessageError(s);
569} 575}
570 576
571HRESULT CArchiveExtractCallback::SendMessageError_with_LastError(const char *message, const FString &path) 577HRESULT CArchiveExtractCallback::SendMessageError_with_LastError(const char *message, const FString &path) const
572{ 578{
573 const HRESULT errorCode = GetLastError_noZero_HRESULT(); 579 const HRESULT errorCode = GetLastError_noZero_HRESULT();
574 return SendMessageError_with_Error(errorCode, message, path); 580 return SendMessageError_with_Error(errorCode, message, path);
575} 581}
576 582
577HRESULT CArchiveExtractCallback::SendMessageError2(HRESULT errorCode, const char *message, const FString &path1, const FString &path2) 583HRESULT CArchiveExtractCallback::SendMessageError2(HRESULT errorCode, const char *message, const FString &path1, const FString &path2) const
578{ 584{
579 UString s (message); 585 UString s (message);
580 if (errorCode != 0) 586 if (errorCode != 0)
@@ -588,7 +594,7 @@ HRESULT CArchiveExtractCallback::SendMessageError2(HRESULT errorCode, const char
588} 594}
589 595
590HRESULT CArchiveExtractCallback::SendMessageError2_with_LastError( 596HRESULT CArchiveExtractCallback::SendMessageError2_with_LastError(
591 const char *message, const FString &path1, const FString &path2) 597 const char *message, const FString &path1, const FString &path2) const
592{ 598{
593 const HRESULT errorCode = GetLastError_noZero_HRESULT(); 599 const HRESULT errorCode = GetLastError_noZero_HRESULT();
594 return SendMessageError2(errorCode, message, path1, path2); 600 return SendMessageError2(errorCode, message, path1, path2);
@@ -627,6 +633,7 @@ Z7_COM7F_IMF(CGetProp::GetProp(PROPID propID, PROPVARIANT *value))
627struct CLinkLevelsInfo 633struct CLinkLevelsInfo
628{ 634{
629 bool IsAbsolute; 635 bool IsAbsolute;
636 bool ParentDirDots_after_NonParent;
630 int LowLevel; 637 int LowLevel;
631 int FinalLevel; 638 int FinalLevel;
632 639
@@ -640,6 +647,8 @@ void CLinkLevelsInfo::Parse(const UString &path, bool isWSL)
640 NName::IsAbsolutePath(path); 647 NName::IsAbsolutePath(path);
641 LowLevel = 0; 648 LowLevel = 0;
642 FinalLevel = 0; 649 FinalLevel = 0;
650 ParentDirDots_after_NonParent = false;
651 bool nonParentDir = false;
643 652
644 UStringVector parts; 653 UStringVector parts;
645 SplitPathToParts(path, parts); 654 SplitPathToParts(path, parts);
@@ -658,12 +667,17 @@ void CLinkLevelsInfo::Parse(const UString &path, bool isWSL)
658 continue; 667 continue;
659 if (s.IsEqualTo("..")) 668 if (s.IsEqualTo(".."))
660 { 669 {
670 if (IsAbsolute || nonParentDir)
671 ParentDirDots_after_NonParent = true;
661 level--; 672 level--;
662 if (LowLevel > level) 673 if (LowLevel > level)
663 LowLevel = level; 674 LowLevel = level;
664 } 675 }
665 else 676 else
677 {
678 nonParentDir = true;
666 level++; 679 level++;
680 }
667 } 681 }
668 682
669 FinalLevel = level; 683 FinalLevel = level;
@@ -915,7 +929,7 @@ HRESULT CArchiveExtractCallback::ReadLink()
915#ifndef _WIN32 929#ifndef _WIN32
916 930
917static HRESULT GetOwner(IInArchive *archive, 931static HRESULT GetOwner(IInArchive *archive,
918 UInt32 index, UInt32 pidName, UInt32 pidId, COwnerInfo &res) 932 UInt32 index, UInt32 pidName, UInt32 pidId, CProcessedFileInfo::COwnerInfo &res)
919{ 933{
920 { 934 {
921 NWindows::NCOM::CPropVariant prop; 935 NWindows::NCOM::CPropVariant prop;
@@ -1047,7 +1061,7 @@ void CArchiveExtractCallback::CorrectPathParts()
1047} 1061}
1048 1062
1049 1063
1050void CArchiveExtractCallback::GetFiTimesCAM(CFiTimesCAM &pt) 1064static void GetFiTimesCAM(const CProcessedFileInfo &fi, CFiTimesCAM &pt, const CArc &arc)
1051{ 1065{
1052 pt.CTime_Defined = false; 1066 pt.CTime_Defined = false;
1053 pt.ATime_Defined = false; 1067 pt.ATime_Defined = false;
@@ -1055,27 +1069,27 @@ void CArchiveExtractCallback::GetFiTimesCAM(CFiTimesCAM &pt)
1055 1069
1056 // if (Write_MTime) 1070 // if (Write_MTime)
1057 { 1071 {
1058 if (_fi.MTime.Def) 1072 if (fi.MTime.Def)
1059 { 1073 {
1060 _fi.MTime.Write_To_FiTime(pt.MTime); 1074 fi.MTime.Write_To_FiTime(pt.MTime);
1061 pt.MTime_Defined = true; 1075 pt.MTime_Defined = true;
1062 } 1076 }
1063 else if (_arc->MTime.Def) 1077 else if (arc.MTime.Def)
1064 { 1078 {
1065 _arc->MTime.Write_To_FiTime(pt.MTime); 1079 arc.MTime.Write_To_FiTime(pt.MTime);
1066 pt.MTime_Defined = true; 1080 pt.MTime_Defined = true;
1067 } 1081 }
1068 } 1082 }
1069 1083
1070 if (/* Write_CTime && */ _fi.CTime.Def) 1084 if (/* Write_CTime && */ fi.CTime.Def)
1071 { 1085 {
1072 _fi.CTime.Write_To_FiTime(pt.CTime); 1086 fi.CTime.Write_To_FiTime(pt.CTime);
1073 pt.CTime_Defined = true; 1087 pt.CTime_Defined = true;
1074 } 1088 }
1075 1089
1076 if (/* Write_ATime && */ _fi.ATime.Def) 1090 if (/* Write_ATime && */ fi.ATime.Def)
1077 { 1091 {
1078 _fi.ATime.Write_To_FiTime(pt.ATime); 1092 fi.ATime.Write_To_FiTime(pt.ATime);
1079 pt.ATime_Defined = true; 1093 pt.ATime_Defined = true;
1080 } 1094 }
1081} 1095}
@@ -1086,6 +1100,7 @@ void CArchiveExtractCallback::CreateFolders()
1086 // 21.04 : we don't change original (_item.PathParts) here 1100 // 21.04 : we don't change original (_item.PathParts) here
1087 UStringVector pathParts = _item.PathParts; 1101 UStringVector pathParts = _item.PathParts;
1088 1102
1103 bool isFinal = true;
1089 // bool is_DirOp = false; 1104 // bool is_DirOp = false;
1090 if (!pathParts.IsEmpty()) 1105 if (!pathParts.IsEmpty())
1091 { 1106 {
@@ -1095,12 +1110,15 @@ void CArchiveExtractCallback::CreateFolders()
1095 but if we create dir item here, it's not problem. */ 1110 but if we create dir item here, it's not problem. */
1096 if (!_item.IsDir 1111 if (!_item.IsDir
1097 #ifdef SUPPORT_LINKS 1112 #ifdef SUPPORT_LINKS
1098 #ifndef WIN32 1113 // #ifndef WIN32
1099 || !_link.LinkPath.IsEmpty() 1114 || !_link.LinkPath.IsEmpty()
1100 #endif 1115 // #endif
1101 #endif 1116 #endif
1102 ) 1117 )
1118 {
1103 pathParts.DeleteBack(); 1119 pathParts.DeleteBack();
1120 isFinal = false; // last path part was excluded
1121 }
1104 // else is_DirOp = true; 1122 // else is_DirOp = true;
1105 } 1123 }
1106 1124
@@ -1124,7 +1142,7 @@ void CArchiveExtractCallback::CreateFolders()
1124 */ 1142 */
1125 1143
1126 FString fullPathNew; 1144 FString fullPathNew;
1127 CreateComplexDirectory(pathParts, fullPathNew); 1145 CreateComplexDirectory(pathParts, isFinal, fullPathNew);
1128 1146
1129 /* 1147 /*
1130 if (is_DirOp) 1148 if (is_DirOp)
@@ -1145,12 +1163,12 @@ void CArchiveExtractCallback::CreateFolders()
1145 return; 1163 return;
1146 1164
1147 CDirPathTime pt; 1165 CDirPathTime pt;
1148 GetFiTimesCAM(pt); 1166 GetFiTimesCAM(_fi, pt, *_arc);
1149 1167
1150 if (pt.IsSomeTimeDefined()) 1168 if (pt.IsSomeTimeDefined())
1151 { 1169 {
1152 pt.Path = fullPathNew; 1170 pt.Path = fullPathNew;
1153 pt.SetDirTime(); 1171 pt.SetDirTime_to_FS_2();
1154 _extractedFolders.Add(pt); 1172 _extractedFolders.Add(pt);
1155 } 1173 }
1156} 1174}
@@ -1292,9 +1310,11 @@ HRESULT CArchiveExtractCallback::CheckExistFile(FString &fullProcessedPath, bool
1292 1310
1293 1311
1294 1312
1295 1313/*
1296 1314return:
1297 1315 needExit = false: caller will use (outStreamLoc) and _hashStreamSpec
1316 needExit = true : caller will not use (outStreamLoc) and _hashStreamSpec.
1317*/
1298HRESULT CArchiveExtractCallback::GetExtractStream(CMyComPtr<ISequentialOutStream> &outStreamLoc, bool &needExit) 1318HRESULT CArchiveExtractCallback::GetExtractStream(CMyComPtr<ISequentialOutStream> &outStreamLoc, bool &needExit)
1299{ 1319{
1300 needExit = true; 1320 needExit = true;
@@ -1383,12 +1403,15 @@ HRESULT CArchiveExtractCallback::GetExtractStream(CMyComPtr<ISequentialOutStream
1383 { 1403 {
1384 bool linkWasSet = false; 1404 bool linkWasSet = false;
1385 RINOK(SetLink(fullProcessedPath, _link, linkWasSet)) 1405 RINOK(SetLink(fullProcessedPath, _link, linkWasSet))
1406/*
1407 // we don't set attributes for placeholder.
1386 if (linkWasSet) 1408 if (linkWasSet)
1387 { 1409 {
1388 _isSymLinkCreated = _link.Is_AnySymLink(); 1410 _isSymLinkCreated = _link.Is_AnySymLink();
1389 SetAttrib(); 1411 SetAttrib();
1390 // printf("\nlinkWasSet %s\n", GetAnsiString(_diskFilePath)); 1412 // printf("\nlinkWasSet %s\n", GetAnsiString(_diskFilePath));
1391 } 1413 }
1414*/
1392 } 1415 }
1393 #endif // UNDER_CE 1416 #endif // UNDER_CE
1394 1417
@@ -1414,11 +1437,17 @@ HRESULT CArchiveExtractCallback::GetExtractStream(CMyComPtr<ISequentialOutStream
1414 hl = fullProcessedPath; 1437 hl = fullProcessedPath;
1415 else 1438 else
1416 { 1439 {
1417 if (!MyCreateHardLink(fullProcessedPath, hl)) 1440 bool link_was_Created = false;
1418 return SendMessageError2_with_LastError(kCantCreateHardLink, fullProcessedPath, hl); 1441 RINOK(CreateHardLink2(fullProcessedPath, hl, link_was_Created))
1442 if (!link_was_Created)
1443 return S_OK;
1419 // printf("\nHard linkWasSet Archive_Get_HardLinkNode %s\n", GetAnsiString(_diskFilePath)); 1444 // printf("\nHard linkWasSet Archive_Get_HardLinkNode %s\n", GetAnsiString(_diskFilePath));
1420 // _needSetAttrib = true; // do we need to set attribute ? 1445 // _needSetAttrib = true; // do we need to set attribute ?
1421 SetAttrib(); 1446 SetAttrib();
1447 /* if we set (needExit = false) here, _hashStreamSpec will be used,
1448 and hash will be calulated for all hard links files (it's slower).
1449 But "Test" operation also calculates hashes.
1450 */
1422 needExit = false; 1451 needExit = false;
1423 return S_OK; 1452 return S_OK;
1424 } 1453 }
@@ -1943,7 +1972,7 @@ HRESULT CArchiveExtractCallback::CloseFile()
1943 #endif 1972 #endif
1944 1973
1945 CFiTimesCAM t; 1974 CFiTimesCAM t;
1946 GetFiTimesCAM(t); 1975 GetFiTimesCAM(_fi, t, *_arc);
1947 1976
1948 // #ifdef _WIN32 1977 // #ifdef _WIN32
1949 if (t.IsSomeTimeDefined()) 1978 if (t.IsSomeTimeDefined())
@@ -1970,52 +1999,219 @@ HRESULT CArchiveExtractCallback::CloseFile()
1970 1999
1971#ifdef SUPPORT_LINKS 2000#ifdef SUPPORT_LINKS
1972 2001
2002static bool CheckLinkPath_in_FS_for_pathParts(const FString &path, const UStringVector &v)
2003{
2004 FString path2 = path;
2005 FOR_VECTOR (i, v)
2006 {
2007 // if (i == v.Size() - 1) path = path2; // we don't need last part in returned path
2008 path2 += us2fs(v[i]);
2009 NFind::CFileInfo fi;
2010 // printf("\nCheckLinkPath_in_FS_for_pathParts(): %s\n", GetOemString(path2).Ptr());
2011 if (fi.Find(path2) && fi.IsOsSymLink())
2012 return false;
2013 path2.Add_PathSepar();
2014 }
2015 return true;
2016}
2017
1973/* 2018/*
1974in: 2019link.isRelative / relative_item_PathPrefix
1975 link.LinkPath : must be relative (non-absolute) path in any case !!! 2020 false / empty
1976 link.isRelative / target path that must stored as created link: 2021 true / item path without last part
1977 == false / _dirPathPrefix_Full + link.LinkPath
1978 == true / link.LinkPath
1979*/ 2022*/
2023static bool CheckLinkPath_in_FS(
2024 const FString &pathPrefix_in_FS,
2025 const CPostLink &postLink,
2026 const UString &relative_item_PathPrefix)
2027{
2028 const CLinkInfo &link = postLink.LinkInfo;
2029 if (postLink.item_PathParts.IsEmpty() || link.LinkPath.IsEmpty())
2030 return false;
2031 FString path;
2032 {
2033 const UString &s = postLink.item_PathParts[0];
2034 if (!s.IsEmpty() && !NName::IsAbsolutePath(s))
2035 path = pathPrefix_in_FS; // item_PathParts is relative. So we use absolutre prefix
2036 }
2037 if (!CheckLinkPath_in_FS_for_pathParts(path, postLink.item_PathParts))
2038 return false;
2039 path += us2fs(relative_item_PathPrefix);
2040 UStringVector v;
2041 SplitPathToParts(link.LinkPath, v);
2042 // we check target paths:
2043 return CheckLinkPath_in_FS_for_pathParts(path, v);
2044}
2045
2046static const unsigned k_DangLevel_MAX_for_Link_over_Link = 9;
2047
2048HRESULT CArchiveExtractCallback::CreateHardLink2(
2049 const FString &newFilePath, const FString &existFilePath, bool &link_was_Created) const
2050{
2051 link_was_Created = false;
2052 if (_ntOptions.SymLinks_DangerousLevel <= k_DangLevel_MAX_for_Link_over_Link)
2053 {
2054 NFind::CFileInfo fi;
2055 if (fi.Find(existFilePath) && fi.IsOsSymLink())
2056 return SendMessageError2(0, k_HardLink_to_SymLink_Ignored, newFilePath, existFilePath);
2057 }
2058 if (!MyCreateHardLink(newFilePath, existFilePath))
2059 return SendMessageError2_with_LastError(kCantCreateHardLink, newFilePath, existFilePath);
2060 link_was_Created = true;
2061 return S_OK;
2062}
2063
2064
1980 2065
1981HRESULT CArchiveExtractCallback::SetLink( 2066HRESULT CArchiveExtractCallback::SetLink(
1982 const FString &fullProcessedPath_from, 2067 const FString &fullProcessedPath_from,
1983 const CLinkInfo &link, 2068 const CLinkInfo &link,
1984 bool &linkWasSet) 2069 bool &linkWasSet) // placeholder was created
1985{ 2070{
1986 linkWasSet = false; 2071 linkWasSet = false;
1987 if (link.LinkPath.IsEmpty()) 2072 if (link.LinkPath.IsEmpty())
1988 return S_OK; 2073 return S_OK;
1989 if (!_ntOptions.SymLinks.Val && link.Is_AnySymLink()) 2074 if (!_ntOptions.SymLinks.Val && link.Is_AnySymLink())
1990 return S_OK; 2075 return S_OK;
2076 CPostLink postLink;
2077 postLink.Index_in_Arc = _index;
2078 postLink.item_IsDir = _item.IsDir;
2079 postLink.item_Path = _item.Path;
2080 postLink.item_PathParts = _item.PathParts;
2081 postLink.item_FileInfo = _fi;
2082 postLink.fullProcessedPath_from = fullProcessedPath_from;
2083 postLink.LinkInfo = link;
2084 _postLinks.Add(postLink);
2085
2086 // file doesn't exist in most cases. So we don't check for error.
2087 DeleteLinkFileAlways_or_RemoveEmptyDir(fullProcessedPath_from, false); // checkThatFileIsEmpty = false
2088
2089 NIO::COutFile outFile;
2090 if (!outFile.Create_NEW(fullProcessedPath_from))
2091 return SendMessageError("Cannot create temporary link file", fullProcessedPath_from);
2092#if 0 // 1 for debug
2093 // here we can write link path to temporary link file placeholder,
2094 // but empty placeholder is better, because we don't want to get any non-eampty data instead of link file.
2095 AString s;
2096 ConvertUnicodeToUTF8(link.LinkPath, s);
2097 outFile.WriteFull(s, s.Len());
2098#endif
2099 linkWasSet = true;
2100 return S_OK;
2101}
2102
2103
2104// if file/dir is symbolic link it will remove only link itself
2105HRESULT CArchiveExtractCallback::DeleteLinkFileAlways_or_RemoveEmptyDir(
2106 const FString &path, bool checkThatFileIsEmpty) const
2107{
2108 NFile::NFind::CFileInfo fi;
2109 if (fi.Find(path)) // followLink = false
1991 { 2110 {
1992 UString path; 2111 if (fi.IsDir())
1993 if (link.isRelative)
1994 { 2112 {
1995 // _item.PathParts : parts that will be created in output folder. 2113 if (RemoveDirAlways_if_Empty(path))
1996 // we want to get directory prefix of link item. 2114 return S_OK;
1997 // so we remove file name (last non-empty part) from PathParts:
1998 UStringVector v = _item.PathParts;
1999 while (!v.IsEmpty())
2000 {
2001 const unsigned len = v.Back().Len();
2002 v.DeleteBack();
2003 if (len)
2004 break;
2005 }
2006 path = MakePathFromParts(v);
2007 NName::NormalizeDirPathPrefix(path);
2008 } 2115 }
2009 path += link.LinkPath; 2116 else
2117 {
2118 // link file placeholder must be empty
2119 if (checkThatFileIsEmpty && !fi.IsOsSymLink() && fi.Size != 0)
2120 return SendMessageError("Temporary link file is not empty", path);
2121 if (DeleteFileAlways(path))
2122 return S_OK;
2123 }
2124 if (GetLastError() != ERROR_FILE_NOT_FOUND)
2125 return SendMessageError_with_LastError(
2126 fi.IsDir() ?
2127 k_CantDelete_Dir_for_SymLink:
2128 k_CantDelete_File_for_SymLink,
2129 path);
2130 }
2131 return S_OK;
2132}
2133
2134
2135/*
2136in:
2137 link.LinkPath : must be relative (non-absolute) path in any case !!!
2138 link.isRelative / target path that must stored as created link:
2139 == false / _dirPathPrefix_Full + link.LinkPath
2140 == true / link.LinkPath
2141*/
2142static HRESULT SetLink2(const CArchiveExtractCallback &callback,
2143 const CPostLink &postLink, bool &linkWasSet)
2144{
2145 const CLinkInfo &link = postLink.LinkInfo;
2146 const FString &fullProcessedPath_from = postLink.fullProcessedPath_from; // full file path in FS (fullProcessedPath_from)
2147
2148 const unsigned level = callback._ntOptions.SymLinks_DangerousLevel;
2149 if (level < 20)
2150 {
2010 /* 2151 /*
2011 path is calculated virtual target path of link 2152 We want to use additional check for links that can link to directory.
2012 path is relative to root folder of extracted items 2153 - linux: all symbolic links are files.
2013 if (!link.isRelative), then (path == link.LinkPath) 2154 - windows: we can have file/directory symbolic link,
2155 but file symbolic link works like directory link in windows.
2156 So we use additional check for all relative links.
2157
2158 We don't allow decreasing of final level of link.
2159 So if some another extracted file will use this link,
2160 then number of real path parts (after link redirection) cannot be
2161 smaller than number of requested path parts from archive records.
2162
2163 here we check only (link.LinkPath) without (_item.PathParts).
2014 */ 2164 */
2015 if (!IsSafePath(path, link.Is_WSL())) 2165 CLinkLevelsInfo li;
2016 return SendMessageError2(0, // errorCode 2166 li.Parse(link.LinkPath, link.Is_WSL());
2017 "Dangerous link path was ignored", 2167 bool isDang;
2018 us2fs(_item.Path), us2fs(link.LinkPath)); 2168 UString relativePathPrefix;
2169 if (li.IsAbsolute // unexpected
2170 || li.ParentDirDots_after_NonParent
2171 || (level <= 5 && link.isRelative && li.FinalLevel < 1) // final level lower
2172 || (level <= 5 && link.isRelative && li.LowLevel < 0) // negative temporary levels
2173 )
2174 isDang = true;
2175 else // if (!isDang)
2176 {
2177 UString path;
2178 if (link.isRelative)
2179 {
2180 // item_PathParts : parts that will be created in output folder.
2181 // we want to get directory prefix of link item.
2182 // so we remove file name (last non-empty part) from PathParts:
2183 UStringVector v = postLink.item_PathParts;
2184 while (!v.IsEmpty())
2185 {
2186 const unsigned len = v.Back().Len();
2187 v.DeleteBack();
2188 if (len)
2189 break;
2190 }
2191 path = MakePathFromParts(v);
2192 NName::NormalizeDirPathPrefix(path);
2193 relativePathPrefix = path;
2194 }
2195 path += link.LinkPath;
2196 /*
2197 path is calculated virtual target path of link
2198 path is relative to root folder of extracted items
2199 if (!link.isRelative), then (path == link.LinkPath)
2200 */
2201 isDang = false;
2202 if (!IsSafePath(path, link.Is_WSL()))
2203 isDang = true;
2204 }
2205 const char *message = NULL;
2206 if (isDang)
2207 message = "Dangerous link path was ignored";
2208 else if (level <= k_DangLevel_MAX_for_Link_over_Link
2209 && !CheckLinkPath_in_FS(callback._dirPathPrefix_Full,
2210 postLink, relativePathPrefix))
2211 message = "Dangerous link via another link was ignored";
2212 if (message)
2213 return callback.SendMessageError2(0, // errorCode
2214 message, us2fs(postLink.item_Path), us2fs(link.LinkPath));
2019 } 2215 }
2020 2216
2021 FString target; // target path that will be stored to link field 2217 FString target; // target path that will be stored to link field
@@ -2025,8 +2221,8 @@ HRESULT CArchiveExtractCallback::SetLink(
2025 // all hard links and absolute symbolic links 2221 // all hard links and absolute symbolic links
2026 // relatPath == link.LinkPath 2222 // relatPath == link.LinkPath
2027 // we get absolute link path for target: 2223 // we get absolute link path for target:
2028 if (!NName::GetFullPath(_dirPathPrefix_Full, us2fs(link.LinkPath), target)) 2224 if (!NName::GetFullPath(callback._dirPathPrefix_Full, us2fs(link.LinkPath), target))
2029 return SendMessageError("Incorrect link path", us2fs(link.LinkPath)); 2225 return callback.SendMessageError("Incorrect link path", us2fs(link.LinkPath));
2030 // (target) is (_dirPathPrefix_Full + relatPath) 2226 // (target) is (_dirPathPrefix_Full + relatPath)
2031 } 2227 }
2032 else 2228 else
@@ -2036,21 +2232,24 @@ HRESULT CArchiveExtractCallback::SetLink(
2036 target = us2fs(link.LinkPath); 2232 target = us2fs(link.LinkPath);
2037 } 2233 }
2038 if (target.IsEmpty()) 2234 if (target.IsEmpty())
2039 return SendMessageError("Empty link", fullProcessedPath_from); 2235 return callback.SendMessageError("Empty link", fullProcessedPath_from);
2040 2236
2041 if (link.Is_HardLink() /* || link.IsCopyLink */) 2237 if (link.Is_HardLink() /* || link.IsCopyLink */)
2042 { 2238 {
2043 // if (link.isHardLink) 2239 // if (link.isHardLink)
2044 { 2240 {
2045 if (!MyCreateHardLink(fullProcessedPath_from, target)) 2241 RINOK(callback.DeleteLinkFileAlways_or_RemoveEmptyDir(fullProcessedPath_from, true)) // checkThatFileIsEmpty
2046 return SendMessageError2_with_LastError(kCantCreateHardLink, fullProcessedPath_from, target); 2242 {
2243 // RINOK(SendMessageError_with_LastError(k_Cant_DeleteTempLinkFile, fullProcessedPath_from))
2244 }
2245 return callback.CreateHardLink2(fullProcessedPath_from, target, linkWasSet);
2047 /* 2246 /*
2048 RINOK(PrepareOperation(NArchive::NExtract::NAskMode::kExtract)) 2247 RINOK(PrepareOperation(NArchive::NExtract::NAskMode::kExtract))
2049 _op_WasReported = true; 2248 _op_WasReported = true;
2050 RINOK(SetOperationResult(NArchive::NExtract::NOperationResult::kOK)) 2249 RINOK(SetOperationResult(NArchive::NExtract::NOperationResult::kOK))
2051 */
2052 linkWasSet = true; 2250 linkWasSet = true;
2053 return S_OK; 2251 return S_OK;
2252 */
2054 } 2253 }
2055 /* 2254 /*
2056 // IsCopyLink 2255 // IsCopyLink
@@ -2086,36 +2285,10 @@ HRESULT CArchiveExtractCallback::SetLink(
2086 */ 2285 */
2087 2286
2088#ifdef _WIN32 2287#ifdef _WIN32
2089 const bool isDir = (_item.IsDir || link.LinkType == k_LinkType_Junction); 2288 const bool isDir = (postLink.item_IsDir || link.LinkType == k_LinkType_Junction);
2090#endif 2289#endif
2091 2290
2092 if (!_ntOptions.SymLinks_AllowDangerous.Val && link.isRelative) 2291
2093 {
2094 /*
2095 We want to use additional check for links that can link to directory.
2096 - linux: all symbolic links are files.
2097 - windows: we can have file/directory symbolic link,
2098 but file symbolic link works like directory link in windows.
2099 So we use additional check for all relative links.
2100
2101 We don't allow decreasing of final level of link.
2102 So if some another extracted file will use this link,
2103 then number of real path parts (after link redirection) cannot be
2104 smaller than number of requested path parts from archive records.
2105
2106 Now we check only (link.LinkPath) without (_item.PathParts).
2107 */
2108 CLinkLevelsInfo levelsInfo;
2109 levelsInfo.Parse(link.LinkPath, link.Is_WSL());
2110 if (levelsInfo.FinalLevel < 1
2111 // || levelsInfo.LowLevel < 0 // we allow negative temporary levels
2112 || levelsInfo.IsAbsolute)
2113 return SendMessageError2(0, // errorCode
2114 "Dangerous symbolic link path was ignored",
2115 us2fs(_item.Path), us2fs(link.LinkPath));
2116 }
2117
2118
2119#ifdef _WIN32 2292#ifdef _WIN32
2120 CByteBuffer data; 2293 CByteBuffer data;
2121 // printf("\nFillLinkData(): %s\n", GetOemString(target).Ptr()); 2294 // printf("\nFillLinkData(): %s\n", GetOemString(target).Ptr());
@@ -2127,7 +2300,7 @@ HRESULT CArchiveExtractCallback::SetLink(
2127 else 2300 else
2128 FillLinkData_WinLink(data, fs2us(target), link.LinkType != k_LinkType_Junction); 2301 FillLinkData_WinLink(data, fs2us(target), link.LinkType != k_LinkType_Junction);
2129 if (data.Size() == 0) 2302 if (data.Size() == 0)
2130 return SendMessageError("Cannot fill link data", us2fs(_item.Path)); 2303 return callback.SendMessageError("Cannot fill link data", us2fs(postLink.item_Path));
2131 /* 2304 /*
2132 if (NtReparse_Size != data.Size() || memcmp(NtReparse_Data, data, data.Size()) != 0) 2305 if (NtReparse_Size != data.Size() || memcmp(NtReparse_Data, data, data.Size()) != 0)
2133 SendMessageError("reconstructed Reparse is different", fs2us(target)); 2306 SendMessageError("reconstructed Reparse is different", fs2us(target));
@@ -2136,14 +2309,18 @@ HRESULT CArchiveExtractCallback::SetLink(
2136 // we check that reparse data is correct, but we ignore attr.MinorError. 2309 // we check that reparse data is correct, but we ignore attr.MinorError.
2137 CReparseAttr attr; 2310 CReparseAttr attr;
2138 if (!attr.Parse(data, data.Size())) 2311 if (!attr.Parse(data, data.Size()))
2139 return SendMessageError("Internal error for symbolic link file", us2fs(_item.Path)); 2312 return callback.SendMessageError("Internal error for symbolic link file", us2fs(postLink.item_Path));
2140 } 2313 }
2314#endif
2315
2316 RINOK(callback.DeleteLinkFileAlways_or_RemoveEmptyDir(fullProcessedPath_from, true)) // checkThatFileIsEmpty
2317#ifdef _WIN32
2141 if (!NFile::NIO::SetReparseData(fullProcessedPath_from, isDir, data, (DWORD)data.Size())) 2318 if (!NFile::NIO::SetReparseData(fullProcessedPath_from, isDir, data, (DWORD)data.Size()))
2142#else // ! _WIN32 2319#else // ! _WIN32
2143 if (!NFile::NIO::SetSymLink(fullProcessedPath_from, target)) 2320 if (!NFile::NIO::SetSymLink(fullProcessedPath_from, target))
2144#endif // ! _WIN32 2321#endif // ! _WIN32
2145 { 2322 {
2146 return SendMessageError_with_LastError(kCantCreateSymLink, fullProcessedPath_from); 2323 return callback.SendMessageError_with_LastError(kCantCreateSymLink, fullProcessedPath_from);
2147 } 2324 }
2148 linkWasSet = true; 2325 linkWasSet = true;
2149 return S_OK; 2326 return S_OK;
@@ -2392,6 +2569,7 @@ HRESULT CArchiveExtractCallback::CloseReparseAndFile()
2392 _curSize_Defined = true; 2569 _curSize_Defined = true;
2393 if (needSetReparse) 2570 if (needSetReparse)
2394 { 2571 {
2572 // empty file was created so we must delete it.
2395 // in Linux : we must delete empty file before symbolic link creation 2573 // in Linux : we must delete empty file before symbolic link creation
2396 // in Windows : we can create symbolic link even without file deleting 2574 // in Windows : we can create symbolic link even without file deleting
2397 if (!DeleteFileAlways(_diskFilePath)) 2575 if (!DeleteFileAlways(_diskFilePath))
@@ -2404,9 +2582,12 @@ HRESULT CArchiveExtractCallback::CloseReparseAndFile()
2404 // link.isJunction = true; // for debug 2582 // link.isJunction = true; // for debug
2405 link.Normalize_to_RelativeSafe(_removePathParts); 2583 link.Normalize_to_RelativeSafe(_removePathParts);
2406 RINOK(SetLink(_diskFilePath, link, linkWasSet)) 2584 RINOK(SetLink(_diskFilePath, link, linkWasSet))
2585/*
2586 // we don't set attributes for placeholder.
2407 if (linkWasSet) 2587 if (linkWasSet)
2408 _isSymLinkCreated = true; // link.IsSymLink(); 2588 _isSymLinkCreated = true; // link.IsSymLink();
2409 else 2589 else
2590*/
2410 _needSetAttrib = false; 2591 _needSetAttrib = false;
2411 } 2592 }
2412 } 2593 }
@@ -2416,13 +2597,37 @@ HRESULT CArchiveExtractCallback::CloseReparseAndFile()
2416} 2597}
2417 2598
2418 2599
2419void CArchiveExtractCallback::SetAttrib() 2600static void SetAttrib_Base(const FString &path, const CProcessedFileInfo &fi,
2601 const CArchiveExtractCallback &callback)
2420{ 2602{
2421 #ifndef _WIN32 2603#ifndef _WIN32
2604 if (fi.Owner.Id_Defined &&
2605 fi.Group.Id_Defined)
2606 {
2607 if (my_chown(path, fi.Owner.Id, fi.Group.Id) != 0)
2608 callback.SendMessageError_with_LastError("Cannot set owner", path);
2609 }
2610#endif
2611
2612 if (fi.Attrib_Defined)
2613 {
2614 // const AString s = GetAnsiString(_diskFilePath);
2615 // printf("\nSetFileAttrib_PosixHighDetect: %s: hex:%x\n", s.Ptr(), _fi.Attrib);
2616 if (!SetFileAttrib_PosixHighDetect(path, fi.Attrib))
2617 {
2618 // do we need error message here in Windows and in posix?
2619 callback.SendMessageError_with_LastError("Cannot set file attribute", path);
2620 }
2621 }
2622}
2623
2624void CArchiveExtractCallback::SetAttrib() const
2625{
2626#ifndef _WIN32
2422 // Linux now doesn't support permissions for symlinks 2627 // Linux now doesn't support permissions for symlinks
2423 if (_isSymLinkCreated) 2628 if (_isSymLinkCreated)
2424 return; 2629 return;
2425 #endif 2630#endif
2426 2631
2427 if (_itemFailure 2632 if (_itemFailure
2428 || _diskFilePath.IsEmpty() 2633 || _diskFilePath.IsEmpty()
@@ -2430,29 +2635,39 @@ void CArchiveExtractCallback::SetAttrib()
2430 || !_extractMode) 2635 || !_extractMode)
2431 return; 2636 return;
2432 2637
2433 #ifndef _WIN32 2638 SetAttrib_Base(_diskFilePath, _fi, *this);
2434 if (_fi.Owner.Id_Defined && 2639}
2435 _fi.Group.Id_Defined) 2640
2436 {
2437 if (my_chown(_diskFilePath, _fi.Owner.Id, _fi.Group.Id) != 0)
2438 {
2439 SendMessageError_with_LastError("Cannot set owner", _diskFilePath);
2440 }
2441 }
2442 #endif
2443 2641
2444 if (_fi.Attrib_Defined) 2642#ifdef Z7_USE_SECURITY_CODE
2643HRESULT CArchiveExtractCallback::SetSecurityInfo(UInt32 indexInArc, const FString &path) const
2644{
2645 if (!_stdOutMode && _extractMode && _ntOptions.NtSecurity.Val && _arc->GetRawProps)
2445 { 2646 {
2446 // const AString s = GetAnsiString(_diskFilePath); 2647 const void *data;
2447 // printf("\nSetFileAttrib_PosixHighDetect: %s: hex:%x\n", s.Ptr(), _fi.Attrib); 2648 UInt32 dataSize;
2448 bool res = SetFileAttrib_PosixHighDetect(_diskFilePath, _fi.Attrib); 2649 UInt32 propType;
2449 if (!res) 2650 _arc->GetRawProps->GetRawProp(indexInArc, kpidNtSecure, &data, &dataSize, &propType);
2651 if (dataSize != 0)
2450 { 2652 {
2451 // do we need error message here in Windows and in posix? 2653 if (propType != NPropDataType::kRaw)
2452 SendMessageError_with_LastError("Cannot set file attribute", _diskFilePath); 2654 return E_FAIL;
2655 if (CheckNtSecure((const Byte *)data, dataSize))
2656 {
2657 SECURITY_INFORMATION securInfo = DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION;
2658 if (_saclEnabled)
2659 securInfo |= SACL_SECURITY_INFORMATION;
2660 // if (!
2661 ::SetFileSecurityW(fs2us(path), securInfo, (PSECURITY_DESCRIPTOR)(void *)(const Byte *)(data));
2662 {
2663 // RINOK(SendMessageError_with_LastError("SetFileSecurity FAILS", path))
2664 }
2665 }
2453 } 2666 }
2454 } 2667 }
2668 return S_OK;
2455} 2669}
2670#endif // Z7_USE_SECURITY_CODE
2456 2671
2457 2672
2458Z7_COM7F_IMF(CArchiveExtractCallback::SetOperationResult(Int32 opRes)) 2673Z7_COM7F_IMF(CArchiveExtractCallback::SetOperationResult(Int32 opRes))
@@ -2490,27 +2705,9 @@ Z7_COM7F_IMF(CArchiveExtractCallback::SetOperationResult(Int32 opRes))
2490 2705
2491 RINOK(CloseReparseAndFile()) 2706 RINOK(CloseReparseAndFile())
2492 2707
2493 #ifdef Z7_USE_SECURITY_CODE 2708#ifdef Z7_USE_SECURITY_CODE
2494 if (!_stdOutMode && _extractMode && _ntOptions.NtSecurity.Val && _arc->GetRawProps) 2709 RINOK(SetSecurityInfo(_index, _diskFilePath))
2495 { 2710#endif
2496 const void *data;
2497 UInt32 dataSize;
2498 UInt32 propType;
2499 _arc->GetRawProps->GetRawProp(_index, kpidNtSecure, &data, &dataSize, &propType);
2500 if (dataSize != 0)
2501 {
2502 if (propType != NPropDataType::kRaw)
2503 return E_FAIL;
2504 if (CheckNtSecure((const Byte *)data, dataSize))
2505 {
2506 SECURITY_INFORMATION securInfo = DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION;
2507 if (_saclEnabled)
2508 securInfo |= SACL_SECURITY_INFORMATION;
2509 ::SetFileSecurityW(fs2us(_diskFilePath), securInfo, (PSECURITY_DESCRIPTOR)(void *)(const Byte *)(data));
2510 }
2511 }
2512 }
2513 #endif // Z7_USE_SECURITY_CODE
2514 2711
2515 if (!_curSize_Defined) 2712 if (!_curSize_Defined)
2516 GetUnpackSize(); 2713 GetUnpackSize();
@@ -2754,15 +2951,58 @@ void CDirPathSortPair::SetNumSlashes(const FChar *s)
2754} 2951}
2755 2952
2756 2953
2757bool CDirPathTime::SetDirTime() const 2954bool CFiTimesCAM::SetDirTime_to_FS(CFSTR path) const
2758{ 2955{
2759 return NDir::SetDirTime(Path, 2956 // it's same function for dir and for file
2957 return NDir::SetDirTime(path,
2760 CTime_Defined ? &CTime : NULL, 2958 CTime_Defined ? &CTime : NULL,
2761 ATime_Defined ? &ATime : NULL, 2959 ATime_Defined ? &ATime : NULL,
2762 MTime_Defined ? &MTime : NULL); 2960 MTime_Defined ? &MTime : NULL);
2763} 2961}
2764 2962
2765 2963
2964#ifdef SUPPORT_LINKS
2965
2966bool CFiTimesCAM::SetLinkFileTime_to_FS(CFSTR path) const
2967{
2968 // it's same function for dir and for file
2969 return NDir::SetLinkFileTime(path,
2970 CTime_Defined ? &CTime : NULL,
2971 ATime_Defined ? &ATime : NULL,
2972 MTime_Defined ? &MTime : NULL);
2973}
2974
2975HRESULT CArchiveExtractCallback::SetPostLinks() const
2976{
2977 FOR_VECTOR (i, _postLinks)
2978 {
2979 const CPostLink &link = _postLinks[i];
2980 bool linkWasSet = false;
2981 RINOK(SetLink2(*this, link, linkWasSet))
2982 if (linkWasSet)
2983 {
2984#ifdef _WIN32
2985 // Linux now doesn't support permissions for symlinks
2986 SetAttrib_Base(link.fullProcessedPath_from, link.item_FileInfo, *this);
2987#endif
2988
2989 CFiTimesCAM pt;
2990 GetFiTimesCAM(link.item_FileInfo, pt, *_arc);
2991 if (pt.IsSomeTimeDefined())
2992 pt.SetLinkFileTime_to_FS(link.fullProcessedPath_from);
2993
2994#ifdef Z7_USE_SECURITY_CODE
2995 // we set security information after timestamps setting
2996 RINOK(SetSecurityInfo(link.Index_in_Arc, link.fullProcessedPath_from))
2997#endif
2998 }
2999 }
3000 return S_OK;
3001}
3002
3003#endif
3004
3005
2766HRESULT CArchiveExtractCallback::SetDirsTimes() 3006HRESULT CArchiveExtractCallback::SetDirsTimes()
2767{ 3007{
2768 if (!_arc) 3008 if (!_arc)
@@ -2786,7 +3026,7 @@ HRESULT CArchiveExtractCallback::SetDirsTimes()
2786 for (i = 0; i < pairs.Size(); i++) 3026 for (i = 0; i < pairs.Size(); i++)
2787 { 3027 {
2788 const CDirPathTime &dpt = _extractedFolders[pairs[i].Index]; 3028 const CDirPathTime &dpt = _extractedFolders[pairs[i].Index];
2789 if (!dpt.SetDirTime()) 3029 if (!dpt.SetDirTime_to_FS_2())
2790 { 3030 {
2791 // result = E_FAIL; 3031 // result = E_FAIL;
2792 // do we need error message here in Windows and in posix? 3032 // do we need error message here in Windows and in posix?
@@ -2818,10 +3058,20 @@ HRESULT CArchiveExtractCallback::SetDirsTimes()
2818 3058
2819HRESULT CArchiveExtractCallback::CloseArc() 3059HRESULT CArchiveExtractCallback::CloseArc()
2820{ 3060{
3061 // we call CloseReparseAndFile() here because we can have non-closed file in some cases?
2821 HRESULT res = CloseReparseAndFile(); 3062 HRESULT res = CloseReparseAndFile();
2822 const HRESULT res2 = SetDirsTimes(); 3063#ifdef SUPPORT_LINKS
2823 if (res == S_OK) 3064 {
2824 res = res2; 3065 const HRESULT res2 = SetPostLinks();
3066 if (res == S_OK)
3067 res = res2;
3068 }
3069#endif
3070 {
3071 const HRESULT res2 = SetDirsTimes();
3072 if (res == S_OK)
3073 res = res2;
3074 }
2825 _arc = NULL; 3075 _arc = NULL;
2826 return res; 3076 return res;
2827} 3077}
diff --git a/CPP/7zip/UI/Common/ArchiveExtractCallback.h b/CPP/7zip/UI/Common/ArchiveExtractCallback.h
index 71fa3ef..3c62763 100644
--- a/CPP/7zip/UI/Common/ArchiveExtractCallback.h
+++ b/CPP/7zip/UI/Common/ArchiveExtractCallback.h
@@ -52,7 +52,6 @@ struct CExtractNtOptions
52{ 52{
53 CBoolPair NtSecurity; 53 CBoolPair NtSecurity;
54 CBoolPair SymLinks; 54 CBoolPair SymLinks;
55 CBoolPair SymLinks_AllowDangerous;
56 CBoolPair HardLinks; 55 CBoolPair HardLinks;
57 CBoolPair AltStreams; 56 CBoolPair AltStreams;
58 bool ReplaceColonForAltStream; 57 bool ReplaceColonForAltStream;
@@ -66,6 +65,8 @@ struct CExtractNtOptions
66 bool PreserveATime; 65 bool PreserveATime;
67 bool OpenShareForWrite; 66 bool OpenShareForWrite;
68 67
68 unsigned SymLinks_DangerousLevel;
69
69 UInt64 MemLimit; 70 UInt64 MemLimit;
70 71
71 CExtractNtOptions(): 72 CExtractNtOptions():
@@ -74,10 +75,10 @@ struct CExtractNtOptions
74 ExtractOwner(false), 75 ExtractOwner(false),
75 PreserveATime(false), 76 PreserveATime(false),
76 OpenShareForWrite(false), 77 OpenShareForWrite(false),
78 SymLinks_DangerousLevel(5),
77 MemLimit((UInt64)(Int64)-1) 79 MemLimit((UInt64)(Int64)-1)
78 { 80 {
79 SymLinks.Val = true; 81 SymLinks.Val = true;
80 SymLinks_AllowDangerous.Val = false;
81 HardLinks.Val = true; 82 HardLinks.Val = true;
82 AltStreams.Val = true; 83 AltStreams.Val = true;
83 84
@@ -166,19 +167,22 @@ struct CFiTimesCAM
166 ATime_Defined | 167 ATime_Defined |
167 MTime_Defined; 168 MTime_Defined;
168 } 169 }
170 bool SetDirTime_to_FS(CFSTR path) const;
171#ifdef SUPPORT_LINKS
172 bool SetLinkFileTime_to_FS(CFSTR path) const;
173#endif
169}; 174};
170 175
171struct CDirPathTime: public CFiTimesCAM 176struct CDirPathTime: public CFiTimesCAM
172{ 177{
173 FString Path; 178 FString Path;
174 179
175 bool SetDirTime() const; 180 bool SetDirTime_to_FS_2() const { return SetDirTime_to_FS(Path); }
176}; 181};
177 182
178 183
179#ifdef SUPPORT_LINKS 184#ifdef SUPPORT_LINKS
180 185
181
182enum ELinkType 186enum ELinkType
183{ 187{
184 k_LinkType_HardLink, 188 k_LinkType_HardLink,
@@ -227,6 +231,15 @@ private:
227#endif // SUPPORT_LINKS 231#endif // SUPPORT_LINKS
228 232
229 233
234
235struct CProcessedFileInfo
236{
237 CArcTime CTime;
238 CArcTime ATime;
239 CArcTime MTime;
240 UInt32 Attrib;
241 bool Attrib_Defined;
242
230#ifndef _WIN32 243#ifndef _WIN32
231 244
232struct COwnerInfo 245struct COwnerInfo
@@ -243,8 +256,76 @@ struct COwnerInfo
243 } 256 }
244}; 257};
245 258
259 COwnerInfo Owner;
260 COwnerInfo Group;
246#endif 261#endif
247 262
263 void Clear()
264 {
265#ifndef _WIN32
266 Attrib_Defined = false;
267 Owner.Clear();
268#endif
269 }
270
271 bool IsReparse() const
272 {
273 return (Attrib_Defined && (Attrib & FILE_ATTRIBUTE_REPARSE_POINT) != 0);
274 }
275
276 bool IsLinuxSymLink() const
277 {
278 return (Attrib_Defined && MY_LIN_S_ISLNK(Attrib >> 16));
279 }
280
281 void SetFromPosixAttrib(UInt32 a)
282 {
283 // here we set only part of combined attribute required by SetFileAttrib() call
284 #ifdef _WIN32
285 // Windows sets FILE_ATTRIBUTE_NORMAL, if we try to set 0 as attribute.
286 Attrib = MY_LIN_S_ISDIR(a) ?
287 FILE_ATTRIBUTE_DIRECTORY :
288 FILE_ATTRIBUTE_ARCHIVE;
289 if ((a & 0222) == 0) // (& S_IWUSR) in p7zip
290 Attrib |= FILE_ATTRIBUTE_READONLY;
291 // 22.00 : we need type bits for (MY_LIN_S_IFLNK) for IsLinuxSymLink()
292 a &= MY_LIN_S_IFMT;
293 if (a == MY_LIN_S_IFLNK)
294 Attrib |= (a << 16);
295 #else
296 Attrib = (a << 16) | FILE_ATTRIBUTE_UNIX_EXTENSION;
297 #endif
298 Attrib_Defined = true;
299 }
300};
301
302
303#ifdef SUPPORT_LINKS
304
305struct CPostLink
306{
307 UInt32 Index_in_Arc;
308 bool item_IsDir; // _item.IsDir
309 UString item_Path; // _item.Path;
310 UStringVector item_PathParts; // _item.PathParts;
311 CProcessedFileInfo item_FileInfo; // _fi
312 FString fullProcessedPath_from; // full file path in FS
313 CLinkInfo LinkInfo;
314};
315
316/*
317struct CPostLinks
318{
319 void Clear()
320 {
321 Links.Clear();
322 }
323};
324*/
325
326#endif // SUPPORT_LINKS
327
328
248 329
249class CArchiveExtractCallback Z7_final: 330class CArchiveExtractCallback Z7_final:
250 public IArchiveExtractCallback, 331 public IArchiveExtractCallback,
@@ -292,8 +373,9 @@ public:
292private: 373private:
293 374
294 const CArc *_arc; 375 const CArc *_arc;
376public:
295 CExtractNtOptions _ntOptions; 377 CExtractNtOptions _ntOptions;
296 378private:
297 bool _encrypted; 379 bool _encrypted;
298 bool _isSplit; 380 bool _isSplit;
299 bool _curSize_Defined; 381 bool _curSize_Defined;
@@ -325,7 +407,9 @@ private:
325 CMyComPtr<ICryptoGetTextPassword> _cryptoGetTextPassword; 407 CMyComPtr<ICryptoGetTextPassword> _cryptoGetTextPassword;
326 408
327 FString _dirPathPrefix; 409 FString _dirPathPrefix;
410public:
328 FString _dirPathPrefix_Full; 411 FString _dirPathPrefix_Full;
412private:
329 413
330 #ifndef Z7_SFX 414 #ifndef Z7_SFX
331 415
@@ -337,49 +421,7 @@ private:
337 CReadArcItem _item; 421 CReadArcItem _item;
338 FString _diskFilePath; 422 FString _diskFilePath;
339 423
340 struct CProcessedFileInfo 424 CProcessedFileInfo _fi;
341 {
342 CArcTime CTime;
343 CArcTime ATime;
344 CArcTime MTime;
345 UInt32 Attrib;
346 bool Attrib_Defined;
347
348 #ifndef _WIN32
349 COwnerInfo Owner;
350 COwnerInfo Group;
351 #endif
352
353 bool IsReparse() const
354 {
355 return (Attrib_Defined && (Attrib & FILE_ATTRIBUTE_REPARSE_POINT) != 0);
356 }
357
358 bool IsLinuxSymLink() const
359 {
360 return (Attrib_Defined && MY_LIN_S_ISLNK(Attrib >> 16));
361 }
362
363 void SetFromPosixAttrib(UInt32 a)
364 {
365 // here we set only part of combined attribute required by SetFileAttrib() call
366 #ifdef _WIN32
367 // Windows sets FILE_ATTRIBUTE_NORMAL, if we try to set 0 as attribute.
368 Attrib = MY_LIN_S_ISDIR(a) ?
369 FILE_ATTRIBUTE_DIRECTORY :
370 FILE_ATTRIBUTE_ARCHIVE;
371 if ((a & 0222) == 0) // (& S_IWUSR) in p7zip
372 Attrib |= FILE_ATTRIBUTE_READONLY;
373 // 22.00 : we need type bits for (MY_LIN_S_IFLNK) for IsLinuxSymLink()
374 a &= MY_LIN_S_IFMT;
375 if (a == MY_LIN_S_IFLNK)
376 Attrib |= (a << 16);
377 #else
378 Attrib = (a << 16) | FILE_ATTRIBUTE_UNIX_EXTENSION;
379 #endif
380 Attrib_Defined = true;
381 }
382 } _fi;
383 425
384 UInt64 _position; 426 UInt64 _position;
385 UInt64 _curSize; 427 UInt64 _curSize;
@@ -421,20 +463,21 @@ private:
421 // CObjectVector<NWindows::NFile::NDir::CDelayedSymLink> _delayedSymLinks; 463 // CObjectVector<NWindows::NFile::NDir::CDelayedSymLink> _delayedSymLinks;
422 #endif 464 #endif
423 465
424 void CreateComplexDirectory(const UStringVector &dirPathParts, FString &fullPath); 466 void CreateComplexDirectory(
467 const UStringVector &dirPathParts, bool isFinal, FString &fullPath);
425 HRESULT GetTime(UInt32 index, PROPID propID, CArcTime &ft); 468 HRESULT GetTime(UInt32 index, PROPID propID, CArcTime &ft);
426 HRESULT GetUnpackSize(); 469 HRESULT GetUnpackSize();
427 470
428 FString Hash_GetFullFilePath(); 471 FString Hash_GetFullFilePath();
429 472
430 void SetAttrib(); 473 void SetAttrib() const;
431 474
432public: 475public:
433 HRESULT SendMessageError(const char *message, const FString &path); 476 HRESULT SendMessageError(const char *message, const FString &path) const;
434 HRESULT SendMessageError_with_Error(HRESULT errorCode, const char *message, const FString &path); 477 HRESULT SendMessageError_with_Error(HRESULT errorCode, const char *message, const FString &path) const;
435 HRESULT SendMessageError_with_LastError(const char *message, const FString &path); 478 HRESULT SendMessageError_with_LastError(const char *message, const FString &path) const;
436 HRESULT SendMessageError2(HRESULT errorCode, const char *message, const FString &path1, const FString &path2); 479 HRESULT SendMessageError2(HRESULT errorCode, const char *message, const FString &path1, const FString &path2) const;
437 HRESULT SendMessageError2_with_LastError(const char *message, const FString &path1, const FString &path2); 480 HRESULT SendMessageError2_with_LastError(const char *message, const FString &path1, const FString &path2) const;
438 481
439#if defined(_WIN32) && !defined(UNDER_CE) && !defined(Z7_SFX) 482#if defined(_WIN32) && !defined(UNDER_CE) && !defined(Z7_SFX)
440 NExtract::NZoneIdMode::EEnum ZoneMode; 483 NExtract::NZoneIdMode::EEnum ZoneMode;
@@ -497,10 +540,11 @@ public:
497 UInt64 packSize); 540 UInt64 packSize);
498 541
499 542
500 #ifdef SUPPORT_LINKS 543#ifdef SUPPORT_LINKS
501 544
502private: 545private:
503 CHardLinks _hardLinks; 546 CHardLinks _hardLinks;
547 CObjectVector<CPostLink> _postLinks;
504 CLinkInfo _link; 548 CLinkInfo _link;
505 // const void *NtReparse_Data; 549 // const void *NtReparse_Data;
506 // UInt32 NtReparse_Size; 550 // UInt32 NtReparse_Size;
@@ -512,13 +556,16 @@ private:
512 const FString &fullProcessedPath_from, 556 const FString &fullProcessedPath_from,
513 const CLinkInfo &linkInfo, 557 const CLinkInfo &linkInfo,
514 bool &linkWasSet); 558 bool &linkWasSet);
559 HRESULT SetPostLinks() const;
515 560
516public: 561public:
517 // call PrepareHardLinks() after Init() 562 HRESULT CreateHardLink2(const FString &newFilePath,
563 const FString &existFilePath, bool &link_was_Created) const;
564 HRESULT DeleteLinkFileAlways_or_RemoveEmptyDir(const FString &path, bool checkThatFileIsEmpty) const;
518 HRESULT PrepareHardLinks(const CRecordVector<UInt32> *realIndices); // NULL means all items 565 HRESULT PrepareHardLinks(const CRecordVector<UInt32> *realIndices); // NULL means all items
566#endif
519 567
520 #endif 568private:
521
522 569
523 #ifdef SUPPORT_ALT_STREAMS 570 #ifdef SUPPORT_ALT_STREAMS
524 CObjectVector<CIndexToPathPair> _renamedFiles; 571 CObjectVector<CIndexToPathPair> _renamedFiles;
@@ -526,6 +573,7 @@ public:
526 573
527 // call it after Init() 574 // call it after Init()
528 575
576public:
529 #ifndef Z7_SFX 577 #ifndef Z7_SFX
530 void SetBaseParentFolderIndex(UInt32 indexInArc) 578 void SetBaseParentFolderIndex(UInt32 indexInArc)
531 { 579 {
@@ -547,7 +595,6 @@ private:
547 595
548 HRESULT Read_fi_Props(); 596 HRESULT Read_fi_Props();
549 void CorrectPathParts(); 597 void CorrectPathParts();
550 void GetFiTimesCAM(CFiTimesCAM &pt);
551 void CreateFolders(); 598 void CreateFolders();
552 599
553 HRESULT CheckExistFile(FString &fullProcessedPath, bool &needExit); 600 HRESULT CheckExistFile(FString &fullProcessedPath, bool &needExit);
@@ -556,8 +603,8 @@ private:
556 603
557 HRESULT CloseFile(); 604 HRESULT CloseFile();
558 HRESULT CloseReparseAndFile(); 605 HRESULT CloseReparseAndFile();
559 HRESULT CloseReparseAndFile2();
560 HRESULT SetDirsTimes(); 606 HRESULT SetDirsTimes();
607 HRESULT SetSecurityInfo(UInt32 indexInArc, const FString &path) const;
561}; 608};
562 609
563 610
diff --git a/CPP/Windows/FileDir.cpp b/CPP/Windows/FileDir.cpp
index 10c4e98..ad0d8c9 100644
--- a/CPP/Windows/FileDir.cpp
+++ b/CPP/Windows/FileDir.cpp
@@ -124,7 +124,7 @@ bool GetSystemDir(FString &path)
124#endif // UNDER_CE 124#endif // UNDER_CE
125 125
126 126
127bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime) 127static bool SetFileTime_Base(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime, DWORD dwFlagsAndAttributes)
128{ 128{
129 #ifndef _UNICODE 129 #ifndef _UNICODE
130 if (!g_IsNT) 130 if (!g_IsNT)
@@ -137,14 +137,14 @@ bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CF
137 HANDLE hDir = INVALID_HANDLE_VALUE; 137 HANDLE hDir = INVALID_HANDLE_VALUE;
138 IF_USE_MAIN_PATH 138 IF_USE_MAIN_PATH
139 hDir = ::CreateFileW(fs2us(path), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 139 hDir = ::CreateFileW(fs2us(path), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
140 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 140 NULL, OPEN_EXISTING, dwFlagsAndAttributes, NULL);
141 #ifdef Z7_LONG_PATH 141 #ifdef Z7_LONG_PATH
142 if (hDir == INVALID_HANDLE_VALUE && USE_SUPER_PATH) 142 if (hDir == INVALID_HANDLE_VALUE && USE_SUPER_PATH)
143 { 143 {
144 UString superPath; 144 UString superPath;
145 if (GetSuperPath(path, superPath, USE_MAIN_PATH)) 145 if (GetSuperPath(path, superPath, USE_MAIN_PATH))
146 hDir = ::CreateFileW(superPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 146 hDir = ::CreateFileW(superPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
147 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 147 NULL, OPEN_EXISTING, dwFlagsAndAttributes, NULL);
148 } 148 }
149 #endif 149 #endif
150 150
@@ -157,6 +157,15 @@ bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CF
157 return res; 157 return res;
158} 158}
159 159
160bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime)
161{
162 return SetFileTime_Base(path, cTime, aTime, mTime, FILE_FLAG_BACKUP_SEMANTICS);
163}
164
165bool SetLinkFileTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime)
166{
167 return SetFileTime_Base(path, cTime, aTime, mTime, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT);
168}
160 169
161 170
162bool SetFileAttrib(CFSTR path, DWORD attrib) 171bool SetFileAttrib(CFSTR path, DWORD attrib)
@@ -1173,17 +1182,15 @@ bool GetCurrentDir(FString &path)
1173 1182
1174 1183
1175 1184
1176bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime) 1185static bool SetFileTime_Base(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime, const int flags)
1177{ 1186{
1178 // need testing 1187 // need testing
1179 /* 1188 /*
1180 struct utimbuf buf; 1189 struct utimbuf buf;
1181 struct stat st; 1190 struct stat st;
1182 UNUSED_VAR(cTime) 1191 UNUSED_VAR(cTime)
1183
1184 printf("\nstat = %s\n", path); 1192 printf("\nstat = %s\n", path);
1185 int ret = stat(path, &st); 1193 int ret = stat(path, &st);
1186
1187 if (ret == 0) 1194 if (ret == 0)
1188 { 1195 {
1189 buf.actime = st.st_atime; 1196 buf.actime = st.st_atime;
@@ -1195,47 +1202,42 @@ bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CF
1195 buf.actime = cur_time; 1202 buf.actime = cur_time;
1196 buf.modtime = cur_time; 1203 buf.modtime = cur_time;
1197 } 1204 }
1198
1199 if (aTime) 1205 if (aTime)
1200 { 1206 {
1201 UInt32 ut; 1207 UInt32 ut;
1202 if (NTime::FileTimeToUnixTime(*aTime, ut)) 1208 if (NTime::FileTimeToUnixTime(*aTime, ut))
1203 buf.actime = ut; 1209 buf.actime = ut;
1204 } 1210 }
1205
1206 if (mTime) 1211 if (mTime)
1207 { 1212 {
1208 UInt32 ut; 1213 UInt32 ut;
1209 if (NTime::FileTimeToUnixTime(*mTime, ut)) 1214 if (NTime::FileTimeToUnixTime(*mTime, ut))
1210 buf.modtime = ut; 1215 buf.modtime = ut;
1211 } 1216 }
1212
1213 return utime(path, &buf) == 0; 1217 return utime(path, &buf) == 0;
1214 */ 1218 */
1215 1219
1216 // if (!aTime && !mTime) return true; 1220 // if (!aTime && !mTime) return true;
1217
1218 struct timespec times[2]; 1221 struct timespec times[2];
1219 UNUSED_VAR(cTime) 1222 UNUSED_VAR(cTime)
1220
1221 bool needChange; 1223 bool needChange;
1222 needChange = FiTime_To_timespec(aTime, times[0]); 1224 needChange = FiTime_To_timespec(aTime, times[0]);
1223 needChange |= FiTime_To_timespec(mTime, times[1]); 1225 needChange |= FiTime_To_timespec(mTime, times[1]);
1224 1226 // if (mTime) { printf("\n time = %ld.%9ld\n", mTime->tv_sec, mTime->tv_nsec); }
1225 /*
1226 if (mTime)
1227 {
1228 printf("\n time = %ld.%9ld\n", mTime->tv_sec, mTime->tv_nsec);
1229 }
1230 */
1231
1232 if (!needChange) 1227 if (!needChange)
1233 return true; 1228 return true;
1234 const int flags = 0; // follow link
1235 // = AT_SYMLINK_NOFOLLOW; // don't follow link
1236 return utimensat(AT_FDCWD, path, times, flags) == 0; 1229 return utimensat(AT_FDCWD, path, times, flags) == 0;
1237} 1230}
1238 1231
1232bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime)
1233{
1234 return SetFileTime_Base(path, cTime, aTime, mTime, 0); // (flags = 0) means follow_link
1235}
1236
1237bool SetLinkFileTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime)
1238{
1239 return SetFileTime_Base(path, cTime, aTime, mTime, AT_SYMLINK_NOFOLLOW);
1240}
1239 1241
1240 1242
1241struct C_umask 1243struct C_umask
diff --git a/CPP/Windows/FileDir.h b/CPP/Windows/FileDir.h
index 65e6368..9ba98fc 100644
--- a/CPP/Windows/FileDir.h
+++ b/CPP/Windows/FileDir.h
@@ -18,9 +18,20 @@ bool GetSystemDir(FString &path);
18WIN32 API : SetFileTime() doesn't allow to set zero timestamps in file 18WIN32 API : SetFileTime() doesn't allow to set zero timestamps in file
19but linux : allows unix time = 0 in filesystem 19but linux : allows unix time = 0 in filesystem
20*/ 20*/
21 21/*
22SetDirTime() can be used to set time for file or for dir.
23If path is symbolic link, SetDirTime() will follow symbolic link,
24and it will set timestamps of symbolic link's target file or dir.
25*/
22bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime); 26bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime);
23 27
28/*
29SetLinkFileTime() doesn't follow symbolic link,
30and it sets timestamps for symbolic link file itself.
31If (path) is not symbolic link, it still can work (at least in some new OS versions).
32*/
33bool SetLinkFileTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime);
34
24 35
25#ifdef _WIN32 36#ifdef _WIN32
26 37
diff --git a/DOC/7zip.wxs b/DOC/7zip.wxs
index d369074..703e22e 100644
--- a/DOC/7zip.wxs
+++ b/DOC/7zip.wxs
@@ -1,7 +1,7 @@
1<?xml version="1.0"?> 1<?xml version="1.0"?>
2 2
3<?define VerMajor = "25" ?> 3<?define VerMajor = "25" ?>
4<?define VerMinor = "00" ?> 4<?define VerMinor = "01" ?>
5<?define VerBuild = "00" ?> 5<?define VerBuild = "00" ?>
6<?define MmVer = "$(var.VerMajor).$(var.VerMinor)" ?> 6<?define MmVer = "$(var.VerMajor).$(var.VerMinor)" ?>
7<?define MmHex = "$(var.VerMajor)$(var.VerMinor)" ?> 7<?define MmHex = "$(var.VerMajor)$(var.VerMinor)" ?>
diff --git a/DOC/readme.txt b/DOC/readme.txt
index 7fbbdc8..cc89a39 100644
--- a/DOC/readme.txt
+++ b/DOC/readme.txt
@@ -1,4 +1,4 @@
17-Zip 25.00 Sources 17-Zip 25.01 Sources
2------------------- 2-------------------
3 3
47-Zip is a file archiver for Windows. 47-Zip is a file archiver for Windows.
diff --git a/DOC/src-history.txt b/DOC/src-history.txt
index 70b11b5..48c9647 100644
--- a/DOC/src-history.txt
+++ b/DOC/src-history.txt
@@ -1,6 +1,14 @@
1HISTORY of the 7-Zip source code 1HISTORY of the 7-Zip source code
2-------------------------------- 2--------------------------------
3 3
425.01 2025-08-03
5-------------------------
6- The code for handling symbolic links has been changed
7 to provide greater security when extracting files from archives.
8 Command line switch -snld20 can be used to bypass default security
9 checks when creating symbolic links.
10
11
425.00 2025-07-05 1225.00 2025-07-05
5------------------------- 13-------------------------
6- 7-Zip for Windows can now use more than 64 CPU threads for compression 14- 7-Zip for Windows can now use more than 64 CPU threads for compression
@@ -11,6 +19,8 @@ HISTORY of the 7-Zip source code
11- deflate (zip/gz) compression speed was increased by 1-3%. 19- deflate (zip/gz) compression speed was increased by 1-3%.
12- improved support for zip, cpio and fat archives. 20- improved support for zip, cpio and fat archives.
13- fixed some bugs and vulnerabilities. 21- fixed some bugs and vulnerabilities.
22- the bug was fixed : CVE-2025-53816 : 7-Zip could work incorrectly for some incorrect RAR archives.
23- the bug was fixed : CVE-2025-53817 : 7-Zip could crash for some incorrect COM (Compound File) archives.
14 24
15 25
1624.09 2024-11-29 2624.09 2024-11-29