• Bug#1109782: unblock: 7zip/25.00+dfsg-1 (10/13)

    From Bastian Germann@21:1/5 to All on Wed Jul 23 19:40:02 2025
    [continued from previous message]

    + REPLACE_SLASHES_from_Linux_to_Sys(u)
    + LinkPath = u;
    + // (LinkPath) uses system path separator.
    + // windows: (LinkPath) doesn't contain linux separator (slash).
    + return true;
    }
    +

    +// in/out: (LinkPath) uses system path separator
    +// in/out: windows: (LinkPath) doesn't contain linux separator (slash).
    +// out: (LinkPath) is relative path, and LinkPath[0] is not path separator
    +// out: isRelative changed to false, if any prefix was removed.
    +// note: absolute windows links "c:\" to root will be reduced to empty string: +void CLinkInfo::Remove_AbsPathPrefixes()
    +{
    + while (!LinkPath.IsEmpty())
    + {
    + unsigned n = 0;
    + if (!Is_WSL())
    + {
    + n =
    +#ifndef _WIN32
    + isWindowsPath ?
    + NName::GetRootPrefixSize_WINDOWS(LinkPath) :
    +#endif
    + NName::GetRootPrefixSize(LinkPath);
    +/*
    + // "c:path" will be ignored later as "Dangerous absolute path"
    + // so check is not required
    + if (n == 0
    +#ifndef _WIN32
    + && isWindowsPath
    +#endif
    + && NName::IsDrivePath2(LinkPath))
    + n = 2;
    +*/
    + }
    + if (n == 0)
    + {
    + if (!IS_PATH_SEPAR(LinkPath[0]))
    + break;
    + n = 1;
    + }
    + isRelative = false; // (LinkPath) will be treated as relative to root folder of archive
    + LinkPath.DeleteFrontal(n);
    + }
    +}

    -bool CLinkInfo::Parse(const Byte *data, size_t dataSize, bool isLinuxData)
    +
    +/*
    + it removes redundant separators, if there are double separators,
    + but it keeps double separators at start of string //name/.
    + in/out: system path separator is used
    + windows: slash character (linux separator) is not treated as separator
    + windows: (path) doesn't contain linux separator (slash).
    +*/
    +static void RemoveRedundantPathSeparators(UString &path)
    {
    - Clear();
    - // this->isLinux = isLinuxData;
    -
    - if (isLinuxData)
    + wchar_t *dest = path.GetBuf();
    + const wchar_t * const start = dest;
    + const wchar_t *src = dest;
    + for (;;)
    {
    - isJunction = false;
    - isHardLink = false;
    - AString utf;
    - if (dataSize >= (1 << 12))
    - return false;
    - utf.SetFrom_CalcLen((const char *)data, (unsigned)dataSize);
    - UString u;
    - if (!ConvertUTF8ToUnicode(utf, u))
    - return false;
    - linkPath = u;
    -
    - // in linux symbolic data: we expect that linux separator '/' is used
    - // if windows link was created, then we also must use linux separator
    - if (u.IsEmpty())
    - return false;
    - const wchar_t c = u[0];
    - isRelative = !IS_PATH_SEPAR(c);
    - return true;
    + wchar_t c = *src++;
    + if (c == 0)
    + break;
    + // if (IS_PATH_SEPAR(c)) // for Windows: we can change (/) to (\).
    + if (c == WCHAR_PATH_SEPARATOR)
    + {
    + if (dest - start >= 2 && dest[-1] == WCHAR_PATH_SEPARATOR)
    + continue;
    + // c = WCHAR_PATH_SEPARATOR; // for Windows: we can change (/) to (\).
    + }
    + *dest++ = c;
    }
    + *dest = 0;
    + path.ReleaseBuf_SetLen((unsigned)(dest - path.Ptr()));
    +}

    - CReparseAttr reparse;
    - if (!reparse.Parse(data, dataSize))
    - return false;
    - isHardLink = false;
    - // isCopyLink = false;
    - linkPath = reparse.GetPath();
    - isJunction = reparse.IsMountPoint();
    -
    - if (reparse.IsSymLink_WSL())
    +
    +// in/out: (LinkPath) uses system path separator
    +// in/out: windows: (LinkPath) doesn't contain linux separator (slash).
    +// out: (LinkPath) is relative path, and LinkPath[0] is not path separator +void CLinkInfo::Normalize_to_RelativeSafe(UStringVector &removePathParts)
    +{
    + // We WILL NOT WRITE original absolute link path from archive to filesystem. + // So here we remove all root prefixes from (LinkPath).
    + // If we see any absolute root prefix, then we suppose that this prefix is virtual prefix
    + // that shows that link is relative to root folder of archive
    + RemoveRedundantPathSeparators(LinkPath);
    + // LinkPath = "\\\\?\\r:test\\test2"; // for debug
    + Remove_AbsPathPrefixes();
    + // (LinkPath) now is relative:
    + // if (isRelative == false), then (LinkPath) is relative to root folder of archive
    + // if (isRelative == true ), then (LinkPath) is relative to current item
    + if (LinkPath.IsEmpty() || isRelative || removePathParts.Size() == 0)
    + return;
    +
    + // if LinkPath is prefixed by _removePathParts, we remove these paths
    + UStringVector pathParts;
    + SplitPathToParts(LinkPath, pathParts);
    + bool badPrefix = false;
    {
    - isWSL = true;
    - isRelative = reparse.IsRelative_WSL();
    + FOR_VECTOR (i, removePathParts)
    + {
    + if (i >= pathParts.Size()
    + || CompareFileNames(removePathParts[i], pathParts[i]) != 0)
    + {
    + badPrefix = true;
    + break;
    + }
    + }
    }
    - else
    - isRelative = reparse.IsRelative_Win();
    -
    - // FIXME !!!
    - #ifndef _WIN32
    - linkPath.Replace(L'\\', WCHAR_PATH_SEPARATOR);
    - #endif
    -
    - return true;
    + if (!badPrefix)
    + pathParts.DeleteFrontal(removePathParts.Size());
    + LinkPath = MakePathFromParts(pathParts);
    + Remove_AbsPathPrefixes();
    }
    -
    +
    #endif // SUPPORT_LINKS


    @@ -2239,12 +2332,12 @@
    {
    HRESULT res = S_OK;

    - #ifdef SUPPORT_LINKS
    +#ifdef SUPPORT_LINKS

    size_t reparseSize = 0;
    bool repraseMode = false;
    bool needSetReparse = false;
    - CLinkInfo linkInfo;
    + CLinkInfo link;

    if (_bufPtrSeqOutStream)
    {
    @@ -2258,15 +2351,19 @@
    needSetReparse = reparse.Parse(_outMemBuf, reparseSize, errorCode);
    if (needSetReparse)
    {
    - UString linkPath = reparse.GetPath();
    + UString LinkPath = reparse.GetPath();
    #ifndef _WIN32
    - linkPath.Replace(L'\\', WCHAR_PATH_SEPARATOR);
    + LinkPath.Replace(L'\\', WCHAR_PATH_SEPARATOR);
    #endif
    }
    */
    - needSetReparse = linkInfo.Parse(_outMemBuf, reparseSize, _is_SymLink_in_Data_Linux);
    + needSetReparse = _is_SymLink_in_Data_Linux ?
    + link.Parse_from_LinuxData(_outMemBuf, reparseSize) :
    + link.Parse_from_WindowsReparseData(_outMemBuf, reparseSize);
    if (!needSetReparse)
    res = SendMessageError_with_LastError("Incorrect reparse stream", us2fs(_item.Path));
    + // (link.LinkPath) uses system path separator.
    + // windows: (link.LinkPath) doesn't contain linux separator (slash).
    }
    else
    {
    @@ -2281,23 +2378,18 @@
    _bufPtrSeqOutStream.Release();
    }

    - #endif // SUPPORT_LINKS
    -
    +#endif // SUPPORT_LINKS

    const HRESULT res2 = CloseFile();
    -
    if (res == S_OK)
    res = res2;
    -
    RINOK(res)

    - #ifdef SUPPORT_LINKS
    +#ifdef SUPPORT_LINKS
    if (repraseMode)
    {
    _curSize = reparseSize;
    _curSize_Defined = true;
    -
    - #ifdef SUPPORT_LINKS
    if (needSetReparse)
    {
    // in Linux : we must delete empty file before symbolic link creation @@ -2307,31 +2399,19 @@
    RINOK(SendMessageError_with_LastError("can't delete file", _diskFilePath))
    }
    {
    - /*
    - // for DEBUG ONLY: we can extract sym links as WSL links
    - // to eliminate (non-admin) errors for sym links.
    - #ifdef _WIN32
    - if (!linkInfo.isHardLink && !linkInfo.isJunction)
    - linkInfo.isWSL = true;
    - #endif
    - */
    bool linkWasSet = false;
    - RINOK(SetFromLinkPath(_diskFilePath, linkInfo, linkWasSet))
    + // link.LinkPath = "r:\\1\\2"; // for debug
    + // link.isJunction = true; // for debug
    + link.Normalize_to_RelativeSafe(_removePathParts);
    + RINOK(SetLink(_diskFilePath, link, linkWasSet))
    if (linkWasSet)
    - _isSymLinkCreated = linkInfo.IsSymLink();
    + _isSymLinkCreated = true; // link.IsSymLink();
    else
    _needSetAttrib = false;
    }
    - /*
    - if (!NFile::NIO::SetReparseData(_diskFilePath, _item.IsDir, ))
    - {
    - res = SendMessageError_with_LastError(kCantCreateSymLink, _diskFilePath);
    - }
    - */
    }
    - #endif
    }
    - #endif
    +#endif // SUPPORT_LINKS
    return res;
    }

    diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Common/ArchiveExtractCallback.h 7zip-25.00+dfsg/CPP/7zip/UI/Common/ArchiveExtractCallback.h
    --- 7zip-24.09+dfsg/CPP/7zip/UI/Common/ArchiveExtractCallback.h 2024-10-11 17:00:00.000000000 +0200
    +++ 7zip-25.00+dfsg/CPP/7zip/UI/Common/ArchiveExtractCallback.h 2025-06-16 13:00:00.000000000 +0200
    @@ -178,36 +178,50 @@

    #ifdef SUPPORT_LINKS

    +
    +enum ELinkType
    +{
    + k_LinkType_HardLink,
    + k_LinkType_PureSymLink,
    + k_LinkType_Junction,
    + k_LinkType_WSL
    + // , k_LinkType_CopyLink;
    +};
    +
    +
    struct CLinkInfo
    {
    - // bool isCopyLink;
    - bool isHardLink;
    - bool isJunction;
    + ELinkType LinkType;
    bool isRelative;
    - bool isWSL;
    - UString linkPath;
    + // if (isRelative == false), then (LinkPath) is relative to root folder of archive
    + // if (isRelative == true ), then (LinkPath) is relative to current item + bool isWindowsPath;
    + UString LinkPath;
    +
    + bool Is_HardLink() const { return LinkType == k_LinkType_HardLink; }
    + bool Is_AnySymLink() const { return LinkType != k_LinkType_HardLink; }

    - bool IsSymLink() const { return !isHardLink; }
    + bool Is_WSL() c