xref: /aosp_15_r20/external/lzma/CPP/7zip/UI/Agent/ArchiveFolderOut.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // ArchiveFolderOut.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../Common/ComTry.h"
6 
7 #include "../../../Windows/FileDir.h"
8 
9 #include "../../Common/FileStreams.h"
10 #include "../../Common/LimitedStreams.h"
11 
12 #include "../../Compress/CopyCoder.h"
13 
14 #include "../Common/WorkDir.h"
15 
16 #include "Agent.h"
17 
18 using namespace NWindows;
19 using namespace NFile;
20 using namespace NDir;
21 
GetPathParts(UStringVector & pathParts,bool & isAltStreamFolder)22 void CAgentFolder::GetPathParts(UStringVector &pathParts, bool &isAltStreamFolder)
23 {
24   if (_proxy2)
25     _proxy2->GetDirPathParts(_proxyDirIndex, pathParts, isAltStreamFolder);
26   else
27     _proxy->GetDirPathParts(_proxyDirIndex, pathParts);
28 }
29 
Delete_EmptyFolder_And_EmptySubFolders(const FString & path)30 static bool Delete_EmptyFolder_And_EmptySubFolders(const FString &path)
31 {
32   {
33     const FString pathPrefix = path + FCHAR_PATH_SEPARATOR;
34     CObjectVector<FString> names;
35     {
36       NFind::CDirEntry fileInfo;
37       NFind::CEnumerator enumerator;
38       enumerator.SetDirPrefix(pathPrefix);
39       for (;;)
40       {
41         bool found;
42         if (!enumerator.Next(fileInfo, found))
43           return false;
44         if (!found)
45           break;
46         if (fileInfo.IsDir())
47           names.Add(fileInfo.Name);
48       }
49     }
50     bool res = true;
51     FOR_VECTOR (i, names)
52     {
53       if (!Delete_EmptyFolder_And_EmptySubFolders(pathPrefix + names[i]))
54         res = false;
55     }
56     if (!res)
57       return false;
58   }
59   // we clear read-only attrib to remove read-only dir
60   if (!SetFileAttrib(path, 0))
61     return false;
62   return RemoveDir(path);
63 }
64 
65 
66 
67 struct C_CopyFileProgress_to_FolderCallback_MoveArc Z7_final:
68   public ICopyFileProgress
69 {
70   IFolderArchiveUpdateCallback_MoveArc *Callback;
71   HRESULT CallbackResult;
72 
CopyFileProgressZ7_final73   virtual DWORD CopyFileProgress(UInt64 total, UInt64 current) Z7_override
74   {
75     HRESULT res = Callback->MoveArc_Progress(total, current);
76     CallbackResult = res;
77     // we can ignore E_ABORT here, because we update archive,
78     // and we want to get correct archive after updating
79     if (res == E_ABORT)
80       res = S_OK;
81     return res == S_OK ? PROGRESS_CONTINUE : PROGRESS_CANCEL;
82   }
83 
C_CopyFileProgress_to_FolderCallback_MoveArcZ7_final84   C_CopyFileProgress_to_FolderCallback_MoveArc(
85       IFolderArchiveUpdateCallback_MoveArc *callback) :
86     Callback(callback),
87     CallbackResult(S_OK)
88     {}
89 };
90 
91 
CommonUpdateOperation(AGENT_OP operation,bool moveMode,const wchar_t * newItemName,const NUpdateArchive::CActionSet * actionSet,const UInt32 * indices,UInt32 numItems,IProgress * progress)92 HRESULT CAgentFolder::CommonUpdateOperation(
93     AGENT_OP operation,
94     bool moveMode,
95     const wchar_t *newItemName,
96     const NUpdateArchive::CActionSet *actionSet,
97     const UInt32 *indices, UInt32 numItems,
98     IProgress *progress)
99 {
100   if (moveMode && _agentSpec->_isHashHandler)
101     return E_NOTIMPL;
102 
103   if (!_agentSpec->CanUpdate())
104     return E_NOTIMPL;
105 
106   CMyComPtr<IFolderArchiveUpdateCallback> updateCallback100;
107   if (progress)
108     progress->QueryInterface(IID_IFolderArchiveUpdateCallback, (void **)&updateCallback100);
109 
110   try
111   {
112 
113   RINOK(_agentSpec->SetFolder(this))
114 
115   // ---------- Save FolderItem ----------
116 
117   UStringVector pathParts;
118   bool isAltStreamFolder = false;
119   GetPathParts(pathParts, isAltStreamFolder);
120 
121   FStringVector requestedPaths;
122   FStringVector processedPaths;
123 
124   CWorkDirTempFile tempFile;
125   RINOK(tempFile.CreateTempFile(us2fs(_agentSpec->_archiveFilePath)))
126   {
127     CMyComPtr<IOutStream> tailStream;
128     const CArc &arc = *_agentSpec->_archiveLink.GetArc();
129 
130     if (arc.ArcStreamOffset == 0)
131       tailStream = tempFile.OutStream;
132     else
133     {
134       if (arc.Offset < 0)
135         return E_NOTIMPL;
136       RINOK(arc.InStream->Seek(0, STREAM_SEEK_SET, NULL))
137       RINOK(NCompress::CopyStream_ExactSize(arc.InStream, tempFile.OutStream, arc.ArcStreamOffset, NULL))
138       CTailOutStream *tailStreamSpec = new CTailOutStream;
139       tailStream = tailStreamSpec;
140       tailStreamSpec->Stream = tempFile.OutStream;
141       tailStreamSpec->Offset = arc.ArcStreamOffset;
142       tailStreamSpec->Init();
143     }
144 
145     HRESULT result;
146 
147     switch ((int)operation)
148     {
149       case AGENT_OP_Delete:
150         result = _agentSpec->DeleteItems(tailStream, indices, numItems, updateCallback100);
151         break;
152       case AGENT_OP_CreateFolder:
153         result = _agentSpec->CreateFolder(tailStream, newItemName, updateCallback100);
154         break;
155       case AGENT_OP_Rename:
156         result = _agentSpec->RenameItem(tailStream, indices, numItems, newItemName, updateCallback100);
157         break;
158       case AGENT_OP_Comment:
159         result = _agentSpec->CommentItem(tailStream, indices, numItems, newItemName, updateCallback100);
160         break;
161       case AGENT_OP_CopyFromFile:
162         result = _agentSpec->UpdateOneFile(tailStream, indices, numItems, newItemName, updateCallback100);
163         break;
164       case AGENT_OP_Uni:
165         {
166           Byte actionSetByte[NUpdateArchive::NPairState::kNumValues];
167           for (unsigned i = 0; i < NUpdateArchive::NPairState::kNumValues; i++)
168             actionSetByte[i] = (Byte)actionSet->StateActions[i];
169           result = _agentSpec->DoOperation2(
170               moveMode ? &requestedPaths : NULL,
171               moveMode ? &processedPaths : NULL,
172               tailStream, actionSetByte, NULL, updateCallback100);
173           break;
174         }
175       default:
176         return E_FAIL;
177     }
178 
179     RINOK(result)
180   }
181 
182   _agentSpec->KeepModeForNextOpen();
183   _agent->Close();
184 
185   // before 9.26: if there was error for MoveToOriginal archive was closed.
186   // now: we reopen archive after close
187 
188   // m_FolderItem = NULL;
189   _items.Clear();
190   _proxyDirIndex = k_Proxy_RootDirIndex;
191 
192   CMyComPtr<IFolderArchiveUpdateCallback_MoveArc> updateCallback_MoveArc;
193   if (progress)
194     progress->QueryInterface(IID_IFolderArchiveUpdateCallback_MoveArc, (void **)&updateCallback_MoveArc);
195 
196   HRESULT res;
197   if (updateCallback_MoveArc)
198   {
199     const FString &tempFilePath = tempFile.Get_TempFilePath();
200     UInt64 totalSize = 0;
201     {
202       NFind::CFileInfo fi;
203       if (fi.Find(tempFilePath))
204         totalSize = fi.Size;
205     }
206     RINOK(updateCallback_MoveArc->MoveArc_Start(
207         fs2us(tempFilePath),
208         fs2us(tempFile.Get_OriginalFilePath()),
209         totalSize,
210         1)) // updateMode
211 
212     C_CopyFileProgress_to_FolderCallback_MoveArc prox(updateCallback_MoveArc);
213     res = tempFile.MoveToOriginal(
214         true, // deleteOriginal
215         &prox);
216     if (res == S_OK)
217     {
218       res = updateCallback_MoveArc->MoveArc_Finish();
219       // we don't return after E_ABORT here, because
220       // we want to reopen new archive still.
221     }
222     else if (prox.CallbackResult != S_OK)
223       res = prox.CallbackResult;
224 
225     // if updating callback returned E_ABORT,
226     // then openCallback still can return E_ABORT also.
227     // So ReOpen() will return with E_ABORT.
228     // But we want to open archive still.
229     // And Before_ArcReopen() call will clear user break status in that case.
230     RINOK(updateCallback_MoveArc->Before_ArcReopen())
231   }
232   else
233     res = tempFile.MoveToOriginal(true); // deleteOriginal
234 
235   // RINOK(res);
236   if (res == S_OK)
237   {
238     if (moveMode)
239     {
240       unsigned i;
241       for (i = 0; i < processedPaths.Size(); i++)
242       {
243         DeleteFileAlways(processedPaths[i]);
244       }
245       for (i = 0; i < requestedPaths.Size(); i++)
246       {
247         const FString &fs = requestedPaths[i];
248         if (NFind::DoesDirExist(fs))
249           Delete_EmptyFolder_And_EmptySubFolders(fs);
250       }
251     }
252   }
253 
254   {
255     CMyComPtr<IArchiveOpenCallback> openCallback;
256     if (updateCallback100)
257       updateCallback100->QueryInterface(IID_IArchiveOpenCallback, (void **)&openCallback);
258     RINOK(_agent->ReOpen(openCallback))
259   }
260 
261   // CAgent::ReOpen() deletes _proxy and _proxy2
262   // _items.Clear();
263   _proxy = NULL;
264   _proxy2 = NULL;
265   // _proxyDirIndex = k_Proxy_RootDirIndex;
266   _isAltStreamFolder = false;
267 
268 
269   // ---------- Restore FolderItem ----------
270 
271   CMyComPtr<IFolderFolder> archiveFolder;
272   RINOK(_agent->BindToRootFolder(&archiveFolder))
273 
274   // CAgent::BindToRootFolder() changes _proxy and _proxy2
275   _proxy = _agentSpec->_proxy;
276   _proxy2 = _agentSpec->_proxy2;
277 
278   if (_proxy)
279   {
280     FOR_VECTOR (i, pathParts)
281     {
282       const int next = _proxy->FindSubDir(_proxyDirIndex, pathParts[i]);
283       if (next == -1)
284         break;
285       _proxyDirIndex = (unsigned)next;
286     }
287   }
288 
289   if (_proxy2)
290   {
291     if (pathParts.IsEmpty() && isAltStreamFolder)
292     {
293       _proxyDirIndex = k_Proxy2_AltRootDirIndex;
294     }
295     else FOR_VECTOR (i, pathParts)
296     {
297       const bool dirOnly = (i + 1 < pathParts.Size() || !isAltStreamFolder);
298       const int index = _proxy2->FindItem(_proxyDirIndex, pathParts[i], dirOnly);
299       if (index == -1)
300         break;
301 
302       const CProxyFile2 &file = _proxy2->Files[_proxy2->Dirs[_proxyDirIndex].Items[index]];
303 
304       if (dirOnly)
305         _proxyDirIndex = (unsigned)file.DirIndex;
306       else
307       {
308         if (file.AltDirIndex != -1)
309           _proxyDirIndex = (unsigned)file.AltDirIndex;
310         break;
311       }
312     }
313   }
314 
315   /*
316   if (pathParts.IsEmpty() && isAltStreamFolder)
317   {
318     CMyComPtr<IFolderAltStreams> folderAltStreams;
319     archiveFolder.QueryInterface(IID_IFolderAltStreams, &folderAltStreams);
320     if (folderAltStreams)
321     {
322       CMyComPtr<IFolderFolder> newFolder;
323       folderAltStreams->BindToAltStreams((UInt32)(Int32)-1, &newFolder);
324       if (newFolder)
325         archiveFolder = newFolder;
326     }
327   }
328 
329   FOR_VECTOR (i, pathParts)
330   {
331     CMyComPtr<IFolderFolder> newFolder;
332 
333     if (isAltStreamFolder && i == pathParts.Size() - 1)
334     {
335       CMyComPtr<IFolderAltStreams> folderAltStreams;
336       archiveFolder.QueryInterface(IID_IFolderAltStreams, &folderAltStreams);
337       if (folderAltStreams)
338         folderAltStreams->BindToAltStreams(pathParts[i], &newFolder);
339     }
340     else
341       archiveFolder->BindToFolder(pathParts[i], &newFolder);
342 
343     if (!newFolder)
344       break;
345     archiveFolder = newFolder;
346   }
347 
348   CMyComPtr<IArchiveFolderInternal> archiveFolderInternal;
349   RINOK(archiveFolder.QueryInterface(IID_IArchiveFolderInternal, &archiveFolderInternal));
350   CAgentFolder *agentFolder;
351   RINOK(archiveFolderInternal->GetAgentFolder(&agentFolder));
352   _proxyDirIndex = agentFolder->_proxyDirIndex;
353   // _parentFolder = agentFolder->_parentFolder;
354   */
355 
356   if (_proxy2)
357     _isAltStreamFolder = _proxy2->IsAltDir(_proxyDirIndex);
358 
359   return res;
360 
361   }
362   catch(const UString &s)
363   {
364     if (updateCallback100)
365     {
366       UString s2 ("Error: ");
367       s2 += s;
368       RINOK(updateCallback100->UpdateErrorMessage(s2))
369       return E_FAIL;
370     }
371     throw;
372   }
373 }
374 
375 
Z7_COM7F_IMF(CAgentFolder::CopyFrom (Int32 moveMode,const wchar_t * fromFolderPath,const wchar_t * const * itemsPaths,UInt32 numItems,IProgress * progress))376 Z7_COM7F_IMF(CAgentFolder::CopyFrom(Int32 moveMode,
377     const wchar_t *fromFolderPath, /* test it */
378     const wchar_t * const *itemsPaths,
379     UInt32 numItems,
380     IProgress *progress))
381 {
382   COM_TRY_BEGIN
383   {
384     RINOK(_agentSpec->SetFiles(fromFolderPath, itemsPaths, numItems))
385     return CommonUpdateOperation(AGENT_OP_Uni, (moveMode != 0), NULL,
386         &NUpdateArchive::k_ActionSet_Add,
387         NULL, 0, progress);
388   }
389   COM_TRY_END
390 }
391 
Z7_COM7F_IMF(CAgentFolder::CopyFromFile (UInt32 destIndex,const wchar_t * itemPath,IProgress * progress))392 Z7_COM7F_IMF(CAgentFolder::CopyFromFile(UInt32 destIndex, const wchar_t *itemPath, IProgress *progress))
393 {
394   COM_TRY_BEGIN
395   return CommonUpdateOperation(AGENT_OP_CopyFromFile, false, itemPath,
396       &NUpdateArchive::k_ActionSet_Add,
397       &destIndex, 1, progress);
398   COM_TRY_END
399 }
400 
Z7_COM7F_IMF(CAgentFolder::Delete (const UInt32 * indices,UInt32 numItems,IProgress * progress))401 Z7_COM7F_IMF(CAgentFolder::Delete(const UInt32 *indices, UInt32 numItems, IProgress *progress))
402 {
403   COM_TRY_BEGIN
404   return CommonUpdateOperation(AGENT_OP_Delete, false, NULL,
405       &NUpdateArchive::k_ActionSet_Delete, indices, numItems, progress);
406   COM_TRY_END
407 }
408 
Z7_COM7F_IMF(CAgentFolder::CreateFolder (const wchar_t * name,IProgress * progress))409 Z7_COM7F_IMF(CAgentFolder::CreateFolder(const wchar_t *name, IProgress *progress))
410 {
411   COM_TRY_BEGIN
412 
413   if (_isAltStreamFolder)
414     return E_NOTIMPL;
415 
416   if (_proxy2)
417   {
418     if (_proxy2->IsThere_SubDir(_proxyDirIndex, name))
419       return ERROR_ALREADY_EXISTS;
420   }
421   else
422   {
423     if (_proxy->FindSubDir(_proxyDirIndex, name) != -1)
424       return ERROR_ALREADY_EXISTS;
425   }
426 
427   return CommonUpdateOperation(AGENT_OP_CreateFolder, false, name, NULL, NULL, 0, progress);
428   COM_TRY_END
429 }
430 
Z7_COM7F_IMF(CAgentFolder::Rename (UInt32 index,const wchar_t * newName,IProgress * progress))431 Z7_COM7F_IMF(CAgentFolder::Rename(UInt32 index, const wchar_t *newName, IProgress *progress))
432 {
433   COM_TRY_BEGIN
434   return CommonUpdateOperation(AGENT_OP_Rename, false, newName, NULL,
435       &index, 1, progress);
436   COM_TRY_END
437 }
438 
Z7_COM7F_IMF(CAgentFolder::CreateFile (const wchar_t *,IProgress *))439 Z7_COM7F_IMF(CAgentFolder::CreateFile(const wchar_t * /* name */, IProgress * /* progress */))
440 {
441   return E_NOTIMPL;
442 }
443 
Z7_COM7F_IMF(CAgentFolder::SetProperty (UInt32 index,PROPID propID,const PROPVARIANT * value,IProgress * progress))444 Z7_COM7F_IMF(CAgentFolder::SetProperty(UInt32 index, PROPID propID,
445     const PROPVARIANT *value, IProgress *progress))
446 {
447   COM_TRY_BEGIN
448   if (propID != kpidComment || value->vt != VT_BSTR)
449     return E_NOTIMPL;
450   if (!_agentSpec || !_agentSpec->GetTypeOfArc(_agentSpec->GetArc()).IsEqualTo_Ascii_NoCase("zip"))
451     return E_NOTIMPL;
452 
453   return CommonUpdateOperation(AGENT_OP_Comment, false, value->bstrVal, NULL,
454       &index, 1, progress);
455   COM_TRY_END
456 }
457