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