• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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