blob: 315945ba7dfccf4d3ce7a9fd9a59c6f7ffd26c26 [file] [log] [blame]
// 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;
}