diff options
Diffstat (limited to 'CPP/7zip/UI/Common/ArchiveExtractCallback.cpp')
-rw-r--r--[-rwxr-xr-x] | CPP/7zip/UI/Common/ArchiveExtractCallback.cpp | 965 |
1 files changed, 836 insertions, 129 deletions
diff --git a/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp b/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp index 9c9c0422..c365d66f 100755..100644 --- a/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp +++ b/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp @@ -2,42 +2,200 @@ #include "StdAfx.h" -#include "Common/ComTry.h" -#include "Common/StringConvert.h" -#include "Common/Wildcard.h" +#undef sprintf +#undef printf -#include "Windows/FileDir.h" -#include "Windows/FileFind.h" -#include "Windows/FileName.h" -#include "Windows/PropVariant.h" -#include "Windows/PropVariantConversions.h" +#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)); + } -static const char *kCantAutoRename = "ERROR: Can not create file with auto name"; -static const char *kCantRenameFile = "ERROR: Can not rename existing file "; -static const char *kCantDeleteOutputFile = "ERROR: Can not delete output file "; + 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, bool crcMode, + 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; - _crcMode = crcMode; _unpTotal = 1; _packTotal = packSize; @@ -45,14 +203,31 @@ void CArchiveExtractCallback::Init( _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; - NFile::NName::NormalizeDirPathPrefix(_directoryPath); + NName::NormalizeDirPathPrefix(_directoryPath); + NDir::MyGetFullPathName(directoryPath, _directoryPathFull); } STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 size) @@ -109,15 +284,48 @@ STDMETHODIMP CArchiveExtractCallback::SetRatioInfo(const UInt64 *inSize, const U 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) { - fullPath = _directoryPath; - for (int i = 0; i < dirPathParts.Size(); i++) + 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; - fullPath += us2fs(dirPathParts[i]); - NFile::NDirectory::MyCreateDirectory(fullPath); + const UString &s = dirPathParts[i]; + fullPath += us2fs(s); + #ifdef _WIN32 + if (_pathMode == NExtract::NPathMode::kAbsPaths) + if (i == 0 && IsDriveName(s)) + continue; + #endif + CreateDir(fullPath); } } @@ -143,18 +351,95 @@ HRESULT CArchiveExtractCallback::GetUnpackSize() HRESULT CArchiveExtractCallback::SendMessageError(const char *message, const FString &path) { - return _extractCallback2->MessageError(GetUnicodeString(message) + fs2us(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; + 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 - _crcStream.Release(); + *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; @@ -163,7 +448,7 @@ STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStre IInArchive *archive = _arc->Archive; RINOK(_arc->GetItemPath(index, fullPath)); - RINOK(IsArchiveItemFolder(archive, index, _fi.IsDir)); + RINOK(Archive_IsItem_Folder(archive, index, _fi.IsDir)); _filePath = fullPath; @@ -178,26 +463,220 @@ STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStre _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 - RINOK(GetArchiveItemBoolProp(archive, index, kpidEncrypted, _encrypted)); + 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(fullPath, !_fi.IsDir)) + if (!_wildcardCensor->CheckPath(_isAltStream, fullPath, !_fi.IsDir)) return S_OK; } - if (askExtractMode == NArchive::NExtract::NAskMode::kExtract && !_testMode) + + UStringVector pathParts; + + if (_use_baseParentFolder_mode) { - if (_stdOutMode) + 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) { - CMyComPtr<ISequentialOutStream> outStreamLoc = new CStdOutFileStream; - *outStream = outStreamLoc.Detach(); - return S_OK; + 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)); @@ -219,35 +698,15 @@ STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStre bool isAnti = false; RINOK(_arc->IsItemAnti(index, isAnti)); - UStringVector pathParts; - SplitPathToParts(fullPath, pathParts); - - if (pathParts.IsEmpty()) - return E_FAIL; - int numRemovePathParts = 0; - switch(_pathMode) - { - case NExtract::NPathMode::kFullPathnames: - break; - case NExtract::NPathMode::kCurrentPathnames: - { - numRemovePathParts = _removePathParts.Size(); - if (pathParts.Size() <= numRemovePathParts) - return E_FAIL; - for (int i = 0; i < numRemovePathParts; i++) - if (_removePathParts[i].CompareNoCase(pathParts[i]) != 0) - return E_FAIL; - break; - } - case NExtract::NPathMode::kNoPathnames: - { - numRemovePathParts = pathParts.Size() - 1; - break; - } - } - pathParts.Delete(0, numRemovePathParts); - MakeCorrectPath(pathParts); + 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) @@ -261,65 +720,72 @@ STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStre FString fullPathNew; CreateComplexDirectory(pathParts, fullPathNew); if (_fi.IsDir) - NFile::NDirectory::SetDirTime(fullPathNew, + { + _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 = _directoryPath + us2fs(processedPath); + FString fullProcessedPath = us2fs(processedPath); + if (_pathMode != NExtract::NPathMode::kAbsPaths || + !NName::IsAbsolutePath(processedPath)) + fullProcessedPath = _directoryPath + fullProcessedPath; if (_fi.IsDir) { _diskFilePath = fullProcessedPath; if (isAnti) - NFile::NDirectory::MyRemoveDirectory(_diskFilePath); - return S_OK; + RemoveDir(_diskFilePath); + #ifdef SUPPORT_LINKS + if (linkPath.IsEmpty()) + #endif + return S_OK; } - - if (!_isSplit) + else if (!_isSplit) { - NFile::NFind::CFileInfo fileInfo; + NFind::CFileInfo fileInfo; if (fileInfo.Find(fullProcessedPath)) { - switch(_overwriteMode) + switch (_overwriteMode) { - case NExtract::NOverwriteMode::kSkipExisting: + case NExtract::NOverwriteMode::kSkip: return S_OK; - case NExtract::NOverwriteMode::kAskBefore: + 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(fullProcessedPath), &fileInfo.MTime, &fileInfo.Size, fullPath, + fs2us(realFullProcessedPath), &fileInfo.MTime, &fileInfo.Size, fullPath, _fi.MTimeDefined ? &_fi.MTime : NULL, _curSizeDefined ? &_curSize : NULL, &overwiteResult)) - switch(overwiteResult) + switch (overwiteResult) { - case NOverwriteAnswer::kCancel: - return E_ABORT; - case NOverwriteAnswer::kNo: - return S_OK; - case NOverwriteAnswer::kNoToAll: - _overwriteMode = NExtract::NOverwriteMode::kSkipExisting; - return S_OK; - case NOverwriteAnswer::kYesToAll: - _overwriteMode = NExtract::NOverwriteMode::kWithoutPrompt; - break; - case NOverwriteAnswer::kYes: - break; - case NOverwriteAnswer::kAutoRename: - _overwriteMode = NExtract::NOverwriteMode::kAutoRename; - break; + 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::kAutoRename) + if (_overwriteMode == NExtract::NOverwriteMode::kRename) { if (!AutoRenamePath(fullProcessedPath)) { @@ -327,7 +793,7 @@ STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStre return E_FAIL; } } - else if (_overwriteMode == NExtract::NOverwriteMode::kAutoRenameExisting) + else if (_overwriteMode == NExtract::NOverwriteMode::kRenameExisting) { FString existPath = fullProcessedPath; if (!AutoRenamePath(existPath)) @@ -335,57 +801,186 @@ STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStre RINOK(SendMessageError(kCantAutoRename, fullProcessedPath)); return E_FAIL; } - if (!NFile::NDirectory::MyMoveFile(fullProcessedPath, existPath)) + // 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 (!NFile::NDirectory::DeleteFileAlways(fullProcessedPath)) + { + 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) { - _outFileStreamSpec = new COutFileStream; - CMyComPtr<ISequentialOutStream> outStreamLoc(_outFileStreamSpec); - if (!_outFileStreamSpec->Open(fullProcessedPath, _isSplit ? OPEN_ALWAYS: CREATE_ALWAYS)) + #ifdef SUPPORT_LINKS + + if (!linkPath.IsEmpty()) { - // if (::GetLastError() != ERROR_FILE_EXISTS || !isSplit) + #ifndef UNDER_CE + + UString relatPath; + if (isRelative) + relatPath = GetDirPrefixOf(_filePath); + relatPath += linkPath; + + if (!IsSafePath(relatPath)) { - RINOK(SendMessageError("can not open output file ", fullProcessedPath)); - return S_OK; + 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 } - if (_isSplit) + else + #endif // SUPPORT_LINKS { - RINOK(_outFileStreamSpec->Seek(_position, STREAM_SEEK_SET, NULL)); + 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; + } } - _outFileStream = outStreamLoc; - *outStream = outStreamLoc.Detach(); } - _diskFilePath = fullProcessedPath; - } - else - { - *outStream = NULL; + + outStreamLoc = _outFileStream; } - if (_crcMode) +} + + #ifndef _SFX + + if (_hashStream) { - _crcStreamSpec = new COutStreamWithCRC; - _crcStream = _crcStreamSpec; - CMyComPtr<ISequentialOutStream> crcStream = _crcStreamSpec; - _crcStreamSpec->SetStream(*outStream); - if (*outStream) - (*outStream)->Release(); - *outStream = crcStream.Detach(); - _crcStreamSpec->Init(true); + 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 } @@ -393,6 +988,12 @@ STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStre STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode) { COM_TRY_BEGIN + + #ifndef _SFX + if (ExtractToStreamCallback) + return ExtractToStreamCallback->PrepareOperation7(askExtractMode); + #endif + _extractMode = false; switch (askExtractMode) { @@ -411,24 +1012,25 @@ STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode) STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult) { COM_TRY_BEGIN - switch(operationResult) - { - case NArchive::NExtract::NOperationResult::kOK: - case NArchive::NExtract::NOperationResult::kUnSupportedMethod: - case NArchive::NExtract::NOperationResult::kCRCError: - case NArchive::NExtract::NOperationResult::kDataError: - break; - default: - _outFileStream.Release(); - return E_FAIL; - } - if (_crcStream) + + #ifndef _SFX + if (ExtractToStreamCallback) + return ExtractToStreamCallback->SetOperationResult7(operationResult, _encrypted); + #endif + + #ifndef _SFX + + if (_hashStreamWasUsed) { - CrcSum += _crcStreamSpec->GetCRC(); - _curSize = _crcStreamSpec->GetSize(); + _hashStreamSpec->_hash->Final(_fi.IsDir, _isAltStream, _filePath); + _curSize = _hashStreamSpec->GetSize(); _curSizeDefined = true; - _crcStream.Release(); + _hashStreamSpec->ReleaseStream(); + _hashStreamWasUsed = false; } + + #endif + if (_outFileStream) { _outFileStreamSpec->SetTime( @@ -440,17 +1042,48 @@ STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult) 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, (void *)data); + } + } + } + #endif + if (!_curSizeDefined) GetUnpackSize(); if (_curSizeDefined) - UnpackSize += _curSize; + { + if (_isAltStream) + AltStreams_UnpackSize += _curSize; + else + UnpackSize += _curSize; + } + if (_fi.IsDir) NumFolders++; + else if (_isAltStream) + NumAltStreams++; else NumFiles++; if (_extractMode && _fi.AttribDefined) - NFile::NDirectory::MySetFileAttributes(_diskFilePath, _fi.Attrib); + SetFileAttrib(_diskFilePath, _fi.Attrib); RINOK(_extractCallback2->SetOperationResult(operationResult, _encrypted)); return S_OK; COM_TRY_END @@ -482,3 +1115,77 @@ STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password) 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; +} |