// 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 *realIndices) { _hardLinks.Clear(); if (!_arc->Ask_INode) return S_OK; IInArchive *archive = _arc->Archive; CRecordVector &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 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 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 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 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; }