1 // FSFolderCopy.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../Common/MyWindows.h"
6
7 #include "../../../Common/Defs.h"
8 #include "../../../Common/StringConvert.h"
9 #include "../../../Common/Wildcard.h"
10
11 #include "../../../Windows/DLL.h"
12 #include "../../../Windows/ErrorMsg.h"
13 #include "../../../Windows/FileDir.h"
14 #include "../../../Windows/FileName.h"
15
16 #include "../../Common/FilePathAutoRename.h"
17
18 #include "FSFolder.h"
19
20 using namespace NWindows;
21 using namespace NFile;
22 using namespace NDir;
23 using namespace NName;
24 using namespace NFind;
25
26 #ifndef _UNICODE
27 extern bool g_IsNT;
28 #endif
29
30 namespace NFsFolder {
31
32 static const char * const k_CannotCopyDirToAltStream = "Cannot copy folder as alternate stream";
33
34
MyCopyFile(CFSTR inPath,CFSTR outPath,DWORD attrib)35 HRESULT CCopyStateIO::MyCopyFile(CFSTR inPath, CFSTR outPath, DWORD attrib)
36 {
37 ErrorFileIndex = -1;
38 ErrorMessage.Empty();
39 CurrentSize = 0;
40
41 {
42 const size_t kBufSize = 1 << 16;
43 CByteArr buf(kBufSize);
44
45 NIO::CInFile inFile;
46 NIO::COutFile outFile;
47
48 if (!inFile.Open(inPath))
49 {
50 ErrorFileIndex = 0;
51 return S_OK;
52 }
53
54 if (!outFile.Create_ALWAYS(outPath))
55 {
56 ErrorFileIndex = 1;
57 return S_OK;
58 }
59
60 for (;;)
61 {
62 UInt32 num;
63 if (!inFile.Read(buf, kBufSize, num))
64 {
65 ErrorFileIndex = 0;
66 return S_OK;
67 }
68 if (num == 0)
69 break;
70
71 UInt32 written = 0;
72 if (!outFile.Write(buf, num, written))
73 {
74 ErrorFileIndex = 1;
75 return S_OK;
76 }
77 if (written != num)
78 {
79 ErrorMessage = "Write error";
80 return S_OK;
81 }
82 CurrentSize += num;
83 if (Progress)
84 {
85 UInt64 completed = StartPos + CurrentSize;
86 RINOK(Progress->SetCompleted(&completed))
87 }
88 }
89 }
90
91 /* SetFileAttrib("path:alt_stream_name") sets attributes for main file "path".
92 But we don't want to change attributes of main file, when we write alt stream.
93 So we need INVALID_FILE_ATTRIBUTES for alt stream here */
94
95 if (attrib != INVALID_FILE_ATTRIBUTES)
96 SetFileAttrib(outPath, attrib);
97
98 if (DeleteSrcFile)
99 {
100 if (!DeleteFileAlways(inPath))
101 {
102 ErrorFileIndex = 0;
103 return S_OK;
104 }
105 }
106
107 return S_OK;
108 }
109
110
111 /*
112 static bool IsItWindows2000orHigher()
113 {
114 OSVERSIONINFO versionInfo;
115 versionInfo.dwOSVersionInfoSize = sizeof(versionInfo);
116 if (!::GetVersionEx(&versionInfo))
117 return false;
118 return (versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) &&
119 (versionInfo.dwMajorVersion >= 5);
120 }
121 */
122
123 struct CProgressInfo
124 {
125 UInt64 TotalSize;
126 UInt64 StartPos;
127 UInt64 FileSize;
128 IProgress *Progress;
129 HRESULT ProgressResult;
130
InitNFsFolder::CProgressInfo131 void Init() { ProgressResult = S_OK; }
132 };
133
134 #ifndef PROGRESS_CONTINUE
135
136 #define PROGRESS_CONTINUE 0
137 #define PROGRESS_CANCEL 1
138
139 #define COPY_FILE_FAIL_IF_EXISTS 0x00000001
140
141 typedef
142 DWORD
143 (WINAPI* LPPROGRESS_ROUTINE)(
144 LARGE_INTEGER TotalFileSize,
145 LARGE_INTEGER TotalBytesTransferred,
146 LARGE_INTEGER StreamSize,
147 LARGE_INTEGER StreamBytesTransferred,
148 DWORD dwStreamNumber,
149 DWORD dwCallbackReason,
150 HANDLE hSourceFile,
151 HANDLE hDestinationFile,
152 LPVOID lpData
153 );
154
155 #endif
156
CopyProgressRoutine(LARGE_INTEGER TotalFileSize,LARGE_INTEGER TotalBytesTransferred,LARGE_INTEGER,LARGE_INTEGER,DWORD,DWORD,HANDLE,HANDLE,LPVOID lpData)157 static DWORD CALLBACK CopyProgressRoutine(
158 LARGE_INTEGER TotalFileSize, // file size
159 LARGE_INTEGER TotalBytesTransferred, // bytes transferred
160 LARGE_INTEGER /* StreamSize */, // bytes in stream
161 LARGE_INTEGER /* StreamBytesTransferred */, // bytes transferred for stream
162 DWORD /* dwStreamNumber */, // current stream
163 DWORD /* dwCallbackReason */, // callback reason
164 HANDLE /* hSourceFile */, // handle to source file
165 HANDLE /* hDestinationFile */, // handle to destination file
166 LPVOID lpData // from CopyFileEx
167 )
168 {
169 // StreamSize = StreamSize;
170 // StreamBytesTransferred = StreamBytesTransferred;
171 // dwStreamNumber = dwStreamNumber;
172 // dwCallbackReason = dwCallbackReason;
173
174 CProgressInfo &pi = *(CProgressInfo *)lpData;
175
176 if ((UInt64)TotalFileSize.QuadPart > pi.FileSize)
177 {
178 pi.TotalSize += (UInt64)TotalFileSize.QuadPart - pi.FileSize;
179 pi.FileSize = (UInt64)TotalFileSize.QuadPart;
180 pi.ProgressResult = pi.Progress->SetTotal(pi.TotalSize);
181 }
182 const UInt64 completed = pi.StartPos + (UInt64)TotalBytesTransferred.QuadPart;
183 pi.ProgressResult = pi.Progress->SetCompleted(&completed);
184 return (pi.ProgressResult == S_OK ? PROGRESS_CONTINUE : PROGRESS_CANCEL);
185 }
186
187 #if !defined(Z7_WIN32_WINNT_MIN) || Z7_WIN32_WINNT_MIN < 0x0500 // win2000
188 #define Z7_USE_DYN_MoveFileWithProgressW
189 #endif
190
191 #ifdef Z7_USE_DYN_MoveFileWithProgressW
192 // nt4
193 typedef BOOL (WINAPI * Func_CopyFileExA)(
194 IN LPCSTR lpExistingFileName,
195 IN LPCSTR lpNewFileName,
196 IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,
197 IN LPVOID lpData OPTIONAL,
198 IN LPBOOL pbCancel OPTIONAL,
199 IN DWORD dwCopyFlags
200 );
201
202 // nt4
203 typedef BOOL (WINAPI * Func_CopyFileExW)(
204 IN LPCWSTR lpExistingFileName,
205 IN LPCWSTR lpNewFileName,
206 IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,
207 IN LPVOID lpData OPTIONAL,
208 IN LPBOOL pbCancel OPTIONAL,
209 IN DWORD dwCopyFlags
210 );
211
212 // win2000
213 typedef BOOL (WINAPI * Func_MoveFileWithProgressW)(
214 IN LPCWSTR lpExistingFileName,
215 IN LPCWSTR lpNewFileName,
216 IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,
217 IN LPVOID lpData OPTIONAL,
218 IN DWORD dwFlags
219 );
220 #endif
221
222 struct CCopyState
223 {
224 CProgressInfo ProgressInfo;
225 IFolderOperationsExtractCallback *Callback;
226 bool MoveMode;
227 bool UseReadWriteMode;
228 bool IsAltStreamsDest;
229
230 #ifdef Z7_USE_DYN_MoveFileWithProgressW
231 private:
232 Func_CopyFileExW my_CopyFileExW;
233 #ifndef UNDER_CE
234 Func_MoveFileWithProgressW my_MoveFileWithProgressW;
235 #endif
236 #ifndef _UNICODE
237 Func_CopyFileExA my_CopyFileExA;
238 #endif
239 public:
240 CCopyState();
241 #endif
242
243 bool CopyFile_NT(const wchar_t *oldFile, const wchar_t *newFile);
244 bool CopyFile_Sys(CFSTR oldFile, CFSTR newFile);
245 bool MoveFile_Sys(CFSTR oldFile, CFSTR newFile);
246
247 HRESULT CallProgress();
248
IsCallbackProgressErrorNFsFolder::CCopyState249 bool IsCallbackProgressError() { return ProgressInfo.ProgressResult != S_OK; }
250 };
251
CallProgress()252 HRESULT CCopyState::CallProgress()
253 {
254 return ProgressInfo.Progress->SetCompleted(&ProgressInfo.StartPos);
255 }
256
257 #ifdef Z7_USE_DYN_MoveFileWithProgressW
258
CCopyState()259 CCopyState::CCopyState()
260 {
261 my_CopyFileExW = NULL;
262 #ifndef UNDER_CE
263 my_MoveFileWithProgressW = NULL;
264 #endif
265 #ifndef _UNICODE
266 my_CopyFileExA = NULL;
267 if (!g_IsNT)
268 {
269 my_CopyFileExA = Z7_GET_PROC_ADDRESS(
270 Func_CopyFileExA, ::GetModuleHandleA("kernel32.dll"),
271 "CopyFileExA");
272 }
273 else
274 #endif
275 {
276 const HMODULE module = ::GetModuleHandleW(
277 #ifdef UNDER_CE
278 L"coredll.dll"
279 #else
280 L"kernel32.dll"
281 #endif
282 );
283 my_CopyFileExW = Z7_GET_PROC_ADDRESS(
284 Func_CopyFileExW, module,
285 "CopyFileExW");
286 #ifndef UNDER_CE
287 my_MoveFileWithProgressW = Z7_GET_PROC_ADDRESS(
288 Func_MoveFileWithProgressW, module,
289 "MoveFileWithProgressW");
290 #endif
291 }
292 }
293
294 #endif
295
296 /* WinXP-64:
297 CopyFileW(fromFile, toFile:altStream)
298 OK - there are NO alt streams in fromFile
299 ERROR_INVALID_PARAMETER - there are alt streams in fromFile
300 */
301
CopyFile_NT(const wchar_t * oldFile,const wchar_t * newFile)302 bool CCopyState::CopyFile_NT(const wchar_t *oldFile, const wchar_t *newFile)
303 {
304 BOOL cancelFlag = FALSE;
305 #ifdef Z7_USE_DYN_MoveFileWithProgressW
306 if (my_CopyFileExW)
307 #endif
308 return BOOLToBool(
309 #ifdef Z7_USE_DYN_MoveFileWithProgressW
310 my_CopyFileExW
311 #else
312 CopyFileExW
313 #endif
314 (oldFile, newFile, CopyProgressRoutine,
315 &ProgressInfo, &cancelFlag, COPY_FILE_FAIL_IF_EXISTS));
316 #ifdef Z7_USE_DYN_MoveFileWithProgressW
317 return BOOLToBool(::CopyFileW(oldFile, newFile, TRUE));
318 #endif
319 }
320
CopyFile_Sys(CFSTR oldFile,CFSTR newFile)321 bool CCopyState::CopyFile_Sys(CFSTR oldFile, CFSTR newFile)
322 {
323 #ifndef _UNICODE
324 if (!g_IsNT)
325 {
326 #ifdef Z7_USE_DYN_MoveFileWithProgressW
327 if (my_CopyFileExA)
328 #endif
329 {
330 BOOL cancelFlag = FALSE;
331 if (
332 #ifdef Z7_USE_DYN_MoveFileWithProgressW
333 my_CopyFileExA
334 #else
335 CopyFileExA
336 #endif
337 (fs2fas(oldFile), fs2fas(newFile),
338 CopyProgressRoutine, &ProgressInfo, &cancelFlag, COPY_FILE_FAIL_IF_EXISTS))
339 return true;
340 if (::GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
341 return false;
342 }
343 return BOOLToBool(::CopyFile(fs2fas(oldFile), fs2fas(newFile), TRUE));
344 }
345 else
346 #endif
347 {
348 IF_USE_MAIN_PATH_2(oldFile, newFile)
349 {
350 if (CopyFile_NT(fs2us(oldFile), fs2us(newFile)))
351 return true;
352 }
353 #ifdef Z7_LONG_PATH
354 if (USE_SUPER_PATH_2)
355 {
356 if (IsCallbackProgressError())
357 return false;
358 UString superPathOld, superPathNew;
359 if (!GetSuperPaths(oldFile, newFile, superPathOld, superPathNew, USE_MAIN_PATH_2))
360 return false;
361 if (CopyFile_NT(superPathOld, superPathNew))
362 return true;
363 }
364 #endif
365 return false;
366 }
367 }
368
MoveFile_Sys(CFSTR oldFile,CFSTR newFile)369 bool CCopyState::MoveFile_Sys(CFSTR oldFile, CFSTR newFile)
370 {
371 #ifndef UNDER_CE
372 // if (IsItWindows2000orHigher())
373 // {
374 #ifdef Z7_USE_DYN_MoveFileWithProgressW
375 if (my_MoveFileWithProgressW)
376 #endif
377 {
378 IF_USE_MAIN_PATH_2(oldFile, newFile)
379 {
380 if (
381 #ifdef Z7_USE_DYN_MoveFileWithProgressW
382 my_MoveFileWithProgressW
383 #else
384 MoveFileWithProgressW
385 #endif
386 (fs2us(oldFile), fs2us(newFile), CopyProgressRoutine,
387 &ProgressInfo, MOVEFILE_COPY_ALLOWED))
388 return true;
389 }
390 #ifdef Z7_LONG_PATH
391 if ((!(USE_MAIN_PATH_2) || ::GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) && USE_SUPER_PATH_2)
392 {
393 if (IsCallbackProgressError())
394 return false;
395 UString superPathOld, superPathNew;
396 if (!GetSuperPaths(oldFile, newFile, superPathOld, superPathNew, USE_MAIN_PATH_2))
397 return false;
398 if (
399 #ifdef Z7_USE_DYN_MoveFileWithProgressW
400 my_MoveFileWithProgressW
401 #else
402 MoveFileWithProgressW
403 #endif
404 (superPathOld, superPathNew, CopyProgressRoutine,
405 &ProgressInfo, MOVEFILE_COPY_ALLOWED))
406 return true;
407 }
408 #endif
409 if (::GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
410 return false;
411 }
412 // }
413 // else
414 #endif
415 return MyMoveFile(oldFile, newFile);
416 }
417
SendMessageError(IFolderOperationsExtractCallback * callback,const wchar_t * message,const FString & fileName)418 static HRESULT SendMessageError(IFolderOperationsExtractCallback *callback,
419 const wchar_t *message, const FString &fileName)
420 {
421 UString s = message;
422 s += " : ";
423 s += fs2us(fileName);
424 return callback->ShowMessage(s);
425 }
426
SendMessageError(IFolderOperationsExtractCallback * callback,const char * message,const FString & fileName)427 static HRESULT SendMessageError(IFolderOperationsExtractCallback *callback,
428 const char *message, const FString &fileName)
429 {
430 return SendMessageError(callback, MultiByteToUnicodeString(message), fileName);
431 }
432
Return_LastError_or_FAIL()433 static DWORD Return_LastError_or_FAIL()
434 {
435 DWORD errorCode = GetLastError();
436 if (errorCode == 0)
437 errorCode = (DWORD)E_FAIL;
438 return errorCode;
439 }
440
GetLastErrorMessage()441 static UString GetLastErrorMessage()
442 {
443 return NError::MyFormatMessage(Return_LastError_or_FAIL());
444 }
445
SendLastErrorMessage(IFolderOperationsExtractCallback * callback,const FString & fileName)446 HRESULT SendLastErrorMessage(IFolderOperationsExtractCallback *callback, const FString &fileName)
447 {
448 return SendMessageError(callback, GetLastErrorMessage(), fileName);
449 }
450
CopyFile_Ask(CCopyState & state,const FString & srcPath,const CFileInfo & srcFileInfo,const FString & destPath)451 static HRESULT CopyFile_Ask(
452 CCopyState &state,
453 const FString &srcPath,
454 const CFileInfo &srcFileInfo,
455 const FString &destPath)
456 {
457 if (CompareFileNames(destPath, srcPath) == 0)
458 {
459 RINOK(SendMessageError(state.Callback,
460 state.MoveMode ?
461 "Cannot move file onto itself" :
462 "Cannot copy file onto itself"
463 , destPath))
464 return E_ABORT;
465 }
466
467 Int32 writeAskResult;
468 CMyComBSTR destPathResult;
469 RINOK(state.Callback->AskWrite(
470 fs2us(srcPath),
471 BoolToInt(false),
472 &srcFileInfo.MTime, &srcFileInfo.Size,
473 fs2us(destPath),
474 &destPathResult,
475 &writeAskResult))
476
477 if (IntToBool(writeAskResult))
478 {
479 FString destPathNew = us2fs((LPCOLESTR)destPathResult);
480 RINOK(state.Callback->SetCurrentFilePath(fs2us(srcPath)))
481
482 if (state.UseReadWriteMode)
483 {
484 NFsFolder::CCopyStateIO state2;
485 state2.Progress = state.Callback;
486 state2.DeleteSrcFile = state.MoveMode;
487 state2.TotalSize = state.ProgressInfo.TotalSize;
488 state2.StartPos = state.ProgressInfo.StartPos;
489
490 RINOK(state2.MyCopyFile(srcPath, destPathNew,
491 state.IsAltStreamsDest ? INVALID_FILE_ATTRIBUTES: srcFileInfo.Attrib))
492
493 if (state2.ErrorFileIndex >= 0)
494 {
495 if (state2.ErrorMessage.IsEmpty())
496 state2.ErrorMessage = GetLastErrorMessage();
497 FString errorName;
498 if (state2.ErrorFileIndex == 0)
499 errorName = srcPath;
500 else
501 errorName = destPathNew;
502 RINOK(SendMessageError(state.Callback, state2.ErrorMessage, errorName))
503 return E_ABORT;
504 }
505 state.ProgressInfo.StartPos += state2.CurrentSize;
506 }
507 else
508 {
509 state.ProgressInfo.FileSize = srcFileInfo.Size;
510 bool res;
511 if (state.MoveMode)
512 res = state.MoveFile_Sys(srcPath, destPathNew);
513 else
514 res = state.CopyFile_Sys(srcPath, destPathNew);
515 RINOK(state.ProgressInfo.ProgressResult)
516 if (!res)
517 {
518 // GetLastError() is ERROR_REQUEST_ABORTED in case of PROGRESS_CANCEL.
519 RINOK(SendMessageError(state.Callback, GetLastErrorMessage(), destPathNew))
520 return E_ABORT;
521 }
522 state.ProgressInfo.StartPos += state.ProgressInfo.FileSize;
523 }
524 }
525 else
526 {
527 if (state.ProgressInfo.TotalSize >= srcFileInfo.Size)
528 {
529 state.ProgressInfo.TotalSize -= srcFileInfo.Size;
530 RINOK(state.ProgressInfo.Progress->SetTotal(state.ProgressInfo.TotalSize))
531 }
532 }
533 return state.CallProgress();
534 }
535
CombinePath(const FString & folderPath,const FString & fileName)536 static FString CombinePath(const FString &folderPath, const FString &fileName)
537 {
538 FString s (folderPath);
539 s.Add_PathSepar(); // FCHAR_PATH_SEPARATOR
540 s += fileName;
541 return s;
542 }
543
IsDestChild(const FString & src,const FString & dest)544 static bool IsDestChild(const FString &src, const FString &dest)
545 {
546 unsigned len = src.Len();
547 if (dest.Len() < len)
548 return false;
549 if (dest.Len() != len && dest[len] != FCHAR_PATH_SEPARATOR)
550 return false;
551 return CompareFileNames(dest.Left(len), src) == 0;
552 }
553
CopyFolder(CCopyState & state,const FString & srcPath,const FString & destPath)554 static HRESULT CopyFolder(
555 CCopyState &state,
556 const FString &srcPath, // without TAIL separator
557 const FString &destPath) // without TAIL separator
558 {
559 RINOK(state.CallProgress())
560
561 if (IsDestChild(srcPath, destPath))
562 {
563 RINOK(SendMessageError(state.Callback,
564 state.MoveMode ?
565 "Cannot copy folder onto itself" :
566 "Cannot move folder onto itself"
567 , destPath))
568 return E_ABORT;
569 }
570
571 if (state.MoveMode)
572 {
573 if (state.MoveFile_Sys(srcPath, destPath))
574 return S_OK;
575
576 // MSDN: MoveFile() fails for dirs on different volumes.
577 }
578
579 if (!CreateComplexDir(destPath))
580 {
581 RINOK(SendMessageError(state.Callback, "Cannot create folder", destPath))
582 return E_ABORT;
583 }
584
585 CEnumerator enumerator;
586 enumerator.SetDirPrefix(CombinePath(srcPath, FString()));
587
588 for (;;)
589 {
590 NFind::CFileInfo fi;
591 bool found;
592 if (!enumerator.Next(fi, found))
593 {
594 SendLastErrorMessage(state.Callback, srcPath);
595 return S_OK;
596 }
597 if (!found)
598 break;
599 const FString srcPath2 = CombinePath(srcPath, fi.Name);
600 const FString destPath2 = CombinePath(destPath, fi.Name);
601 if (fi.IsDir())
602 {
603 RINOK(CopyFolder(state, srcPath2, destPath2))
604 }
605 else
606 {
607 RINOK(CopyFile_Ask(state, srcPath2, fi, destPath2))
608 }
609 }
610
611 if (state.MoveMode)
612 {
613 if (!RemoveDir(srcPath))
614 {
615 RINOK(SendMessageError(state.Callback, "Cannot remove folder", srcPath))
616 return E_ABORT;
617 }
618 }
619
620 return S_OK;
621 }
622
Z7_COM7F_IMF(CFSFolder::CopyTo (Int32 moveMode,const UInt32 * indices,UInt32 numItems,Int32,Int32,const wchar_t * path,IFolderOperationsExtractCallback * callback))623 Z7_COM7F_IMF(CFSFolder::CopyTo(Int32 moveMode, const UInt32 *indices, UInt32 numItems,
624 Int32 /* includeAltStreams */, Int32 /* replaceAltStreamColon */,
625 const wchar_t *path, IFolderOperationsExtractCallback *callback))
626 {
627 if (numItems == 0)
628 return S_OK;
629
630 const FString destPath = us2fs(path);
631 if (destPath.IsEmpty())
632 return E_INVALIDARG;
633
634 const bool isAltDest = NName::IsAltPathPrefix(destPath);
635 const bool isDirectPath = (!isAltDest && !IsPathSepar(destPath.Back()));
636
637 if (isDirectPath)
638 if (numItems > 1)
639 return E_INVALIDARG;
640
641 CFsFolderStat stat;
642 stat.Progress = callback;
643
644 UInt32 i;
645 for (i = 0; i < numItems; i++)
646 {
647 const UInt32 index = indices[i];
648 /*
649 if (index >= Files.Size())
650 {
651 size += Streams[index - Files.Size()].Size;
652 // numFiles++;
653 continue;
654 }
655 */
656 const CDirItem &fi = Files[index];
657 if (fi.IsDir())
658 {
659 if (!isAltDest)
660 {
661 stat.Path = _path;
662 stat.Path += GetRelPath(fi);
663 RINOK(stat.Enumerate())
664 }
665 stat.NumFolders++;
666 }
667 else
668 {
669 stat.NumFiles++;
670 stat.Size += fi.Size;
671 }
672 }
673
674 /*
675 if (stat.NumFolders != 0 && isAltDest)
676 return E_NOTIMPL;
677 */
678
679 RINOK(callback->SetTotal(stat.Size))
680 RINOK(callback->SetNumFiles(stat.NumFiles))
681
682 UInt64 completedSize = 0;
683 RINOK(callback->SetCompleted(&completedSize))
684
685 CCopyState state;
686 state.ProgressInfo.TotalSize = stat.Size;
687 state.ProgressInfo.StartPos = 0;
688 state.ProgressInfo.Progress = callback;
689 state.ProgressInfo.Init();
690 state.Callback = callback;
691 state.MoveMode = IntToBool(moveMode);
692 state.IsAltStreamsDest = isAltDest;
693 /* CopyFileW(fromFile, toFile:altStream) returns ERROR_INVALID_PARAMETER,
694 if there are alt streams in fromFile.
695 So we don't use CopyFileW() for alt Streams. */
696 state.UseReadWriteMode = isAltDest;
697
698 for (i = 0; i < numItems; i++)
699 {
700 const UInt32 index = indices[i];
701 if (index >= (UInt32)Files.Size())
702 continue;
703 const CDirItem &fi = Files[index];
704 FString destPath2 = destPath;
705 if (!isDirectPath)
706 destPath2 += fi.Name;
707 FString srcPath;
708 GetFullPath(fi, srcPath);
709
710 if (fi.IsDir())
711 {
712 if (isAltDest)
713 {
714 RINOK(SendMessageError(callback, k_CannotCopyDirToAltStream, srcPath))
715 }
716 else
717 {
718 RINOK(CopyFolder(state, srcPath, destPath2))
719 }
720 }
721 else
722 {
723 RINOK(CopyFile_Ask(state, srcPath, fi, destPath2))
724 }
725 }
726 return S_OK;
727 }
728
729
730
731 /* we can call CopyFileSystemItems() from CDropTarget::Drop() */
732
CopyFileSystemItems(const UStringVector & itemsPaths,const FString & destDirPrefix,bool moveMode,IFolderOperationsExtractCallback * callback)733 HRESULT CopyFileSystemItems(
734 const UStringVector &itemsPaths,
735 const FString &destDirPrefix,
736 bool moveMode,
737 IFolderOperationsExtractCallback *callback)
738 {
739 if (itemsPaths.IsEmpty())
740 return S_OK;
741
742 if (destDirPrefix.IsEmpty())
743 return E_INVALIDARG;
744
745 const bool isAltDest = NName::IsAltPathPrefix(destDirPrefix);
746
747 CFsFolderStat stat;
748 stat.Progress = callback;
749
750 {
751 FOR_VECTOR (i, itemsPaths)
752 {
753 const UString &path = itemsPaths[i];
754 CFileInfo fi;
755 if (!fi.Find(us2fs(path)))
756 continue;
757 if (fi.IsDir())
758 {
759 if (!isAltDest)
760 {
761 stat.Path = us2fs(path);
762 RINOK(stat.Enumerate())
763 }
764 stat.NumFolders++;
765 }
766 else
767 {
768 stat.NumFiles++;
769 stat.Size += fi.Size;
770 }
771 }
772 }
773
774 /*
775 if (stat.NumFolders != 0 && isAltDest)
776 return E_NOTIMPL;
777 */
778
779 RINOK(callback->SetTotal(stat.Size))
780 // RINOK(progress->SetNumFiles(stat.NumFiles));
781
782 UInt64 completedSize = 0;
783 RINOK(callback->SetCompleted(&completedSize))
784
785 CCopyState state;
786 state.ProgressInfo.TotalSize = stat.Size;
787 state.ProgressInfo.StartPos = 0;
788 state.ProgressInfo.Progress = callback;
789 state.ProgressInfo.Init();
790 state.Callback = callback;
791 state.MoveMode = moveMode;
792 state.IsAltStreamsDest = isAltDest;
793 /* CopyFileW(fromFile, toFile:altStream) returns ERROR_INVALID_PARAMETER,
794 if there are alt streams in fromFile.
795 So we don't use CopyFileW() for alt Streams. */
796 state.UseReadWriteMode = isAltDest;
797
798 FOR_VECTOR (i, itemsPaths)
799 {
800 const UString path = itemsPaths[i];
801 CFileInfo fi;
802
803 if (!fi.Find(us2fs(path)))
804 {
805 RINOK(SendMessageError(callback, "Cannot find the file", us2fs(path)))
806 continue;
807 }
808
809 FString destPath = destDirPrefix;
810 destPath += fi.Name;
811
812 if (fi.IsDir())
813 {
814 if (isAltDest)
815 {
816 RINOK(SendMessageError(callback, k_CannotCopyDirToAltStream, us2fs(path)))
817 }
818 else
819 {
820 RINOK(CopyFolder(state, us2fs(path), destPath))
821 }
822 }
823 else
824 {
825 RINOK(CopyFile_Ask(state, us2fs(path), fi, destPath))
826 }
827 }
828 return S_OK;
829 }
830
831
832 /* we don't use CFSFolder::CopyFrom() because the caller of CopyFrom()
833 is optimized for IFolderArchiveUpdateCallback interface,
834 but we want to use IFolderOperationsExtractCallback interface instead */
835
Z7_COM7F_IMF(CFSFolder::CopyFrom (Int32,const wchar_t *,const wchar_t * const *,UInt32,IProgress *))836 Z7_COM7F_IMF(CFSFolder::CopyFrom(Int32 /* moveMode */, const wchar_t * /* fromFolderPath */,
837 const wchar_t * const * /* itemsPaths */, UInt32 /* numItems */, IProgress * /* progress */))
838 {
839 /*
840 Z7_DECL_CMyComPtr_QI_FROM(
841 IFolderOperationsExtractCallback,
842 callback, progress)
843 if (!callback)
844 return E_NOTIMPL;
845 return CopyFileSystemItems(_path,
846 moveMode, fromDirPrefix,
847 itemsPaths, numItems, callback);
848 */
849 return E_NOTIMPL;
850 }
851
Z7_COM7F_IMF(CFSFolder::CopyFromFile (UInt32,const wchar_t *,IProgress *))852 Z7_COM7F_IMF(CFSFolder::CopyFromFile(UInt32 /* index */, const wchar_t * /* fullFilePath */, IProgress * /* progress */))
853 {
854 return E_NOTIMPL;
855 }
856
857 }
858