// ArchiveFolderOut.cpp #include "StdAfx.h" #include "../../../Common/ComTry.h" #include "../../../Windows/FileDir.h" #include "../../Common/FileStreams.h" #include "../../Common/LimitedStreams.h" #include "../../Compress/CopyCoder.h" #include "../Common/WorkDir.h" #include "Agent.h" using namespace NWindows; using namespace NFile; using namespace NDir; void CAgentFolder::GetPathParts(UStringVector &pathParts, bool &isAltStreamFolder) { if (_proxy2) _proxy2->GetDirPathParts(_proxyDirIndex, pathParts, isAltStreamFolder); else _proxy->GetDirPathParts(_proxyDirIndex, pathParts); } static bool Delete_EmptyFolder_And_EmptySubFolders(const FString &path) { { const FString pathPrefix = path + FCHAR_PATH_SEPARATOR; CObjectVector names; { NFind::CDirEntry fileInfo; NFind::CEnumerator enumerator; enumerator.SetDirPrefix(pathPrefix); for (;;) { bool found; if (!enumerator.Next(fileInfo, found)) return false; if (!found) break; if (fileInfo.IsDir()) names.Add(fileInfo.Name); } } bool res = true; FOR_VECTOR (i, names) { if (!Delete_EmptyFolder_And_EmptySubFolders(pathPrefix + names[i])) res = false; } if (!res) return false; } // we clear read-only attrib to remove read-only dir if (!SetFileAttrib(path, 0)) return false; return RemoveDir(path); } struct C_CopyFileProgress_to_FolderCallback_MoveArc Z7_final: public ICopyFileProgress { IFolderArchiveUpdateCallback_MoveArc *Callback; HRESULT CallbackResult; virtual DWORD CopyFileProgress(UInt64 total, UInt64 current) Z7_override { HRESULT res = Callback->MoveArc_Progress(total, current); CallbackResult = res; // we can ignore E_ABORT here, because we update archive, // and we want to get correct archive after updating if (res == E_ABORT) res = S_OK; return res == S_OK ? PROGRESS_CONTINUE : PROGRESS_CANCEL; } C_CopyFileProgress_to_FolderCallback_MoveArc( IFolderArchiveUpdateCallback_MoveArc *callback) : Callback(callback), CallbackResult(S_OK) {} }; HRESULT CAgentFolder::CommonUpdateOperation( AGENT_OP operation, bool moveMode, const wchar_t *newItemName, const NUpdateArchive::CActionSet *actionSet, const UInt32 *indices, UInt32 numItems, IProgress *progress) { if (moveMode && _agentSpec->_isHashHandler) return E_NOTIMPL; if (!_agentSpec->CanUpdate()) return E_NOTIMPL; CMyComPtr updateCallback100; if (progress) progress->QueryInterface(IID_IFolderArchiveUpdateCallback, (void **)&updateCallback100); try { RINOK(_agentSpec->SetFolder(this)) // ---------- Save FolderItem ---------- UStringVector pathParts; bool isAltStreamFolder = false; GetPathParts(pathParts, isAltStreamFolder); FStringVector requestedPaths; FStringVector processedPaths; CWorkDirTempFile tempFile; RINOK(tempFile.CreateTempFile(us2fs(_agentSpec->_archiveFilePath))) { CMyComPtr tailStream; const CArc &arc = *_agentSpec->_archiveLink.GetArc(); if (arc.ArcStreamOffset == 0) tailStream = tempFile.OutStream; else { if (arc.Offset < 0) return E_NOTIMPL; RINOK(arc.InStream->Seek(0, STREAM_SEEK_SET, NULL)) RINOK(NCompress::CopyStream_ExactSize(arc.InStream, tempFile.OutStream, arc.ArcStreamOffset, NULL)) CTailOutStream *tailStreamSpec = new CTailOutStream; tailStream = tailStreamSpec; tailStreamSpec->Stream = tempFile.OutStream; tailStreamSpec->Offset = arc.ArcStreamOffset; tailStreamSpec->Init(); } HRESULT result; switch ((int)operation) { case AGENT_OP_Delete: result = _agentSpec->DeleteItems(tailStream, indices, numItems, updateCallback100); break; case AGENT_OP_CreateFolder: result = _agentSpec->CreateFolder(tailStream, newItemName, updateCallback100); break; case AGENT_OP_Rename: result = _agentSpec->RenameItem(tailStream, indices, numItems, newItemName, updateCallback100); break; case AGENT_OP_Comment: result = _agentSpec->CommentItem(tailStream, indices, numItems, newItemName, updateCallback100); break; case AGENT_OP_CopyFromFile: result = _agentSpec->UpdateOneFile(tailStream, indices, numItems, newItemName, updateCallback100); break; case AGENT_OP_Uni: { Byte actionSetByte[NUpdateArchive::NPairState::kNumValues]; for (unsigned i = 0; i < NUpdateArchive::NPairState::kNumValues; i++) actionSetByte[i] = (Byte)actionSet->StateActions[i]; result = _agentSpec->DoOperation2( moveMode ? &requestedPaths : NULL, moveMode ? &processedPaths : NULL, tailStream, actionSetByte, NULL, updateCallback100); break; } default: return E_FAIL; } RINOK(result) } _agentSpec->KeepModeForNextOpen(); _agent->Close(); // before 9.26: if there was error for MoveToOriginal archive was closed. // now: we reopen archive after close // m_FolderItem = NULL; _items.Clear(); _proxyDirIndex = k_Proxy_RootDirIndex; CMyComPtr updateCallback_MoveArc; if (progress) progress->QueryInterface(IID_IFolderArchiveUpdateCallback_MoveArc, (void **)&updateCallback_MoveArc); HRESULT res; if (updateCallback_MoveArc) { const FString &tempFilePath = tempFile.Get_TempFilePath(); UInt64 totalSize = 0; { NFind::CFileInfo fi; if (fi.Find(tempFilePath)) totalSize = fi.Size; } RINOK(updateCallback_MoveArc->MoveArc_Start( fs2us(tempFilePath), fs2us(tempFile.Get_OriginalFilePath()), totalSize, 1)) // updateMode C_CopyFileProgress_to_FolderCallback_MoveArc prox(updateCallback_MoveArc); res = tempFile.MoveToOriginal( true, // deleteOriginal &prox); if (res == S_OK) { res = updateCallback_MoveArc->MoveArc_Finish(); // we don't return after E_ABORT here, because // we want to reopen new archive still. } else if (prox.CallbackResult != S_OK) res = prox.CallbackResult; // if updating callback returned E_ABORT, // then openCallback still can return E_ABORT also. // So ReOpen() will return with E_ABORT. // But we want to open archive still. // And Before_ArcReopen() call will clear user break status in that case. RINOK(updateCallback_MoveArc->Before_ArcReopen()) } else res = tempFile.MoveToOriginal(true); // deleteOriginal // RINOK(res); if (res == S_OK) { if (moveMode) { unsigned i; for (i = 0; i < processedPaths.Size(); i++) { DeleteFileAlways(processedPaths[i]); } for (i = 0; i < requestedPaths.Size(); i++) { const FString &fs = requestedPaths[i]; if (NFind::DoesDirExist(fs)) Delete_EmptyFolder_And_EmptySubFolders(fs); } } } { CMyComPtr openCallback; if (updateCallback100) updateCallback100->QueryInterface(IID_IArchiveOpenCallback, (void **)&openCallback); RINOK(_agent->ReOpen(openCallback)) } // CAgent::ReOpen() deletes _proxy and _proxy2 // _items.Clear(); _proxy = NULL; _proxy2 = NULL; // _proxyDirIndex = k_Proxy_RootDirIndex; _isAltStreamFolder = false; // ---------- Restore FolderItem ---------- CMyComPtr archiveFolder; RINOK(_agent->BindToRootFolder(&archiveFolder)) // CAgent::BindToRootFolder() changes _proxy and _proxy2 _proxy = _agentSpec->_proxy; _proxy2 = _agentSpec->_proxy2; if (_proxy) { FOR_VECTOR (i, pathParts) { const int next = _proxy->FindSubDir(_proxyDirIndex, pathParts[i]); if (next == -1) break; _proxyDirIndex = (unsigned)next; } } if (_proxy2) { if (pathParts.IsEmpty() && isAltStreamFolder) { _proxyDirIndex = k_Proxy2_AltRootDirIndex; } else FOR_VECTOR (i, pathParts) { const bool dirOnly = (i + 1 < pathParts.Size() || !isAltStreamFolder); const int index = _proxy2->FindItem(_proxyDirIndex, pathParts[i], dirOnly); if (index == -1) break; const CProxyFile2 &file = _proxy2->Files[_proxy2->Dirs[_proxyDirIndex].Items[index]]; if (dirOnly) _proxyDirIndex = (unsigned)file.DirIndex; else { if (file.AltDirIndex != -1) _proxyDirIndex = (unsigned)file.AltDirIndex; break; } } } /* if (pathParts.IsEmpty() && isAltStreamFolder) { CMyComPtr folderAltStreams; archiveFolder.QueryInterface(IID_IFolderAltStreams, &folderAltStreams); if (folderAltStreams) { CMyComPtr newFolder; folderAltStreams->BindToAltStreams((UInt32)(Int32)-1, &newFolder); if (newFolder) archiveFolder = newFolder; } } FOR_VECTOR (i, pathParts) { CMyComPtr newFolder; if (isAltStreamFolder && i == pathParts.Size() - 1) { CMyComPtr folderAltStreams; archiveFolder.QueryInterface(IID_IFolderAltStreams, &folderAltStreams); if (folderAltStreams) folderAltStreams->BindToAltStreams(pathParts[i], &newFolder); } else archiveFolder->BindToFolder(pathParts[i], &newFolder); if (!newFolder) break; archiveFolder = newFolder; } CMyComPtr archiveFolderInternal; RINOK(archiveFolder.QueryInterface(IID_IArchiveFolderInternal, &archiveFolderInternal)); CAgentFolder *agentFolder; RINOK(archiveFolderInternal->GetAgentFolder(&agentFolder)); _proxyDirIndex = agentFolder->_proxyDirIndex; // _parentFolder = agentFolder->_parentFolder; */ if (_proxy2) _isAltStreamFolder = _proxy2->IsAltDir(_proxyDirIndex); return res; } catch(const UString &s) { if (updateCallback100) { UString s2 ("Error: "); s2 += s; RINOK(updateCallback100->UpdateErrorMessage(s2)) return E_FAIL; } throw; } } Z7_COM7F_IMF(CAgentFolder::CopyFrom(Int32 moveMode, const wchar_t *fromFolderPath, /* test it */ const wchar_t * const *itemsPaths, UInt32 numItems, IProgress *progress)) { COM_TRY_BEGIN { RINOK(_agentSpec->SetFiles(fromFolderPath, itemsPaths, numItems)) return CommonUpdateOperation(AGENT_OP_Uni, (moveMode != 0), NULL, &NUpdateArchive::k_ActionSet_Add, NULL, 0, progress); } COM_TRY_END } Z7_COM7F_IMF(CAgentFolder::CopyFromFile(UInt32 destIndex, const wchar_t *itemPath, IProgress *progress)) { COM_TRY_BEGIN return CommonUpdateOperation(AGENT_OP_CopyFromFile, false, itemPath, &NUpdateArchive::k_ActionSet_Add, &destIndex, 1, progress); COM_TRY_END } Z7_COM7F_IMF(CAgentFolder::Delete(const UInt32 *indices, UInt32 numItems, IProgress *progress)) { COM_TRY_BEGIN return CommonUpdateOperation(AGENT_OP_Delete, false, NULL, &NUpdateArchive::k_ActionSet_Delete, indices, numItems, progress); COM_TRY_END } Z7_COM7F_IMF(CAgentFolder::CreateFolder(const wchar_t *name, IProgress *progress)) { COM_TRY_BEGIN if (_isAltStreamFolder) return E_NOTIMPL; if (_proxy2) { if (_proxy2->IsThere_SubDir(_proxyDirIndex, name)) return ERROR_ALREADY_EXISTS; } else { if (_proxy->FindSubDir(_proxyDirIndex, name) != -1) return ERROR_ALREADY_EXISTS; } return CommonUpdateOperation(AGENT_OP_CreateFolder, false, name, NULL, NULL, 0, progress); COM_TRY_END } Z7_COM7F_IMF(CAgentFolder::Rename(UInt32 index, const wchar_t *newName, IProgress *progress)) { COM_TRY_BEGIN return CommonUpdateOperation(AGENT_OP_Rename, false, newName, NULL, &index, 1, progress); COM_TRY_END } Z7_COM7F_IMF(CAgentFolder::CreateFile(const wchar_t * /* name */, IProgress * /* progress */)) { return E_NOTIMPL; } Z7_COM7F_IMF(CAgentFolder::SetProperty(UInt32 index, PROPID propID, const PROPVARIANT *value, IProgress *progress)) { COM_TRY_BEGIN if (propID != kpidComment || value->vt != VT_BSTR) return E_NOTIMPL; if (!_agentSpec || !_agentSpec->GetTypeOfArc(_agentSpec->GetArc()).IsEqualTo_Ascii_NoCase("zip")) return E_NOTIMPL; return CommonUpdateOperation(AGENT_OP_Comment, false, value->bstrVal, NULL, &index, 1, progress); COM_TRY_END }