// UpdateCallback.cpp #include "StdAfx.h" #ifndef _7ZIP_ST #include "../../../Windows/Synchronization.h" #endif #include "../../../Common/ComTry.h" #include "../../../Common/IntToString.h" #include "../../../Common/StringConvert.h" #include "../../../Common/Wildcard.h" #include "../../../Windows/FileDir.h" #include "../../../Windows/FileName.h" #include "../../../Windows/PropVariant.h" #include "../../Common/StreamObjects.h" #include "UpdateCallback.h" #if defined(_WIN32) && !defined(UNDER_CE) #define _USE_SECURITY_CODE #include "../../../Windows/SecurityUtils.h" #endif using namespace NWindows; using namespace NFile; #ifndef _7ZIP_ST static NSynchronization::CCriticalSection g_CriticalSection; #define MT_LOCK NSynchronization::CCriticalSectionLock lock(g_CriticalSection); #else #define MT_LOCK #endif #ifdef _USE_SECURITY_CODE bool InitLocalPrivileges(); #endif CArchiveUpdateCallback::CArchiveUpdateCallback(): _hardIndex_From((UInt32)(Int32)-1), Callback(NULL), DirItems(NULL), ParentDirItem(NULL), Arc(NULL), ArcItems(NULL), UpdatePairs(NULL), NewNames(NULL), ShareForWrite(false), StdInMode(false), KeepOriginalItemNames(false), StoreNtSecurity(false), StoreHardLinks(false), StoreSymLinks(false), ProcessedItemsStatuses(NULL) { #ifdef _USE_SECURITY_CODE _saclEnabled = InitLocalPrivileges(); #endif } STDMETHODIMP CArchiveUpdateCallback::SetTotal(UInt64 size) { COM_TRY_BEGIN return Callback->SetTotal(size); COM_TRY_END } STDMETHODIMP CArchiveUpdateCallback::SetCompleted(const UInt64 *completeValue) { COM_TRY_BEGIN return Callback->SetCompleted(completeValue); COM_TRY_END } STDMETHODIMP CArchiveUpdateCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) { COM_TRY_BEGIN return Callback->SetRatioInfo(inSize, outSize); COM_TRY_END } /* static const CStatProp kProps[] = { { NULL, kpidPath, VT_BSTR}, { NULL, kpidIsDir, VT_BOOL}, { NULL, kpidSize, VT_UI8}, { NULL, kpidCTime, VT_FILETIME}, { NULL, kpidATime, VT_FILETIME}, { NULL, kpidMTime, VT_FILETIME}, { NULL, kpidAttrib, VT_UI4}, { NULL, kpidIsAnti, VT_BOOL} }; STDMETHODIMP CArchiveUpdateCallback::EnumProperties(IEnumSTATPROPSTG **) { return CStatPropEnumerator::CreateEnumerator(kProps, ARRAY_SIZE(kProps), enumerator); } */ STDMETHODIMP CArchiveUpdateCallback::GetUpdateItemInfo(UInt32 index, Int32 *newData, Int32 *newProps, UInt32 *indexInArchive) { COM_TRY_BEGIN RINOK(Callback->CheckBreak()); const CUpdatePair2 &up = (*UpdatePairs)[index]; if (newData) *newData = BoolToInt(up.NewData); if (newProps) *newProps = BoolToInt(up.NewProps); if (indexInArchive) { *indexInArchive = (UInt32)(Int32)-1; if (up.ExistInArchive()) *indexInArchive = (ArcItems == 0) ? up.ArcIndex : (*ArcItems)[up.ArcIndex].IndexInServer; } return S_OK; COM_TRY_END } STDMETHODIMP CArchiveUpdateCallback::GetRootProp(PROPID propID, PROPVARIANT *value) { NCOM::CPropVariant prop; switch (propID) { case kpidIsDir: prop = true; break; case kpidAttrib: if (ParentDirItem) prop = ParentDirItem->Attrib; break; case kpidCTime: if (ParentDirItem) prop = ParentDirItem->CTime; break; case kpidATime: if (ParentDirItem) prop = ParentDirItem->ATime; break; case kpidMTime: if (ParentDirItem) prop = ParentDirItem->MTime; break; } prop.Detach(value); return S_OK; } STDMETHODIMP CArchiveUpdateCallback::GetParent(UInt32 /* index */, UInt32 *parent, UInt32 *parentType) { *parentType = NParentType::kDir; *parent = (UInt32)(Int32)-1; return S_OK; } STDMETHODIMP CArchiveUpdateCallback::GetNumRawProps(UInt32 *numProps) { *numProps = 0; if (StoreNtSecurity) *numProps = 1; return S_OK; } STDMETHODIMP CArchiveUpdateCallback::GetRawPropInfo(UInt32 /* index */, BSTR *name, PROPID *propID) { *name = NULL; *propID = kpidNtSecure; return S_OK; } STDMETHODIMP CArchiveUpdateCallback::GetRootRawProp(PROPID #ifdef _USE_SECURITY_CODE propID #endif , const void **data, UInt32 *dataSize, UInt32 *propType) { *data = 0; *dataSize = 0; *propType = 0; if (!StoreNtSecurity) return S_OK; #ifdef _USE_SECURITY_CODE if (propID == kpidNtSecure) { if (StdInMode) return S_OK; if (ParentDirItem) { if (ParentDirItem->SecureIndex < 0) return S_OK; const CByteBuffer &buf = DirItems->SecureBlocks.Bufs[ParentDirItem->SecureIndex]; *data = buf; *dataSize = (UInt32)buf.Size(); *propType = NPropDataType::kRaw; return S_OK; } if (Arc && Arc->GetRootProps) return Arc->GetRootProps->GetRootRawProp(propID, data, dataSize, propType); } #endif return S_OK; } // #ifdef _USE_SECURITY_CODE // #endif STDMETHODIMP CArchiveUpdateCallback::GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType) { *data = 0; *dataSize = 0; *propType = 0; if (propID == kpidNtSecure || propID == kpidNtReparse) { if (StdInMode) return S_OK; const CUpdatePair2 &up = (*UpdatePairs)[index]; if (up.UseArcProps && up.ExistInArchive() && Arc->GetRawProps) return Arc->GetRawProps->GetRawProp( ArcItems ? (*ArcItems)[up.ArcIndex].IndexInServer : up.ArcIndex, propID, data, dataSize, propType); { /* if (!up.NewData) return E_FAIL; */ if (up.IsAnti) return S_OK; #ifndef UNDER_CE const CDirItem &di = DirItems->Items[up.DirIndex]; #endif #ifdef _USE_SECURITY_CODE if (propID == kpidNtSecure) { if (!StoreNtSecurity) return S_OK; if (di.SecureIndex < 0) return S_OK; const CByteBuffer &buf = DirItems->SecureBlocks.Bufs[di.SecureIndex]; *data = buf; *dataSize = (UInt32)buf.Size(); *propType = NPropDataType::kRaw; } else #endif { // propID == kpidNtReparse if (!StoreSymLinks) return S_OK; #ifndef UNDER_CE const CByteBuffer *buf = &di.ReparseData2; if (buf->Size() == 0) buf = &di.ReparseData; if (buf->Size() != 0) { *data = *buf; *dataSize = (UInt32)buf->Size(); *propType = NPropDataType::kRaw; } #endif } return S_OK; } } return S_OK; } #ifndef UNDER_CE static UString GetRelativePath(const UString &to, const UString &from) { UStringVector partsTo, partsFrom; SplitPathToParts(to, partsTo); SplitPathToParts(from, partsFrom); unsigned i; for (i = 0;; i++) { if (i + 1 >= partsFrom.Size() || i + 1 >= partsTo.Size()) break; if (CompareFileNames(partsFrom[i], partsTo[i]) != 0) break; } if (i == 0) { #ifdef _WIN32 if (NName::IsDrivePath(to) || NName::IsDrivePath(from)) return to; #endif } UString s; unsigned k; for (k = i + 1; k < partsFrom.Size(); k++) s += L".." WSTRING_PATH_SEPARATOR; for (k = i; k < partsTo.Size(); k++) { if (k != i) s.Add_PathSepar(); s += partsTo[k]; } return s; } #endif STDMETHODIMP CArchiveUpdateCallback::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) { COM_TRY_BEGIN const CUpdatePair2 &up = (*UpdatePairs)[index]; NCOM::CPropVariant prop; if (up.NewData) { /* if (propID == kpidIsHardLink) { prop = _isHardLink; prop.Detach(value); return S_OK; } */ if (propID == kpidSymLink) { if (index == _hardIndex_From) { prop.Detach(value); return S_OK; } if (up.DirIndex >= 0) { #ifndef UNDER_CE const CDirItem &di = DirItems->Items[up.DirIndex]; // if (di.IsDir()) { CReparseAttr attr; if (attr.Parse(di.ReparseData, di.ReparseData.Size())) { UString simpleName = attr.GetPath(); if (attr.IsRelative()) prop = simpleName; else { const FString phyPath = DirItems->GetPhyPath(up.DirIndex); FString fullPath; if (NDir::MyGetFullPathName(phyPath, fullPath)) { prop = GetRelativePath(simpleName, fs2us(fullPath)); } } prop.Detach(value); return S_OK; } } #endif } } else if (propID == kpidHardLink) { if (index == _hardIndex_From) { const CKeyKeyValPair &pair = _map[_hardIndex_To]; const CUpdatePair2 &up2 = (*UpdatePairs)[pair.Value]; prop = DirItems->GetLogPath(up2.DirIndex); prop.Detach(value); return S_OK; } if (up.DirIndex >= 0) { prop.Detach(value); return S_OK; } } } if (up.IsAnti && propID != kpidIsDir && propID != kpidPath && propID != kpidIsAltStream) { switch (propID) { case kpidSize: prop = (UInt64)0; break; case kpidIsAnti: prop = true; break; } } else if (propID == kpidPath && up.NewNameIndex >= 0) prop = (*NewNames)[up.NewNameIndex]; else if (propID == kpidShortName && up.NewNameIndex >= 0 && up.IsMainRenameItem) { // we can generate new ShortName here; } else if ((up.UseArcProps || (KeepOriginalItemNames && (propID == kpidPath || propID == kpidIsAltStream))) && up.ExistInArchive() && Archive) return Archive->GetProperty(ArcItems ? (*ArcItems)[up.ArcIndex].IndexInServer : up.ArcIndex, propID, value); else if (up.ExistOnDisk()) { const CDirItem &di = DirItems->Items[up.DirIndex]; switch (propID) { case kpidPath: prop = DirItems->GetLogPath(up.DirIndex); break; case kpidIsDir: prop = di.IsDir(); break; case kpidSize: prop = di.IsDir() ? (UInt64)0 : di.Size; break; case kpidAttrib: prop = di.Attrib; break; case kpidCTime: prop = di.CTime; break; case kpidATime: prop = di.ATime; break; case kpidMTime: prop = di.MTime; break; case kpidIsAltStream: prop = di.IsAltStream; break; #if defined(_WIN32) && !defined(UNDER_CE) // case kpidShortName: prop = di.ShortName; break; #endif } } prop.Detach(value); return S_OK; COM_TRY_END } #ifndef _7ZIP_ST static NSynchronization::CCriticalSection CS; #endif STDMETHODIMP CArchiveUpdateCallback::GetStream2(UInt32 index, ISequentialInStream **inStream, UInt32 mode) { COM_TRY_BEGIN *inStream = NULL; const CUpdatePair2 &up = (*UpdatePairs)[index]; if (!up.NewData) return E_FAIL; RINOK(Callback->CheckBreak()); // RINOK(Callback->Finalize()); bool isDir = IsDir(up); if (up.IsAnti) { UString name; if (up.ArcIndex >= 0) name = (*ArcItems)[up.ArcIndex].Name; else if (up.DirIndex >= 0) name = DirItems->GetLogPath(up.DirIndex); RINOK(Callback->GetStream(name, isDir, true, mode)); /* 9.33: fixed. Handlers expect real stream object for files, even for anti-file. so we return empty stream */ if (!isDir) { CBufInStream *inStreamSpec = new CBufInStream(); CMyComPtr inStreamLoc = inStreamSpec; inStreamSpec->Init(NULL, 0); *inStream = inStreamLoc.Detach(); } return S_OK; } RINOK(Callback->GetStream(DirItems->GetLogPath(up.DirIndex), isDir, false, mode)); if (isDir) return S_OK; if (StdInMode) { if (mode != NUpdateNotifyOp::kAdd && mode != NUpdateNotifyOp::kUpdate) return S_OK; CStdInFileStream *inStreamSpec = new CStdInFileStream; CMyComPtr inStreamLoc(inStreamSpec); *inStream = inStreamLoc.Detach(); } else { CInFileStream *inStreamSpec = new CInFileStream; CMyComPtr inStreamLoc(inStreamSpec); inStreamSpec->SupportHardLinks = StoreHardLinks; inStreamSpec->Callback = this; inStreamSpec->CallbackRef = index; const FString path = DirItems->GetPhyPath(up.DirIndex); _openFiles_Indexes.Add(index); _openFiles_Paths.Add(path); #if defined(_WIN32) && !defined(UNDER_CE) if (DirItems->Items[up.DirIndex].AreReparseData()) { if (!inStreamSpec->File.OpenReparse(path)) { return Callback->OpenFileError(path, ::GetLastError()); } } else #endif if (!inStreamSpec->OpenShared(path, ShareForWrite)) { return Callback->OpenFileError(path, ::GetLastError()); } if (StoreHardLinks) { CStreamFileProps props; if (inStreamSpec->GetProps2(&props) == S_OK) { if (props.NumLinks > 1) { CKeyKeyValPair pair; pair.Key1 = props.VolID; pair.Key2 = props.FileID_Low; pair.Value = index; unsigned numItems = _map.Size(); unsigned pairIndex = _map.AddToUniqueSorted2(pair); if (numItems == _map.Size()) { // const CKeyKeyValPair &pair2 = _map.Pairs[pairIndex]; _hardIndex_From = index; _hardIndex_To = pairIndex; // we could return NULL as stream, but it's better to return real stream // return S_OK; } } } } if (ProcessedItemsStatuses) { #ifndef _7ZIP_ST NSynchronization::CCriticalSectionLock lock(CS); #endif ProcessedItemsStatuses[(unsigned)up.DirIndex] = 1; } *inStream = inStreamLoc.Detach(); } return S_OK; COM_TRY_END } STDMETHODIMP CArchiveUpdateCallback::SetOperationResult(Int32 opRes) { COM_TRY_BEGIN return Callback->SetOperationResult(opRes); COM_TRY_END } STDMETHODIMP CArchiveUpdateCallback::GetStream(UInt32 index, ISequentialInStream **inStream) { COM_TRY_BEGIN return GetStream2(index, inStream, (*UpdatePairs)[index].ArcIndex < 0 ? NUpdateNotifyOp::kAdd : NUpdateNotifyOp::kUpdate); COM_TRY_END } STDMETHODIMP CArchiveUpdateCallback::ReportOperation(UInt32 indexType, UInt32 index, UInt32 op) { COM_TRY_BEGIN bool isDir = false; if (indexType == NArchive::NEventIndexType::kOutArcIndex) { UString name; if (index != (UInt32)(Int32)-1) { const CUpdatePair2 &up = (*UpdatePairs)[index]; if (up.ExistOnDisk()) { name = DirItems->GetLogPath(up.DirIndex); isDir = DirItems->Items[up.DirIndex].IsDir(); } } return Callback->ReportUpdateOpeartion(op, name.IsEmpty() ? NULL : name.Ptr(), isDir); } wchar_t temp[16]; UString s2; const wchar_t *s = NULL; if (indexType == NArchive::NEventIndexType::kInArcIndex) { if (index != (UInt32)(Int32)-1) { if (ArcItems) { const CArcItem &ai = (*ArcItems)[index]; s = ai.Name; isDir = ai.IsDir; } else if (Arc) { RINOK(Arc->GetItemPath(index, s2)); s = s2; RINOK(Archive_IsItem_Dir(Arc->Archive, index, isDir)); } } } else if (indexType == NArchive::NEventIndexType::kBlockIndex) { temp[0] = '#'; ConvertUInt32ToString(index, temp + 1); s = temp; } if (!s) s = L""; return Callback->ReportUpdateOpeartion(op, s, isDir); COM_TRY_END } STDMETHODIMP CArchiveUpdateCallback::ReportExtractResult(UInt32 indexType, UInt32 index, Int32 opRes) { COM_TRY_BEGIN bool isEncrypted = false; wchar_t temp[16]; UString s2; const wchar_t *s = NULL; if (indexType == NArchive::NEventIndexType::kOutArcIndex) { /* UString name; if (index != (UInt32)(Int32)-1) { const CUpdatePair2 &up = (*UpdatePairs)[index]; if (up.ExistOnDisk()) { s2 = DirItems->GetLogPath(up.DirIndex); s = s2; } } */ return E_FAIL; } if (indexType == NArchive::NEventIndexType::kInArcIndex) { if (index != (UInt32)(Int32)-1) { if (ArcItems) s = (*ArcItems)[index].Name; else if (Arc) { RINOK(Arc->GetItemPath(index, s2)); s = s2; } if (Archive) { RINOK(Archive_GetItemBoolProp(Archive, index, kpidEncrypted, isEncrypted)); } } } else if (indexType == NArchive::NEventIndexType::kBlockIndex) { temp[0] = '#'; ConvertUInt32ToString(index, temp + 1); s = temp; } return Callback->ReportExtractResult(opRes, BoolToInt(isEncrypted), s); COM_TRY_END } STDMETHODIMP CArchiveUpdateCallback::GetVolumeSize(UInt32 index, UInt64 *size) { if (VolumesSizes.Size() == 0) return S_FALSE; if (index >= (UInt32)VolumesSizes.Size()) index = VolumesSizes.Size() - 1; *size = VolumesSizes[index]; return S_OK; } STDMETHODIMP CArchiveUpdateCallback::GetVolumeStream(UInt32 index, ISequentialOutStream **volumeStream) { COM_TRY_BEGIN FChar temp[16]; ConvertUInt32ToString(index + 1, temp); FString res = temp; while (res.Len() < 2) res.InsertAtFront(FTEXT('0')); FString fileName = VolName; fileName += FTEXT('.'); fileName += res; fileName += VolExt; COutFileStream *streamSpec = new COutFileStream; CMyComPtr streamLoc(streamSpec); if (!streamSpec->Create(fileName, false)) return ::GetLastError(); *volumeStream = streamLoc.Detach(); return S_OK; COM_TRY_END } STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password) { COM_TRY_BEGIN return Callback->CryptoGetTextPassword2(passwordIsDefined, password); COM_TRY_END } STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword(BSTR *password) { COM_TRY_BEGIN return Callback->CryptoGetTextPassword(password); COM_TRY_END } HRESULT CArchiveUpdateCallback::InFileStream_On_Error(UINT_PTR val, DWORD error) { if (error == ERROR_LOCK_VIOLATION) { MT_LOCK UInt32 index = (UInt32)val; FOR_VECTOR(i, _openFiles_Indexes) { if (_openFiles_Indexes[i] == index) { RINOK(Callback->ReadingFileError(_openFiles_Paths[i], error)); break; } } } return HRESULT_FROM_WIN32(error); } void CArchiveUpdateCallback::InFileStream_On_Destroy(UINT_PTR val) { MT_LOCK UInt32 index = (UInt32)val; FOR_VECTOR(i, _openFiles_Indexes) { if (_openFiles_Indexes[i] == index) { _openFiles_Indexes.Delete(i); _openFiles_Paths.Delete(i); return; } } throw 20141125; }