mirror of
https://github.com/ip7z/7zip.git
synced 2025-12-06 07:12:00 +01:00
Compare commits
2 commits
de687fa10d
...
14b9eb8dbc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14b9eb8dbc | ||
|
|
5e96a82794 |
|
|
@ -1,7 +1,7 @@
|
|||
#define MY_VER_MAJOR 25
|
||||
#define MY_VER_MINOR 0
|
||||
#define MY_VER_MINOR 1
|
||||
#define MY_VER_BUILD 0
|
||||
#define MY_VERSION_NUMBERS "25.00"
|
||||
#define MY_VERSION_NUMBERS "25.01"
|
||||
#define MY_VERSION MY_VERSION_NUMBERS
|
||||
|
||||
#ifdef MY_CPU_NAME
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
#define MY_VERSION_CPU MY_VERSION
|
||||
#endif
|
||||
|
||||
#define MY_DATE "2025-07-05"
|
||||
#define MY_DATE "2025-08-03"
|
||||
#undef MY_COPYRIGHT
|
||||
#undef MY_VERSION_COPYRIGHT_DATE
|
||||
#define MY_AUTHOR_NAME "Igor Pavlov"
|
||||
|
|
|
|||
|
|
@ -598,7 +598,7 @@ void MatchFinder_Init(void *_p)
|
|||
|
||||
#ifdef MY_CPU_X86_OR_AMD64
|
||||
#if defined(__clang__) && (__clang_major__ >= 4) \
|
||||
|| defined(Z7_GCC_VERSION) && (Z7_GCC_VERSION >= 40701)
|
||||
|| defined(Z7_GCC_VERSION) && (Z7_GCC_VERSION >= 40900)
|
||||
// || defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 1900)
|
||||
|
||||
#define USE_LZFIND_SATUR_SUB_128
|
||||
|
|
|
|||
|
|
@ -1148,26 +1148,6 @@ SOURCE=..\..\Compress\PpmdZip.cpp
|
|||
SOURCE=..\..\Compress\PpmdZip.h
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "RangeCoder"
|
||||
|
||||
# PROP Default_Filter ""
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\Compress\RangeCoder.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\Compress\RangeCoderBit.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\Compress\RangeCoderBitTree.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\Compress\RangeCoderOpt.h
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Shrink"
|
||||
|
||||
# PROP Default_Filter ""
|
||||
|
|
|
|||
|
|
@ -66,18 +66,14 @@ HRESULT CThreadInfo::Create()
|
|||
if (wres == 0) { wres = CanWriteEvent.Create();
|
||||
if (wres == 0)
|
||||
{
|
||||
wres =
|
||||
#ifdef _WIN32
|
||||
if (Encoder->_props.NumThreadGroups != 0)
|
||||
{
|
||||
const UInt32 group = ThreadNextGroup_GetNext(&Encoder->ThreadNextGroup);
|
||||
wres = Thread.Create_With_Group(MFThread, this, group, 0); // affinity
|
||||
}
|
||||
else
|
||||
Encoder->_props.NumThreadGroups > 1 ?
|
||||
Thread.Create_With_Group(MFThread, this, ThreadNextGroup_GetNext(&Encoder->ThreadNextGroup), 0) : // affinity
|
||||
#endif
|
||||
if (Encoder->_props.Affinity != 0)
|
||||
wres = Thread.Create_With_Affinity(MFThread, this, (CAffinityMask)Encoder->_props.Affinity);
|
||||
else
|
||||
wres = Thread.Create(MFThread, this);
|
||||
Encoder->_props.Affinity != 0 ?
|
||||
Thread.Create_With_Affinity(MFThread, this, (CAffinityMask)Encoder->_props.Affinity) :
|
||||
Thread.Create(MFThread, this);
|
||||
}}}
|
||||
return HRESULT_FROM_WIN32(wres);
|
||||
}
|
||||
|
|
@ -935,11 +931,10 @@ void CEncoder::WriteBytes(const Byte *data, UInt32 sizeInBits, unsigned lastByte
|
|||
HRESULT CEncoder::CodeReal(ISequentialInStream *inStream, ISequentialOutStream *outStream,
|
||||
const UInt64 * /* inSize */, const UInt64 * /* outSize */, ICompressProgressInfo *progress)
|
||||
{
|
||||
ThreadNextGroup_Init(&ThreadNextGroup, _props.NumThreadGroups, 0); // startGroup
|
||||
|
||||
NumBlocks = 0;
|
||||
#ifndef Z7_ST
|
||||
Progress = progress;
|
||||
ThreadNextGroup_Init(&ThreadNextGroup, _props.NumThreadGroups, 0); // startGroup
|
||||
RINOK(Create())
|
||||
for (UInt32 t = 0; t < NumThreads; t++)
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -636,7 +636,7 @@ HRESULT CProxyArc2::Load(const CArc &arc, IProgress *progress)
|
|||
file.Name = (const wchar_t *)p;
|
||||
file.NameLen = 0;
|
||||
if (size >= sizeof(wchar_t))
|
||||
file.NameLen = size / sizeof(wchar_t) - 1;
|
||||
file.NameLen = size / (unsigned)sizeof(wchar_t) - 1;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ else
|
|||
|
||||
SYS_OBJS = \
|
||||
$O/MyWindows.o \
|
||||
$O/TimeUtils.o \
|
||||
|
||||
endif
|
||||
|
||||
|
|
@ -53,6 +52,7 @@ WIN_OBJS = \
|
|||
$O/FileName.o \
|
||||
$O/PropVariant.o \
|
||||
$O/PropVariantConv.o \
|
||||
$O/TimeUtils.o \
|
||||
|
||||
7ZIP_COMMON_OBJS = \
|
||||
$O/FileStreams.o \
|
||||
|
|
|
|||
|
|
@ -341,7 +341,7 @@ static const CSwitchForm kSwitchForms[] =
|
|||
{ "spf", SWFRM_STRING_SINGL(0) },
|
||||
|
||||
{ "snh", SWFRM_MINUS },
|
||||
{ "snld", SWFRM_MINUS },
|
||||
{ "snld", SWFRM_STRING },
|
||||
{ "snl", SWFRM_MINUS },
|
||||
{ "sni", SWFRM_SIMPLE },
|
||||
|
||||
|
|
@ -1479,14 +1479,8 @@ void CArcCmdLineParser::Parse2(CArcCmdLineOptions &options)
|
|||
|
||||
SetBoolPair(parser, NKey::kStoreOwnerId, options.StoreOwnerId);
|
||||
SetBoolPair(parser, NKey::kStoreOwnerName, options.StoreOwnerName);
|
||||
|
||||
CBoolPair symLinks_AllowDangerous;
|
||||
SetBoolPair(parser, NKey::kSymLinks_AllowDangerous, symLinks_AllowDangerous);
|
||||
|
||||
|
||||
/*
|
||||
bool supportSymLink = options.SymLinks.Val;
|
||||
|
||||
if (!options.SymLinks.Def)
|
||||
{
|
||||
if (isExtractOrList)
|
||||
|
|
@ -1494,7 +1488,6 @@ void CArcCmdLineParser::Parse2(CArcCmdLineOptions &options)
|
|||
else
|
||||
supportSymLink = false;
|
||||
}
|
||||
|
||||
#ifdef ENV_HAVE_LSTAT
|
||||
if (supportSymLink)
|
||||
global_use_lstat = 1;
|
||||
|
|
@ -1503,7 +1496,6 @@ void CArcCmdLineParser::Parse2(CArcCmdLineOptions &options)
|
|||
#endif
|
||||
*/
|
||||
|
||||
|
||||
if (isExtractOrList)
|
||||
{
|
||||
CExtractOptionsBase &eo = options.ExtractOptions;
|
||||
|
|
@ -1527,7 +1519,15 @@ void CArcCmdLineParser::Parse2(CArcCmdLineOptions &options)
|
|||
if (!options.SymLinks.Def)
|
||||
nt.SymLinks.Val = true;
|
||||
|
||||
nt.SymLinks_AllowDangerous = symLinks_AllowDangerous;
|
||||
if (parser[NKey::kSymLinks_AllowDangerous].ThereIs)
|
||||
{
|
||||
const UString &s = parser[NKey::kSymLinks_AllowDangerous].PostStrings[0];
|
||||
UInt32 v = 9; // default value for "-snld" instead of default = 5 without "-snld".
|
||||
if (!s.IsEmpty())
|
||||
if (!StringToUInt32(s, v))
|
||||
throw CArcCmdLineException("Unsupported switch postfix -snld", s);
|
||||
nt.SymLinks_DangerousLevel = (unsigned)v;
|
||||
}
|
||||
|
||||
nt.ReplaceColonForAltStream = parser[NKey::kReplaceColonForAltStream].ThereIs;
|
||||
nt.WriteToAltStreamIfColon = parser[NKey::kWriteToAltStreamIfColon].ThereIs;
|
||||
|
|
|
|||
|
|
@ -54,10 +54,14 @@ static const char * const kCantSetFileLen = "Cannot set length for output file";
|
|||
#ifdef SUPPORT_LINKS
|
||||
static const char * const kCantCreateHardLink = "Cannot create hard link";
|
||||
static const char * const kCantCreateSymLink = "Cannot create symbolic link";
|
||||
static const char * const k_HardLink_to_SymLink_Ignored = "Hard link to symbolic link was ignored";
|
||||
static const char * const k_CantDelete_File_for_SymLink = "Cannot delete file for symbolic link creation";
|
||||
static const char * const k_CantDelete_Dir_for_SymLink = "Cannot delete directory for symbolic link creation";
|
||||
#endif
|
||||
|
||||
static const unsigned k_LinkDataSize_LIMIT = 1 << 12;
|
||||
|
||||
#ifdef SUPPORT_LINKS
|
||||
#if WCHAR_PATH_SEPARATOR != L'/'
|
||||
// we convert linux slashes to windows slashes for further processing.
|
||||
// also we convert linux backslashes to BackslashReplacement character.
|
||||
|
|
@ -67,7 +71,7 @@ static const unsigned k_LinkDataSize_LIMIT = 1 << 12;
|
|||
#else
|
||||
#define REPLACE_SLASHES_from_Linux_to_Sys(s)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef Z7_SFX
|
||||
|
||||
|
|
@ -328,6 +332,7 @@ void CArchiveExtractCallback::Init(
|
|||
|
||||
#ifdef SUPPORT_LINKS
|
||||
_hardLinks.Clear();
|
||||
_postLinks.Clear();
|
||||
#endif
|
||||
|
||||
#ifdef SUPPORT_ALT_STREAMS
|
||||
|
|
@ -455,7 +460,8 @@ Z7_COM7F_IMF(CArchiveExtractCallback::SetRatioInfo(const UInt64 *inSize, const U
|
|||
}
|
||||
|
||||
|
||||
void CArchiveExtractCallback::CreateComplexDirectory(const UStringVector &dirPathParts, FString &fullPath)
|
||||
void CArchiveExtractCallback::CreateComplexDirectory(
|
||||
const UStringVector &dirPathParts, bool isFinal, FString &fullPath)
|
||||
{
|
||||
// we use (_item.IsDir) in this function
|
||||
|
||||
|
|
@ -487,7 +493,7 @@ void CArchiveExtractCallback::CreateComplexDirectory(const UStringVector &dirPat
|
|||
const UString &s = dirPathParts[i];
|
||||
fullPath += us2fs(s);
|
||||
|
||||
const bool isFinalDir = (i == dirPathParts.Size() - 1 && _item.IsDir);
|
||||
const bool isFinalDir = (i == dirPathParts.Size() - 1 && isFinal && _item.IsDir);
|
||||
|
||||
if (fullPath.IsEmpty())
|
||||
{
|
||||
|
|
@ -548,7 +554,7 @@ static void AddPathToMessage(UString &s, const FString &path)
|
|||
s += fs2us(path);
|
||||
}
|
||||
|
||||
HRESULT CArchiveExtractCallback::SendMessageError(const char *message, const FString &path)
|
||||
HRESULT CArchiveExtractCallback::SendMessageError(const char *message, const FString &path) const
|
||||
{
|
||||
UString s (message);
|
||||
AddPathToMessage(s, path);
|
||||
|
|
@ -556,7 +562,7 @@ HRESULT CArchiveExtractCallback::SendMessageError(const char *message, const FSt
|
|||
}
|
||||
|
||||
|
||||
HRESULT CArchiveExtractCallback::SendMessageError_with_Error(HRESULT errorCode, const char *message, const FString &path)
|
||||
HRESULT CArchiveExtractCallback::SendMessageError_with_Error(HRESULT errorCode, const char *message, const FString &path) const
|
||||
{
|
||||
UString s (message);
|
||||
if (errorCode != S_OK)
|
||||
|
|
@ -568,13 +574,13 @@ HRESULT CArchiveExtractCallback::SendMessageError_with_Error(HRESULT errorCode,
|
|||
return _extractCallback2->MessageError(s);
|
||||
}
|
||||
|
||||
HRESULT CArchiveExtractCallback::SendMessageError_with_LastError(const char *message, const FString &path)
|
||||
HRESULT CArchiveExtractCallback::SendMessageError_with_LastError(const char *message, const FString &path) const
|
||||
{
|
||||
const HRESULT errorCode = GetLastError_noZero_HRESULT();
|
||||
return SendMessageError_with_Error(errorCode, message, path);
|
||||
}
|
||||
|
||||
HRESULT CArchiveExtractCallback::SendMessageError2(HRESULT errorCode, const char *message, const FString &path1, const FString &path2)
|
||||
HRESULT CArchiveExtractCallback::SendMessageError2(HRESULT errorCode, const char *message, const FString &path1, const FString &path2) const
|
||||
{
|
||||
UString s (message);
|
||||
if (errorCode != 0)
|
||||
|
|
@ -588,7 +594,7 @@ HRESULT CArchiveExtractCallback::SendMessageError2(HRESULT errorCode, const char
|
|||
}
|
||||
|
||||
HRESULT CArchiveExtractCallback::SendMessageError2_with_LastError(
|
||||
const char *message, const FString &path1, const FString &path2)
|
||||
const char *message, const FString &path1, const FString &path2) const
|
||||
{
|
||||
const HRESULT errorCode = GetLastError_noZero_HRESULT();
|
||||
return SendMessageError2(errorCode, message, path1, path2);
|
||||
|
|
@ -627,6 +633,7 @@ Z7_COM7F_IMF(CGetProp::GetProp(PROPID propID, PROPVARIANT *value))
|
|||
struct CLinkLevelsInfo
|
||||
{
|
||||
bool IsAbsolute;
|
||||
bool ParentDirDots_after_NonParent;
|
||||
int LowLevel;
|
||||
int FinalLevel;
|
||||
|
||||
|
|
@ -640,6 +647,8 @@ void CLinkLevelsInfo::Parse(const UString &path, bool isWSL)
|
|||
NName::IsAbsolutePath(path);
|
||||
LowLevel = 0;
|
||||
FinalLevel = 0;
|
||||
ParentDirDots_after_NonParent = false;
|
||||
bool nonParentDir = false;
|
||||
|
||||
UStringVector parts;
|
||||
SplitPathToParts(path, parts);
|
||||
|
|
@ -658,13 +667,18 @@ void CLinkLevelsInfo::Parse(const UString &path, bool isWSL)
|
|||
continue;
|
||||
if (s.IsEqualTo(".."))
|
||||
{
|
||||
if (IsAbsolute || nonParentDir)
|
||||
ParentDirDots_after_NonParent = true;
|
||||
level--;
|
||||
if (LowLevel > level)
|
||||
LowLevel = level;
|
||||
}
|
||||
else
|
||||
{
|
||||
nonParentDir = true;
|
||||
level++;
|
||||
}
|
||||
}
|
||||
|
||||
FinalLevel = level;
|
||||
}
|
||||
|
|
@ -915,7 +929,7 @@ HRESULT CArchiveExtractCallback::ReadLink()
|
|||
#ifndef _WIN32
|
||||
|
||||
static HRESULT GetOwner(IInArchive *archive,
|
||||
UInt32 index, UInt32 pidName, UInt32 pidId, COwnerInfo &res)
|
||||
UInt32 index, UInt32 pidName, UInt32 pidId, CProcessedFileInfo::COwnerInfo &res)
|
||||
{
|
||||
{
|
||||
NWindows::NCOM::CPropVariant prop;
|
||||
|
|
@ -1047,7 +1061,7 @@ void CArchiveExtractCallback::CorrectPathParts()
|
|||
}
|
||||
|
||||
|
||||
void CArchiveExtractCallback::GetFiTimesCAM(CFiTimesCAM &pt)
|
||||
static void GetFiTimesCAM(const CProcessedFileInfo &fi, CFiTimesCAM &pt, const CArc &arc)
|
||||
{
|
||||
pt.CTime_Defined = false;
|
||||
pt.ATime_Defined = false;
|
||||
|
|
@ -1055,27 +1069,27 @@ void CArchiveExtractCallback::GetFiTimesCAM(CFiTimesCAM &pt)
|
|||
|
||||
// if (Write_MTime)
|
||||
{
|
||||
if (_fi.MTime.Def)
|
||||
if (fi.MTime.Def)
|
||||
{
|
||||
_fi.MTime.Write_To_FiTime(pt.MTime);
|
||||
fi.MTime.Write_To_FiTime(pt.MTime);
|
||||
pt.MTime_Defined = true;
|
||||
}
|
||||
else if (_arc->MTime.Def)
|
||||
else if (arc.MTime.Def)
|
||||
{
|
||||
_arc->MTime.Write_To_FiTime(pt.MTime);
|
||||
arc.MTime.Write_To_FiTime(pt.MTime);
|
||||
pt.MTime_Defined = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (/* Write_CTime && */ _fi.CTime.Def)
|
||||
if (/* Write_CTime && */ fi.CTime.Def)
|
||||
{
|
||||
_fi.CTime.Write_To_FiTime(pt.CTime);
|
||||
fi.CTime.Write_To_FiTime(pt.CTime);
|
||||
pt.CTime_Defined = true;
|
||||
}
|
||||
|
||||
if (/* Write_ATime && */ _fi.ATime.Def)
|
||||
if (/* Write_ATime && */ fi.ATime.Def)
|
||||
{
|
||||
_fi.ATime.Write_To_FiTime(pt.ATime);
|
||||
fi.ATime.Write_To_FiTime(pt.ATime);
|
||||
pt.ATime_Defined = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1086,6 +1100,7 @@ void CArchiveExtractCallback::CreateFolders()
|
|||
// 21.04 : we don't change original (_item.PathParts) here
|
||||
UStringVector pathParts = _item.PathParts;
|
||||
|
||||
bool isFinal = true;
|
||||
// bool is_DirOp = false;
|
||||
if (!pathParts.IsEmpty())
|
||||
{
|
||||
|
|
@ -1095,12 +1110,15 @@ void CArchiveExtractCallback::CreateFolders()
|
|||
but if we create dir item here, it's not problem. */
|
||||
if (!_item.IsDir
|
||||
#ifdef SUPPORT_LINKS
|
||||
#ifndef WIN32
|
||||
// #ifndef WIN32
|
||||
|| !_link.LinkPath.IsEmpty()
|
||||
#endif
|
||||
// #endif
|
||||
#endif
|
||||
)
|
||||
{
|
||||
pathParts.DeleteBack();
|
||||
isFinal = false; // last path part was excluded
|
||||
}
|
||||
// else is_DirOp = true;
|
||||
}
|
||||
|
||||
|
|
@ -1124,7 +1142,7 @@ void CArchiveExtractCallback::CreateFolders()
|
|||
*/
|
||||
|
||||
FString fullPathNew;
|
||||
CreateComplexDirectory(pathParts, fullPathNew);
|
||||
CreateComplexDirectory(pathParts, isFinal, fullPathNew);
|
||||
|
||||
/*
|
||||
if (is_DirOp)
|
||||
|
|
@ -1145,12 +1163,12 @@ void CArchiveExtractCallback::CreateFolders()
|
|||
return;
|
||||
|
||||
CDirPathTime pt;
|
||||
GetFiTimesCAM(pt);
|
||||
GetFiTimesCAM(_fi, pt, *_arc);
|
||||
|
||||
if (pt.IsSomeTimeDefined())
|
||||
{
|
||||
pt.Path = fullPathNew;
|
||||
pt.SetDirTime();
|
||||
pt.SetDirTime_to_FS_2();
|
||||
_extractedFolders.Add(pt);
|
||||
}
|
||||
}
|
||||
|
|
@ -1292,9 +1310,11 @@ HRESULT CArchiveExtractCallback::CheckExistFile(FString &fullProcessedPath, bool
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
return:
|
||||
needExit = false: caller will use (outStreamLoc) and _hashStreamSpec
|
||||
needExit = true : caller will not use (outStreamLoc) and _hashStreamSpec.
|
||||
*/
|
||||
HRESULT CArchiveExtractCallback::GetExtractStream(CMyComPtr<ISequentialOutStream> &outStreamLoc, bool &needExit)
|
||||
{
|
||||
needExit = true;
|
||||
|
|
@ -1383,12 +1403,15 @@ HRESULT CArchiveExtractCallback::GetExtractStream(CMyComPtr<ISequentialOutStream
|
|||
{
|
||||
bool linkWasSet = false;
|
||||
RINOK(SetLink(fullProcessedPath, _link, linkWasSet))
|
||||
/*
|
||||
// we don't set attributes for placeholder.
|
||||
if (linkWasSet)
|
||||
{
|
||||
_isSymLinkCreated = _link.Is_AnySymLink();
|
||||
SetAttrib();
|
||||
// printf("\nlinkWasSet %s\n", GetAnsiString(_diskFilePath));
|
||||
}
|
||||
*/
|
||||
}
|
||||
#endif // UNDER_CE
|
||||
|
||||
|
|
@ -1414,11 +1437,17 @@ HRESULT CArchiveExtractCallback::GetExtractStream(CMyComPtr<ISequentialOutStream
|
|||
hl = fullProcessedPath;
|
||||
else
|
||||
{
|
||||
if (!MyCreateHardLink(fullProcessedPath, hl))
|
||||
return SendMessageError2_with_LastError(kCantCreateHardLink, fullProcessedPath, hl);
|
||||
bool link_was_Created = false;
|
||||
RINOK(CreateHardLink2(fullProcessedPath, hl, link_was_Created))
|
||||
if (!link_was_Created)
|
||||
return S_OK;
|
||||
// printf("\nHard linkWasSet Archive_Get_HardLinkNode %s\n", GetAnsiString(_diskFilePath));
|
||||
// _needSetAttrib = true; // do we need to set attribute ?
|
||||
SetAttrib();
|
||||
/* if we set (needExit = false) here, _hashStreamSpec will be used,
|
||||
and hash will be calulated for all hard links files (it's slower).
|
||||
But "Test" operation also calculates hashes.
|
||||
*/
|
||||
needExit = false;
|
||||
return S_OK;
|
||||
}
|
||||
|
|
@ -1943,7 +1972,7 @@ HRESULT CArchiveExtractCallback::CloseFile()
|
|||
#endif
|
||||
|
||||
CFiTimesCAM t;
|
||||
GetFiTimesCAM(t);
|
||||
GetFiTimesCAM(_fi, t, *_arc);
|
||||
|
||||
// #ifdef _WIN32
|
||||
if (t.IsSomeTimeDefined())
|
||||
|
|
@ -1970,6 +1999,139 @@ HRESULT CArchiveExtractCallback::CloseFile()
|
|||
|
||||
#ifdef SUPPORT_LINKS
|
||||
|
||||
static bool CheckLinkPath_in_FS_for_pathParts(const FString &path, const UStringVector &v)
|
||||
{
|
||||
FString path2 = path;
|
||||
FOR_VECTOR (i, v)
|
||||
{
|
||||
// if (i == v.Size() - 1) path = path2; // we don't need last part in returned path
|
||||
path2 += us2fs(v[i]);
|
||||
NFind::CFileInfo fi;
|
||||
// printf("\nCheckLinkPath_in_FS_for_pathParts(): %s\n", GetOemString(path2).Ptr());
|
||||
if (fi.Find(path2) && fi.IsOsSymLink())
|
||||
return false;
|
||||
path2.Add_PathSepar();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
link.isRelative / relative_item_PathPrefix
|
||||
false / empty
|
||||
true / item path without last part
|
||||
*/
|
||||
static bool CheckLinkPath_in_FS(
|
||||
const FString &pathPrefix_in_FS,
|
||||
const CPostLink &postLink,
|
||||
const UString &relative_item_PathPrefix)
|
||||
{
|
||||
const CLinkInfo &link = postLink.LinkInfo;
|
||||
if (postLink.item_PathParts.IsEmpty() || link.LinkPath.IsEmpty())
|
||||
return false;
|
||||
FString path;
|
||||
{
|
||||
const UString &s = postLink.item_PathParts[0];
|
||||
if (!s.IsEmpty() && !NName::IsAbsolutePath(s))
|
||||
path = pathPrefix_in_FS; // item_PathParts is relative. So we use absolutre prefix
|
||||
}
|
||||
if (!CheckLinkPath_in_FS_for_pathParts(path, postLink.item_PathParts))
|
||||
return false;
|
||||
path += us2fs(relative_item_PathPrefix);
|
||||
UStringVector v;
|
||||
SplitPathToParts(link.LinkPath, v);
|
||||
// we check target paths:
|
||||
return CheckLinkPath_in_FS_for_pathParts(path, v);
|
||||
}
|
||||
|
||||
static const unsigned k_DangLevel_MAX_for_Link_over_Link = 9;
|
||||
|
||||
HRESULT CArchiveExtractCallback::CreateHardLink2(
|
||||
const FString &newFilePath, const FString &existFilePath, bool &link_was_Created) const
|
||||
{
|
||||
link_was_Created = false;
|
||||
if (_ntOptions.SymLinks_DangerousLevel <= k_DangLevel_MAX_for_Link_over_Link)
|
||||
{
|
||||
NFind::CFileInfo fi;
|
||||
if (fi.Find(existFilePath) && fi.IsOsSymLink())
|
||||
return SendMessageError2(0, k_HardLink_to_SymLink_Ignored, newFilePath, existFilePath);
|
||||
}
|
||||
if (!MyCreateHardLink(newFilePath, existFilePath))
|
||||
return SendMessageError2_with_LastError(kCantCreateHardLink, newFilePath, existFilePath);
|
||||
link_was_Created = true;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
HRESULT CArchiveExtractCallback::SetLink(
|
||||
const FString &fullProcessedPath_from,
|
||||
const CLinkInfo &link,
|
||||
bool &linkWasSet) // placeholder was created
|
||||
{
|
||||
linkWasSet = false;
|
||||
if (link.LinkPath.IsEmpty())
|
||||
return S_OK;
|
||||
if (!_ntOptions.SymLinks.Val && link.Is_AnySymLink())
|
||||
return S_OK;
|
||||
CPostLink postLink;
|
||||
postLink.Index_in_Arc = _index;
|
||||
postLink.item_IsDir = _item.IsDir;
|
||||
postLink.item_Path = _item.Path;
|
||||
postLink.item_PathParts = _item.PathParts;
|
||||
postLink.item_FileInfo = _fi;
|
||||
postLink.fullProcessedPath_from = fullProcessedPath_from;
|
||||
postLink.LinkInfo = link;
|
||||
_postLinks.Add(postLink);
|
||||
|
||||
// file doesn't exist in most cases. So we don't check for error.
|
||||
DeleteLinkFileAlways_or_RemoveEmptyDir(fullProcessedPath_from, false); // checkThatFileIsEmpty = false
|
||||
|
||||
NIO::COutFile outFile;
|
||||
if (!outFile.Create_NEW(fullProcessedPath_from))
|
||||
return SendMessageError("Cannot create temporary link file", fullProcessedPath_from);
|
||||
#if 0 // 1 for debug
|
||||
// here we can write link path to temporary link file placeholder,
|
||||
// but empty placeholder is better, because we don't want to get any non-eampty data instead of link file.
|
||||
AString s;
|
||||
ConvertUnicodeToUTF8(link.LinkPath, s);
|
||||
outFile.WriteFull(s, s.Len());
|
||||
#endif
|
||||
linkWasSet = true;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
// if file/dir is symbolic link it will remove only link itself
|
||||
HRESULT CArchiveExtractCallback::DeleteLinkFileAlways_or_RemoveEmptyDir(
|
||||
const FString &path, bool checkThatFileIsEmpty) const
|
||||
{
|
||||
NFile::NFind::CFileInfo fi;
|
||||
if (fi.Find(path)) // followLink = false
|
||||
{
|
||||
if (fi.IsDir())
|
||||
{
|
||||
if (RemoveDirAlways_if_Empty(path))
|
||||
return S_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
// link file placeholder must be empty
|
||||
if (checkThatFileIsEmpty && !fi.IsOsSymLink() && fi.Size != 0)
|
||||
return SendMessageError("Temporary link file is not empty", path);
|
||||
if (DeleteFileAlways(path))
|
||||
return S_OK;
|
||||
}
|
||||
if (GetLastError() != ERROR_FILE_NOT_FOUND)
|
||||
return SendMessageError_with_LastError(
|
||||
fi.IsDir() ?
|
||||
k_CantDelete_Dir_for_SymLink:
|
||||
k_CantDelete_File_for_SymLink,
|
||||
path);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
in:
|
||||
link.LinkPath : must be relative (non-absolute) path in any case !!!
|
||||
|
|
@ -1977,25 +2139,48 @@ in:
|
|||
== false / _dirPathPrefix_Full + link.LinkPath
|
||||
== true / link.LinkPath
|
||||
*/
|
||||
|
||||
HRESULT CArchiveExtractCallback::SetLink(
|
||||
const FString &fullProcessedPath_from,
|
||||
const CLinkInfo &link,
|
||||
bool &linkWasSet)
|
||||
static HRESULT SetLink2(const CArchiveExtractCallback &callback,
|
||||
const CPostLink &postLink, bool &linkWasSet)
|
||||
{
|
||||
linkWasSet = false;
|
||||
if (link.LinkPath.IsEmpty())
|
||||
return S_OK;
|
||||
if (!_ntOptions.SymLinks.Val && link.Is_AnySymLink())
|
||||
return S_OK;
|
||||
const CLinkInfo &link = postLink.LinkInfo;
|
||||
const FString &fullProcessedPath_from = postLink.fullProcessedPath_from; // full file path in FS (fullProcessedPath_from)
|
||||
|
||||
const unsigned level = callback._ntOptions.SymLinks_DangerousLevel;
|
||||
if (level < 20)
|
||||
{
|
||||
/*
|
||||
We want to use additional check for links that can link to directory.
|
||||
- linux: all symbolic links are files.
|
||||
- windows: we can have file/directory symbolic link,
|
||||
but file symbolic link works like directory link in windows.
|
||||
So we use additional check for all relative links.
|
||||
|
||||
We don't allow decreasing of final level of link.
|
||||
So if some another extracted file will use this link,
|
||||
then number of real path parts (after link redirection) cannot be
|
||||
smaller than number of requested path parts from archive records.
|
||||
|
||||
here we check only (link.LinkPath) without (_item.PathParts).
|
||||
*/
|
||||
CLinkLevelsInfo li;
|
||||
li.Parse(link.LinkPath, link.Is_WSL());
|
||||
bool isDang;
|
||||
UString relativePathPrefix;
|
||||
if (li.IsAbsolute // unexpected
|
||||
|| li.ParentDirDots_after_NonParent
|
||||
|| (level <= 5 && link.isRelative && li.FinalLevel < 1) // final level lower
|
||||
|| (level <= 5 && link.isRelative && li.LowLevel < 0) // negative temporary levels
|
||||
)
|
||||
isDang = true;
|
||||
else // if (!isDang)
|
||||
{
|
||||
UString path;
|
||||
if (link.isRelative)
|
||||
{
|
||||
// _item.PathParts : parts that will be created in output folder.
|
||||
// item_PathParts : parts that will be created in output folder.
|
||||
// we want to get directory prefix of link item.
|
||||
// so we remove file name (last non-empty part) from PathParts:
|
||||
UStringVector v = _item.PathParts;
|
||||
UStringVector v = postLink.item_PathParts;
|
||||
while (!v.IsEmpty())
|
||||
{
|
||||
const unsigned len = v.Back().Len();
|
||||
|
|
@ -2005,6 +2190,7 @@ HRESULT CArchiveExtractCallback::SetLink(
|
|||
}
|
||||
path = MakePathFromParts(v);
|
||||
NName::NormalizeDirPathPrefix(path);
|
||||
relativePathPrefix = path;
|
||||
}
|
||||
path += link.LinkPath;
|
||||
/*
|
||||
|
|
@ -2012,10 +2198,20 @@ HRESULT CArchiveExtractCallback::SetLink(
|
|||
path is relative to root folder of extracted items
|
||||
if (!link.isRelative), then (path == link.LinkPath)
|
||||
*/
|
||||
isDang = false;
|
||||
if (!IsSafePath(path, link.Is_WSL()))
|
||||
return SendMessageError2(0, // errorCode
|
||||
"Dangerous link path was ignored",
|
||||
us2fs(_item.Path), us2fs(link.LinkPath));
|
||||
isDang = true;
|
||||
}
|
||||
const char *message = NULL;
|
||||
if (isDang)
|
||||
message = "Dangerous link path was ignored";
|
||||
else if (level <= k_DangLevel_MAX_for_Link_over_Link
|
||||
&& !CheckLinkPath_in_FS(callback._dirPathPrefix_Full,
|
||||
postLink, relativePathPrefix))
|
||||
message = "Dangerous link via another link was ignored";
|
||||
if (message)
|
||||
return callback.SendMessageError2(0, // errorCode
|
||||
message, us2fs(postLink.item_Path), us2fs(link.LinkPath));
|
||||
}
|
||||
|
||||
FString target; // target path that will be stored to link field
|
||||
|
|
@ -2025,8 +2221,8 @@ HRESULT CArchiveExtractCallback::SetLink(
|
|||
// all hard links and absolute symbolic links
|
||||
// relatPath == link.LinkPath
|
||||
// we get absolute link path for target:
|
||||
if (!NName::GetFullPath(_dirPathPrefix_Full, us2fs(link.LinkPath), target))
|
||||
return SendMessageError("Incorrect link path", us2fs(link.LinkPath));
|
||||
if (!NName::GetFullPath(callback._dirPathPrefix_Full, us2fs(link.LinkPath), target))
|
||||
return callback.SendMessageError("Incorrect link path", us2fs(link.LinkPath));
|
||||
// (target) is (_dirPathPrefix_Full + relatPath)
|
||||
}
|
||||
else
|
||||
|
|
@ -2036,21 +2232,24 @@ HRESULT CArchiveExtractCallback::SetLink(
|
|||
target = us2fs(link.LinkPath);
|
||||
}
|
||||
if (target.IsEmpty())
|
||||
return SendMessageError("Empty link", fullProcessedPath_from);
|
||||
return callback.SendMessageError("Empty link", fullProcessedPath_from);
|
||||
|
||||
if (link.Is_HardLink() /* || link.IsCopyLink */)
|
||||
{
|
||||
// if (link.isHardLink)
|
||||
{
|
||||
if (!MyCreateHardLink(fullProcessedPath_from, target))
|
||||
return SendMessageError2_with_LastError(kCantCreateHardLink, fullProcessedPath_from, target);
|
||||
RINOK(callback.DeleteLinkFileAlways_or_RemoveEmptyDir(fullProcessedPath_from, true)) // checkThatFileIsEmpty
|
||||
{
|
||||
// RINOK(SendMessageError_with_LastError(k_Cant_DeleteTempLinkFile, fullProcessedPath_from))
|
||||
}
|
||||
return callback.CreateHardLink2(fullProcessedPath_from, target, linkWasSet);
|
||||
/*
|
||||
RINOK(PrepareOperation(NArchive::NExtract::NAskMode::kExtract))
|
||||
_op_WasReported = true;
|
||||
RINOK(SetOperationResult(NArchive::NExtract::NOperationResult::kOK))
|
||||
*/
|
||||
linkWasSet = true;
|
||||
return S_OK;
|
||||
*/
|
||||
}
|
||||
/*
|
||||
// IsCopyLink
|
||||
|
|
@ -2086,35 +2285,9 @@ HRESULT CArchiveExtractCallback::SetLink(
|
|||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
const bool isDir = (_item.IsDir || link.LinkType == k_LinkType_Junction);
|
||||
const bool isDir = (postLink.item_IsDir || link.LinkType == k_LinkType_Junction);
|
||||
#endif
|
||||
|
||||
if (!_ntOptions.SymLinks_AllowDangerous.Val && link.isRelative)
|
||||
{
|
||||
/*
|
||||
We want to use additional check for links that can link to directory.
|
||||
- linux: all symbolic links are files.
|
||||
- windows: we can have file/directory symbolic link,
|
||||
but file symbolic link works like directory link in windows.
|
||||
So we use additional check for all relative links.
|
||||
|
||||
We don't allow decreasing of final level of link.
|
||||
So if some another extracted file will use this link,
|
||||
then number of real path parts (after link redirection) cannot be
|
||||
smaller than number of requested path parts from archive records.
|
||||
|
||||
Now we check only (link.LinkPath) without (_item.PathParts).
|
||||
*/
|
||||
CLinkLevelsInfo levelsInfo;
|
||||
levelsInfo.Parse(link.LinkPath, link.Is_WSL());
|
||||
if (levelsInfo.FinalLevel < 1
|
||||
// || levelsInfo.LowLevel < 0 // we allow negative temporary levels
|
||||
|| levelsInfo.IsAbsolute)
|
||||
return SendMessageError2(0, // errorCode
|
||||
"Dangerous symbolic link path was ignored",
|
||||
us2fs(_item.Path), us2fs(link.LinkPath));
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
CByteBuffer data;
|
||||
|
|
@ -2127,7 +2300,7 @@ HRESULT CArchiveExtractCallback::SetLink(
|
|||
else
|
||||
FillLinkData_WinLink(data, fs2us(target), link.LinkType != k_LinkType_Junction);
|
||||
if (data.Size() == 0)
|
||||
return SendMessageError("Cannot fill link data", us2fs(_item.Path));
|
||||
return callback.SendMessageError("Cannot fill link data", us2fs(postLink.item_Path));
|
||||
/*
|
||||
if (NtReparse_Size != data.Size() || memcmp(NtReparse_Data, data, data.Size()) != 0)
|
||||
SendMessageError("reconstructed Reparse is different", fs2us(target));
|
||||
|
|
@ -2136,14 +2309,18 @@ HRESULT CArchiveExtractCallback::SetLink(
|
|||
// we check that reparse data is correct, but we ignore attr.MinorError.
|
||||
CReparseAttr attr;
|
||||
if (!attr.Parse(data, data.Size()))
|
||||
return SendMessageError("Internal error for symbolic link file", us2fs(_item.Path));
|
||||
return callback.SendMessageError("Internal error for symbolic link file", us2fs(postLink.item_Path));
|
||||
}
|
||||
#endif
|
||||
|
||||
RINOK(callback.DeleteLinkFileAlways_or_RemoveEmptyDir(fullProcessedPath_from, true)) // checkThatFileIsEmpty
|
||||
#ifdef _WIN32
|
||||
if (!NFile::NIO::SetReparseData(fullProcessedPath_from, isDir, data, (DWORD)data.Size()))
|
||||
#else // ! _WIN32
|
||||
if (!NFile::NIO::SetSymLink(fullProcessedPath_from, target))
|
||||
#endif // ! _WIN32
|
||||
{
|
||||
return SendMessageError_with_LastError(kCantCreateSymLink, fullProcessedPath_from);
|
||||
return callback.SendMessageError_with_LastError(kCantCreateSymLink, fullProcessedPath_from);
|
||||
}
|
||||
linkWasSet = true;
|
||||
return S_OK;
|
||||
|
|
@ -2392,6 +2569,7 @@ HRESULT CArchiveExtractCallback::CloseReparseAndFile()
|
|||
_curSize_Defined = true;
|
||||
if (needSetReparse)
|
||||
{
|
||||
// empty file was created so we must delete it.
|
||||
// in Linux : we must delete empty file before symbolic link creation
|
||||
// in Windows : we can create symbolic link even without file deleting
|
||||
if (!DeleteFileAlways(_diskFilePath))
|
||||
|
|
@ -2404,9 +2582,12 @@ HRESULT CArchiveExtractCallback::CloseReparseAndFile()
|
|||
// link.isJunction = true; // for debug
|
||||
link.Normalize_to_RelativeSafe(_removePathParts);
|
||||
RINOK(SetLink(_diskFilePath, link, linkWasSet))
|
||||
/*
|
||||
// we don't set attributes for placeholder.
|
||||
if (linkWasSet)
|
||||
_isSymLinkCreated = true; // link.IsSymLink();
|
||||
else
|
||||
*/
|
||||
_needSetAttrib = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -2416,7 +2597,31 @@ HRESULT CArchiveExtractCallback::CloseReparseAndFile()
|
|||
}
|
||||
|
||||
|
||||
void CArchiveExtractCallback::SetAttrib()
|
||||
static void SetAttrib_Base(const FString &path, const CProcessedFileInfo &fi,
|
||||
const CArchiveExtractCallback &callback)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
if (fi.Owner.Id_Defined &&
|
||||
fi.Group.Id_Defined)
|
||||
{
|
||||
if (my_chown(path, fi.Owner.Id, fi.Group.Id) != 0)
|
||||
callback.SendMessageError_with_LastError("Cannot set owner", path);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (fi.Attrib_Defined)
|
||||
{
|
||||
// const AString s = GetAnsiString(_diskFilePath);
|
||||
// printf("\nSetFileAttrib_PosixHighDetect: %s: hex:%x\n", s.Ptr(), _fi.Attrib);
|
||||
if (!SetFileAttrib_PosixHighDetect(path, fi.Attrib))
|
||||
{
|
||||
// do we need error message here in Windows and in posix?
|
||||
callback.SendMessageError_with_LastError("Cannot set file attribute", path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CArchiveExtractCallback::SetAttrib() const
|
||||
{
|
||||
#ifndef _WIN32
|
||||
// Linux now doesn't support permissions for symlinks
|
||||
|
|
@ -2430,29 +2635,39 @@ void CArchiveExtractCallback::SetAttrib()
|
|||
|| !_extractMode)
|
||||
return;
|
||||
|
||||
#ifndef _WIN32
|
||||
if (_fi.Owner.Id_Defined &&
|
||||
_fi.Group.Id_Defined)
|
||||
{
|
||||
if (my_chown(_diskFilePath, _fi.Owner.Id, _fi.Group.Id) != 0)
|
||||
{
|
||||
SendMessageError_with_LastError("Cannot set owner", _diskFilePath);
|
||||
SetAttrib_Base(_diskFilePath, _fi, *this);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (_fi.Attrib_Defined)
|
||||
|
||||
#ifdef Z7_USE_SECURITY_CODE
|
||||
HRESULT CArchiveExtractCallback::SetSecurityInfo(UInt32 indexInArc, const FString &path) const
|
||||
{
|
||||
// const AString s = GetAnsiString(_diskFilePath);
|
||||
// printf("\nSetFileAttrib_PosixHighDetect: %s: hex:%x\n", s.Ptr(), _fi.Attrib);
|
||||
bool res = SetFileAttrib_PosixHighDetect(_diskFilePath, _fi.Attrib);
|
||||
if (!res)
|
||||
if (!_stdOutMode && _extractMode && _ntOptions.NtSecurity.Val && _arc->GetRawProps)
|
||||
{
|
||||
// do we need error message here in Windows and in posix?
|
||||
SendMessageError_with_LastError("Cannot set file attribute", _diskFilePath);
|
||||
const void *data;
|
||||
UInt32 dataSize;
|
||||
UInt32 propType;
|
||||
_arc->GetRawProps->GetRawProp(indexInArc, kpidNtSecure, &data, &dataSize, &propType);
|
||||
if (dataSize != 0)
|
||||
{
|
||||
if (propType != NPropDataType::kRaw)
|
||||
return E_FAIL;
|
||||
if (CheckNtSecure((const Byte *)data, dataSize))
|
||||
{
|
||||
SECURITY_INFORMATION securInfo = DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION;
|
||||
if (_saclEnabled)
|
||||
securInfo |= SACL_SECURITY_INFORMATION;
|
||||
// if (!
|
||||
::SetFileSecurityW(fs2us(path), securInfo, (PSECURITY_DESCRIPTOR)(void *)(const Byte *)(data));
|
||||
{
|
||||
// RINOK(SendMessageError_with_LastError("SetFileSecurity FAILS", path))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
#endif // Z7_USE_SECURITY_CODE
|
||||
|
||||
|
||||
Z7_COM7F_IMF(CArchiveExtractCallback::SetOperationResult(Int32 opRes))
|
||||
|
|
@ -2491,26 +2706,8 @@ Z7_COM7F_IMF(CArchiveExtractCallback::SetOperationResult(Int32 opRes))
|
|||
RINOK(CloseReparseAndFile())
|
||||
|
||||
#ifdef Z7_USE_SECURITY_CODE
|
||||
if (!_stdOutMode && _extractMode && _ntOptions.NtSecurity.Val && _arc->GetRawProps)
|
||||
{
|
||||
const void *data;
|
||||
UInt32 dataSize;
|
||||
UInt32 propType;
|
||||
_arc->GetRawProps->GetRawProp(_index, kpidNtSecure, &data, &dataSize, &propType);
|
||||
if (dataSize != 0)
|
||||
{
|
||||
if (propType != NPropDataType::kRaw)
|
||||
return E_FAIL;
|
||||
if (CheckNtSecure((const Byte *)data, dataSize))
|
||||
{
|
||||
SECURITY_INFORMATION securInfo = DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION;
|
||||
if (_saclEnabled)
|
||||
securInfo |= SACL_SECURITY_INFORMATION;
|
||||
::SetFileSecurityW(fs2us(_diskFilePath), securInfo, (PSECURITY_DESCRIPTOR)(void *)(const Byte *)(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // Z7_USE_SECURITY_CODE
|
||||
RINOK(SetSecurityInfo(_index, _diskFilePath))
|
||||
#endif
|
||||
|
||||
if (!_curSize_Defined)
|
||||
GetUnpackSize();
|
||||
|
|
@ -2754,15 +2951,58 @@ void CDirPathSortPair::SetNumSlashes(const FChar *s)
|
|||
}
|
||||
|
||||
|
||||
bool CDirPathTime::SetDirTime() const
|
||||
bool CFiTimesCAM::SetDirTime_to_FS(CFSTR path) const
|
||||
{
|
||||
return NDir::SetDirTime(Path,
|
||||
// it's same function for dir and for file
|
||||
return NDir::SetDirTime(path,
|
||||
CTime_Defined ? &CTime : NULL,
|
||||
ATime_Defined ? &ATime : NULL,
|
||||
MTime_Defined ? &MTime : NULL);
|
||||
}
|
||||
|
||||
|
||||
#ifdef SUPPORT_LINKS
|
||||
|
||||
bool CFiTimesCAM::SetLinkFileTime_to_FS(CFSTR path) const
|
||||
{
|
||||
// it's same function for dir and for file
|
||||
return NDir::SetLinkFileTime(path,
|
||||
CTime_Defined ? &CTime : NULL,
|
||||
ATime_Defined ? &ATime : NULL,
|
||||
MTime_Defined ? &MTime : NULL);
|
||||
}
|
||||
|
||||
HRESULT CArchiveExtractCallback::SetPostLinks() const
|
||||
{
|
||||
FOR_VECTOR (i, _postLinks)
|
||||
{
|
||||
const CPostLink &link = _postLinks[i];
|
||||
bool linkWasSet = false;
|
||||
RINOK(SetLink2(*this, link, linkWasSet))
|
||||
if (linkWasSet)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// Linux now doesn't support permissions for symlinks
|
||||
SetAttrib_Base(link.fullProcessedPath_from, link.item_FileInfo, *this);
|
||||
#endif
|
||||
|
||||
CFiTimesCAM pt;
|
||||
GetFiTimesCAM(link.item_FileInfo, pt, *_arc);
|
||||
if (pt.IsSomeTimeDefined())
|
||||
pt.SetLinkFileTime_to_FS(link.fullProcessedPath_from);
|
||||
|
||||
#ifdef Z7_USE_SECURITY_CODE
|
||||
// we set security information after timestamps setting
|
||||
RINOK(SetSecurityInfo(link.Index_in_Arc, link.fullProcessedPath_from))
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
HRESULT CArchiveExtractCallback::SetDirsTimes()
|
||||
{
|
||||
if (!_arc)
|
||||
|
|
@ -2786,7 +3026,7 @@ HRESULT CArchiveExtractCallback::SetDirsTimes()
|
|||
for (i = 0; i < pairs.Size(); i++)
|
||||
{
|
||||
const CDirPathTime &dpt = _extractedFolders[pairs[i].Index];
|
||||
if (!dpt.SetDirTime())
|
||||
if (!dpt.SetDirTime_to_FS_2())
|
||||
{
|
||||
// result = E_FAIL;
|
||||
// do we need error message here in Windows and in posix?
|
||||
|
|
@ -2818,10 +3058,20 @@ HRESULT CArchiveExtractCallback::SetDirsTimes()
|
|||
|
||||
HRESULT CArchiveExtractCallback::CloseArc()
|
||||
{
|
||||
// we call CloseReparseAndFile() here because we can have non-closed file in some cases?
|
||||
HRESULT res = CloseReparseAndFile();
|
||||
#ifdef SUPPORT_LINKS
|
||||
{
|
||||
const HRESULT res2 = SetPostLinks();
|
||||
if (res == S_OK)
|
||||
res = res2;
|
||||
}
|
||||
#endif
|
||||
{
|
||||
const HRESULT res2 = SetDirsTimes();
|
||||
if (res == S_OK)
|
||||
res = res2;
|
||||
}
|
||||
_arc = NULL;
|
||||
return res;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ struct CExtractNtOptions
|
|||
{
|
||||
CBoolPair NtSecurity;
|
||||
CBoolPair SymLinks;
|
||||
CBoolPair SymLinks_AllowDangerous;
|
||||
CBoolPair HardLinks;
|
||||
CBoolPair AltStreams;
|
||||
bool ReplaceColonForAltStream;
|
||||
|
|
@ -66,6 +65,8 @@ struct CExtractNtOptions
|
|||
bool PreserveATime;
|
||||
bool OpenShareForWrite;
|
||||
|
||||
unsigned SymLinks_DangerousLevel;
|
||||
|
||||
UInt64 MemLimit;
|
||||
|
||||
CExtractNtOptions():
|
||||
|
|
@ -74,10 +75,10 @@ struct CExtractNtOptions
|
|||
ExtractOwner(false),
|
||||
PreserveATime(false),
|
||||
OpenShareForWrite(false),
|
||||
SymLinks_DangerousLevel(5),
|
||||
MemLimit((UInt64)(Int64)-1)
|
||||
{
|
||||
SymLinks.Val = true;
|
||||
SymLinks_AllowDangerous.Val = false;
|
||||
HardLinks.Val = true;
|
||||
AltStreams.Val = true;
|
||||
|
||||
|
|
@ -166,19 +167,22 @@ struct CFiTimesCAM
|
|||
ATime_Defined |
|
||||
MTime_Defined;
|
||||
}
|
||||
bool SetDirTime_to_FS(CFSTR path) const;
|
||||
#ifdef SUPPORT_LINKS
|
||||
bool SetLinkFileTime_to_FS(CFSTR path) const;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct CDirPathTime: public CFiTimesCAM
|
||||
{
|
||||
FString Path;
|
||||
|
||||
bool SetDirTime() const;
|
||||
bool SetDirTime_to_FS_2() const { return SetDirTime_to_FS(Path); }
|
||||
};
|
||||
|
||||
|
||||
#ifdef SUPPORT_LINKS
|
||||
|
||||
|
||||
enum ELinkType
|
||||
{
|
||||
k_LinkType_HardLink,
|
||||
|
|
@ -227,6 +231,15 @@ private:
|
|||
#endif // SUPPORT_LINKS
|
||||
|
||||
|
||||
|
||||
struct CProcessedFileInfo
|
||||
{
|
||||
CArcTime CTime;
|
||||
CArcTime ATime;
|
||||
CArcTime MTime;
|
||||
UInt32 Attrib;
|
||||
bool Attrib_Defined;
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
struct COwnerInfo
|
||||
|
|
@ -243,8 +256,76 @@ struct COwnerInfo
|
|||
}
|
||||
};
|
||||
|
||||
COwnerInfo Owner;
|
||||
COwnerInfo Group;
|
||||
#endif
|
||||
|
||||
void Clear()
|
||||
{
|
||||
#ifndef _WIN32
|
||||
Attrib_Defined = false;
|
||||
Owner.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool IsReparse() const
|
||||
{
|
||||
return (Attrib_Defined && (Attrib & FILE_ATTRIBUTE_REPARSE_POINT) != 0);
|
||||
}
|
||||
|
||||
bool IsLinuxSymLink() const
|
||||
{
|
||||
return (Attrib_Defined && MY_LIN_S_ISLNK(Attrib >> 16));
|
||||
}
|
||||
|
||||
void SetFromPosixAttrib(UInt32 a)
|
||||
{
|
||||
// here we set only part of combined attribute required by SetFileAttrib() call
|
||||
#ifdef _WIN32
|
||||
// Windows sets FILE_ATTRIBUTE_NORMAL, if we try to set 0 as attribute.
|
||||
Attrib = MY_LIN_S_ISDIR(a) ?
|
||||
FILE_ATTRIBUTE_DIRECTORY :
|
||||
FILE_ATTRIBUTE_ARCHIVE;
|
||||
if ((a & 0222) == 0) // (& S_IWUSR) in p7zip
|
||||
Attrib |= FILE_ATTRIBUTE_READONLY;
|
||||
// 22.00 : we need type bits for (MY_LIN_S_IFLNK) for IsLinuxSymLink()
|
||||
a &= MY_LIN_S_IFMT;
|
||||
if (a == MY_LIN_S_IFLNK)
|
||||
Attrib |= (a << 16);
|
||||
#else
|
||||
Attrib = (a << 16) | FILE_ATTRIBUTE_UNIX_EXTENSION;
|
||||
#endif
|
||||
Attrib_Defined = true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#ifdef SUPPORT_LINKS
|
||||
|
||||
struct CPostLink
|
||||
{
|
||||
UInt32 Index_in_Arc;
|
||||
bool item_IsDir; // _item.IsDir
|
||||
UString item_Path; // _item.Path;
|
||||
UStringVector item_PathParts; // _item.PathParts;
|
||||
CProcessedFileInfo item_FileInfo; // _fi
|
||||
FString fullProcessedPath_from; // full file path in FS
|
||||
CLinkInfo LinkInfo;
|
||||
};
|
||||
|
||||
/*
|
||||
struct CPostLinks
|
||||
{
|
||||
void Clear()
|
||||
{
|
||||
Links.Clear();
|
||||
}
|
||||
};
|
||||
*/
|
||||
|
||||
#endif // SUPPORT_LINKS
|
||||
|
||||
|
||||
|
||||
class CArchiveExtractCallback Z7_final:
|
||||
public IArchiveExtractCallback,
|
||||
|
|
@ -292,8 +373,9 @@ public:
|
|||
private:
|
||||
|
||||
const CArc *_arc;
|
||||
public:
|
||||
CExtractNtOptions _ntOptions;
|
||||
|
||||
private:
|
||||
bool _encrypted;
|
||||
bool _isSplit;
|
||||
bool _curSize_Defined;
|
||||
|
|
@ -325,7 +407,9 @@ private:
|
|||
CMyComPtr<ICryptoGetTextPassword> _cryptoGetTextPassword;
|
||||
|
||||
FString _dirPathPrefix;
|
||||
public:
|
||||
FString _dirPathPrefix_Full;
|
||||
private:
|
||||
|
||||
#ifndef Z7_SFX
|
||||
|
||||
|
|
@ -337,49 +421,7 @@ private:
|
|||
CReadArcItem _item;
|
||||
FString _diskFilePath;
|
||||
|
||||
struct CProcessedFileInfo
|
||||
{
|
||||
CArcTime CTime;
|
||||
CArcTime ATime;
|
||||
CArcTime MTime;
|
||||
UInt32 Attrib;
|
||||
bool Attrib_Defined;
|
||||
|
||||
#ifndef _WIN32
|
||||
COwnerInfo Owner;
|
||||
COwnerInfo Group;
|
||||
#endif
|
||||
|
||||
bool IsReparse() const
|
||||
{
|
||||
return (Attrib_Defined && (Attrib & FILE_ATTRIBUTE_REPARSE_POINT) != 0);
|
||||
}
|
||||
|
||||
bool IsLinuxSymLink() const
|
||||
{
|
||||
return (Attrib_Defined && MY_LIN_S_ISLNK(Attrib >> 16));
|
||||
}
|
||||
|
||||
void SetFromPosixAttrib(UInt32 a)
|
||||
{
|
||||
// here we set only part of combined attribute required by SetFileAttrib() call
|
||||
#ifdef _WIN32
|
||||
// Windows sets FILE_ATTRIBUTE_NORMAL, if we try to set 0 as attribute.
|
||||
Attrib = MY_LIN_S_ISDIR(a) ?
|
||||
FILE_ATTRIBUTE_DIRECTORY :
|
||||
FILE_ATTRIBUTE_ARCHIVE;
|
||||
if ((a & 0222) == 0) // (& S_IWUSR) in p7zip
|
||||
Attrib |= FILE_ATTRIBUTE_READONLY;
|
||||
// 22.00 : we need type bits for (MY_LIN_S_IFLNK) for IsLinuxSymLink()
|
||||
a &= MY_LIN_S_IFMT;
|
||||
if (a == MY_LIN_S_IFLNK)
|
||||
Attrib |= (a << 16);
|
||||
#else
|
||||
Attrib = (a << 16) | FILE_ATTRIBUTE_UNIX_EXTENSION;
|
||||
#endif
|
||||
Attrib_Defined = true;
|
||||
}
|
||||
} _fi;
|
||||
CProcessedFileInfo _fi;
|
||||
|
||||
UInt64 _position;
|
||||
UInt64 _curSize;
|
||||
|
|
@ -421,20 +463,21 @@ private:
|
|||
// CObjectVector<NWindows::NFile::NDir::CDelayedSymLink> _delayedSymLinks;
|
||||
#endif
|
||||
|
||||
void CreateComplexDirectory(const UStringVector &dirPathParts, FString &fullPath);
|
||||
void CreateComplexDirectory(
|
||||
const UStringVector &dirPathParts, bool isFinal, FString &fullPath);
|
||||
HRESULT GetTime(UInt32 index, PROPID propID, CArcTime &ft);
|
||||
HRESULT GetUnpackSize();
|
||||
|
||||
FString Hash_GetFullFilePath();
|
||||
|
||||
void SetAttrib();
|
||||
void SetAttrib() const;
|
||||
|
||||
public:
|
||||
HRESULT SendMessageError(const char *message, const FString &path);
|
||||
HRESULT SendMessageError_with_Error(HRESULT errorCode, const char *message, const FString &path);
|
||||
HRESULT SendMessageError_with_LastError(const char *message, const FString &path);
|
||||
HRESULT SendMessageError2(HRESULT errorCode, const char *message, const FString &path1, const FString &path2);
|
||||
HRESULT SendMessageError2_with_LastError(const char *message, const FString &path1, const FString &path2);
|
||||
HRESULT SendMessageError(const char *message, const FString &path) const;
|
||||
HRESULT SendMessageError_with_Error(HRESULT errorCode, const char *message, const FString &path) const;
|
||||
HRESULT SendMessageError_with_LastError(const char *message, const FString &path) const;
|
||||
HRESULT SendMessageError2(HRESULT errorCode, const char *message, const FString &path1, const FString &path2) const;
|
||||
HRESULT SendMessageError2_with_LastError(const char *message, const FString &path1, const FString &path2) const;
|
||||
|
||||
#if defined(_WIN32) && !defined(UNDER_CE) && !defined(Z7_SFX)
|
||||
NExtract::NZoneIdMode::EEnum ZoneMode;
|
||||
|
|
@ -501,6 +544,7 @@ public:
|
|||
|
||||
private:
|
||||
CHardLinks _hardLinks;
|
||||
CObjectVector<CPostLink> _postLinks;
|
||||
CLinkInfo _link;
|
||||
// const void *NtReparse_Data;
|
||||
// UInt32 NtReparse_Size;
|
||||
|
|
@ -512,13 +556,16 @@ private:
|
|||
const FString &fullProcessedPath_from,
|
||||
const CLinkInfo &linkInfo,
|
||||
bool &linkWasSet);
|
||||
HRESULT SetPostLinks() const;
|
||||
|
||||
public:
|
||||
// call PrepareHardLinks() after Init()
|
||||
HRESULT CreateHardLink2(const FString &newFilePath,
|
||||
const FString &existFilePath, bool &link_was_Created) const;
|
||||
HRESULT DeleteLinkFileAlways_or_RemoveEmptyDir(const FString &path, bool checkThatFileIsEmpty) const;
|
||||
HRESULT PrepareHardLinks(const CRecordVector<UInt32> *realIndices); // NULL means all items
|
||||
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
#ifdef SUPPORT_ALT_STREAMS
|
||||
CObjectVector<CIndexToPathPair> _renamedFiles;
|
||||
|
|
@ -526,6 +573,7 @@ public:
|
|||
|
||||
// call it after Init()
|
||||
|
||||
public:
|
||||
#ifndef Z7_SFX
|
||||
void SetBaseParentFolderIndex(UInt32 indexInArc)
|
||||
{
|
||||
|
|
@ -547,7 +595,6 @@ private:
|
|||
|
||||
HRESULT Read_fi_Props();
|
||||
void CorrectPathParts();
|
||||
void GetFiTimesCAM(CFiTimesCAM &pt);
|
||||
void CreateFolders();
|
||||
|
||||
HRESULT CheckExistFile(FString &fullProcessedPath, bool &needExit);
|
||||
|
|
@ -556,8 +603,8 @@ private:
|
|||
|
||||
HRESULT CloseFile();
|
||||
HRESULT CloseReparseAndFile();
|
||||
HRESULT CloseReparseAndFile2();
|
||||
HRESULT SetDirsTimes();
|
||||
HRESULT SetSecurityInfo(UInt32 indexInArc, const FString &path) const;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ bool GetSystemDir(FString &path)
|
|||
#endif // UNDER_CE
|
||||
|
||||
|
||||
bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime)
|
||||
static bool SetFileTime_Base(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime, DWORD dwFlagsAndAttributes)
|
||||
{
|
||||
#ifndef _UNICODE
|
||||
if (!g_IsNT)
|
||||
|
|
@ -137,14 +137,14 @@ bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CF
|
|||
HANDLE hDir = INVALID_HANDLE_VALUE;
|
||||
IF_USE_MAIN_PATH
|
||||
hDir = ::CreateFileW(fs2us(path), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
NULL, OPEN_EXISTING, dwFlagsAndAttributes, NULL);
|
||||
#ifdef Z7_LONG_PATH
|
||||
if (hDir == INVALID_HANDLE_VALUE && USE_SUPER_PATH)
|
||||
{
|
||||
UString superPath;
|
||||
if (GetSuperPath(path, superPath, USE_MAIN_PATH))
|
||||
hDir = ::CreateFileW(superPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
NULL, OPEN_EXISTING, dwFlagsAndAttributes, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -157,6 +157,15 @@ bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CF
|
|||
return res;
|
||||
}
|
||||
|
||||
bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime)
|
||||
{
|
||||
return SetFileTime_Base(path, cTime, aTime, mTime, FILE_FLAG_BACKUP_SEMANTICS);
|
||||
}
|
||||
|
||||
bool SetLinkFileTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime)
|
||||
{
|
||||
return SetFileTime_Base(path, cTime, aTime, mTime, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT);
|
||||
}
|
||||
|
||||
|
||||
bool SetFileAttrib(CFSTR path, DWORD attrib)
|
||||
|
|
@ -1173,17 +1182,15 @@ bool GetCurrentDir(FString &path)
|
|||
|
||||
|
||||
|
||||
bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime)
|
||||
static bool SetFileTime_Base(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime, const int flags)
|
||||
{
|
||||
// need testing
|
||||
/*
|
||||
struct utimbuf buf;
|
||||
struct stat st;
|
||||
UNUSED_VAR(cTime)
|
||||
|
||||
printf("\nstat = %s\n", path);
|
||||
int ret = stat(path, &st);
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
buf.actime = st.st_atime;
|
||||
|
|
@ -1195,47 +1202,42 @@ bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CF
|
|||
buf.actime = cur_time;
|
||||
buf.modtime = cur_time;
|
||||
}
|
||||
|
||||
if (aTime)
|
||||
{
|
||||
UInt32 ut;
|
||||
if (NTime::FileTimeToUnixTime(*aTime, ut))
|
||||
buf.actime = ut;
|
||||
}
|
||||
|
||||
if (mTime)
|
||||
{
|
||||
UInt32 ut;
|
||||
if (NTime::FileTimeToUnixTime(*mTime, ut))
|
||||
buf.modtime = ut;
|
||||
}
|
||||
|
||||
return utime(path, &buf) == 0;
|
||||
*/
|
||||
|
||||
// if (!aTime && !mTime) return true;
|
||||
|
||||
struct timespec times[2];
|
||||
UNUSED_VAR(cTime)
|
||||
|
||||
bool needChange;
|
||||
needChange = FiTime_To_timespec(aTime, times[0]);
|
||||
needChange |= FiTime_To_timespec(mTime, times[1]);
|
||||
|
||||
/*
|
||||
if (mTime)
|
||||
{
|
||||
printf("\n time = %ld.%9ld\n", mTime->tv_sec, mTime->tv_nsec);
|
||||
}
|
||||
*/
|
||||
|
||||
// if (mTime) { printf("\n time = %ld.%9ld\n", mTime->tv_sec, mTime->tv_nsec); }
|
||||
if (!needChange)
|
||||
return true;
|
||||
const int flags = 0; // follow link
|
||||
// = AT_SYMLINK_NOFOLLOW; // don't follow link
|
||||
return utimensat(AT_FDCWD, path, times, flags) == 0;
|
||||
}
|
||||
|
||||
bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime)
|
||||
{
|
||||
return SetFileTime_Base(path, cTime, aTime, mTime, 0); // (flags = 0) means follow_link
|
||||
}
|
||||
|
||||
bool SetLinkFileTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime)
|
||||
{
|
||||
return SetFileTime_Base(path, cTime, aTime, mTime, AT_SYMLINK_NOFOLLOW);
|
||||
}
|
||||
|
||||
|
||||
struct C_umask
|
||||
|
|
|
|||
|
|
@ -18,9 +18,20 @@ bool GetSystemDir(FString &path);
|
|||
WIN32 API : SetFileTime() doesn't allow to set zero timestamps in file
|
||||
but linux : allows unix time = 0 in filesystem
|
||||
*/
|
||||
|
||||
/*
|
||||
SetDirTime() can be used to set time for file or for dir.
|
||||
If path is symbolic link, SetDirTime() will follow symbolic link,
|
||||
and it will set timestamps of symbolic link's target file or dir.
|
||||
*/
|
||||
bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime);
|
||||
|
||||
/*
|
||||
SetLinkFileTime() doesn't follow symbolic link,
|
||||
and it sets timestamps for symbolic link file itself.
|
||||
If (path) is not symbolic link, it still can work (at least in some new OS versions).
|
||||
*/
|
||||
bool SetLinkFileTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime);
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<?define VerMajor = "25" ?>
|
||||
<?define VerMinor = "00" ?>
|
||||
<?define VerMinor = "01" ?>
|
||||
<?define VerBuild = "00" ?>
|
||||
<?define MmVer = "$(var.VerMajor).$(var.VerMinor)" ?>
|
||||
<?define MmHex = "$(var.VerMajor)$(var.VerMinor)" ?>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
7-Zip 25.00 Sources
|
||||
7-Zip 25.01 Sources
|
||||
-------------------
|
||||
|
||||
7-Zip is a file archiver for Windows.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,14 @@
|
|||
HISTORY of the 7-Zip source code
|
||||
--------------------------------
|
||||
|
||||
25.01 2025-08-03
|
||||
-------------------------
|
||||
- The code for handling symbolic links has been changed
|
||||
to provide greater security when extracting files from archives.
|
||||
Command line switch -snld20 can be used to bypass default security
|
||||
checks when creating symbolic links.
|
||||
|
||||
|
||||
25.00 2025-07-05
|
||||
-------------------------
|
||||
- 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
|
|||
- deflate (zip/gz) compression speed was increased by 1-3%.
|
||||
- improved support for zip, cpio and fat archives.
|
||||
- fixed some bugs and vulnerabilities.
|
||||
- the bug was fixed : CVE-2025-53816 : 7-Zip could work incorrectly for some incorrect RAR archives.
|
||||
- the bug was fixed : CVE-2025-53817 : 7-Zip could crash for some incorrect COM (Compound File) archives.
|
||||
|
||||
|
||||
24.09 2024-11-29
|
||||
|
|
|
|||
Loading…
Reference in a new issue