1644 lines
40 KiB
C++
1644 lines
40 KiB
C++
// ArchiveExtractCallback.cpp
|
|
|
|
#include "StdAfx.h"
|
|
|
|
#undef sprintf
|
|
#undef printf
|
|
|
|
#include "../../../../C/Alloc.h"
|
|
|
|
#include "../../../Common/ComTry.h"
|
|
#include "../../../Common/IntToString.h"
|
|
#include "../../../Common/StringConvert.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(_SFX)
|
|
#define _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 *kCantAutoRename = "Can not create file with auto name";
|
|
static const char *kCantRenameFile = "Can not rename existing file";
|
|
static const char *kCantDeleteOutputFile = "Can not delete output file";
|
|
static const char *kCantDeleteOutputDir = "Can not delete output folder";
|
|
|
|
|
|
#ifndef _SFX
|
|
|
|
STDMETHODIMP 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
|
|
|
|
#ifdef _USE_SECURITY_CODE
|
|
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
|
|
|
|
#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;
|
|
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)
|
|
hardIDs.Add(h);
|
|
}
|
|
}
|
|
}
|
|
|
|
hardIDs.Sort2();
|
|
|
|
{
|
|
// wee 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
|
|
|
|
CArchiveExtractCallback::CArchiveExtractCallback():
|
|
WriteCTime(true),
|
|
WriteATime(true),
|
|
WriteMTime(true),
|
|
_multiArchives(false)
|
|
{
|
|
LocalProgressSpec = new CLocalProgress();
|
|
_localProgress = LocalProgressSpec;
|
|
|
|
#ifdef _USE_SECURITY_CODE
|
|
_saclEnabled = InitLocalPrivileges();
|
|
#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)
|
|
{
|
|
_extractedFolderPaths.Clear();
|
|
_extractedFolderIndices.Clear();
|
|
|
|
#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);
|
|
_extractCallback2.QueryInterface(IID_IArchiveExtractCallbackMessage, &_callbackMessage);
|
|
_extractCallback2.QueryInterface(IID_IFolderArchiveExtractCallback2, &_folderArchiveExtractCallback2);
|
|
|
|
#ifndef _SFX
|
|
|
|
_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 _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);
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP 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;
|
|
}
|
|
|
|
STDMETHODIMP 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
|
|
}
|
|
|
|
STDMETHODIMP 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)
|
|
{
|
|
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);
|
|
#if defined(_WIN32) && !defined(UNDER_CE)
|
|
if (_pathMode == NExtract::NPathMode::kAbsPaths)
|
|
if (i == 0 && s.Len() == 2 && NName::IsDrivePath2(s))
|
|
continue;
|
|
#endif
|
|
CreateDir(fullPath);
|
|
}
|
|
}
|
|
|
|
HRESULT CArchiveExtractCallback::GetTime(int index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined)
|
|
{
|
|
filetimeIsDefined = false;
|
|
NCOM::CPropVariant prop;
|
|
RINOK(_arc->Archive->GetProperty(index, propID, &prop));
|
|
if (prop.vt == VT_FILETIME)
|
|
{
|
|
filetime = prop.filetime;
|
|
filetimeIsDefined = (filetime.dwHighDateTime != 0 || filetime.dwLowDateTime != 0);
|
|
}
|
|
else if (prop.vt != VT_EMPTY)
|
|
return E_FAIL;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CArchiveExtractCallback::GetUnpackSize()
|
|
{
|
|
return _arc->GetItemSize(_index, _curSize, _curSizeDefined);
|
|
}
|
|
|
|
static void AddPathToMessage(UString &s, const FString &path)
|
|
{
|
|
s.AddAscii(" : ");
|
|
s += fs2us(path);
|
|
}
|
|
|
|
HRESULT CArchiveExtractCallback::SendMessageError(const char *message, const FString &path)
|
|
{
|
|
UString s;
|
|
s.AddAscii(message);
|
|
AddPathToMessage(s, path);
|
|
return _extractCallback2->MessageError(s);
|
|
}
|
|
|
|
HRESULT CArchiveExtractCallback::SendMessageError_with_LastError(const char *message, const FString &path)
|
|
{
|
|
DWORD errorCode = GetLastError();
|
|
UString s;
|
|
s.AddAscii(message);
|
|
if (errorCode != 0)
|
|
{
|
|
s.AddAscii(" : ");
|
|
s += NError::MyFormatMessage(errorCode);
|
|
}
|
|
AddPathToMessage(s, path);
|
|
return _extractCallback2->MessageError(s);
|
|
}
|
|
|
|
HRESULT CArchiveExtractCallback::SendMessageError2(const char *message, const FString &path1, const FString &path2)
|
|
{
|
|
UString s;
|
|
s.AddAscii(message);
|
|
AddPathToMessage(s, path1);
|
|
AddPathToMessage(s, path2);
|
|
return _extractCallback2->MessageError(s);
|
|
}
|
|
|
|
#ifndef _SFX
|
|
|
|
STDMETHODIMP 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
|
|
|
|
|
|
#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(pos + 1);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
bool IsSafePath(const UString &path)
|
|
{
|
|
if (NName::IsAbsolutePath(path))
|
|
return false;
|
|
|
|
UStringVector parts;
|
|
SplitPathToParts(path, parts);
|
|
unsigned level = 0;
|
|
|
|
FOR_VECTOR (i, parts)
|
|
{
|
|
const UString &s = parts[i];
|
|
if (s.IsEmpty())
|
|
{
|
|
if (i == 0)
|
|
return false;
|
|
continue;
|
|
}
|
|
if (s == L".")
|
|
continue;
|
|
if (s == L"..")
|
|
{
|
|
if (level == 0)
|
|
return false;
|
|
level--;
|
|
}
|
|
else
|
|
level++;
|
|
}
|
|
|
|
return level > 0;
|
|
}
|
|
|
|
|
|
bool CensorNode_CheckPath2(const NWildcard::CCensorNode &node, const CReadArcItem &item, bool &include)
|
|
{
|
|
bool found = false;
|
|
|
|
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 += L':';
|
|
back += item.AltStreamName;
|
|
bool include2;
|
|
|
|
if (node.CheckPathVect(pathParts2,
|
|
true, // isFile,
|
|
include2))
|
|
{
|
|
include = include2;
|
|
return true;
|
|
}
|
|
|
|
#endif
|
|
|
|
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));
|
|
}
|
|
}
|
|
|
|
#endif
|
|
*/
|
|
|
|
STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode)
|
|
{
|
|
COM_TRY_BEGIN
|
|
|
|
*outStream = NULL;
|
|
|
|
#ifndef _SFX
|
|
if (_hashStream)
|
|
_hashStreamSpec->ReleaseStream();
|
|
_hashStreamWasUsed = false;
|
|
#endif
|
|
|
|
_outFileStream.Release();
|
|
|
|
_encrypted = false;
|
|
_position = 0;
|
|
_isSplit = false;
|
|
|
|
_curSize = 0;
|
|
_curSizeDefined = false;
|
|
_index = index;
|
|
|
|
_diskFilePath.Empty();
|
|
|
|
// _fi.Clear();
|
|
|
|
#ifdef SUPPORT_LINKS
|
|
// _CopyFile_Path.Empty();
|
|
linkPath.Empty();
|
|
#endif
|
|
|
|
IInArchive *archive = _arc->Archive;
|
|
|
|
#ifndef _SFX
|
|
_item._use_baseParentFolder_mode = _use_baseParentFolder_mode;
|
|
if (_use_baseParentFolder_mode)
|
|
{
|
|
_item._baseParentFolder = _baseParentFolder;
|
|
if (_pathMode == NExtract::NPathMode::kFullPaths ||
|
|
_pathMode == NExtract::NPathMode::kAbsPaths)
|
|
_item._baseParentFolder = -1;
|
|
}
|
|
#endif
|
|
|
|
#ifdef SUPPORT_ALT_STREAMS
|
|
_item.WriteToAltStreamIfColon = _ntOptions.WriteToAltStreamIfColon;
|
|
#endif
|
|
|
|
RINOK(_arc->GetItem(index, _item));
|
|
|
|
{
|
|
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
|
|
|
|
// bool isCopyLink = false;
|
|
bool isHardLink = false;
|
|
bool isJunction = false;
|
|
bool isRelative = false;
|
|
|
|
{
|
|
NCOM::CPropVariant prop;
|
|
RINOK(archive->GetProperty(index, kpidHardLink, &prop));
|
|
if (prop.vt == VT_BSTR)
|
|
{
|
|
isHardLink = true;
|
|
// isCopyLink = false;
|
|
isRelative = false; // RAR5, TAR: hard links are from root folder of archive
|
|
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)
|
|
{
|
|
isHardLink = false;
|
|
isCopyLink = true;
|
|
isRelative = false; // RAR5: copy links are from root folder of archive
|
|
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)
|
|
{
|
|
isHardLink = false;
|
|
// isCopyLink = false;
|
|
isRelative = true; // RAR5, TAR: symbolic links can be relative
|
|
linkPath.SetFromBstr(prop.bstrVal);
|
|
}
|
|
else if (prop.vt != VT_EMPTY)
|
|
return E_FAIL;
|
|
}
|
|
|
|
|
|
bool isOkReparse = false;
|
|
|
|
if (linkPath.IsEmpty() && _arc->GetRawProps)
|
|
{
|
|
const void *data;
|
|
UInt32 dataSize;
|
|
UInt32 propType;
|
|
|
|
_arc->GetRawProps->GetRawProp(_index, kpidNtReparse, &data, &dataSize, &propType);
|
|
|
|
if (dataSize != 0)
|
|
{
|
|
if (propType != NPropDataType::kRaw)
|
|
return E_FAIL;
|
|
UString s;
|
|
CReparseAttr reparse;
|
|
isOkReparse = reparse.Parse((const Byte *)data, dataSize);
|
|
if (isOkReparse)
|
|
{
|
|
isHardLink = false;
|
|
// isCopyLink = false;
|
|
linkPath = reparse.GetPath();
|
|
isJunction = reparse.IsMountPoint();
|
|
isRelative = reparse.IsRelative();
|
|
#ifndef _WIN32
|
|
linkPath.Replace(L'\\', WCHAR_PATH_SEPARATOR);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!linkPath.IsEmpty())
|
|
{
|
|
#ifdef _WIN32
|
|
linkPath.Replace(L'/', WCHAR_PATH_SEPARATOR);
|
|
#endif
|
|
|
|
// rar5 uses "\??\" prefix for absolute links
|
|
if (linkPath.IsPrefixedBy(WSTRING_PATH_SEPARATOR L"??" WSTRING_PATH_SEPARATOR))
|
|
{
|
|
isRelative = false;
|
|
linkPath.DeleteFrontal(4);
|
|
}
|
|
|
|
for (;;)
|
|
// while (NName::IsAbsolutePath(linkPath))
|
|
{
|
|
unsigned n = NName::GetRootPrefixSize(linkPath);
|
|
if (n == 0)
|
|
break;
|
|
isRelative = false;
|
|
linkPath.DeleteFrontal(n);
|
|
}
|
|
}
|
|
|
|
if (!linkPath.IsEmpty() && !isRelative && _removePathParts.Size() != 0)
|
|
{
|
|
UStringVector pathParts;
|
|
SplitPathToParts(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());
|
|
linkPath = MakePathFromParts(pathParts);
|
|
}
|
|
|
|
#endif
|
|
|
|
RINOK(Archive_GetItemBoolProp(archive, index, kpidEncrypted, _encrypted));
|
|
|
|
RINOK(GetUnpackSize());
|
|
|
|
#ifdef SUPPORT_ALT_STREAMS
|
|
|
|
if (!_ntOptions.AltStreams.Val && _item.IsAltStream)
|
|
return S_OK;
|
|
|
|
#endif
|
|
|
|
|
|
UStringVector &pathParts = _item.PathParts;
|
|
|
|
if (_wildcardCensor)
|
|
{
|
|
if (!CensorNode_CheckPath(*_wildcardCensor, _item))
|
|
return S_OK;
|
|
}
|
|
|
|
#ifndef _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
|
|
{
|
|
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::kFullPaths:
|
|
case NExtract::NPathMode::kAbsPaths:
|
|
break;
|
|
*/
|
|
}
|
|
|
|
pathParts.DeleteFrontal(numRemovePathParts);
|
|
}
|
|
|
|
#ifndef _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 += L':';
|
|
name += _item.AltStreamName;
|
|
}
|
|
#endif
|
|
|
|
return ExtractToStreamCallback->GetStream7(name, BoolToInt(_item.IsDir), outStream, askExtractMode, GetProp);
|
|
}
|
|
|
|
#endif
|
|
|
|
CMyComPtr<ISequentialOutStream> outStreamLoc;
|
|
|
|
if (askExtractMode == NArchive::NExtract::NAskMode::kExtract && !_testMode)
|
|
{
|
|
if (_stdOutMode)
|
|
{
|
|
outStreamLoc = new CStdOutFileStream;
|
|
}
|
|
else
|
|
{
|
|
{
|
|
NCOM::CPropVariant prop;
|
|
RINOK(archive->GetProperty(index, kpidAttrib, &prop));
|
|
if (prop.vt == VT_UI4)
|
|
{
|
|
_fi.Attrib = prop.ulVal;
|
|
_fi.AttribDefined = true;
|
|
}
|
|
else if (prop.vt == VT_EMPTY)
|
|
_fi.AttribDefined = false;
|
|
else
|
|
return E_FAIL;
|
|
}
|
|
|
|
RINOK(GetTime(index, kpidCTime, _fi.CTime, _fi.CTimeDefined));
|
|
RINOK(GetTime(index, kpidATime, _fi.ATime, _fi.ATimeDefined));
|
|
RINOK(GetTime(index, kpidMTime, _fi.MTime, _fi.MTimeDefined));
|
|
|
|
bool isAnti = false;
|
|
RINOK(_arc->IsItemAnti(index, isAnti));
|
|
|
|
#ifdef SUPPORT_ALT_STREAMS
|
|
if (!_item.IsAltStream
|
|
|| !pathParts.IsEmpty()
|
|
|| !(_removePartsForAltStreams || _pathMode == NExtract::NPathMode::kNoPathsAlt))
|
|
#endif
|
|
Correct_FsPath(_pathMode == NExtract::NPathMode::kAbsPaths, 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;
|
|
}
|
|
else if (_pathMode == NExtract::NPathMode::kAbsPaths &&
|
|
NWildcard::GetNumPrefixParts_if_DrivePath(pathParts) == pathParts.Size())
|
|
pathParts.AddNew();
|
|
|
|
UString &name = pathParts.Back();
|
|
if (needColon)
|
|
name += (wchar_t)(_ntOptions.ReplaceColonForAltStream ? L'_' : L':');
|
|
name += s;
|
|
}
|
|
|
|
#endif
|
|
|
|
UString processedPath = MakePathFromParts(pathParts);
|
|
|
|
if (!isAnti)
|
|
{
|
|
if (!_item.IsDir)
|
|
{
|
|
if (!pathParts.IsEmpty())
|
|
pathParts.DeleteBack();
|
|
}
|
|
|
|
if (!pathParts.IsEmpty())
|
|
{
|
|
FString fullPathNew;
|
|
CreateComplexDirectory(pathParts, fullPathNew);
|
|
if (_item.IsDir)
|
|
{
|
|
_extractedFolderPaths.Add(fullPathNew);
|
|
_extractedFolderIndices.Add(index);
|
|
SetDirTime(fullPathNew,
|
|
(WriteCTime && _fi.CTimeDefined) ? &_fi.CTime : NULL,
|
|
(WriteATime && _fi.ATimeDefined) ? &_fi.ATime : NULL,
|
|
(WriteMTime && _fi.MTimeDefined) ? &_fi.MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
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)
|
|
{
|
|
int renIndex = _renamedFiles.FindInSorted(CIndexToPathPair(_item.ParentIndex));
|
|
if (renIndex >= 0)
|
|
{
|
|
const CIndexToPathPair &pair = _renamedFiles[renIndex];
|
|
fullProcessedPath = pair.Path;
|
|
fullProcessedPath += (FChar)':';
|
|
UString s = _item.AltStreamName;
|
|
Correct_AltStream_Name(s);
|
|
fullProcessedPath += us2fs(s);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
bool isRenamed = false;
|
|
|
|
if (_item.IsDir)
|
|
{
|
|
_diskFilePath = fullProcessedPath;
|
|
if (isAnti)
|
|
RemoveDir(_diskFilePath);
|
|
#ifdef SUPPORT_LINKS
|
|
if (linkPath.IsEmpty())
|
|
#endif
|
|
return S_OK;
|
|
}
|
|
else if (!_isSplit)
|
|
{
|
|
|
|
// ----- Is file (not split) -----
|
|
NFind::CFileInfo fileInfo;
|
|
if (fileInfo.Find(fullProcessedPath))
|
|
{
|
|
switch (_overwriteMode)
|
|
{
|
|
case NExtract::NOverwriteMode::kSkip:
|
|
return S_OK;
|
|
case NExtract::NOverwriteMode::kAsk:
|
|
{
|
|
int slashPos = fullProcessedPath.ReverseFind_PathSepar();
|
|
FString realFullProcessedPath = fullProcessedPath.Left(slashPos + 1) + fileInfo.Name;
|
|
|
|
Int32 overwriteResult;
|
|
RINOK(_extractCallback2->AskOverwrite(
|
|
fs2us(realFullProcessedPath), &fileInfo.MTime, &fileInfo.Size, _item.Path,
|
|
_fi.MTimeDefined ? &_fi.MTime : NULL,
|
|
_curSizeDefined ? &_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;
|
|
}
|
|
}
|
|
}
|
|
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 raname folders. So it's OK to use it for folders too
|
|
if (!MyMoveFile(fullProcessedPath, existPath))
|
|
{
|
|
RINOK(SendMessageError2(kCantRenameFile, existPath, fullProcessedPath));
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
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
|
|
{
|
|
bool needDelete = true;
|
|
if (needDelete)
|
|
{
|
|
if (!DeleteFileAlways(fullProcessedPath))
|
|
{
|
|
RINOK(SendMessageError_with_LastError(kCantDeleteOutputFile, fullProcessedPath));
|
|
return S_OK;
|
|
// return E_FAIL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else // not Find(fullProcessedPath)
|
|
{
|
|
// we need to clear READ-ONLY of parent before creating alt stream
|
|
#if defined(_WIN32) && !defined(UNDER_CE)
|
|
int colonPos = NName::FindAltStreamColon(fullProcessedPath);
|
|
if (colonPos >= 0 && fullProcessedPath[(unsigned)colonPos + 1] != 0)
|
|
{
|
|
FString parentFsPath = fullProcessedPath;
|
|
parentFsPath.DeleteFrom(colonPos);
|
|
NFind::CFileInfo parentFi;
|
|
if (parentFi.Find(parentFsPath))
|
|
{
|
|
if (parentFi.IsReadOnly())
|
|
SetFileAttrib(parentFsPath, parentFi.Attrib & ~FILE_ATTRIBUTE_READONLY);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
// ----- END of code for Is file (not split) -----
|
|
|
|
}
|
|
_diskFilePath = fullProcessedPath;
|
|
|
|
|
|
if (!isAnti)
|
|
{
|
|
#ifdef SUPPORT_LINKS
|
|
|
|
if (!linkPath.IsEmpty())
|
|
{
|
|
#ifndef UNDER_CE
|
|
|
|
UString relatPath;
|
|
if (isRelative)
|
|
relatPath = GetDirPrefixOf(_item.Path);
|
|
relatPath += linkPath;
|
|
|
|
if (!IsSafePath(relatPath))
|
|
{
|
|
RINOK(SendMessageError("Dangerous link path was ignored", us2fs(relatPath)));
|
|
}
|
|
else
|
|
{
|
|
FString existPath;
|
|
if (isHardLink /* || isCopyLink */ || !isRelative)
|
|
{
|
|
if (!NName::GetFullPath(_dirPathPrefix_Full, us2fs(relatPath), existPath))
|
|
{
|
|
RINOK(SendMessageError("Incorrect path", us2fs(relatPath)));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
existPath = us2fs(linkPath);
|
|
}
|
|
|
|
if (!existPath.IsEmpty())
|
|
{
|
|
if (isHardLink /* || isCopyLink */)
|
|
{
|
|
// if (isHardLink)
|
|
{
|
|
if (!MyCreateHardLink(fullProcessedPath, existPath))
|
|
{
|
|
RINOK(SendMessageError2("Can not create hard link", fullProcessedPath, existPath));
|
|
// return S_OK;
|
|
}
|
|
}
|
|
/*
|
|
else
|
|
{
|
|
NFind::CFileInfo fi;
|
|
if (!fi.Find(existPath))
|
|
{
|
|
RINOK(SendMessageError2("Can not find the file for copying", existPath, fullProcessedPath));
|
|
}
|
|
else
|
|
{
|
|
if (_curSizeDefined && _curSize == fi.Size)
|
|
_CopyFile_Path = existPath;
|
|
else
|
|
{
|
|
RINOK(SendMessageError2("File size collision for file copying", existPath, fullProcessedPath));
|
|
}
|
|
|
|
// RINOK(MyCopyFile(existPath, fullProcessedPath));
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
else if (_ntOptions.SymLinks.Val)
|
|
{
|
|
// bool isSymLink = true; // = false for junction
|
|
if (_item.IsDir && !isRelative)
|
|
{
|
|
// if it's before Vista we use Junction Point
|
|
// isJunction = true;
|
|
// convertToAbs = true;
|
|
}
|
|
|
|
CByteBuffer data;
|
|
if (FillLinkData(data, fs2us(existPath), !isJunction))
|
|
{
|
|
CReparseAttr attr;
|
|
if (!attr.Parse(data, data.Size()))
|
|
{
|
|
RINOK(SendMessageError("Internal error for symbolic link file", us2fs(_item.Path)));
|
|
// return E_FAIL;
|
|
}
|
|
else
|
|
if (!NFile::NIO::SetReparseData(fullProcessedPath, _item.IsDir, data, (DWORD)data.Size()))
|
|
{
|
|
RINOK(SendMessageError_with_LastError("Can not create symbolic link", fullProcessedPath));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
if (linkPath.IsEmpty() /* || !_CopyFile_Path.IsEmpty() */)
|
|
#endif // SUPPORT_LINKS
|
|
{
|
|
bool needWriteFile = true;
|
|
|
|
#ifdef SUPPORT_LINKS
|
|
if (!_hardLinks.IDs.IsEmpty() && !_item.IsAltStream)
|
|
{
|
|
CHardLinkNode h;
|
|
bool defined;
|
|
RINOK(Archive_Get_HardLinkNode(archive, index, h, defined));
|
|
if (defined)
|
|
{
|
|
{
|
|
int linkIndex = _hardLinks.IDs.FindInSorted2(h);
|
|
if (linkIndex >= 0)
|
|
{
|
|
FString &hl = _hardLinks.Links[linkIndex];
|
|
if (hl.IsEmpty())
|
|
hl = fullProcessedPath;
|
|
else
|
|
{
|
|
if (!MyCreateHardLink(fullProcessedPath, hl))
|
|
{
|
|
RINOK(SendMessageError2("Can not create hard link", fullProcessedPath, hl));
|
|
return S_OK;
|
|
}
|
|
needWriteFile = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (needWriteFile)
|
|
{
|
|
_outFileStreamSpec = new COutFileStream;
|
|
CMyComPtr<ISequentialOutStream> outStreamLoc2(_outFileStreamSpec);
|
|
if (!_outFileStreamSpec->Open(fullProcessedPath, _isSplit ? OPEN_ALWAYS: CREATE_ALWAYS))
|
|
{
|
|
// if (::GetLastError() != ERROR_FILE_EXISTS || !isSplit)
|
|
{
|
|
RINOK(SendMessageError_with_LastError("Can not open output file", fullProcessedPath));
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
|
|
#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
|
|
|
|
if (_isSplit)
|
|
{
|
|
RINOK(_outFileStreamSpec->Seek(_position, STREAM_SEEK_SET, NULL));
|
|
}
|
|
|
|
_outFileStream = outStreamLoc2;
|
|
}
|
|
}
|
|
}
|
|
|
|
outStreamLoc = _outFileStream;
|
|
}
|
|
}
|
|
|
|
#ifndef _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
|
|
|
|
|
|
if (outStreamLoc)
|
|
{
|
|
/*
|
|
#ifdef SUPPORT_LINKS
|
|
|
|
if (!_CopyFile_Path.IsEmpty())
|
|
{
|
|
RINOK(PrepareOperation(askExtractMode));
|
|
RINOK(MyCopyFile(outStreamLoc));
|
|
return SetOperationResult(NArchive::NExtract::NOperationResult::kOK);
|
|
}
|
|
|
|
if (isCopyLink && _testMode)
|
|
return S_OK;
|
|
|
|
#endif
|
|
*/
|
|
|
|
*outStream = outStreamLoc.Detach();
|
|
}
|
|
|
|
return S_OK;
|
|
|
|
COM_TRY_END
|
|
}
|
|
|
|
|
|
STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode)
|
|
{
|
|
COM_TRY_BEGIN
|
|
|
|
#ifndef _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: 0);
|
|
|
|
COM_TRY_END
|
|
}
|
|
|
|
|
|
STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 opRes)
|
|
{
|
|
COM_TRY_BEGIN
|
|
|
|
#ifndef _SFX
|
|
if (ExtractToStreamCallback)
|
|
return ExtractToStreamCallback->SetOperationResult7(opRes, BoolToInt(_encrypted));
|
|
#endif
|
|
|
|
#ifndef _SFX
|
|
|
|
if (_hashStreamWasUsed)
|
|
{
|
|
_hashStreamSpec->_hash->Final(_item.IsDir,
|
|
#ifdef SUPPORT_ALT_STREAMS
|
|
_item.IsAltStream
|
|
#else
|
|
false
|
|
#endif
|
|
, _item.Path);
|
|
_curSize = _hashStreamSpec->GetSize();
|
|
_curSizeDefined = true;
|
|
_hashStreamSpec->ReleaseStream();
|
|
_hashStreamWasUsed = false;
|
|
}
|
|
|
|
#endif
|
|
|
|
if (_outFileStream)
|
|
{
|
|
_outFileStreamSpec->SetTime(
|
|
(WriteCTime && _fi.CTimeDefined) ? &_fi.CTime : NULL,
|
|
(WriteATime && _fi.ATimeDefined) ? &_fi.ATime : NULL,
|
|
(WriteMTime && _fi.MTimeDefined) ? &_fi.MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL));
|
|
_curSize = _outFileStreamSpec->ProcessedSize;
|
|
_curSizeDefined = true;
|
|
RINOK(_outFileStreamSpec->Close());
|
|
_outFileStream.Release();
|
|
}
|
|
|
|
#ifdef _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 *)data);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!_curSizeDefined)
|
|
GetUnpackSize();
|
|
|
|
if (_curSizeDefined)
|
|
{
|
|
#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 (!_stdOutMode && _extractMode && _fi.AttribDefined)
|
|
SetFileAttrib(_diskFilePath, _fi.Attrib, &_delayedSymLinks);
|
|
|
|
RINOK(_extractCallback2->SetOperationResult(opRes, BoolToInt(_encrypted)));
|
|
|
|
return S_OK;
|
|
|
|
COM_TRY_END
|
|
}
|
|
|
|
STDMETHODIMP CArchiveExtractCallback::ReportExtractResult(UInt32 indexType, UInt32 index, Int32 opRes)
|
|
{
|
|
if (_folderArchiveExtractCallback2)
|
|
{
|
|
bool isEncrypted = false;
|
|
wchar_t temp[16];
|
|
UString s2;
|
|
const wchar_t *s = NULL;
|
|
|
|
if (indexType == NArchive::NEventIndexType::kInArcIndex && index != (UInt32)(Int32)-1)
|
|
{
|
|
CReadArcItem item;
|
|
RINOK(_arc->GetItem(index, item));
|
|
s2 = item.Path;
|
|
s = s2;
|
|
RINOK(Archive_GetItemBoolProp(_arc->Archive, index, kpidEncrypted, isEncrypted));
|
|
}
|
|
else
|
|
{
|
|
temp[0] = '#';
|
|
ConvertUInt32ToString(index, temp + 1);
|
|
s = temp;
|
|
// if (indexType == NArchive::NEventIndexType::kBlockIndex) {}
|
|
}
|
|
|
|
return _folderArchiveExtractCallback2->ReportExtractResult(opRes, isEncrypted, s);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password)
|
|
{
|
|
COM_TRY_BEGIN
|
|
if (!_cryptoGetTextPassword)
|
|
{
|
|
RINOK(_extractCallback2.QueryInterface(IID_ICryptoGetTextPassword,
|
|
&_cryptoGetTextPassword));
|
|
}
|
|
return _cryptoGetTextPassword->CryptoGetTextPassword(password);
|
|
COM_TRY_END
|
|
}
|
|
|
|
|
|
struct CExtrRefSortPair
|
|
{
|
|
unsigned Len;
|
|
unsigned Index;
|
|
|
|
int Compare(const CExtrRefSortPair &a) const;
|
|
};
|
|
|
|
#define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; }
|
|
|
|
int CExtrRefSortPair::Compare(const CExtrRefSortPair &a) const
|
|
{
|
|
RINOZ(-MyCompare(Len, a.Len));
|
|
return MyCompare(Index, a.Index);
|
|
}
|
|
|
|
static unsigned GetNumSlashes(const FChar *s)
|
|
{
|
|
for (unsigned numSlashes = 0;;)
|
|
{
|
|
FChar c = *s++;
|
|
if (c == 0)
|
|
return numSlashes;
|
|
if (IS_PATH_SEPAR(c))
|
|
numSlashes++;
|
|
}
|
|
}
|
|
|
|
HRESULT CArchiveExtractCallback::SetDirsTimes()
|
|
{
|
|
HRESULT result = S_OK;
|
|
CRecordVector<CExtrRefSortPair> pairs;
|
|
pairs.ClearAndSetSize(_extractedFolderPaths.Size());
|
|
unsigned i;
|
|
|
|
for (i = 0; i < _extractedFolderPaths.Size(); i++)
|
|
{
|
|
CExtrRefSortPair &pair = pairs[i];
|
|
pair.Index = i;
|
|
pair.Len = GetNumSlashes(_extractedFolderPaths[i]);
|
|
}
|
|
|
|
pairs.Sort2();
|
|
|
|
for (i = 0; i < pairs.Size(); i++)
|
|
{
|
|
int pairIndex = pairs[i].Index;
|
|
int index = _extractedFolderIndices[pairIndex];
|
|
|
|
FILETIME CTime;
|
|
FILETIME ATime;
|
|
FILETIME MTime;
|
|
|
|
bool CTimeDefined;
|
|
bool ATimeDefined;
|
|
bool MTimeDefined;
|
|
|
|
RINOK(GetTime(index, kpidCTime, CTime, CTimeDefined));
|
|
RINOK(GetTime(index, kpidATime, ATime, ATimeDefined));
|
|
RINOK(GetTime(index, kpidMTime, MTime, MTimeDefined));
|
|
|
|
// printf("\n%S", _extractedFolderPaths[pairIndex]);
|
|
SetDirTime(_extractedFolderPaths[pairIndex],
|
|
(WriteCTime && CTimeDefined) ? &CTime : NULL,
|
|
(WriteATime && ATimeDefined) ? &ATime : NULL,
|
|
(WriteMTime && MTimeDefined) ? &MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL));
|
|
}
|
|
|
|
for (int i = 0; i != _delayedSymLinks.Size(); ++i)
|
|
if (!_delayedSymLinks[i].Create())
|
|
result = E_FAIL;
|
|
|
|
_delayedSymLinks.Clear();
|
|
|
|
return result;
|
|
}
|