Compare commits

..

2 commits

Author SHA1 Message Date
unxed 14b9eb8dbc
Merge e60fa1f578 into 5e96a82794 2025-08-03 08:16:46 -05:00
Igor Pavlov 5e96a82794 25.01 2025-08-03 16:14:59 +05:00
14 changed files with 582 additions and 287 deletions

View file

@ -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"

View file

@ -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

View file

@ -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 ""

View file

@ -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,14 +931,13 @@ 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
#ifndef Z7_ST
Progress = progress;
ThreadNextGroup_Init(&ThreadNextGroup, _props.NumThreadGroups, 0); // startGroup
RINOK(Create())
for (UInt32 t = 0; t < NumThreads; t++)
#endif
#endif
{
#ifndef Z7_ST
CThreadInfo &ti = ThreadsInfo[t];

View file

@ -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

View file

@ -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 \

View file

@ -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;

View file

@ -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
@ -326,13 +330,14 @@ void CArchiveExtractCallback::Init(
_outFileStream.Release();
_bufPtrSeqOutStream.Release();
#ifdef SUPPORT_LINKS
#ifdef SUPPORT_LINKS
_hardLinks.Clear();
#endif
_postLinks.Clear();
#endif
#ifdef SUPPORT_ALT_STREAMS
#ifdef SUPPORT_ALT_STREAMS
_renamedFiles.Clear();
#endif
#endif
_ntOptions = ntOptions;
_wildcardCensor = wildcardCensor;
@ -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,12 +667,17 @@ 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;
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,52 +1999,219 @@ 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;
}
/*
in:
link.LinkPath : must be relative (non-absolute) path in any case !!!
link.isRelative / target path that must stored as created link:
== false / _dirPathPrefix_Full + link.LinkPath
== true / link.LinkPath
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)
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
{
UString path;
if (link.isRelative)
if (fi.IsDir())
{
// _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;
while (!v.IsEmpty())
{
const unsigned len = v.Back().Len();
v.DeleteBack();
if (len)
break;
}
path = MakePathFromParts(v);
NName::NormalizeDirPathPrefix(path);
if (RemoveDirAlways_if_Empty(path))
return S_OK;
}
path += link.LinkPath;
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 !!!
link.isRelative / target path that must stored as created link:
== false / _dirPathPrefix_Full + link.LinkPath
== true / link.LinkPath
*/
static HRESULT SetLink2(const CArchiveExtractCallback &callback,
const CPostLink &postLink, bool &linkWasSet)
{
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)
{
/*
path is calculated virtual target path of link
path is relative to root folder of extracted items
if (!link.isRelative), then (path == link.LinkPath)
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).
*/
if (!IsSafePath(path, link.Is_WSL()))
return SendMessageError2(0, // errorCode
"Dangerous link path was ignored",
us2fs(_item.Path), us2fs(link.LinkPath));
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.
// we want to get directory prefix of link item.
// so we remove file name (last non-empty part) from PathParts:
UStringVector v = postLink.item_PathParts;
while (!v.IsEmpty())
{
const unsigned len = v.Back().Len();
v.DeleteBack();
if (len)
break;
}
path = MakePathFromParts(v);
NName::NormalizeDirPathPrefix(path);
relativePathPrefix = path;
}
path += link.LinkPath;
/*
path is calculated virtual target path of link
path is relative to root folder of extracted items
if (!link.isRelative), then (path == link.LinkPath)
*/
isDang = false;
if (!IsSafePath(path, link.Is_WSL()))
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,36 +2285,10 @@ 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;
// printf("\nFillLinkData(): %s\n", GetOemString(target).Ptr());
@ -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,13 +2597,37 @@ HRESULT CArchiveExtractCallback::CloseReparseAndFile()
}
void CArchiveExtractCallback::SetAttrib()
static void SetAttrib_Base(const FString &path, const CProcessedFileInfo &fi,
const CArchiveExtractCallback &callback)
{
#ifndef _WIN32
#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
if (_isSymLinkCreated)
return;
#endif
#endif
if (_itemFailure
|| _diskFilePath.IsEmpty()
@ -2430,31 +2635,41 @@ 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);
}
}
#endif
if (_fi.Attrib_Defined)
{
// 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)
{
// do we need error message here in Windows and in posix?
SendMessageError_with_LastError("Cannot set file attribute", _diskFilePath);
}
}
SetAttrib_Base(_diskFilePath, _fi, *this);
}
#ifdef Z7_USE_SECURITY_CODE
HRESULT CArchiveExtractCallback::SetSecurityInfo(UInt32 indexInArc, const FString &path) const
{
if (!_stdOutMode && _extractMode && _ntOptions.NtSecurity.Val && _arc->GetRawProps)
{
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))
{
COM_TRY_BEGIN
@ -2490,27 +2705,9 @@ 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
#ifdef 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();
const HRESULT res2 = SetDirsTimes();
if (res == S_OK)
res = res2;
#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;
}

View file

@ -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;
@ -497,10 +540,11 @@ public:
UInt64 packSize);
#ifdef SUPPORT_LINKS
#ifdef SUPPORT_LINKS
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
#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;
};

View file

@ -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

View file

@ -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

View file

@ -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)" ?>

View file

@ -1,4 +1,4 @@
7-Zip 25.00 Sources
7-Zip 25.01 Sources
-------------------
7-Zip is a file archiver for Windows.

View file

@ -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