xref: /aosp_15_r20/external/lzma/CPP/7zip/UI/FileManager/FSFolderCopy.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
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