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 const DWORD errorCode = GetLastError();
519 UString errorMessage = NError::MyFormatMessage(Return_LastError_or_FAIL());
520 if (errorCode == ERROR_INVALID_PARAMETER)
521 {
522 NFind::CFileInfo fi;
523 if (fi.Find(srcPath) &&
524 fi.Size > (UInt32)(Int32)-1)
525 {
526 // bool isFsDetected = false;
527 // if (NSystem::Is_File_LimitedBy_4GB(destPathNew, isFsDetected) || !isFsDetected)
528 errorMessage += " File size exceeds 4 GB";
529 }
530 }
531
532 // GetLastError() is ERROR_REQUEST_ABORTED in case of PROGRESS_CANCEL.
533 RINOK(SendMessageError(state.Callback, errorMessage, destPathNew))
534 return E_ABORT;
535 }
536 state.ProgressInfo.StartPos += state.ProgressInfo.FileSize;
537 }
538 }
539 else
540 {
541 if (state.ProgressInfo.TotalSize >= srcFileInfo.Size)
542 {
543 state.ProgressInfo.TotalSize -= srcFileInfo.Size;
544 RINOK(state.ProgressInfo.Progress->SetTotal(state.ProgressInfo.TotalSize))
545 }
546 }
547 return state.CallProgress();
548 }
549
CombinePath(const FString & folderPath,const FString & fileName)550 static FString CombinePath(const FString &folderPath, const FString &fileName)
551 {
552 FString s (folderPath);
553 s.Add_PathSepar(); // FCHAR_PATH_SEPARATOR
554 s += fileName;
555 return s;
556 }
557
IsDestChild(const FString & src,const FString & dest)558 static bool IsDestChild(const FString &src, const FString &dest)
559 {
560 unsigned len = src.Len();
561 if (dest.Len() < len)
562 return false;
563 if (dest.Len() != len && dest[len] != FCHAR_PATH_SEPARATOR)
564 return false;
565 return CompareFileNames(dest.Left(len), src) == 0;
566 }
567
CopyFolder(CCopyState & state,const FString & srcPath,const FString & destPath)568 static HRESULT CopyFolder(
569 CCopyState &state,
570 const FString &srcPath, // without TAIL separator
571 const FString &destPath) // without TAIL separator
572 {
573 RINOK(state.CallProgress())
574
575 if (IsDestChild(srcPath, destPath))
576 {
577 RINOK(SendMessageError(state.Callback,
578 state.MoveMode ?
579 "Cannot copy folder onto itself" :
580 "Cannot move folder onto itself"
581 , destPath))
582 return E_ABORT;
583 }
584
585 if (state.MoveMode)
586 {
587 if (state.MoveFile_Sys(srcPath, destPath))
588 return S_OK;
589
590 // MSDN: MoveFile() fails for dirs on different volumes.
591 }
592
593 if (!CreateComplexDir(destPath))
594 {
595 RINOK(SendMessageError(state.Callback, "Cannot create folder", destPath))
596 return E_ABORT;
597 }
598
599 CEnumerator enumerator;
600 enumerator.SetDirPrefix(CombinePath(srcPath, FString()));
601
602 for (;;)
603 {
604 NFind::CFileInfo fi;
605 bool found;
606 if (!enumerator.Next(fi, found))
607 {
608 SendLastErrorMessage(state.Callback, srcPath);
609 return S_OK;
610 }
611 if (!found)
612 break;
613 const FString srcPath2 = CombinePath(srcPath, fi.Name);
614 const FString destPath2 = CombinePath(destPath, fi.Name);
615 if (fi.IsDir())
616 {
617 RINOK(CopyFolder(state, srcPath2, destPath2))
618 }
619 else
620 {
621 RINOK(CopyFile_Ask(state, srcPath2, fi, destPath2))
622 }
623 }
624
625 if (state.MoveMode)
626 {
627 if (!RemoveDir(srcPath))
628 {
629 RINOK(SendMessageError(state.Callback, "Cannot remove folder", srcPath))
630 return E_ABORT;
631 }
632 }
633
634 return S_OK;
635 }
636
Z7_COM7F_IMF(CFSFolder::CopyTo (Int32 moveMode,const UInt32 * indices,UInt32 numItems,Int32,Int32,const wchar_t * path,IFolderOperationsExtractCallback * callback))637 Z7_COM7F_IMF(CFSFolder::CopyTo(Int32 moveMode, const UInt32 *indices, UInt32 numItems,
638 Int32 /* includeAltStreams */, Int32 /* replaceAltStreamColon */,
639 const wchar_t *path, IFolderOperationsExtractCallback *callback))
640 {
641 if (numItems == 0)
642 return S_OK;
643
644 const FString destPath = us2fs(path);
645 if (destPath.IsEmpty())
646 return E_INVALIDARG;
647
648 const bool isAltDest = NName::IsAltPathPrefix(destPath);
649 const bool isDirectPath = (!isAltDest && !IsPathSepar(destPath.Back()));
650
651 if (isDirectPath)
652 if (numItems > 1)
653 return E_INVALIDARG;
654
655 CFsFolderStat stat;
656 stat.Progress = callback;
657
658 UInt32 i;
659 for (i = 0; i < numItems; i++)
660 {
661 const UInt32 index = indices[i];
662 /*
663 if (index >= Files.Size())
664 {
665 size += Streams[index - Files.Size()].Size;
666 // numFiles++;
667 continue;
668 }
669 */
670 const CDirItem &fi = Files[index];
671 if (fi.IsDir())
672 {
673 if (!isAltDest)
674 {
675 stat.Path = _path;
676 stat.Path += GetRelPath(fi);
677 RINOK(stat.Enumerate())
678 }
679 stat.NumFolders++;
680 }
681 else
682 {
683 stat.NumFiles++;
684 stat.Size += fi.Size;
685 }
686 }
687
688 /*
689 if (stat.NumFolders != 0 && isAltDest)
690 return E_NOTIMPL;
691 */
692
693 RINOK(callback->SetTotal(stat.Size))
694 RINOK(callback->SetNumFiles(stat.NumFiles))
695
696 UInt64 completedSize = 0;
697 RINOK(callback->SetCompleted(&completedSize))
698
699 CCopyState state;
700 state.ProgressInfo.TotalSize = stat.Size;
701 state.ProgressInfo.StartPos = 0;
702 state.ProgressInfo.Progress = callback;
703 state.ProgressInfo.Init();
704 state.Callback = callback;
705 state.MoveMode = IntToBool(moveMode);
706 state.IsAltStreamsDest = isAltDest;
707 /* CopyFileW(fromFile, toFile:altStream) returns ERROR_INVALID_PARAMETER,
708 if there are alt streams in fromFile.
709 So we don't use CopyFileW() for alt Streams. */
710 state.UseReadWriteMode = isAltDest;
711
712 for (i = 0; i < numItems; i++)
713 {
714 const UInt32 index = indices[i];
715 if (index >= (UInt32)Files.Size())
716 continue;
717 const CDirItem &fi = Files[index];
718 FString destPath2 = destPath;
719 if (!isDirectPath)
720 destPath2 += fi.Name;
721 FString srcPath;
722 GetFullPath(fi, srcPath);
723
724 if (fi.IsDir())
725 {
726 if (isAltDest)
727 {
728 RINOK(SendMessageError(callback, k_CannotCopyDirToAltStream, srcPath))
729 }
730 else
731 {
732 RINOK(CopyFolder(state, srcPath, destPath2))
733 }
734 }
735 else
736 {
737 RINOK(CopyFile_Ask(state, srcPath, fi, destPath2))
738 }
739 }
740 return S_OK;
741 }
742
743
744
745 /* we can call CopyFileSystemItems() from CDropTarget::Drop() */
746
CopyFileSystemItems(const UStringVector & itemsPaths,const FString & destDirPrefix,bool moveMode,IFolderOperationsExtractCallback * callback)747 HRESULT CopyFileSystemItems(
748 const UStringVector &itemsPaths,
749 const FString &destDirPrefix,
750 bool moveMode,
751 IFolderOperationsExtractCallback *callback)
752 {
753 if (itemsPaths.IsEmpty())
754 return S_OK;
755
756 if (destDirPrefix.IsEmpty())
757 return E_INVALIDARG;
758
759 const bool isAltDest = NName::IsAltPathPrefix(destDirPrefix);
760
761 CFsFolderStat stat;
762 stat.Progress = callback;
763
764 {
765 FOR_VECTOR (i, itemsPaths)
766 {
767 const UString &path = itemsPaths[i];
768 CFileInfo fi;
769 if (!fi.Find(us2fs(path)))
770 continue;
771 if (fi.IsDir())
772 {
773 if (!isAltDest)
774 {
775 stat.Path = us2fs(path);
776 RINOK(stat.Enumerate())
777 }
778 stat.NumFolders++;
779 }
780 else
781 {
782 stat.NumFiles++;
783 stat.Size += fi.Size;
784 }
785 }
786 }
787
788 /*
789 if (stat.NumFolders != 0 && isAltDest)
790 return E_NOTIMPL;
791 */
792
793 RINOK(callback->SetTotal(stat.Size))
794 // RINOK(progress->SetNumFiles(stat.NumFiles));
795
796 UInt64 completedSize = 0;
797 RINOK(callback->SetCompleted(&completedSize))
798
799 CCopyState state;
800 state.ProgressInfo.TotalSize = stat.Size;
801 state.ProgressInfo.StartPos = 0;
802 state.ProgressInfo.Progress = callback;
803 state.ProgressInfo.Init();
804 state.Callback = callback;
805 state.MoveMode = moveMode;
806 state.IsAltStreamsDest = isAltDest;
807 /* CopyFileW(fromFile, toFile:altStream) returns ERROR_INVALID_PARAMETER,
808 if there are alt streams in fromFile.
809 So we don't use CopyFileW() for alt Streams. */
810 state.UseReadWriteMode = isAltDest;
811
812 FOR_VECTOR (i, itemsPaths)
813 {
814 const UString path = itemsPaths[i];
815 CFileInfo fi;
816
817 if (!fi.Find(us2fs(path)))
818 {
819 RINOK(SendMessageError(callback, "Cannot find the file", us2fs(path)))
820 continue;
821 }
822
823 FString destPath = destDirPrefix;
824 destPath += fi.Name;
825
826 if (fi.IsDir())
827 {
828 if (isAltDest)
829 {
830 RINOK(SendMessageError(callback, k_CannotCopyDirToAltStream, us2fs(path)))
831 }
832 else
833 {
834 RINOK(CopyFolder(state, us2fs(path), destPath))
835 }
836 }
837 else
838 {
839 RINOK(CopyFile_Ask(state, us2fs(path), fi, destPath))
840 }
841 }
842 return S_OK;
843 }
844
845
846 /* we don't use CFSFolder::CopyFrom() because the caller of CopyFrom()
847 is optimized for IFolderArchiveUpdateCallback interface,
848 but we want to use IFolderOperationsExtractCallback interface instead */
849
Z7_COM7F_IMF(CFSFolder::CopyFrom (Int32,const wchar_t *,const wchar_t * const *,UInt32,IProgress *))850 Z7_COM7F_IMF(CFSFolder::CopyFrom(Int32 /* moveMode */, const wchar_t * /* fromFolderPath */,
851 const wchar_t * const * /* itemsPaths */, UInt32 /* numItems */, IProgress * /* progress */))
852 {
853 /*
854 Z7_DECL_CMyComPtr_QI_FROM(
855 IFolderOperationsExtractCallback,
856 callback, progress)
857 if (!callback)
858 return E_NOTIMPL;
859 return CopyFileSystemItems(_path,
860 moveMode, fromDirPrefix,
861 itemsPaths, numItems, callback);
862 */
863 return E_NOTIMPL;
864 }
865
Z7_COM7F_IMF(CFSFolder::CopyFromFile (UInt32,const wchar_t *,IProgress *))866 Z7_COM7F_IMF(CFSFolder::CopyFromFile(UInt32 /* index */, const wchar_t * /* fullFilePath */, IProgress * /* progress */))
867 {
868 return E_NOTIMPL;
869 }
870
871 }
872