mirror of
https://github.com/holub/mame
synced 2025-06-24 13:26:36 +03:00
2593 lines
61 KiB
C++
2593 lines
61 KiB
C++
// ArchiveExtractCallback.cpp
|
|
|
|
#include "StdAfx.h"
|
|
|
|
#undef sprintf
|
|
#undef printf
|
|
|
|
// #include <stdio.h>
|
|
// #include "../../../../C/CpuTicks.h"
|
|
|
|
#include "../../../../C/Alloc.h"
|
|
#include "../../../../C/CpuArch.h"
|
|
|
|
|
|
#include "../../../Common/ComTry.h"
|
|
#include "../../../Common/IntToString.h"
|
|
#include "../../../Common/StringConvert.h"
|
|
#include "../../../Common/UTFConvert.h"
|
|
#include "../../../Common/Wildcard.h"
|
|
|
|
#include "../../../Windows/ErrorMsg.h"
|
|
#include "../../../Windows/FileDir.h"
|
|
#include "../../../Windows/FileFind.h"
|
|
#include "../../../Windows/FileName.h"
|
|
#include "../../../Windows/PropVariant.h"
|
|
#include "../../../Windows/PropVariantConv.h"
|
|
|
|
#if defined(_WIN32) && !defined(UNDER_CE) && !defined(Z7_SFX)
|
|
#define Z7_USE_SECURITY_CODE
|
|
#include "../../../Windows/SecurityUtils.h"
|
|
#endif
|
|
|
|
#include "../../Common/FilePathAutoRename.h"
|
|
#include "../../Common/StreamUtils.h"
|
|
|
|
#include "../Common/ExtractingFilePath.h"
|
|
#include "../Common/PropIDUtils.h"
|
|
|
|
#include "ArchiveExtractCallback.h"
|
|
|
|
using namespace NWindows;
|
|
using namespace NFile;
|
|
using namespace NDir;
|
|
|
|
static const char * const kCantAutoRename = "Cannot create file with auto name";
|
|
static const char * const kCantRenameFile = "Cannot rename existing file";
|
|
static const char * const kCantDeleteOutputFile = "Cannot delete output file";
|
|
static const char * const kCantDeleteOutputDir = "Cannot delete output folder";
|
|
static const char * const kCantOpenOutFile = "Cannot open output file";
|
|
static const char * const kCantOpenInFile = "Cannot open input file";
|
|
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";
|
|
#endif
|
|
|
|
#ifndef Z7_SFX
|
|
|
|
Z7_COM7F_IMF(COutStreamWithHash::Write(const void *data, UInt32 size, UInt32 *processedSize))
|
|
{
|
|
HRESULT result = S_OK;
|
|
if (_stream)
|
|
result = _stream->Write(data, size, &size);
|
|
if (_calculate)
|
|
_hash->Update(data, size);
|
|
_size += size;
|
|
if (processedSize)
|
|
*processedSize = size;
|
|
return result;
|
|
}
|
|
|
|
#endif // Z7_SFX
|
|
|
|
|
|
#ifdef Z7_USE_SECURITY_CODE
|
|
bool InitLocalPrivileges();
|
|
bool InitLocalPrivileges()
|
|
{
|
|
NSecurity::CAccessToken token;
|
|
if (!token.OpenProcessToken(GetCurrentProcess(),
|
|
TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES))
|
|
return false;
|
|
|
|
TOKEN_PRIVILEGES tp;
|
|
|
|
tp.PrivilegeCount = 1;
|
|
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
|
|
|
if (!::LookupPrivilegeValue(NULL, SE_SECURITY_NAME, &tp.Privileges[0].Luid))
|
|
return false;
|
|
if (!token.AdjustPrivileges(&tp))
|
|
return false;
|
|
return (GetLastError() == ERROR_SUCCESS);
|
|
}
|
|
#endif // Z7_USE_SECURITY_CODE
|
|
|
|
|
|
|
|
#if defined(_WIN32) && !defined(UNDER_CE) && !defined(Z7_SFX)
|
|
|
|
static const char * const kOfficeExtensions =
|
|
" doc dot wbk"
|
|
" docx docm dotx dotm docb wll wwl"
|
|
" xls xlt xlm"
|
|
" xlsx xlsm xltx xltm xlsb xla xlam"
|
|
" ppt pot pps ppa ppam"
|
|
" pptx pptm potx potm ppam ppsx ppsm sldx sldm"
|
|
" ";
|
|
|
|
static bool FindExt2(const char *p, const UString &name)
|
|
{
|
|
const int pathPos = name.ReverseFind_PathSepar();
|
|
const int dotPos = name.ReverseFind_Dot();
|
|
if (dotPos < 0
|
|
|| dotPos < pathPos
|
|
|| dotPos == (int)name.Len() - 1)
|
|
return false;
|
|
|
|
AString s;
|
|
for (unsigned pos = (unsigned)(dotPos + 1);; pos++)
|
|
{
|
|
const wchar_t c = name[pos];
|
|
if (c <= 0)
|
|
break;
|
|
if (c >= 0x80)
|
|
return false;
|
|
s += (char)MyCharLower_Ascii((char)c);
|
|
}
|
|
for (unsigned i = 0; p[i] != 0;)
|
|
{
|
|
unsigned j;
|
|
for (j = i; p[j] != ' '; j++);
|
|
if (s.Len() == j - i && memcmp(p + i, (const char *)s, s.Len()) == 0)
|
|
return true;
|
|
i = j + 1;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
static const FChar * const k_ZoneId_StreamName = FTEXT(":Zone.Identifier");
|
|
|
|
void ReadZoneFile_Of_BaseFile(CFSTR fileName2, CByteBuffer &buf)
|
|
{
|
|
FString fileName (fileName2);
|
|
fileName += k_ZoneId_StreamName;
|
|
|
|
buf.Free();
|
|
NIO::CInFile file;
|
|
if (!file.Open(fileName))
|
|
return;
|
|
UInt64 fileSize;
|
|
if (!file.GetLength(fileSize))
|
|
return;
|
|
if (fileSize == 0 || fileSize >= ((UInt32)1 << 16))
|
|
return;
|
|
buf.Alloc((size_t)fileSize);
|
|
size_t processed;
|
|
if (file.ReadFull(buf, (size_t)fileSize, processed) && processed == fileSize)
|
|
return;
|
|
buf.Free();
|
|
}
|
|
|
|
static bool WriteZoneFile(CFSTR fileName, const CByteBuffer &buf)
|
|
{
|
|
NIO::COutFile file;
|
|
if (!file.Create(fileName, true))
|
|
return false;
|
|
return file.WriteFull(buf, buf.Size());
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
#ifdef SUPPORT_LINKS
|
|
|
|
int CHardLinkNode::Compare(const CHardLinkNode &a) const
|
|
{
|
|
if (StreamId < a.StreamId) return -1;
|
|
if (StreamId > a.StreamId) return 1;
|
|
return MyCompare(INode, a.INode);
|
|
}
|
|
|
|
static HRESULT Archive_Get_HardLinkNode(IInArchive *archive, UInt32 index, CHardLinkNode &h, bool &defined)
|
|
{
|
|
h.INode = 0;
|
|
h.StreamId = (UInt64)(Int64)-1;
|
|
defined = false;
|
|
{
|
|
NCOM::CPropVariant prop;
|
|
RINOK(archive->GetProperty(index, kpidINode, &prop))
|
|
if (!ConvertPropVariantToUInt64(prop, h.INode))
|
|
return S_OK;
|
|
}
|
|
{
|
|
NCOM::CPropVariant prop;
|
|
RINOK(archive->GetProperty(index, kpidStreamId, &prop))
|
|
ConvertPropVariantToUInt64(prop, h.StreamId);
|
|
}
|
|
defined = true;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CArchiveExtractCallback::PrepareHardLinks(const CRecordVector<UInt32> *realIndices)
|
|
{
|
|
_hardLinks.Clear();
|
|
|
|
if (!_arc->Ask_INode)
|
|
return S_OK;
|
|
|
|
IInArchive *archive = _arc->Archive;
|
|
CRecordVector<CHardLinkNode> &hardIDs = _hardLinks.IDs;
|
|
|
|
{
|
|
UInt32 numItems;
|
|
if (realIndices)
|
|
numItems = realIndices->Size();
|
|
else
|
|
{
|
|
RINOK(archive->GetNumberOfItems(&numItems))
|
|
}
|
|
|
|
for (UInt32 i = 0; i < numItems; i++)
|
|
{
|
|
CHardLinkNode h;
|
|
bool defined;
|
|
const UInt32 realIndex = realIndices ? (*realIndices)[i] : i;
|
|
|
|
RINOK(Archive_Get_HardLinkNode(archive, realIndex, h, defined))
|
|
if (defined)
|
|
{
|
|
bool isAltStream = false;
|
|
RINOK(Archive_IsItem_AltStream(archive, realIndex, isAltStream))
|
|
if (!isAltStream)
|
|
{
|
|
bool isDir = false;
|
|
RINOK(Archive_IsItem_Dir(archive, realIndex, isDir))
|
|
if (!isDir)
|
|
hardIDs.Add(h);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
hardIDs.Sort2();
|
|
|
|
{
|
|
// we keep only items that have 2 or more items
|
|
unsigned k = 0;
|
|
unsigned numSame = 1;
|
|
for (unsigned i = 1; i < hardIDs.Size(); i++)
|
|
{
|
|
if (hardIDs[i].Compare(hardIDs[i - 1]) != 0)
|
|
numSame = 1;
|
|
else if (++numSame == 2)
|
|
{
|
|
if (i - 1 != k)
|
|
hardIDs[k] = hardIDs[i - 1];
|
|
k++;
|
|
}
|
|
}
|
|
hardIDs.DeleteFrom(k);
|
|
}
|
|
|
|
_hardLinks.PrepareLinks();
|
|
return S_OK;
|
|
}
|
|
|
|
#endif // SUPPORT_LINKS
|
|
|
|
|
|
CArchiveExtractCallback::CArchiveExtractCallback():
|
|
_arc(NULL),
|
|
Write_CTime(true),
|
|
Write_ATime(true),
|
|
Write_MTime(true),
|
|
_multiArchives(false)
|
|
{
|
|
LocalProgressSpec = new CLocalProgress();
|
|
_localProgress = LocalProgressSpec;
|
|
|
|
#ifdef Z7_USE_SECURITY_CODE
|
|
_saclEnabled = InitLocalPrivileges();
|
|
#endif
|
|
}
|
|
|
|
|
|
void CArchiveExtractCallback::InitBeforeNewArchive()
|
|
{
|
|
#if defined(_WIN32) && !defined(UNDER_CE)
|
|
ZoneBuf.Free();
|
|
#endif
|
|
}
|
|
|
|
void CArchiveExtractCallback::Init(
|
|
const CExtractNtOptions &ntOptions,
|
|
const NWildcard::CCensorNode *wildcardCensor,
|
|
const CArc *arc,
|
|
IFolderArchiveExtractCallback *extractCallback2,
|
|
bool stdOutMode, bool testMode,
|
|
const FString &directoryPath,
|
|
const UStringVector &removePathParts, bool removePartsForAltStreams,
|
|
UInt64 packSize)
|
|
{
|
|
ClearExtractedDirsInfo();
|
|
_outFileStream.Release();
|
|
_bufPtrSeqOutStream.Release();
|
|
|
|
#ifdef SUPPORT_LINKS
|
|
_hardLinks.Clear();
|
|
#endif
|
|
|
|
#ifdef SUPPORT_ALT_STREAMS
|
|
_renamedFiles.Clear();
|
|
#endif
|
|
|
|
_ntOptions = ntOptions;
|
|
_wildcardCensor = wildcardCensor;
|
|
|
|
_stdOutMode = stdOutMode;
|
|
_testMode = testMode;
|
|
|
|
// _progressTotal = 0;
|
|
// _progressTotal_Defined = false;
|
|
|
|
_packTotal = packSize;
|
|
_progressTotal = packSize;
|
|
_progressTotal_Defined = true;
|
|
|
|
_extractCallback2 = extractCallback2;
|
|
|
|
/*
|
|
_compressProgress.Release();
|
|
_extractCallback2.QueryInterface(IID_ICompressProgressInfo, &_compressProgress);
|
|
|
|
_callbackMessage.Release();
|
|
_extractCallback2.QueryInterface(IID_IArchiveExtractCallbackMessage2, &_callbackMessage);
|
|
*/
|
|
|
|
_folderArchiveExtractCallback2.Release();
|
|
_extractCallback2.QueryInterface(IID_IFolderArchiveExtractCallback2, &_folderArchiveExtractCallback2);
|
|
|
|
#ifndef Z7_SFX
|
|
|
|
ExtractToStreamCallback.Release();
|
|
_extractCallback2.QueryInterface(IID_IFolderExtractToStreamCallback, &ExtractToStreamCallback);
|
|
if (ExtractToStreamCallback)
|
|
{
|
|
Int32 useStreams = 0;
|
|
if (ExtractToStreamCallback->UseExtractToStream(&useStreams) != S_OK)
|
|
useStreams = 0;
|
|
if (useStreams == 0)
|
|
ExtractToStreamCallback.Release();
|
|
}
|
|
|
|
#endif
|
|
|
|
LocalProgressSpec->Init(extractCallback2, true);
|
|
LocalProgressSpec->SendProgress = false;
|
|
|
|
_removePathParts = removePathParts;
|
|
_removePartsForAltStreams = removePartsForAltStreams;
|
|
|
|
#ifndef Z7_SFX
|
|
_baseParentFolder = (UInt32)(Int32)-1;
|
|
_use_baseParentFolder_mode = false;
|
|
#endif
|
|
|
|
_arc = arc;
|
|
_dirPathPrefix = directoryPath;
|
|
_dirPathPrefix_Full = directoryPath;
|
|
#if defined(_WIN32) && !defined(UNDER_CE)
|
|
if (!NName::IsAltPathPrefix(_dirPathPrefix))
|
|
#endif
|
|
{
|
|
NName::NormalizeDirPathPrefix(_dirPathPrefix);
|
|
NDir::MyGetFullPathName(directoryPath, _dirPathPrefix_Full);
|
|
NName::NormalizeDirPathPrefix(_dirPathPrefix_Full);
|
|
}
|
|
}
|
|
|
|
|
|
Z7_COM7F_IMF(CArchiveExtractCallback::SetTotal(UInt64 size))
|
|
{
|
|
COM_TRY_BEGIN
|
|
_progressTotal = size;
|
|
_progressTotal_Defined = true;
|
|
if (!_multiArchives && _extractCallback2)
|
|
return _extractCallback2->SetTotal(size);
|
|
return S_OK;
|
|
COM_TRY_END
|
|
}
|
|
|
|
|
|
static void NormalizeVals(UInt64 &v1, UInt64 &v2)
|
|
{
|
|
const UInt64 kMax = (UInt64)1 << 31;
|
|
while (v1 > kMax)
|
|
{
|
|
v1 >>= 1;
|
|
v2 >>= 1;
|
|
}
|
|
}
|
|
|
|
|
|
static UInt64 MyMultDiv64(UInt64 unpCur, UInt64 unpTotal, UInt64 packTotal)
|
|
{
|
|
NormalizeVals(packTotal, unpTotal);
|
|
NormalizeVals(unpCur, unpTotal);
|
|
if (unpTotal == 0)
|
|
unpTotal = 1;
|
|
return unpCur * packTotal / unpTotal;
|
|
}
|
|
|
|
|
|
Z7_COM7F_IMF(CArchiveExtractCallback::SetCompleted(const UInt64 *completeValue))
|
|
{
|
|
COM_TRY_BEGIN
|
|
|
|
if (!_extractCallback2)
|
|
return S_OK;
|
|
|
|
UInt64 packCur;
|
|
if (_multiArchives)
|
|
{
|
|
packCur = LocalProgressSpec->InSize;
|
|
if (completeValue && _progressTotal_Defined)
|
|
packCur += MyMultDiv64(*completeValue, _progressTotal, _packTotal);
|
|
completeValue = &packCur;
|
|
}
|
|
return _extractCallback2->SetCompleted(completeValue);
|
|
|
|
COM_TRY_END
|
|
}
|
|
|
|
|
|
Z7_COM7F_IMF(CArchiveExtractCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize))
|
|
{
|
|
COM_TRY_BEGIN
|
|
return _localProgress->SetRatioInfo(inSize, outSize);
|
|
COM_TRY_END
|
|
}
|
|
|
|
|
|
void CArchiveExtractCallback::CreateComplexDirectory(const UStringVector &dirPathParts, FString &fullPath)
|
|
{
|
|
// we use (_item.IsDir) in this function
|
|
|
|
bool isAbsPath = false;
|
|
|
|
if (!dirPathParts.IsEmpty())
|
|
{
|
|
const UString &s = dirPathParts[0];
|
|
if (s.IsEmpty())
|
|
isAbsPath = true;
|
|
#if defined(_WIN32) && !defined(UNDER_CE)
|
|
else
|
|
{
|
|
if (NName::IsDrivePath2(s))
|
|
isAbsPath = true;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (_pathMode == NExtract::NPathMode::kAbsPaths && isAbsPath)
|
|
fullPath.Empty();
|
|
else
|
|
fullPath = _dirPathPrefix;
|
|
|
|
FOR_VECTOR (i, dirPathParts)
|
|
{
|
|
if (i != 0)
|
|
fullPath.Add_PathSepar();
|
|
const UString &s = dirPathParts[i];
|
|
fullPath += us2fs(s);
|
|
|
|
const bool isFinalDir = (i == dirPathParts.Size() - 1 && _item.IsDir);
|
|
|
|
if (fullPath.IsEmpty())
|
|
{
|
|
if (isFinalDir)
|
|
_itemFailure = true;
|
|
continue;
|
|
}
|
|
|
|
#if defined(_WIN32) && !defined(UNDER_CE)
|
|
if (_pathMode == NExtract::NPathMode::kAbsPaths)
|
|
if (i == 0 && s.Len() == 2 && NName::IsDrivePath2(s))
|
|
{
|
|
if (isFinalDir)
|
|
{
|
|
// we don't want to call SetAttrib() for root drive path
|
|
_itemFailure = true;
|
|
}
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
// bool res =
|
|
CreateDir(fullPath);
|
|
// if (!res)
|
|
if (isFinalDir)
|
|
{
|
|
if (!NFile::NFind::DoesDirExist(fullPath))
|
|
{
|
|
_itemFailure = true;
|
|
SendMessageError("Cannot create folder", fullPath);
|
|
// SendMessageError_with_LastError()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT CArchiveExtractCallback::GetTime(UInt32 index, PROPID propID, CArcTime &ft)
|
|
{
|
|
ft.Clear();
|
|
NCOM::CPropVariant prop;
|
|
RINOK(_arc->Archive->GetProperty(index, propID, &prop))
|
|
if (prop.vt == VT_FILETIME)
|
|
ft.Set_From_Prop(prop);
|
|
else if (prop.vt != VT_EMPTY)
|
|
return E_FAIL;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CArchiveExtractCallback::GetUnpackSize()
|
|
{
|
|
return _arc->GetItem_Size(_index, _curSize, _curSize_Defined);
|
|
}
|
|
|
|
static void AddPathToMessage(UString &s, const FString &path)
|
|
{
|
|
s += " : ";
|
|
s += fs2us(path);
|
|
}
|
|
|
|
HRESULT CArchiveExtractCallback::SendMessageError(const char *message, const FString &path)
|
|
{
|
|
UString s (message);
|
|
AddPathToMessage(s, path);
|
|
return _extractCallback2->MessageError(s);
|
|
}
|
|
|
|
HRESULT CArchiveExtractCallback::SendMessageError_with_LastError(const char *message, const FString &path)
|
|
{
|
|
DWORD errorCode = GetLastError();
|
|
if (errorCode == 0)
|
|
errorCode = (DWORD)E_FAIL;
|
|
UString s (message);
|
|
{
|
|
s += " : ";
|
|
s += NError::MyFormatMessage(errorCode);
|
|
}
|
|
AddPathToMessage(s, path);
|
|
return _extractCallback2->MessageError(s);
|
|
}
|
|
|
|
HRESULT CArchiveExtractCallback::SendMessageError2(HRESULT errorCode, const char *message, const FString &path1, const FString &path2)
|
|
{
|
|
UString s (message);
|
|
if (errorCode != 0)
|
|
{
|
|
s += " : ";
|
|
s += NError::MyFormatMessage(errorCode);
|
|
}
|
|
AddPathToMessage(s, path1);
|
|
AddPathToMessage(s, path2);
|
|
return _extractCallback2->MessageError(s);
|
|
}
|
|
|
|
#ifndef Z7_SFX
|
|
|
|
Z7_COM7F_IMF(CGetProp::GetProp(PROPID propID, PROPVARIANT *value))
|
|
{
|
|
/*
|
|
if (propID == kpidName)
|
|
{
|
|
COM_TRY_BEGIN
|
|
NCOM::CPropVariant prop = Name;
|
|
prop.Detach(value);
|
|
return S_OK;
|
|
COM_TRY_END
|
|
}
|
|
*/
|
|
return Arc->Archive->GetProperty(IndexInArc, propID, value);
|
|
}
|
|
|
|
#endif // Z7_SFX
|
|
|
|
|
|
#ifdef SUPPORT_LINKS
|
|
|
|
static UString GetDirPrefixOf(const UString &src)
|
|
{
|
|
UString s (src);
|
|
if (!s.IsEmpty())
|
|
{
|
|
if (IsPathSepar(s.Back()))
|
|
s.DeleteBack();
|
|
int pos = s.ReverseFind_PathSepar();
|
|
s.DeleteFrom((unsigned)(pos + 1));
|
|
}
|
|
return s;
|
|
}
|
|
|
|
#endif // SUPPORT_LINKS
|
|
|
|
struct CLinkLevelsInfo
|
|
{
|
|
bool IsAbsolute;
|
|
int LowLevel;
|
|
int FinalLevel;
|
|
|
|
void Parse(const UString &path);
|
|
};
|
|
|
|
void CLinkLevelsInfo::Parse(const UString &path)
|
|
{
|
|
IsAbsolute = NName::IsAbsolutePath(path);
|
|
|
|
LowLevel = 0;
|
|
FinalLevel = 0;
|
|
|
|
UStringVector parts;
|
|
SplitPathToParts(path, parts);
|
|
int level = 0;
|
|
|
|
FOR_VECTOR (i, parts)
|
|
{
|
|
const UString &s = parts[i];
|
|
if (s.IsEmpty())
|
|
{
|
|
if (i == 0)
|
|
IsAbsolute = true;
|
|
continue;
|
|
}
|
|
if (s == L".")
|
|
continue;
|
|
if (s == L"..")
|
|
{
|
|
level--;
|
|
if (LowLevel > level)
|
|
LowLevel = level;
|
|
}
|
|
else
|
|
level++;
|
|
}
|
|
|
|
FinalLevel = level;
|
|
}
|
|
|
|
|
|
bool IsSafePath(const UString &path);
|
|
bool IsSafePath(const UString &path)
|
|
{
|
|
CLinkLevelsInfo levelsInfo;
|
|
levelsInfo.Parse(path);
|
|
return !levelsInfo.IsAbsolute
|
|
&& levelsInfo.LowLevel >= 0
|
|
&& levelsInfo.FinalLevel > 0;
|
|
}
|
|
|
|
|
|
bool CensorNode_CheckPath2(const NWildcard::CCensorNode &node, const CReadArcItem &item, bool &include);
|
|
bool CensorNode_CheckPath2(const NWildcard::CCensorNode &node, const CReadArcItem &item, bool &include)
|
|
{
|
|
bool found = false;
|
|
|
|
// CheckPathVect() doesn't check path to Parent nodes
|
|
if (node.CheckPathVect(item.PathParts, !item.MainIsDir, include))
|
|
{
|
|
if (!include)
|
|
return true;
|
|
|
|
#ifdef SUPPORT_ALT_STREAMS
|
|
if (!item.IsAltStream)
|
|
return true;
|
|
#endif
|
|
|
|
found = true;
|
|
}
|
|
|
|
#ifdef SUPPORT_ALT_STREAMS
|
|
|
|
if (!item.IsAltStream)
|
|
return false;
|
|
|
|
UStringVector pathParts2 = item.PathParts;
|
|
if (pathParts2.IsEmpty())
|
|
pathParts2.AddNew();
|
|
UString &back = pathParts2.Back();
|
|
back += ':';
|
|
back += item.AltStreamName;
|
|
bool include2;
|
|
|
|
if (node.CheckPathVect(pathParts2,
|
|
true, // isFile,
|
|
include2))
|
|
{
|
|
include = include2;
|
|
return true;
|
|
}
|
|
|
|
#endif // SUPPORT_ALT_STREAMS
|
|
|
|
return found;
|
|
}
|
|
|
|
|
|
bool CensorNode_CheckPath(const NWildcard::CCensorNode &node, const CReadArcItem &item)
|
|
{
|
|
bool include;
|
|
if (CensorNode_CheckPath2(node, item, include))
|
|
return include;
|
|
return false;
|
|
}
|
|
|
|
|
|
static FString MakePath_from_2_Parts(const FString &prefix, const FString &path)
|
|
{
|
|
FString s (prefix);
|
|
#if defined(_WIN32) && !defined(UNDER_CE)
|
|
if (!path.IsEmpty() && path[0] == ':' && !prefix.IsEmpty() && IsPathSepar(prefix.Back()))
|
|
{
|
|
if (!NName::IsDriveRootPath_SuperAllowed(prefix))
|
|
s.DeleteBack();
|
|
}
|
|
#endif
|
|
s += path;
|
|
return s;
|
|
}
|
|
|
|
|
|
|
|
#ifdef SUPPORT_LINKS
|
|
|
|
/*
|
|
struct CTempMidBuffer
|
|
{
|
|
void *Buf;
|
|
|
|
CTempMidBuffer(size_t size): Buf(NULL) { Buf = ::MidAlloc(size); }
|
|
~CTempMidBuffer() { ::MidFree(Buf); }
|
|
};
|
|
|
|
HRESULT CArchiveExtractCallback::MyCopyFile(ISequentialOutStream *outStream)
|
|
{
|
|
const size_t kBufSize = 1 << 16;
|
|
CTempMidBuffer buf(kBufSize);
|
|
if (!buf.Buf)
|
|
return E_OUTOFMEMORY;
|
|
|
|
NIO::CInFile inFile;
|
|
NIO::COutFile outFile;
|
|
|
|
if (!inFile.Open(_copyFile_Path))
|
|
return SendMessageError_with_LastError("Open error", _copyFile_Path);
|
|
|
|
for (;;)
|
|
{
|
|
UInt32 num;
|
|
|
|
if (!inFile.Read(buf.Buf, kBufSize, num))
|
|
return SendMessageError_with_LastError("Read error", _copyFile_Path);
|
|
|
|
if (num == 0)
|
|
return S_OK;
|
|
|
|
|
|
RINOK(WriteStream(outStream, buf.Buf, num));
|
|
}
|
|
}
|
|
*/
|
|
|
|
|
|
HRESULT CArchiveExtractCallback::ReadLink()
|
|
{
|
|
IInArchive *archive = _arc->Archive;
|
|
const UInt32 index = _index;
|
|
_link.Clear();
|
|
|
|
{
|
|
NCOM::CPropVariant prop;
|
|
RINOK(archive->GetProperty(index, kpidHardLink, &prop))
|
|
if (prop.vt == VT_BSTR)
|
|
{
|
|
_link.isHardLink = true;
|
|
// _link.isCopyLink = false;
|
|
_link.isRelative = false; // RAR5, TAR: hard links are from root folder of archive
|
|
_link.linkPath.SetFromBstr(prop.bstrVal);
|
|
}
|
|
else if (prop.vt != VT_EMPTY)
|
|
return E_FAIL;
|
|
}
|
|
|
|
/*
|
|
{
|
|
NCOM::CPropVariant prop;
|
|
RINOK(archive->GetProperty(index, kpidCopyLink, &prop));
|
|
if (prop.vt == VT_BSTR)
|
|
{
|
|
_link.isHardLink = false;
|
|
_link.isCopyLink = true;
|
|
_link.isRelative = false; // RAR5: copy links are from root folder of archive
|
|
_link.linkPath.SetFromBstr(prop.bstrVal);
|
|
}
|
|
else if (prop.vt != VT_EMPTY)
|
|
return E_FAIL;
|
|
}
|
|
*/
|
|
|
|
{
|
|
NCOM::CPropVariant prop;
|
|
RINOK(archive->GetProperty(index, kpidSymLink, &prop))
|
|
if (prop.vt == VT_BSTR)
|
|
{
|
|
_link.isHardLink = false;
|
|
// _link.isCopyLink = false;
|
|
_link.isRelative = true; // RAR5, TAR: symbolic links can be relative
|
|
_link.linkPath.SetFromBstr(prop.bstrVal);
|
|
}
|
|
else if (prop.vt != VT_EMPTY)
|
|
return E_FAIL;
|
|
}
|
|
|
|
NtReparse_Data = NULL;
|
|
NtReparse_Size = 0;
|
|
|
|
if (_link.linkPath.IsEmpty() && _arc->GetRawProps)
|
|
{
|
|
const void *data;
|
|
UInt32 dataSize;
|
|
UInt32 propType;
|
|
|
|
_arc->GetRawProps->GetRawProp(_index, kpidNtReparse, &data, &dataSize, &propType);
|
|
|
|
// if (dataSize == 1234567) // for debug: unpacking without reparse
|
|
if (dataSize != 0)
|
|
{
|
|
if (propType != NPropDataType::kRaw)
|
|
return E_FAIL;
|
|
|
|
// 21.06: we need kpidNtReparse in linux for wim archives created in Windows
|
|
// #ifdef _WIN32
|
|
|
|
NtReparse_Data = data;
|
|
NtReparse_Size = dataSize;
|
|
|
|
CReparseAttr reparse;
|
|
bool isOkReparse = reparse.Parse((const Byte *)data, dataSize);
|
|
if (isOkReparse)
|
|
{
|
|
_link.isHardLink = false;
|
|
// _link.isCopyLink = false;
|
|
_link.linkPath = reparse.GetPath();
|
|
_link.isJunction = reparse.IsMountPoint();
|
|
|
|
if (reparse.IsSymLink_WSL())
|
|
{
|
|
_link.isWSL = true;
|
|
_link.isRelative = reparse.IsRelative_WSL();
|
|
}
|
|
else
|
|
_link.isRelative = reparse.IsRelative_Win();
|
|
|
|
// const AString s = GetAnsiString(_link.linkPath);
|
|
// printf("\n_link.linkPath: %s\n", s.Ptr());
|
|
|
|
#ifndef _WIN32
|
|
_link.linkPath.Replace(L'\\', WCHAR_PATH_SEPARATOR);
|
|
#endif
|
|
}
|
|
// #endif
|
|
}
|
|
}
|
|
|
|
if (_link.linkPath.IsEmpty())
|
|
return S_OK;
|
|
|
|
{
|
|
#ifdef _WIN32
|
|
_link.linkPath.Replace(L'/', WCHAR_PATH_SEPARATOR);
|
|
#endif
|
|
|
|
// rar5 uses "\??\" prefix for absolute links
|
|
if (_link.linkPath.IsPrefixedBy(WSTRING_PATH_SEPARATOR L"??" WSTRING_PATH_SEPARATOR))
|
|
{
|
|
_link.isRelative = false;
|
|
_link.linkPath.DeleteFrontal(4);
|
|
}
|
|
|
|
for (;;)
|
|
// while (NName::IsAbsolutePath(linkPath))
|
|
{
|
|
unsigned n = NName::GetRootPrefixSize(_link.linkPath);
|
|
if (n == 0)
|
|
break;
|
|
_link.isRelative = false;
|
|
_link.linkPath.DeleteFrontal(n);
|
|
}
|
|
}
|
|
|
|
if (_link.linkPath.IsEmpty())
|
|
return S_OK;
|
|
|
|
if (!_link.isRelative && _removePathParts.Size() != 0)
|
|
{
|
|
UStringVector pathParts;
|
|
SplitPathToParts(_link.linkPath, pathParts);
|
|
bool badPrefix = false;
|
|
FOR_VECTOR (i, _removePathParts)
|
|
{
|
|
if (CompareFileNames(_removePathParts[i], pathParts[i]) != 0)
|
|
{
|
|
badPrefix = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!badPrefix)
|
|
pathParts.DeleteFrontal(_removePathParts.Size());
|
|
_link.linkPath = MakePathFromParts(pathParts);
|
|
}
|
|
|
|
/*
|
|
if (!_link.linkPath.IsEmpty())
|
|
{
|
|
printf("\n_link %s to -> %s\n", GetOemString(_item.Path).Ptr(), GetOemString(_link.linkPath).Ptr());
|
|
}
|
|
*/
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
#endif // SUPPORT_LINKS
|
|
|
|
|
|
#ifndef _WIN32
|
|
|
|
static HRESULT GetOwner(IInArchive *archive,
|
|
UInt32 index, UInt32 pidName, UInt32 pidId, COwnerInfo &res)
|
|
{
|
|
{
|
|
NWindows::NCOM::CPropVariant prop;
|
|
RINOK(archive->GetProperty(index, pidId, &prop))
|
|
if (prop.vt == VT_UI4)
|
|
{
|
|
res.Id_Defined = true;
|
|
res.Id = prop.ulVal; // for debug
|
|
// res.Id++; // for debug
|
|
// if (pidId == kpidGroupId) res.Id += 7; // for debug
|
|
// res.Id = 0; // for debug
|
|
}
|
|
else if (prop.vt != VT_EMPTY)
|
|
return E_INVALIDARG;
|
|
}
|
|
{
|
|
NWindows::NCOM::CPropVariant prop;
|
|
RINOK(archive->GetProperty(index, pidName, &prop))
|
|
if (prop.vt == VT_BSTR)
|
|
{
|
|
const UString s = prop.bstrVal;
|
|
ConvertUnicodeToUTF8(s, res.Name);
|
|
}
|
|
else if (prop.vt == VT_UI4)
|
|
{
|
|
res.Id_Defined = true;
|
|
res.Id = prop.ulVal;
|
|
}
|
|
else if (prop.vt != VT_EMPTY)
|
|
return E_INVALIDARG;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
HRESULT CArchiveExtractCallback::Read_fi_Props()
|
|
{
|
|
IInArchive *archive = _arc->Archive;
|
|
const UInt32 index = _index;
|
|
|
|
_fi.Attrib_Defined = false;
|
|
|
|
#ifndef _WIN32
|
|
_fi.Owner.Clear();
|
|
_fi.Group.Clear();
|
|
#endif
|
|
|
|
{
|
|
NCOM::CPropVariant prop;
|
|
RINOK(archive->GetProperty(index, kpidPosixAttrib, &prop))
|
|
if (prop.vt == VT_UI4)
|
|
{
|
|
_fi.SetFromPosixAttrib(prop.ulVal);
|
|
}
|
|
else if (prop.vt != VT_EMPTY)
|
|
return E_FAIL;
|
|
}
|
|
|
|
{
|
|
NCOM::CPropVariant prop;
|
|
RINOK(archive->GetProperty(index, kpidAttrib, &prop))
|
|
if (prop.vt == VT_UI4)
|
|
{
|
|
_fi.Attrib = prop.ulVal;
|
|
_fi.Attrib_Defined = true;
|
|
}
|
|
else if (prop.vt != VT_EMPTY)
|
|
return E_FAIL;
|
|
}
|
|
|
|
RINOK(GetTime(index, kpidCTime, _fi.CTime))
|
|
RINOK(GetTime(index, kpidATime, _fi.ATime))
|
|
RINOK(GetTime(index, kpidMTime, _fi.MTime))
|
|
|
|
#ifndef _WIN32
|
|
if (_ntOptions.ExtractOwner)
|
|
{
|
|
// SendMessageError_with_LastError("_ntOptions.ExtractOwner", _diskFilePath);
|
|
GetOwner(archive, index, kpidUser, kpidUserId, _fi.Owner);
|
|
GetOwner(archive, index, kpidGroup, kpidGroupId, _fi.Group);
|
|
}
|
|
#endif
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
void CArchiveExtractCallback::CorrectPathParts()
|
|
{
|
|
UStringVector &pathParts = _item.PathParts;
|
|
|
|
#ifdef SUPPORT_ALT_STREAMS
|
|
if (!_item.IsAltStream
|
|
|| !pathParts.IsEmpty()
|
|
|| !(_removePartsForAltStreams || _pathMode == NExtract::NPathMode::kNoPathsAlt))
|
|
#endif
|
|
Correct_FsPath(_pathMode == NExtract::NPathMode::kAbsPaths, _keepAndReplaceEmptyDirPrefixes, pathParts, _item.MainIsDir);
|
|
|
|
#ifdef SUPPORT_ALT_STREAMS
|
|
|
|
if (_item.IsAltStream)
|
|
{
|
|
UString s (_item.AltStreamName);
|
|
Correct_AltStream_Name(s);
|
|
bool needColon = true;
|
|
|
|
if (pathParts.IsEmpty())
|
|
{
|
|
pathParts.AddNew();
|
|
if (_removePartsForAltStreams || _pathMode == NExtract::NPathMode::kNoPathsAlt)
|
|
needColon = false;
|
|
}
|
|
#ifdef _WIN32
|
|
else if (_pathMode == NExtract::NPathMode::kAbsPaths &&
|
|
NWildcard::GetNumPrefixParts_if_DrivePath(pathParts) == pathParts.Size())
|
|
pathParts.AddNew();
|
|
#endif
|
|
|
|
UString &name = pathParts.Back();
|
|
if (needColon)
|
|
name += (char)(_ntOptions.ReplaceColonForAltStream ? '_' : ':');
|
|
name += s;
|
|
}
|
|
|
|
#endif // SUPPORT_ALT_STREAMS
|
|
}
|
|
|
|
|
|
void CArchiveExtractCallback::GetFiTimesCAM(CFiTimesCAM &pt)
|
|
{
|
|
pt.CTime_Defined = false;
|
|
pt.ATime_Defined = false;
|
|
pt.MTime_Defined = false;
|
|
|
|
if (Write_MTime)
|
|
{
|
|
if (_fi.MTime.Def)
|
|
{
|
|
_fi.MTime.Write_To_FiTime(pt.MTime);
|
|
pt.MTime_Defined = true;
|
|
}
|
|
else if (_arc->MTime.Def)
|
|
{
|
|
_arc->MTime.Write_To_FiTime(pt.MTime);
|
|
pt.MTime_Defined = true;
|
|
}
|
|
}
|
|
|
|
if (Write_CTime && _fi.CTime.Def)
|
|
{
|
|
_fi.CTime.Write_To_FiTime(pt.CTime);
|
|
pt.CTime_Defined = true;
|
|
}
|
|
|
|
if (Write_ATime && _fi.ATime.Def)
|
|
{
|
|
_fi.ATime.Write_To_FiTime(pt.ATime);
|
|
pt.ATime_Defined = true;
|
|
}
|
|
}
|
|
|
|
|
|
void CArchiveExtractCallback::CreateFolders()
|
|
{
|
|
// 21.04 : we don't change original (_item.PathParts) here
|
|
UStringVector pathParts = _item.PathParts;
|
|
|
|
if (!pathParts.IsEmpty())
|
|
{
|
|
/* v23: if we extract symlink, and we know that it links to dir:
|
|
Linux: we don't create dir item (symlink_from_path) here.
|
|
Windows: SetReparseData() will create dir item, if it doesn't exist,
|
|
but if we create dir item here, it's not problem. */
|
|
if (!_item.IsDir
|
|
#ifdef SUPPORT_LINKS
|
|
#ifndef WIN32
|
|
|| !_link.linkPath.IsEmpty()
|
|
#endif
|
|
#endif
|
|
)
|
|
pathParts.DeleteBack();
|
|
}
|
|
|
|
if (pathParts.IsEmpty())
|
|
return;
|
|
|
|
FString fullPathNew;
|
|
CreateComplexDirectory(pathParts, fullPathNew);
|
|
|
|
if (!_item.IsDir)
|
|
return;
|
|
|
|
if (_itemFailure)
|
|
return;
|
|
|
|
CDirPathTime pt;
|
|
GetFiTimesCAM(pt);
|
|
|
|
if (pt.IsSomeTimeDefined())
|
|
{
|
|
pt.Path = fullPathNew;
|
|
pt.SetDirTime();
|
|
_extractedFolders.Add(pt);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
CheckExistFile(fullProcessedPath)
|
|
it can change: fullProcessedPath, _isRenamed, _overwriteMode
|
|
(needExit = true) means that we must exit GetStream() even for S_OK result.
|
|
*/
|
|
|
|
HRESULT CArchiveExtractCallback::CheckExistFile(FString &fullProcessedPath, bool &needExit)
|
|
{
|
|
needExit = true; // it was set already before
|
|
|
|
NFind::CFileInfo fileInfo;
|
|
|
|
if (fileInfo.Find(fullProcessedPath))
|
|
{
|
|
if (_overwriteMode == NExtract::NOverwriteMode::kSkip)
|
|
return S_OK;
|
|
|
|
if (_overwriteMode == NExtract::NOverwriteMode::kAsk)
|
|
{
|
|
const int slashPos = fullProcessedPath.ReverseFind_PathSepar();
|
|
const FString realFullProcessedPath = fullProcessedPath.Left((unsigned)(slashPos + 1)) + fileInfo.Name;
|
|
|
|
/* (fileInfo) can be symbolic link.
|
|
we can show final file properties here. */
|
|
|
|
FILETIME ft1;
|
|
FiTime_To_FILETIME(fileInfo.MTime, ft1);
|
|
|
|
Int32 overwriteResult;
|
|
RINOK(_extractCallback2->AskOverwrite(
|
|
fs2us(realFullProcessedPath), &ft1, &fileInfo.Size, _item.Path,
|
|
_fi.MTime.Def ? &_fi.MTime.FT : NULL,
|
|
_curSize_Defined ? &_curSize : NULL,
|
|
&overwriteResult))
|
|
|
|
switch (overwriteResult)
|
|
{
|
|
case NOverwriteAnswer::kCancel:
|
|
return E_ABORT;
|
|
case NOverwriteAnswer::kNo:
|
|
return S_OK;
|
|
case NOverwriteAnswer::kNoToAll:
|
|
_overwriteMode = NExtract::NOverwriteMode::kSkip;
|
|
return S_OK;
|
|
|
|
case NOverwriteAnswer::kYes:
|
|
break;
|
|
case NOverwriteAnswer::kYesToAll:
|
|
_overwriteMode = NExtract::NOverwriteMode::kOverwrite;
|
|
break;
|
|
case NOverwriteAnswer::kAutoRename:
|
|
_overwriteMode = NExtract::NOverwriteMode::kRename;
|
|
break;
|
|
default:
|
|
return E_FAIL;
|
|
}
|
|
} // NExtract::NOverwriteMode::kAsk
|
|
|
|
if (_overwriteMode == NExtract::NOverwriteMode::kRename)
|
|
{
|
|
if (!AutoRenamePath(fullProcessedPath))
|
|
{
|
|
RINOK(SendMessageError(kCantAutoRename, fullProcessedPath))
|
|
return E_FAIL;
|
|
}
|
|
_isRenamed = true;
|
|
}
|
|
else if (_overwriteMode == NExtract::NOverwriteMode::kRenameExisting)
|
|
{
|
|
FString existPath (fullProcessedPath);
|
|
if (!AutoRenamePath(existPath))
|
|
{
|
|
RINOK(SendMessageError(kCantAutoRename, fullProcessedPath))
|
|
return E_FAIL;
|
|
}
|
|
// MyMoveFile can rename folders. So it's OK to use it for folders too
|
|
if (!MyMoveFile(fullProcessedPath, existPath))
|
|
{
|
|
HRESULT errorCode = GetLastError_noZero_HRESULT();
|
|
RINOK(SendMessageError2(errorCode, kCantRenameFile, existPath, fullProcessedPath))
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
else // not Rename*
|
|
{
|
|
if (fileInfo.IsDir())
|
|
{
|
|
// do we need to delete all files in folder?
|
|
if (!RemoveDir(fullProcessedPath))
|
|
{
|
|
RINOK(SendMessageError_with_LastError(kCantDeleteOutputDir, fullProcessedPath))
|
|
return S_OK;
|
|
}
|
|
}
|
|
else // fileInfo is not Dir
|
|
{
|
|
if (NFind::DoesFileExist_Raw(fullProcessedPath))
|
|
if (!DeleteFileAlways(fullProcessedPath))
|
|
if (GetLastError() != ERROR_FILE_NOT_FOUND) // check it in linux
|
|
{
|
|
RINOK(SendMessageError_with_LastError(kCantDeleteOutputFile, fullProcessedPath))
|
|
return S_OK;
|
|
// return E_FAIL;
|
|
}
|
|
} // fileInfo is not Dir
|
|
} // not Rename*
|
|
}
|
|
else // not Find(fullProcessedPath)
|
|
{
|
|
#if defined(_WIN32) && !defined(UNDER_CE)
|
|
// we need to clear READ-ONLY of parent before creating alt stream
|
|
int colonPos = NName::FindAltStreamColon(fullProcessedPath);
|
|
if (colonPos >= 0 && fullProcessedPath[(unsigned)colonPos + 1] != 0)
|
|
{
|
|
FString parentFsPath (fullProcessedPath);
|
|
parentFsPath.DeleteFrom((unsigned)colonPos);
|
|
NFind::CFileInfo parentFi;
|
|
if (parentFi.Find(parentFsPath))
|
|
{
|
|
if (parentFi.IsReadOnly())
|
|
SetFileAttrib(parentFsPath, parentFi.Attrib & ~(DWORD)FILE_ATTRIBUTE_READONLY);
|
|
}
|
|
}
|
|
#endif // defined(_WIN32) && !defined(UNDER_CE)
|
|
}
|
|
|
|
needExit = false;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT CArchiveExtractCallback::GetExtractStream(CMyComPtr<ISequentialOutStream> &outStreamLoc, bool &needExit)
|
|
{
|
|
needExit = true;
|
|
|
|
RINOK(Read_fi_Props())
|
|
|
|
#ifdef SUPPORT_LINKS
|
|
IInArchive *archive = _arc->Archive;
|
|
#endif
|
|
|
|
const UInt32 index = _index;
|
|
|
|
bool isAnti = false;
|
|
RINOK(_arc->IsItem_Anti(index, isAnti))
|
|
|
|
CorrectPathParts();
|
|
UString processedPath (MakePathFromParts(_item.PathParts));
|
|
|
|
if (!isAnti)
|
|
{
|
|
// 21.04: CreateFolders doesn't change (_item.PathParts)
|
|
CreateFolders();
|
|
}
|
|
|
|
FString fullProcessedPath (us2fs(processedPath));
|
|
if (_pathMode != NExtract::NPathMode::kAbsPaths
|
|
|| !NName::IsAbsolutePath(processedPath))
|
|
{
|
|
fullProcessedPath = MakePath_from_2_Parts(_dirPathPrefix, fullProcessedPath);
|
|
}
|
|
|
|
#ifdef SUPPORT_ALT_STREAMS
|
|
if (_item.IsAltStream && _item.ParentIndex != (UInt32)(Int32)-1)
|
|
{
|
|
const int renIndex = _renamedFiles.FindInSorted(CIndexToPathPair(_item.ParentIndex));
|
|
if (renIndex != -1)
|
|
{
|
|
const CIndexToPathPair &pair = _renamedFiles[(unsigned)renIndex];
|
|
fullProcessedPath = pair.Path;
|
|
fullProcessedPath += ':';
|
|
UString s (_item.AltStreamName);
|
|
Correct_AltStream_Name(s);
|
|
fullProcessedPath += us2fs(s);
|
|
}
|
|
}
|
|
#endif // SUPPORT_ALT_STREAMS
|
|
|
|
if (_item.IsDir)
|
|
{
|
|
_diskFilePath = fullProcessedPath;
|
|
if (isAnti)
|
|
RemoveDir(_diskFilePath);
|
|
#ifdef SUPPORT_LINKS
|
|
if (_link.linkPath.IsEmpty())
|
|
#endif
|
|
{
|
|
if (!isAnti)
|
|
SetAttrib();
|
|
return S_OK;
|
|
}
|
|
}
|
|
else if (!_isSplit)
|
|
{
|
|
RINOK(CheckExistFile(fullProcessedPath, needExit))
|
|
if (needExit)
|
|
return S_OK;
|
|
needExit = true;
|
|
}
|
|
|
|
_diskFilePath = fullProcessedPath;
|
|
|
|
|
|
if (isAnti)
|
|
{
|
|
needExit = false;
|
|
return S_OK;
|
|
}
|
|
|
|
// not anti
|
|
|
|
#ifdef SUPPORT_LINKS
|
|
|
|
if (!_link.linkPath.IsEmpty())
|
|
{
|
|
#ifndef UNDER_CE
|
|
{
|
|
bool linkWasSet = false;
|
|
RINOK(SetFromLinkPath(fullProcessedPath, _link, linkWasSet))
|
|
if (linkWasSet)
|
|
{
|
|
_isSymLinkCreated = _link.IsSymLink();
|
|
SetAttrib();
|
|
// printf("\nlinkWasSet %s\n", GetAnsiString(_diskFilePath));
|
|
}
|
|
}
|
|
#endif // UNDER_CE
|
|
|
|
// if (_copyFile_Path.IsEmpty())
|
|
{
|
|
needExit = false;
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
if (!_hardLinks.IDs.IsEmpty() && !_item.IsAltStream && !_item.IsDir)
|
|
{
|
|
CHardLinkNode h;
|
|
bool defined;
|
|
RINOK(Archive_Get_HardLinkNode(archive, index, h, defined))
|
|
if (defined)
|
|
{
|
|
const int linkIndex = _hardLinks.IDs.FindInSorted2(h);
|
|
if (linkIndex != -1)
|
|
{
|
|
FString &hl = _hardLinks.Links[(unsigned)linkIndex];
|
|
if (hl.IsEmpty())
|
|
hl = fullProcessedPath;
|
|
else
|
|
{
|
|
if (!MyCreateHardLink(fullProcessedPath, hl))
|
|
{
|
|
HRESULT errorCode = GetLastError_noZero_HRESULT();
|
|
RINOK(SendMessageError2(errorCode, kCantCreateHardLink, fullProcessedPath, hl))
|
|
return S_OK;
|
|
}
|
|
|
|
// printf("\nHard linkWasSet Archive_Get_HardLinkNode %s\n", GetAnsiString(_diskFilePath));
|
|
// _needSetAttrib = true; // do we need to set attribute ?
|
|
SetAttrib();
|
|
needExit = false;
|
|
return S_OK;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // SUPPORT_LINKS
|
|
|
|
|
|
// ---------- CREATE WRITE FILE -----
|
|
|
|
_outFileStreamSpec = new COutFileStream;
|
|
CMyComPtr<IOutStream> outFileStream_Loc(_outFileStreamSpec);
|
|
|
|
if (!_outFileStreamSpec->Open(fullProcessedPath, _isSplit ? OPEN_ALWAYS: CREATE_ALWAYS))
|
|
{
|
|
// if (::GetLastError() != ERROR_FILE_EXISTS || !isSplit)
|
|
{
|
|
RINOK(SendMessageError_with_LastError(kCantOpenOutFile, fullProcessedPath))
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
_needSetAttrib = true;
|
|
|
|
bool is_SymLink_in_Data = false;
|
|
|
|
if (_curSize_Defined && _curSize > 0 && _curSize < (1 << 12))
|
|
{
|
|
if (_fi.IsLinuxSymLink())
|
|
{
|
|
is_SymLink_in_Data = true;
|
|
_is_SymLink_in_Data_Linux = true;
|
|
}
|
|
else if (_fi.IsReparse())
|
|
{
|
|
is_SymLink_in_Data = true;
|
|
_is_SymLink_in_Data_Linux = false;
|
|
}
|
|
}
|
|
|
|
if (is_SymLink_in_Data)
|
|
{
|
|
_outMemBuf.Alloc((size_t)_curSize);
|
|
_bufPtrSeqOutStream_Spec = new CBufPtrSeqOutStream;
|
|
_bufPtrSeqOutStream = _bufPtrSeqOutStream_Spec;
|
|
_bufPtrSeqOutStream_Spec->Init(_outMemBuf, _outMemBuf.Size());
|
|
outStreamLoc = _bufPtrSeqOutStream;
|
|
}
|
|
else // not reprase
|
|
{
|
|
if (_ntOptions.PreAllocateOutFile && !_isSplit && _curSize_Defined && _curSize > (1 << 12))
|
|
{
|
|
// UInt64 ticks = GetCpuTicks();
|
|
_fileLength_that_WasSet = _curSize;
|
|
bool res = _outFileStreamSpec->File.SetLength(_curSize);
|
|
_fileLength_WasSet = res;
|
|
|
|
// ticks = GetCpuTicks() - ticks;
|
|
// printf("\nticks = %10d\n", (unsigned)ticks);
|
|
if (!res)
|
|
{
|
|
RINOK(SendMessageError_with_LastError(kCantSetFileLen, fullProcessedPath))
|
|
}
|
|
|
|
/*
|
|
_outFileStreamSpec->File.Close();
|
|
ticks = GetCpuTicks() - ticks;
|
|
printf("\nticks = %10d\n", (unsigned)ticks);
|
|
return S_FALSE;
|
|
*/
|
|
|
|
/*
|
|
File.SetLength() on FAT (xp64): is fast, but then File.Close() can be slow,
|
|
if we don't write any data.
|
|
File.SetLength() for remote share file (exFAT) can be slow in some cases,
|
|
and the Windows can return "network error" after 1 minute,
|
|
while remote file still can grow.
|
|
We need some way to detect such bad cases and disable PreAllocateOutFile mode.
|
|
*/
|
|
|
|
res = _outFileStreamSpec->SeekToBegin_bool();
|
|
if (!res)
|
|
{
|
|
RINOK(SendMessageError_with_LastError("Cannot seek to begin of file", fullProcessedPath))
|
|
}
|
|
} // PreAllocateOutFile
|
|
|
|
#ifdef SUPPORT_ALT_STREAMS
|
|
if (_isRenamed && !_item.IsAltStream)
|
|
{
|
|
CIndexToPathPair pair(index, fullProcessedPath);
|
|
unsigned oldSize = _renamedFiles.Size();
|
|
unsigned insertIndex = _renamedFiles.AddToUniqueSorted(pair);
|
|
if (oldSize == _renamedFiles.Size())
|
|
_renamedFiles[insertIndex].Path = fullProcessedPath;
|
|
}
|
|
#endif // SUPPORT_ALT_STREAMS
|
|
|
|
if (_isSplit)
|
|
{
|
|
RINOK(outFileStream_Loc->Seek((Int64)_position, STREAM_SEEK_SET, NULL))
|
|
}
|
|
outStreamLoc = outFileStream_Loc;
|
|
} // if not reprase
|
|
|
|
_outFileStream = outFileStream_Loc;
|
|
|
|
needExit = false;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT CArchiveExtractCallback::GetItem(UInt32 index)
|
|
{
|
|
#ifndef Z7_SFX
|
|
_item._use_baseParentFolder_mode = _use_baseParentFolder_mode;
|
|
if (_use_baseParentFolder_mode)
|
|
{
|
|
_item._baseParentFolder = (int)_baseParentFolder;
|
|
if (_pathMode == NExtract::NPathMode::kFullPaths ||
|
|
_pathMode == NExtract::NPathMode::kAbsPaths)
|
|
_item._baseParentFolder = -1;
|
|
}
|
|
#endif // Z7_SFX
|
|
|
|
#ifdef SUPPORT_ALT_STREAMS
|
|
_item.WriteToAltStreamIfColon = _ntOptions.WriteToAltStreamIfColon;
|
|
#endif
|
|
|
|
return _arc->GetItem(index, _item);
|
|
}
|
|
|
|
|
|
Z7_COM7F_IMF(CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode))
|
|
{
|
|
COM_TRY_BEGIN
|
|
|
|
*outStream = NULL;
|
|
|
|
#ifndef Z7_SFX
|
|
if (_hashStream)
|
|
_hashStreamSpec->ReleaseStream();
|
|
_hashStreamWasUsed = false;
|
|
#endif
|
|
|
|
_outFileStream.Release();
|
|
_bufPtrSeqOutStream.Release();
|
|
|
|
_encrypted = false;
|
|
_position = 0;
|
|
_isSplit = false;
|
|
|
|
_curSize = 0;
|
|
_curSize_Defined = false;
|
|
_fileLength_WasSet = false;
|
|
_fileLength_that_WasSet = 0;
|
|
_index = index;
|
|
|
|
_diskFilePath.Empty();
|
|
|
|
_isRenamed = false;
|
|
|
|
// _fi.Clear();
|
|
|
|
// _is_SymLink_in_Data = false;
|
|
_is_SymLink_in_Data_Linux = false;
|
|
|
|
_needSetAttrib = false;
|
|
_isSymLinkCreated = false;
|
|
_itemFailure = false;
|
|
|
|
#ifdef SUPPORT_LINKS
|
|
// _copyFile_Path.Empty();
|
|
_link.Clear();
|
|
#endif
|
|
|
|
_extractMode = false;
|
|
|
|
switch (askExtractMode)
|
|
{
|
|
case NArchive::NExtract::NAskMode::kExtract:
|
|
if (_testMode)
|
|
{
|
|
// askExtractMode = NArchive::NExtract::NAskMode::kTest;
|
|
}
|
|
else
|
|
_extractMode = true;
|
|
break;
|
|
}
|
|
|
|
|
|
IInArchive *archive = _arc->Archive;
|
|
|
|
RINOK(GetItem(index))
|
|
|
|
{
|
|
NCOM::CPropVariant prop;
|
|
RINOK(archive->GetProperty(index, kpidPosition, &prop))
|
|
if (prop.vt != VT_EMPTY)
|
|
{
|
|
if (prop.vt != VT_UI8)
|
|
return E_FAIL;
|
|
_position = prop.uhVal.QuadPart;
|
|
_isSplit = true;
|
|
}
|
|
}
|
|
|
|
#ifdef SUPPORT_LINKS
|
|
RINOK(ReadLink())
|
|
#endif // SUPPORT_LINKS
|
|
|
|
|
|
RINOK(Archive_GetItemBoolProp(archive, index, kpidEncrypted, _encrypted))
|
|
|
|
RINOK(GetUnpackSize())
|
|
|
|
#ifdef SUPPORT_ALT_STREAMS
|
|
if (!_ntOptions.AltStreams.Val && _item.IsAltStream)
|
|
return S_OK;
|
|
#endif // SUPPORT_ALT_STREAMS
|
|
|
|
// we can change (_item.PathParts) in this function
|
|
UStringVector &pathParts = _item.PathParts;
|
|
|
|
if (_wildcardCensor)
|
|
{
|
|
if (!CensorNode_CheckPath(*_wildcardCensor, _item))
|
|
return S_OK;
|
|
}
|
|
|
|
#ifndef Z7_SFX
|
|
if (_use_baseParentFolder_mode)
|
|
{
|
|
if (!pathParts.IsEmpty())
|
|
{
|
|
unsigned numRemovePathParts = 0;
|
|
|
|
#ifdef SUPPORT_ALT_STREAMS
|
|
if (_pathMode == NExtract::NPathMode::kNoPathsAlt && _item.IsAltStream)
|
|
numRemovePathParts = pathParts.Size();
|
|
else
|
|
#endif
|
|
if (_pathMode == NExtract::NPathMode::kNoPaths ||
|
|
_pathMode == NExtract::NPathMode::kNoPathsAlt)
|
|
numRemovePathParts = pathParts.Size() - 1;
|
|
pathParts.DeleteFrontal(numRemovePathParts);
|
|
}
|
|
}
|
|
else
|
|
#endif // Z7_SFX
|
|
{
|
|
if (pathParts.IsEmpty())
|
|
{
|
|
if (_item.IsDir)
|
|
return S_OK;
|
|
/*
|
|
#ifdef SUPPORT_ALT_STREAMS
|
|
if (!_item.IsAltStream)
|
|
#endif
|
|
return E_FAIL;
|
|
*/
|
|
}
|
|
|
|
unsigned numRemovePathParts = 0;
|
|
|
|
switch (_pathMode)
|
|
{
|
|
case NExtract::NPathMode::kFullPaths:
|
|
case NExtract::NPathMode::kCurPaths:
|
|
{
|
|
if (_removePathParts.IsEmpty())
|
|
break;
|
|
bool badPrefix = false;
|
|
|
|
if (pathParts.Size() < _removePathParts.Size())
|
|
badPrefix = true;
|
|
else
|
|
{
|
|
if (pathParts.Size() == _removePathParts.Size())
|
|
{
|
|
if (_removePartsForAltStreams)
|
|
{
|
|
#ifdef SUPPORT_ALT_STREAMS
|
|
if (!_item.IsAltStream)
|
|
#endif
|
|
badPrefix = true;
|
|
}
|
|
else
|
|
{
|
|
if (!_item.MainIsDir)
|
|
badPrefix = true;
|
|
}
|
|
}
|
|
|
|
if (!badPrefix)
|
|
FOR_VECTOR (i, _removePathParts)
|
|
{
|
|
if (CompareFileNames(_removePathParts[i], pathParts[i]) != 0)
|
|
{
|
|
badPrefix = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (badPrefix)
|
|
{
|
|
if (askExtractMode == NArchive::NExtract::NAskMode::kExtract && !_testMode)
|
|
return E_FAIL;
|
|
}
|
|
else
|
|
numRemovePathParts = _removePathParts.Size();
|
|
break;
|
|
}
|
|
|
|
case NExtract::NPathMode::kNoPaths:
|
|
{
|
|
if (!pathParts.IsEmpty())
|
|
numRemovePathParts = pathParts.Size() - 1;
|
|
break;
|
|
}
|
|
case NExtract::NPathMode::kNoPathsAlt:
|
|
{
|
|
#ifdef SUPPORT_ALT_STREAMS
|
|
if (_item.IsAltStream)
|
|
numRemovePathParts = pathParts.Size();
|
|
else
|
|
#endif
|
|
if (!pathParts.IsEmpty())
|
|
numRemovePathParts = pathParts.Size() - 1;
|
|
break;
|
|
}
|
|
case NExtract::NPathMode::kAbsPaths:
|
|
// default:
|
|
break;
|
|
}
|
|
|
|
pathParts.DeleteFrontal(numRemovePathParts);
|
|
}
|
|
|
|
|
|
#ifndef Z7_SFX
|
|
|
|
if (ExtractToStreamCallback)
|
|
{
|
|
if (!GetProp)
|
|
{
|
|
GetProp_Spec = new CGetProp;
|
|
GetProp = GetProp_Spec;
|
|
}
|
|
GetProp_Spec->Arc = _arc;
|
|
GetProp_Spec->IndexInArc = index;
|
|
UString name (MakePathFromParts(pathParts));
|
|
|
|
#ifdef SUPPORT_ALT_STREAMS
|
|
if (_item.IsAltStream)
|
|
{
|
|
if (!pathParts.IsEmpty() || (!_removePartsForAltStreams && _pathMode != NExtract::NPathMode::kNoPathsAlt))
|
|
name += ':';
|
|
name += _item.AltStreamName;
|
|
}
|
|
#endif
|
|
|
|
return ExtractToStreamCallback->GetStream7(name, BoolToInt(_item.IsDir), outStream, askExtractMode, GetProp);
|
|
}
|
|
|
|
#endif // Z7_SFX
|
|
|
|
|
|
CMyComPtr<ISequentialOutStream> outStreamLoc;
|
|
|
|
if (askExtractMode == NArchive::NExtract::NAskMode::kExtract && !_testMode)
|
|
{
|
|
if (_stdOutMode)
|
|
outStreamLoc = new CStdOutFileStream;
|
|
else
|
|
{
|
|
bool needExit = true;
|
|
RINOK(GetExtractStream(outStreamLoc, needExit))
|
|
if (needExit)
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
#ifndef Z7_SFX
|
|
if (_hashStream)
|
|
{
|
|
if (askExtractMode == NArchive::NExtract::NAskMode::kExtract ||
|
|
askExtractMode == NArchive::NExtract::NAskMode::kTest)
|
|
{
|
|
_hashStreamSpec->SetStream(outStreamLoc);
|
|
outStreamLoc = _hashStream;
|
|
_hashStreamSpec->Init(true);
|
|
_hashStreamWasUsed = true;
|
|
}
|
|
}
|
|
#endif // Z7_SFX
|
|
|
|
if (outStreamLoc)
|
|
{
|
|
/*
|
|
#ifdef SUPPORT_LINKS
|
|
if (!_copyFile_Path.IsEmpty())
|
|
{
|
|
RINOK(PrepareOperation(askExtractMode));
|
|
RINOK(MyCopyFile(outStreamLoc));
|
|
return SetOperationResult(NArchive::NExtract::NOperationResult::kOK);
|
|
}
|
|
if (_link.isCopyLink && _testMode)
|
|
return S_OK;
|
|
#endif
|
|
*/
|
|
*outStream = outStreamLoc.Detach();
|
|
}
|
|
|
|
return S_OK;
|
|
|
|
COM_TRY_END
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Z7_COM7F_IMF(CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode))
|
|
{
|
|
COM_TRY_BEGIN
|
|
|
|
#ifndef Z7_SFX
|
|
if (ExtractToStreamCallback)
|
|
return ExtractToStreamCallback->PrepareOperation7(askExtractMode);
|
|
#endif
|
|
|
|
_extractMode = false;
|
|
|
|
switch (askExtractMode)
|
|
{
|
|
case NArchive::NExtract::NAskMode::kExtract:
|
|
if (_testMode)
|
|
askExtractMode = NArchive::NExtract::NAskMode::kTest;
|
|
else
|
|
_extractMode = true;
|
|
break;
|
|
}
|
|
|
|
return _extractCallback2->PrepareOperation(_item.Path, BoolToInt(_item.IsDir),
|
|
askExtractMode, _isSplit ? &_position: NULL);
|
|
|
|
COM_TRY_END
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT CArchiveExtractCallback::CloseFile()
|
|
{
|
|
if (!_outFileStream)
|
|
return S_OK;
|
|
|
|
HRESULT hres = S_OK;
|
|
|
|
const UInt64 processedSize = _outFileStreamSpec->ProcessedSize;
|
|
if (_fileLength_WasSet && _fileLength_that_WasSet > processedSize)
|
|
{
|
|
const bool res = _outFileStreamSpec->File.SetLength(processedSize);
|
|
_fileLength_WasSet = res;
|
|
if (!res)
|
|
{
|
|
const HRESULT hres2 = SendMessageError_with_LastError(kCantSetFileLen, us2fs(_item.Path));
|
|
if (hres == S_OK)
|
|
hres = hres2;
|
|
}
|
|
}
|
|
|
|
_curSize = processedSize;
|
|
_curSize_Defined = true;
|
|
|
|
#if defined(_WIN32) && !defined(UNDER_CE) && !defined(Z7_SFX)
|
|
if (ZoneBuf.Size() != 0
|
|
&& !_item.IsAltStream)
|
|
{
|
|
// if (NFind::DoesFileExist_Raw(tempFilePath))
|
|
if (ZoneMode != NExtract::NZoneIdMode::kOffice ||
|
|
FindExt2(kOfficeExtensions, fs2us(_diskFilePath)))
|
|
{
|
|
// we must write zone file before setting of timestamps
|
|
const FString path = _diskFilePath + k_ZoneId_StreamName;
|
|
if (!WriteZoneFile(path, ZoneBuf))
|
|
{
|
|
// we can't write it in FAT
|
|
// SendMessageError_with_LastError("Can't write Zone.Identifier stream", path);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
CFiTimesCAM t;
|
|
GetFiTimesCAM(t);
|
|
|
|
// #ifdef _WIN32
|
|
if (t.IsSomeTimeDefined())
|
|
_outFileStreamSpec->SetTime(
|
|
t.CTime_Defined ? &t.CTime : NULL,
|
|
t.ATime_Defined ? &t.ATime : NULL,
|
|
t.MTime_Defined ? &t.MTime : NULL);
|
|
// #endif
|
|
|
|
RINOK(_outFileStreamSpec->Close())
|
|
_outFileStream.Release();
|
|
return hres;
|
|
}
|
|
|
|
|
|
#ifdef SUPPORT_LINKS
|
|
|
|
|
|
HRESULT CArchiveExtractCallback::SetFromLinkPath(
|
|
const FString &fullProcessedPath,
|
|
const CLinkInfo &linkInfo,
|
|
bool &linkWasSet)
|
|
{
|
|
linkWasSet = false;
|
|
if (!_ntOptions.SymLinks.Val && !linkInfo.isHardLink)
|
|
return S_OK;
|
|
|
|
UString relatPath;
|
|
|
|
/* if (linkInfo.isRelative)
|
|
linkInfo.linkPath is final link path that must be stored to file link field
|
|
else
|
|
linkInfo.linkPath is path from root of archive. So we must add _dirPathPrefix_Full before linkPath.
|
|
*/
|
|
|
|
if (linkInfo.isRelative)
|
|
relatPath = GetDirPrefixOf(_item.Path);
|
|
relatPath += linkInfo.linkPath;
|
|
|
|
if (!IsSafePath(relatPath))
|
|
{
|
|
return SendMessageError2(
|
|
0, // errorCode
|
|
"Dangerous link path was ignored",
|
|
us2fs(_item.Path),
|
|
us2fs(linkInfo.linkPath)); // us2fs(relatPath)
|
|
}
|
|
|
|
FString existPath;
|
|
if (linkInfo.isHardLink /* || linkInfo.IsCopyLink */ || !linkInfo.isRelative)
|
|
{
|
|
if (!NName::GetFullPath(_dirPathPrefix_Full, us2fs(relatPath), existPath))
|
|
{
|
|
RINOK(SendMessageError("Incorrect path", us2fs(relatPath)))
|
|
}
|
|
}
|
|
else
|
|
{
|
|
existPath = us2fs(linkInfo.linkPath);
|
|
// printf("\nlinkPath = : %s\n", GetOemString(linkInfo.linkPath).Ptr());
|
|
}
|
|
|
|
if (existPath.IsEmpty())
|
|
return SendMessageError("Empty link", fullProcessedPath);
|
|
|
|
if (linkInfo.isHardLink /* || linkInfo.IsCopyLink */)
|
|
{
|
|
// if (linkInfo.isHardLink)
|
|
{
|
|
if (!MyCreateHardLink(fullProcessedPath, existPath))
|
|
{
|
|
const HRESULT errorCode = GetLastError_noZero_HRESULT();
|
|
RINOK(SendMessageError2(errorCode, kCantCreateHardLink, fullProcessedPath, existPath))
|
|
}
|
|
linkWasSet = true;
|
|
return S_OK;
|
|
}
|
|
/*
|
|
// IsCopyLink
|
|
{
|
|
NFind::CFileInfo fi;
|
|
if (!fi.Find(existPath))
|
|
{
|
|
RINOK(SendMessageError2("Cannot find the file for copying", existPath, fullProcessedPath));
|
|
}
|
|
else
|
|
{
|
|
if (_curSize_Defined && _curSize == fi.Size)
|
|
_copyFile_Path = existPath;
|
|
else
|
|
{
|
|
RINOK(SendMessageError2("File size collision for file copying", existPath, fullProcessedPath));
|
|
}
|
|
// RINOK(MyCopyFile(existPath, fullProcessedPath));
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
// is Symbolic link
|
|
|
|
/*
|
|
if (_item.IsDir && !isRelative)
|
|
{
|
|
// Windows before Vista doesn't support symbolic links.
|
|
// we could convert such symbolic links to Junction Points
|
|
// isJunction = true;
|
|
// convertToAbs = true;
|
|
}
|
|
*/
|
|
|
|
if (!_ntOptions.SymLinks_AllowDangerous.Val)
|
|
{
|
|
#ifdef _WIN32
|
|
if (_item.IsDir)
|
|
#endif
|
|
if (linkInfo.isRelative)
|
|
{
|
|
CLinkLevelsInfo levelsInfo;
|
|
levelsInfo.Parse(linkInfo.linkPath);
|
|
if (levelsInfo.FinalLevel < 1 || levelsInfo.IsAbsolute)
|
|
{
|
|
return SendMessageError2(
|
|
0, // errorCode
|
|
"Dangerous symbolic link path was ignored",
|
|
us2fs(_item.Path),
|
|
us2fs(linkInfo.linkPath));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
CByteBuffer data;
|
|
// printf("\nFillLinkData(): %s\n", GetOemString(existPath).Ptr());
|
|
if (!FillLinkData(data, fs2us(existPath), !linkInfo.isJunction, linkInfo.isWSL))
|
|
return SendMessageError("Cannot fill link data", us2fs(_item.Path));
|
|
|
|
/*
|
|
if (NtReparse_Size != data.Size() || memcmp(NtReparse_Data, data, data.Size()) != 0)
|
|
{
|
|
SendMessageError("reconstructed Reparse is different", fs2us(existPath));
|
|
}
|
|
*/
|
|
|
|
CReparseAttr attr;
|
|
if (!attr.Parse(data, data.Size()))
|
|
{
|
|
RINOK(SendMessageError("Internal error for symbolic link file", us2fs(_item.Path)))
|
|
return S_OK;
|
|
}
|
|
if (!NFile::NIO::SetReparseData(fullProcessedPath, _item.IsDir, data, (DWORD)data.Size()))
|
|
{
|
|
RINOK(SendMessageError_with_LastError(kCantCreateSymLink, fullProcessedPath))
|
|
return S_OK;
|
|
}
|
|
linkWasSet = true;
|
|
|
|
return S_OK;
|
|
|
|
|
|
#else // ! _WIN32
|
|
|
|
if (!NFile::NIO::SetSymLink(fullProcessedPath, existPath))
|
|
{
|
|
RINOK(SendMessageError_with_LastError(kCantCreateSymLink, fullProcessedPath))
|
|
return S_OK;
|
|
}
|
|
linkWasSet = true;
|
|
|
|
return S_OK;
|
|
|
|
#endif // ! _WIN32
|
|
}
|
|
|
|
|
|
bool CLinkInfo::Parse(const Byte *data, size_t dataSize, bool isLinuxData)
|
|
{
|
|
Clear();
|
|
// this->isLinux = isLinuxData;
|
|
|
|
if (isLinuxData)
|
|
{
|
|
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;
|
|
}
|
|
|
|
CReparseAttr reparse;
|
|
if (!reparse.Parse(data, dataSize))
|
|
return false;
|
|
isHardLink = false;
|
|
// isCopyLink = false;
|
|
linkPath = reparse.GetPath();
|
|
isJunction = reparse.IsMountPoint();
|
|
|
|
if (reparse.IsSymLink_WSL())
|
|
{
|
|
isWSL = true;
|
|
isRelative = reparse.IsRelative_WSL();
|
|
}
|
|
else
|
|
isRelative = reparse.IsRelative_Win();
|
|
|
|
// FIXME !!!
|
|
#ifndef _WIN32
|
|
linkPath.Replace(L'\\', WCHAR_PATH_SEPARATOR);
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
#endif // SUPPORT_LINKS
|
|
|
|
|
|
HRESULT CArchiveExtractCallback::CloseReparseAndFile()
|
|
{
|
|
HRESULT res = S_OK;
|
|
|
|
#ifdef SUPPORT_LINKS
|
|
|
|
size_t reparseSize = 0;
|
|
bool repraseMode = false;
|
|
bool needSetReparse = false;
|
|
CLinkInfo linkInfo;
|
|
|
|
if (_bufPtrSeqOutStream)
|
|
{
|
|
repraseMode = true;
|
|
reparseSize = _bufPtrSeqOutStream_Spec->GetPos();
|
|
if (_curSize_Defined && reparseSize == _outMemBuf.Size())
|
|
{
|
|
/*
|
|
CReparseAttr reparse;
|
|
DWORD errorCode = 0;
|
|
needSetReparse = reparse.Parse(_outMemBuf, reparseSize, errorCode);
|
|
if (needSetReparse)
|
|
{
|
|
UString linkPath = reparse.GetPath();
|
|
#ifndef _WIN32
|
|
linkPath.Replace(L'\\', WCHAR_PATH_SEPARATOR);
|
|
#endif
|
|
}
|
|
*/
|
|
needSetReparse = linkInfo.Parse(_outMemBuf, reparseSize, _is_SymLink_in_Data_Linux);
|
|
if (!needSetReparse)
|
|
res = SendMessageError_with_LastError("Incorrect reparse stream", us2fs(_item.Path));
|
|
}
|
|
else
|
|
{
|
|
res = SendMessageError_with_LastError("Unknown reparse stream", us2fs(_item.Path));
|
|
}
|
|
if (!needSetReparse && _outFileStream)
|
|
{
|
|
const HRESULT res2 = WriteStream(_outFileStream, _outMemBuf, reparseSize);
|
|
if (res == S_OK)
|
|
res = res2;
|
|
}
|
|
_bufPtrSeqOutStream.Release();
|
|
}
|
|
|
|
#endif // SUPPORT_LINKS
|
|
|
|
|
|
const HRESULT res2 = CloseFile();
|
|
|
|
if (res == S_OK)
|
|
res = res2;
|
|
|
|
RINOK(res)
|
|
|
|
#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
|
|
// in Windows : we can create symbolic link even without file deleting
|
|
if (!DeleteFileAlways(_diskFilePath))
|
|
{
|
|
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))
|
|
if (linkWasSet)
|
|
_isSymLinkCreated = linkInfo.IsSymLink();
|
|
else
|
|
_needSetAttrib = false;
|
|
}
|
|
/*
|
|
if (!NFile::NIO::SetReparseData(_diskFilePath, _item.IsDir, ))
|
|
{
|
|
res = SendMessageError_with_LastError(kCantCreateSymLink, _diskFilePath);
|
|
}
|
|
*/
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
return res;
|
|
}
|
|
|
|
|
|
void CArchiveExtractCallback::SetAttrib()
|
|
{
|
|
#ifndef _WIN32
|
|
// Linux now doesn't support permissions for symlinks
|
|
if (_isSymLinkCreated)
|
|
return;
|
|
#endif
|
|
|
|
if (_itemFailure
|
|
|| _diskFilePath.IsEmpty()
|
|
|| _stdOutMode
|
|
|| !_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);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
Z7_COM7F_IMF(CArchiveExtractCallback::SetOperationResult(Int32 opRes))
|
|
{
|
|
COM_TRY_BEGIN
|
|
|
|
// printf("\nCArchiveExtractCallback::SetOperationResult: %d %s\n", opRes, GetAnsiString(_diskFilePath));
|
|
|
|
#ifndef Z7_SFX
|
|
if (ExtractToStreamCallback)
|
|
{
|
|
GetUnpackSize();
|
|
return ExtractToStreamCallback->SetOperationResult8(opRes, BoolToInt(_encrypted), _curSize);
|
|
}
|
|
#endif
|
|
|
|
#ifndef Z7_SFX
|
|
|
|
if (_hashStreamWasUsed)
|
|
{
|
|
_hashStreamSpec->_hash->Final(_item.IsDir,
|
|
#ifdef SUPPORT_ALT_STREAMS
|
|
_item.IsAltStream
|
|
#else
|
|
false
|
|
#endif
|
|
, _item.Path);
|
|
_curSize = _hashStreamSpec->GetSize();
|
|
_curSize_Defined = true;
|
|
_hashStreamSpec->ReleaseStream();
|
|
_hashStreamWasUsed = false;
|
|
}
|
|
|
|
#endif // Z7_SFX
|
|
|
|
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
|
|
|
|
if (!_curSize_Defined)
|
|
GetUnpackSize();
|
|
|
|
if (_curSize_Defined)
|
|
{
|
|
#ifdef SUPPORT_ALT_STREAMS
|
|
if (_item.IsAltStream)
|
|
AltStreams_UnpackSize += _curSize;
|
|
else
|
|
#endif
|
|
UnpackSize += _curSize;
|
|
}
|
|
|
|
if (_item.IsDir)
|
|
NumFolders++;
|
|
#ifdef SUPPORT_ALT_STREAMS
|
|
else if (_item.IsAltStream)
|
|
NumAltStreams++;
|
|
#endif
|
|
else
|
|
NumFiles++;
|
|
|
|
if (_needSetAttrib)
|
|
SetAttrib();
|
|
|
|
RINOK(_extractCallback2->SetOperationResult(opRes, BoolToInt(_encrypted)))
|
|
|
|
return S_OK;
|
|
|
|
COM_TRY_END
|
|
}
|
|
|
|
|
|
|
|
Z7_COM7F_IMF(CArchiveExtractCallback::ReportExtractResult(UInt32 indexType, UInt32 index, Int32 opRes))
|
|
{
|
|
if (_folderArchiveExtractCallback2)
|
|
{
|
|
bool isEncrypted = false;
|
|
UString s;
|
|
|
|
if (indexType == NArchive::NEventIndexType::kInArcIndex && index != (UInt32)(Int32)-1)
|
|
{
|
|
CReadArcItem item;
|
|
RINOK(_arc->GetItem(index, item))
|
|
s = item.Path;
|
|
RINOK(Archive_GetItemBoolProp(_arc->Archive, index, kpidEncrypted, isEncrypted))
|
|
}
|
|
else
|
|
{
|
|
s = '#';
|
|
s.Add_UInt32(index);
|
|
// if (indexType == NArchive::NEventIndexType::kBlockIndex) {}
|
|
}
|
|
|
|
return _folderArchiveExtractCallback2->ReportExtractResult(opRes, isEncrypted, s);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
Z7_COM7F_IMF(CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password))
|
|
{
|
|
COM_TRY_BEGIN
|
|
if (!_cryptoGetTextPassword)
|
|
{
|
|
RINOK(_extractCallback2.QueryInterface(IID_ICryptoGetTextPassword,
|
|
&_cryptoGetTextPassword))
|
|
}
|
|
return _cryptoGetTextPassword->CryptoGetTextPassword(password);
|
|
COM_TRY_END
|
|
}
|
|
|
|
|
|
// ---------- HASH functions ----------
|
|
|
|
FString CArchiveExtractCallback::Hash_GetFullFilePath()
|
|
{
|
|
// this function changes _item.PathParts.
|
|
CorrectPathParts();
|
|
const UStringVector &pathParts = _item.PathParts;
|
|
const UString processedPath (MakePathFromParts(pathParts));
|
|
FString fullProcessedPath (us2fs(processedPath));
|
|
if (_pathMode != NExtract::NPathMode::kAbsPaths
|
|
|| !NName::IsAbsolutePath(processedPath))
|
|
{
|
|
fullProcessedPath = MakePath_from_2_Parts(
|
|
DirPathPrefix_for_HashFiles,
|
|
// _dirPathPrefix,
|
|
fullProcessedPath);
|
|
}
|
|
return fullProcessedPath;
|
|
}
|
|
|
|
|
|
Z7_COM7F_IMF(CArchiveExtractCallback::GetDiskProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
|
|
{
|
|
COM_TRY_BEGIN
|
|
NCOM::CPropVariant prop;
|
|
if (propID == kpidSize)
|
|
{
|
|
RINOK(GetItem(index))
|
|
const FString fullProcessedPath = Hash_GetFullFilePath();
|
|
NFile::NFind::CFileInfo fi;
|
|
if (fi.Find_FollowLink(fullProcessedPath))
|
|
if (!fi.IsDir())
|
|
prop = (UInt64)fi.Size;
|
|
}
|
|
prop.Detach(value);
|
|
return S_OK;
|
|
COM_TRY_END
|
|
}
|
|
|
|
|
|
Z7_COM7F_IMF(CArchiveExtractCallback::GetStream2(UInt32 index, ISequentialInStream **inStream, UInt32 mode))
|
|
{
|
|
COM_TRY_BEGIN
|
|
*inStream = NULL;
|
|
// if (index != _index) return E_FAIL;
|
|
if (mode != NUpdateNotifyOp::kHashRead)
|
|
return E_FAIL;
|
|
|
|
RINOK(GetItem(index))
|
|
const FString fullProcessedPath = Hash_GetFullFilePath();
|
|
|
|
CInFileStream *inStreamSpec = new CInFileStream;
|
|
CMyComPtr<ISequentialInStream> inStreamRef = inStreamSpec;
|
|
inStreamSpec->Set_PreserveATime(_ntOptions.PreserveATime);
|
|
if (!inStreamSpec->OpenShared(fullProcessedPath, _ntOptions.OpenShareForWrite))
|
|
{
|
|
RINOK(SendMessageError_with_LastError(kCantOpenInFile, fullProcessedPath))
|
|
return S_OK;
|
|
}
|
|
*inStream = inStreamRef.Detach();
|
|
return S_OK;
|
|
COM_TRY_END
|
|
}
|
|
|
|
|
|
Z7_COM7F_IMF(CArchiveExtractCallback::ReportOperation(
|
|
UInt32 /* indexType */, UInt32 /* index */, UInt32 /* op */))
|
|
{
|
|
// COM_TRY_BEGIN
|
|
return S_OK;
|
|
// COM_TRY_END
|
|
}
|
|
|
|
|
|
// ------------ After Extracting functions ------------
|
|
|
|
void CDirPathSortPair::SetNumSlashes(const FChar *s)
|
|
{
|
|
for (unsigned numSlashes = 0;;)
|
|
{
|
|
FChar c = *s++;
|
|
if (c == 0)
|
|
{
|
|
Len = numSlashes;
|
|
return;
|
|
}
|
|
if (IS_PATH_SEPAR(c))
|
|
numSlashes++;
|
|
}
|
|
}
|
|
|
|
|
|
bool CDirPathTime::SetDirTime() const
|
|
{
|
|
return NDir::SetDirTime(Path,
|
|
CTime_Defined ? &CTime : NULL,
|
|
ATime_Defined ? &ATime : NULL,
|
|
MTime_Defined ? &MTime : NULL);
|
|
}
|
|
|
|
|
|
HRESULT CArchiveExtractCallback::SetDirsTimes()
|
|
{
|
|
if (!_arc)
|
|
return S_OK;
|
|
|
|
CRecordVector<CDirPathSortPair> pairs;
|
|
pairs.ClearAndSetSize(_extractedFolders.Size());
|
|
unsigned i;
|
|
|
|
for (i = 0; i < _extractedFolders.Size(); i++)
|
|
{
|
|
CDirPathSortPair &pair = pairs[i];
|
|
pair.Index = i;
|
|
pair.SetNumSlashes(_extractedFolders[i].Path);
|
|
}
|
|
|
|
pairs.Sort2();
|
|
|
|
HRESULT res = S_OK;
|
|
|
|
for (i = 0; i < pairs.Size(); i++)
|
|
{
|
|
const CDirPathTime &dpt = _extractedFolders[pairs[i].Index];
|
|
if (!dpt.SetDirTime())
|
|
{
|
|
// result = E_FAIL;
|
|
// do we need error message here in Windows and in posix?
|
|
// SendMessageError_with_LastError("Cannot set directory time", dpt.Path);
|
|
}
|
|
}
|
|
|
|
/*
|
|
#ifndef _WIN32
|
|
for (i = 0; i < _delayedSymLinks.Size(); i++)
|
|
{
|
|
const CDelayedSymLink &link = _delayedSymLinks[i];
|
|
if (!link.Create())
|
|
{
|
|
if (res == S_OK)
|
|
res = GetLastError_noZero_HRESULT();
|
|
// res = E_FAIL;
|
|
// do we need error message here in Windows and in posix?
|
|
SendMessageError_with_LastError("Cannot create Symbolic Link", link._source);
|
|
}
|
|
}
|
|
#endif // _WIN32
|
|
*/
|
|
|
|
ClearExtractedDirsInfo();
|
|
return res;
|
|
}
|
|
|
|
|
|
HRESULT CArchiveExtractCallback::CloseArc()
|
|
{
|
|
HRESULT res = CloseReparseAndFile();
|
|
const HRESULT res2 = SetDirsTimes();
|
|
if (res == S_OK)
|
|
res = res2;
|
|
_arc = NULL;
|
|
return res;
|
|
}
|