// ArchiveExtractCallback.cpp | |
#include "StdAfx.h" | |
#undef sprintf | |
#undef printf | |
#include "../../../Common/ComTry.h" | |
#include "../../../Common/StringConvert.h" | |
#include "../../../Common/Wildcard.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/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); | |
} | |
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; | |
RINOK(Archive_Get_HardLinkNode(archive, realIndices ? (*realIndices)[i] : i, h, defined)); | |
if (defined) | |
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, | |
UInt64 packSize) | |
{ | |
_extractedFolderPaths.Clear(); | |
_extractedFolderIndices.Clear(); | |
#ifdef SUPPORT_LINKS | |
_hardLinks.Clear(); | |
#endif | |
_ntOptions = ntOptions; | |
_wildcardCensor = wildcardCensor; | |
_stdOutMode = stdOutMode; | |
_testMode = testMode; | |
_unpTotal = 1; | |
_packTotal = packSize; | |
_extractCallback2 = extractCallback2; | |
_compressProgress.Release(); | |
_extractCallback2.QueryInterface(IID_ICompressProgressInfo, &_compressProgress); | |
#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; | |
_baseParentFolder = (UInt32)(Int32)-1; | |
_use_baseParentFolder_mode = false; | |
_arc = arc; | |
_directoryPath = directoryPath; | |
NName::NormalizeDirPathPrefix(_directoryPath); | |
NDir::MyGetFullPathName(directoryPath, _directoryPathFull); | |
} | |
STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 size) | |
{ | |
COM_TRY_BEGIN | |
_unpTotal = size; | |
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; | |
if (_multiArchives) | |
{ | |
if (completeValue != NULL) | |
{ | |
UInt64 packCur = LocalProgressSpec->InSize + MyMultDiv64(*completeValue, _unpTotal, _packTotal); | |
return _extractCallback2->SetCompleted(&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 | |
} | |
#define IS_LETTER_CHAR(c) ((c) >= 'a' && (c) <= 'z' || (c) >= 'A' && (c) <= 'Z') | |
static inline bool IsDriveName(const UString &s) | |
{ | |
return s.Len() == 2 && s[1] == ':' && IS_LETTER_CHAR(s[0]); | |
} | |
void CArchiveExtractCallback::CreateComplexDirectory(const UStringVector &dirPathParts, FString &fullPath) | |
{ | |
bool isAbsPath = false; | |
if (!dirPathParts.IsEmpty()) | |
{ | |
const UString &s = dirPathParts[0]; | |
if (s.IsEmpty()) | |
isAbsPath = true; | |
#ifdef _WIN32 | |
else | |
{ | |
if (dirPathParts.Size() > 1 && IsDriveName(s)) | |
isAbsPath = true; | |
} | |
#endif | |
} | |
if (_pathMode == NExtract::NPathMode::kAbsPaths && isAbsPath) | |
fullPath.Empty(); | |
else | |
fullPath = _directoryPath; | |
FOR_VECTOR (i, dirPathParts) | |
{ | |
if (i > 0) | |
fullPath += FCHAR_PATH_SEPARATOR; | |
const UString &s = dirPathParts[i]; | |
fullPath += us2fs(s); | |
#ifdef _WIN32 | |
if (_pathMode == NExtract::NPathMode::kAbsPaths) | |
if (i == 0 && IsDriveName(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); | |
} | |
HRESULT CArchiveExtractCallback::SendMessageError(const char *message, const FString &path) | |
{ | |
return _extractCallback2->MessageError( | |
UString(L"ERROR: ") + | |
GetUnicodeString(message) + L": " + fs2us(path)); | |
} | |
HRESULT CArchiveExtractCallback::SendMessageError2(const char *message, const FString &path1, const FString &path2) | |
{ | |
return _extractCallback2->MessageError( | |
UString(L"ERROR: ") + | |
GetUnicodeString(message) + UString(L": ") + fs2us(path1) + UString(L" : ") + fs2us(path2)); | |
} | |
#ifndef _SFX | |
STDMETHODIMP CGetProp::GetProp(PROPID propID, PROPVARIANT *value) | |
{ | |
if (propID == kpidName) | |
{ | |
COM_TRY_BEGIN | |
NCOM::CPropVariant prop = Name.Ptr(); | |
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 (s.Back() == WCHAR_PATH_SEPARATOR) | |
s.DeleteBack(); | |
int pos = s.ReverseFind(WCHAR_PATH_SEPARATOR); | |
s.DeleteFrom(pos + 1); | |
} | |
return s; | |
} | |
static bool IsSafePath(const UString &path) | |
{ | |
UStringVector parts; | |
SplitPathToParts(path, parts); | |
int level = 0; | |
FOR_VECTOR(i, parts) | |
{ | |
const UString &s = parts[i]; | |
if (s.IsEmpty()) | |
continue; | |
if (s == L".") | |
continue; | |
if (s == L"..") | |
{ | |
if (level <= 0) | |
return false; | |
level--; | |
} | |
else | |
level++; | |
} | |
return level > 0; | |
} | |
#endif | |
STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode) | |
{ | |
COM_TRY_BEGIN | |
*outStream = 0; | |
#ifndef _SFX | |
if (_hashStream) | |
_hashStreamSpec->ReleaseStream(); | |
_hashStreamWasUsed = false; | |
#endif | |
_outFileStream.Release(); | |
_encrypted = false; | |
_isSplit = false; | |
_isAltStream = false; | |
_curSize = 0; | |
_curSizeDefined = false; | |
_index = index; | |
UString fullPath; | |
IInArchive *archive = _arc->Archive; | |
RINOK(_arc->GetItemPath(index, fullPath)); | |
RINOK(Archive_IsItem_Folder(archive, index, _fi.IsDir)); | |
_filePath = fullPath; | |
{ | |
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 isHardLink = false; | |
bool isJunction = false; | |
bool isRelative = false; | |
UString linkPath; | |
// RINOK(Archive_GetItemBoolProp(archive, index, kpidIsHardLink, isHardLink)); | |
// if (isHardLink) | |
{ | |
NCOM::CPropVariant prop; | |
RINOK(archive->GetProperty(index, kpidHardLink, &prop)); | |
if (prop.vt == VT_BSTR) | |
{ | |
isHardLink = true; | |
linkPath = prop.bstrVal; | |
isRelative = false; // TAR: hard links are from root folder of archive | |
} | |
else if (prop.vt == VT_EMPTY) | |
{ | |
// linkPath.Empty(); | |
} | |
else | |
return E_FAIL; | |
} | |
{ | |
NCOM::CPropVariant prop; | |
RINOK(archive->GetProperty(index, kpidSymLink, &prop)); | |
if (prop.vt == VT_BSTR) | |
{ | |
isHardLink = false; | |
linkPath = prop.bstrVal; | |
isRelative = true; // TAR: symbolic links are relative | |
} | |
else if (prop.vt == VT_EMPTY) | |
{ | |
// linkPath.Empty(); | |
} | |
else | |
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; | |
linkPath = reparse.GetPath(); | |
isJunction = reparse.IsMountPoint(); | |
isRelative = reparse.IsRelative(); | |
#ifndef _WIN32 | |
linkPath.Replace(WCHAR_PATH_SEPARATOR, '/', ); | |
#endif | |
} | |
} | |
} | |
if (!linkPath.IsEmpty()) | |
{ | |
#ifdef _WIN32 | |
linkPath.Replace('/', WCHAR_PATH_SEPARATOR); | |
#endif | |
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 = MakePathNameFromParts(pathParts); | |
} | |
#endif | |
RINOK(Archive_GetItemBoolProp(archive, index, kpidEncrypted, _encrypted)); | |
RINOK(GetUnpackSize()); | |
RINOK(Archive_IsItem_AltStream(archive, index, _isAltStream)); | |
if (!_ntOptions.AltStreams.Val && _isAltStream) | |
return S_OK; | |
if (_wildcardCensor) | |
{ | |
if (!_wildcardCensor->CheckPath(_isAltStream, fullPath, !_fi.IsDir)) | |
return S_OK; | |
} | |
UStringVector pathParts; | |
if (_use_baseParentFolder_mode) | |
{ | |
int baseParent = _baseParentFolder; | |
if (_pathMode == NExtract::NPathMode::kFullPaths || | |
_pathMode == NExtract::NPathMode::kAbsPaths) | |
baseParent = -1; | |
RINOK(_arc->GetItemPathToParent(index, baseParent, pathParts)); | |
if (_pathMode == NExtract::NPathMode::kNoPaths && !pathParts.IsEmpty()) | |
pathParts.DeleteFrontal(pathParts.Size() - 1); | |
} | |
else | |
{ | |
SplitPathToParts(fullPath, pathParts); | |
if (pathParts.IsEmpty()) | |
return E_FAIL; | |
unsigned numRemovePathParts = 0; | |
switch (_pathMode) | |
{ | |
case NExtract::NPathMode::kCurPaths: | |
{ | |
bool badPrefix = false; | |
if (pathParts.Size() <= _removePathParts.Size()) | |
badPrefix = true; | |
else | |
{ | |
FOR_VECTOR (i, _removePathParts) | |
{ | |
if (!_removePathParts[i].IsEqualToNoCase(pathParts[i])) | |
{ | |
badPrefix = true; | |
break; | |
} | |
} | |
} | |
if (badPrefix) | |
{ | |
if (askExtractMode == NArchive::NExtract::NAskMode::kExtract && !_testMode) | |
return E_FAIL; | |
} | |
else | |
numRemovePathParts = _removePathParts.Size(); | |
break; | |
} | |
case NExtract::NPathMode::kNoPaths: | |
{ | |
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; | |
GetProp_Spec->Name = MakePathNameFromParts(pathParts); | |
return ExtractToStreamCallback->GetStream7(GetProp_Spec->Name, _fi.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)); | |
bool replace = _isAltStream ? | |
_ntOptions.ReplaceColonForAltStream : | |
!_ntOptions.WriteToAltStreamIfColon; | |
if (_pathMode != NExtract::NPathMode::kAbsPaths) | |
MakeCorrectPath(_directoryPath.IsEmpty(), pathParts, replace); | |
Correct_IfEmptyLastPart(pathParts); | |
UString processedPath = MakePathNameFromParts(pathParts); | |
if (!isAnti) | |
{ | |
if (!_fi.IsDir) | |
{ | |
if (!pathParts.IsEmpty()) | |
pathParts.DeleteBack(); | |
} | |
if (!pathParts.IsEmpty()) | |
{ | |
FString fullPathNew; | |
CreateComplexDirectory(pathParts, fullPathNew); | |
if (_fi.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 = _directoryPath + fullProcessedPath; | |
if (_fi.IsDir) | |
{ | |
_diskFilePath = fullProcessedPath; | |
if (isAnti) | |
RemoveDir(_diskFilePath); | |
#ifdef SUPPORT_LINKS | |
if (linkPath.IsEmpty()) | |
#endif | |
return S_OK; | |
} | |
else if (!_isSplit) | |
{ | |
NFind::CFileInfo fileInfo; | |
if (fileInfo.Find(fullProcessedPath)) | |
{ | |
switch (_overwriteMode) | |
{ | |
case NExtract::NOverwriteMode::kSkip: | |
return S_OK; | |
case NExtract::NOverwriteMode::kAsk: | |
{ | |
int slashPos = fullProcessedPath.ReverseFind(FTEXT('/')); | |
#ifdef _WIN32 | |
int slash1Pos = fullProcessedPath.ReverseFind(FTEXT('\\')); | |
slashPos = MyMax(slashPos, slash1Pos); | |
#endif | |
FString realFullProcessedPath = fullProcessedPath.Left(slashPos + 1) + fileInfo.Name; | |
Int32 overwiteResult; | |
RINOK(_extractCallback2->AskOverwrite( | |
fs2us(realFullProcessedPath), &fileInfo.MTime, &fileInfo.Size, fullPath, | |
_fi.MTimeDefined ? &_fi.MTime : NULL, | |
_curSizeDefined ? &_curSize : NULL, | |
&overwiteResult)) | |
switch (overwiteResult) | |
{ | |
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; | |
} | |
} | |
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 folders too | |
if (!MyMoveFile(fullProcessedPath, existPath)) | |
{ | |
RINOK(SendMessageError(kCantRenameFile, fullProcessedPath)); | |
return E_FAIL; | |
} | |
} | |
else | |
{ | |
if (fileInfo.IsDir()) | |
{ | |
// do we need to delete all files in folder? | |
if (!RemoveDir(fullProcessedPath)) | |
{ | |
RINOK(SendMessageError(kCantDeleteOutputDir, fullProcessedPath)); | |
return S_OK; | |
} | |
} | |
else if (!DeleteFileAlways(fullProcessedPath)) | |
{ | |
RINOK(SendMessageError(kCantDeleteOutputFile, fullProcessedPath)); | |
return S_OK; | |
// return E_FAIL; | |
} | |
} | |
} | |
} | |
_diskFilePath = fullProcessedPath; | |
if (!isAnti) | |
{ | |
#ifdef SUPPORT_LINKS | |
if (!linkPath.IsEmpty()) | |
{ | |
#ifndef UNDER_CE | |
UString relatPath; | |
if (isRelative) | |
relatPath = GetDirPrefixOf(_filePath); | |
relatPath += linkPath; | |
if (!IsSafePath(relatPath)) | |
{ | |
RINOK(SendMessageError("Dangerous link path was ignored", us2fs(relatPath))); | |
} | |
else | |
{ | |
FString existPath; | |
if (isHardLink || !isRelative) | |
{ | |
if (!NName::GetFullPath(_directoryPathFull, us2fs(relatPath), existPath)) | |
{ | |
RINOK(SendMessageError("Incorrect path", us2fs(relatPath))); | |
} | |
} | |
else | |
{ | |
existPath = us2fs(linkPath); | |
} | |
if (!existPath.IsEmpty()) | |
{ | |
if (isHardLink) | |
{ | |
if (!MyCreateHardLink(fullProcessedPath, existPath)) | |
{ | |
RINOK(SendMessageError2("Can not create hard link", fullProcessedPath, existPath)); | |
// return S_OK; | |
} | |
} | |
else if (_ntOptions.SymLinks.Val) | |
{ | |
// bool isSymLink = true; // = false for junction | |
if (_fi.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())) | |
{ | |
return E_FAIL; // "Internal conversion error"; | |
} | |
if (!NFile::NIO::SetReparseData(fullProcessedPath, _fi.IsDir, data, (DWORD)data.Size())) | |
{ | |
RINOK(SendMessageError("Can not set reparse data", fullProcessedPath)); | |
} | |
} | |
} | |
} | |
} | |
#endif | |
} | |
else | |
#endif // SUPPORT_LINKS | |
{ | |
bool needWriteFile = true; | |
#ifdef SUPPORT_LINKS | |
if (!_hardLinks.IDs.IsEmpty()) | |
{ | |
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> outStreamLoc(_outFileStreamSpec); | |
if (!_outFileStreamSpec->Open(fullProcessedPath, _isSplit ? OPEN_ALWAYS: CREATE_ALWAYS)) | |
{ | |
// if (::GetLastError() != ERROR_FILE_EXISTS || !isSplit) | |
{ | |
RINOK(SendMessageError("Can not open output file ", fullProcessedPath)); | |
return S_OK; | |
} | |
} | |
if (_isSplit) | |
{ | |
RINOK(_outFileStreamSpec->Seek(_position, STREAM_SEEK_SET, NULL)); | |
} | |
_outFileStream = outStreamLoc; | |
} | |
} | |
} | |
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) | |
*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(_filePath, _fi.IsDir, | |
askExtractMode, _isSplit ? &_position: 0); | |
COM_TRY_END | |
} | |
STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult) | |
{ | |
COM_TRY_BEGIN | |
#ifndef _SFX | |
if (ExtractToStreamCallback) | |
return ExtractToStreamCallback->SetOperationResult7(operationResult, _encrypted); | |
#endif | |
#ifndef _SFX | |
if (_hashStreamWasUsed) | |
{ | |
_hashStreamSpec->_hash->Final(_fi.IsDir, _isAltStream, _filePath); | |
_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 (_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) | |
{ | |
if (_isAltStream) | |
AltStreams_UnpackSize += _curSize; | |
else | |
UnpackSize += _curSize; | |
} | |
if (_fi.IsDir) | |
NumFolders++; | |
else if (_isAltStream) | |
NumAltStreams++; | |
else | |
NumFiles++; | |
if (_extractMode && _fi.AttribDefined) | |
SetFileAttrib(_diskFilePath, _fi.Attrib); | |
RINOK(_extractCallback2->SetOperationResult(operationResult, _encrypted)); | |
return S_OK; | |
COM_TRY_END | |
} | |
/* | |
STDMETHODIMP CArchiveExtractCallback::GetInStream( | |
const wchar_t *name, ISequentialInStream **inStream) | |
{ | |
COM_TRY_BEGIN | |
CInFileStream *inFile = new CInFileStream; | |
CMyComPtr<ISequentialInStream> inStreamTemp = inFile; | |
if (!inFile->Open(_srcDirectoryPrefix + name)) | |
return ::GetLastError(); | |
*inStream = inStreamTemp.Detach(); | |
return S_OK; | |
COM_TRY_END | |
} | |
*/ | |
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 | |
{ | |
int Len; | |
int 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 int GetNumSlashes(const FChar *s) | |
{ | |
for (int numSlashes = 0;;) | |
{ | |
FChar c = *s++; | |
if (c == 0) | |
return numSlashes; | |
if ( | |
#ifdef _WIN32 | |
c == FTEXT('\\') || | |
#endif | |
c == FTEXT('/')) | |
numSlashes++; | |
} | |
} | |
HRESULT CArchiveExtractCallback::SetDirsTimes() | |
{ | |
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)); | |
} | |
return S_OK; | |
} |