p7zip/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp
2017-10-11 12:35:36 +02:00

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