chore: initial commit (extracted from Launchers monorepo)
Plugin: ns7zip v2.0.0 Architectures: x86-ansi, x86-unicode, amd64-unicode License: LGPL-2.1-or-later
This commit is contained in:
@@ -0,0 +1,871 @@
|
||||
// FSFolderCopy.cpp
|
||||
|
||||
#include "StdAfx.h"
|
||||
|
||||
#include "../../../Common/MyWindows.h"
|
||||
|
||||
#include "../../../Common/Defs.h"
|
||||
#include "../../../Common/StringConvert.h"
|
||||
#include "../../../Common/Wildcard.h"
|
||||
|
||||
#include "../../../Windows/DLL.h"
|
||||
#include "../../../Windows/ErrorMsg.h"
|
||||
#include "../../../Windows/FileDir.h"
|
||||
#include "../../../Windows/FileName.h"
|
||||
|
||||
#include "../../Common/FilePathAutoRename.h"
|
||||
|
||||
#include "FSFolder.h"
|
||||
|
||||
using namespace NWindows;
|
||||
using namespace NFile;
|
||||
using namespace NDir;
|
||||
using namespace NName;
|
||||
using namespace NFind;
|
||||
|
||||
#ifndef _UNICODE
|
||||
extern bool g_IsNT;
|
||||
#endif
|
||||
|
||||
namespace NFsFolder {
|
||||
|
||||
static const char * const k_CannotCopyDirToAltStream = "Cannot copy folder as alternate stream";
|
||||
|
||||
|
||||
HRESULT CCopyStateIO::MyCopyFile(CFSTR inPath, CFSTR outPath, DWORD attrib)
|
||||
{
|
||||
ErrorFileIndex = -1;
|
||||
ErrorMessage.Empty();
|
||||
CurrentSize = 0;
|
||||
|
||||
{
|
||||
const size_t kBufSize = 1 << 16;
|
||||
CByteArr buf(kBufSize);
|
||||
|
||||
NIO::CInFile inFile;
|
||||
NIO::COutFile outFile;
|
||||
|
||||
if (!inFile.Open(inPath))
|
||||
{
|
||||
ErrorFileIndex = 0;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
if (!outFile.Create_ALWAYS(outPath))
|
||||
{
|
||||
ErrorFileIndex = 1;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
UInt32 num;
|
||||
if (!inFile.Read(buf, kBufSize, num))
|
||||
{
|
||||
ErrorFileIndex = 0;
|
||||
return S_OK;
|
||||
}
|
||||
if (num == 0)
|
||||
break;
|
||||
|
||||
UInt32 written = 0;
|
||||
if (!outFile.Write(buf, num, written))
|
||||
{
|
||||
ErrorFileIndex = 1;
|
||||
return S_OK;
|
||||
}
|
||||
if (written != num)
|
||||
{
|
||||
ErrorMessage = "Write error";
|
||||
return S_OK;
|
||||
}
|
||||
CurrentSize += num;
|
||||
if (Progress)
|
||||
{
|
||||
UInt64 completed = StartPos + CurrentSize;
|
||||
RINOK(Progress->SetCompleted(&completed))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* SetFileAttrib("path:alt_stream_name") sets attributes for main file "path".
|
||||
But we don't want to change attributes of main file, when we write alt stream.
|
||||
So we need INVALID_FILE_ATTRIBUTES for alt stream here */
|
||||
|
||||
if (attrib != INVALID_FILE_ATTRIBUTES)
|
||||
SetFileAttrib(outPath, attrib);
|
||||
|
||||
if (DeleteSrcFile)
|
||||
{
|
||||
if (!DeleteFileAlways(inPath))
|
||||
{
|
||||
ErrorFileIndex = 0;
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
static bool IsItWindows2000orHigher()
|
||||
{
|
||||
OSVERSIONINFO versionInfo;
|
||||
versionInfo.dwOSVersionInfoSize = sizeof(versionInfo);
|
||||
if (!::GetVersionEx(&versionInfo))
|
||||
return false;
|
||||
return (versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) &&
|
||||
(versionInfo.dwMajorVersion >= 5);
|
||||
}
|
||||
*/
|
||||
|
||||
struct CProgressInfo
|
||||
{
|
||||
UInt64 TotalSize;
|
||||
UInt64 StartPos;
|
||||
UInt64 FileSize;
|
||||
IProgress *Progress;
|
||||
HRESULT ProgressResult;
|
||||
|
||||
void Init() { ProgressResult = S_OK; }
|
||||
};
|
||||
|
||||
#ifndef PROGRESS_CONTINUE
|
||||
|
||||
#define PROGRESS_CONTINUE 0
|
||||
#define PROGRESS_CANCEL 1
|
||||
|
||||
#define COPY_FILE_FAIL_IF_EXISTS 0x00000001
|
||||
|
||||
typedef
|
||||
DWORD
|
||||
(WINAPI* LPPROGRESS_ROUTINE)(
|
||||
LARGE_INTEGER TotalFileSize,
|
||||
LARGE_INTEGER TotalBytesTransferred,
|
||||
LARGE_INTEGER StreamSize,
|
||||
LARGE_INTEGER StreamBytesTransferred,
|
||||
DWORD dwStreamNumber,
|
||||
DWORD dwCallbackReason,
|
||||
HANDLE hSourceFile,
|
||||
HANDLE hDestinationFile,
|
||||
LPVOID lpData
|
||||
);
|
||||
|
||||
#endif
|
||||
|
||||
static DWORD CALLBACK CopyProgressRoutine(
|
||||
LARGE_INTEGER TotalFileSize, // file size
|
||||
LARGE_INTEGER TotalBytesTransferred, // bytes transferred
|
||||
LARGE_INTEGER /* StreamSize */, // bytes in stream
|
||||
LARGE_INTEGER /* StreamBytesTransferred */, // bytes transferred for stream
|
||||
DWORD /* dwStreamNumber */, // current stream
|
||||
DWORD /* dwCallbackReason */, // callback reason
|
||||
HANDLE /* hSourceFile */, // handle to source file
|
||||
HANDLE /* hDestinationFile */, // handle to destination file
|
||||
LPVOID lpData // from CopyFileEx
|
||||
)
|
||||
{
|
||||
// StreamSize = StreamSize;
|
||||
// StreamBytesTransferred = StreamBytesTransferred;
|
||||
// dwStreamNumber = dwStreamNumber;
|
||||
// dwCallbackReason = dwCallbackReason;
|
||||
|
||||
CProgressInfo &pi = *(CProgressInfo *)lpData;
|
||||
|
||||
if ((UInt64)TotalFileSize.QuadPart > pi.FileSize)
|
||||
{
|
||||
pi.TotalSize += (UInt64)TotalFileSize.QuadPart - pi.FileSize;
|
||||
pi.FileSize = (UInt64)TotalFileSize.QuadPart;
|
||||
pi.ProgressResult = pi.Progress->SetTotal(pi.TotalSize);
|
||||
}
|
||||
const UInt64 completed = pi.StartPos + (UInt64)TotalBytesTransferred.QuadPart;
|
||||
pi.ProgressResult = pi.Progress->SetCompleted(&completed);
|
||||
return (pi.ProgressResult == S_OK ? PROGRESS_CONTINUE : PROGRESS_CANCEL);
|
||||
}
|
||||
|
||||
#if !defined(Z7_WIN32_WINNT_MIN) || Z7_WIN32_WINNT_MIN < 0x0500 // win2000
|
||||
#define Z7_USE_DYN_MoveFileWithProgressW
|
||||
#endif
|
||||
|
||||
#ifdef Z7_USE_DYN_MoveFileWithProgressW
|
||||
// nt4
|
||||
typedef BOOL (WINAPI * Func_CopyFileExA)(
|
||||
IN LPCSTR lpExistingFileName,
|
||||
IN LPCSTR lpNewFileName,
|
||||
IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,
|
||||
IN LPVOID lpData OPTIONAL,
|
||||
IN LPBOOL pbCancel OPTIONAL,
|
||||
IN DWORD dwCopyFlags
|
||||
);
|
||||
|
||||
// nt4
|
||||
typedef BOOL (WINAPI * Func_CopyFileExW)(
|
||||
IN LPCWSTR lpExistingFileName,
|
||||
IN LPCWSTR lpNewFileName,
|
||||
IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,
|
||||
IN LPVOID lpData OPTIONAL,
|
||||
IN LPBOOL pbCancel OPTIONAL,
|
||||
IN DWORD dwCopyFlags
|
||||
);
|
||||
|
||||
// win2000
|
||||
typedef BOOL (WINAPI * Func_MoveFileWithProgressW)(
|
||||
IN LPCWSTR lpExistingFileName,
|
||||
IN LPCWSTR lpNewFileName,
|
||||
IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,
|
||||
IN LPVOID lpData OPTIONAL,
|
||||
IN DWORD dwFlags
|
||||
);
|
||||
#endif
|
||||
|
||||
struct CCopyState
|
||||
{
|
||||
CProgressInfo ProgressInfo;
|
||||
IFolderOperationsExtractCallback *Callback;
|
||||
bool MoveMode;
|
||||
bool UseReadWriteMode;
|
||||
bool IsAltStreamsDest;
|
||||
|
||||
#ifdef Z7_USE_DYN_MoveFileWithProgressW
|
||||
private:
|
||||
Func_CopyFileExW my_CopyFileExW;
|
||||
#ifndef UNDER_CE
|
||||
Func_MoveFileWithProgressW my_MoveFileWithProgressW;
|
||||
#endif
|
||||
#ifndef _UNICODE
|
||||
Func_CopyFileExA my_CopyFileExA;
|
||||
#endif
|
||||
public:
|
||||
CCopyState();
|
||||
#endif
|
||||
|
||||
bool CopyFile_NT(const wchar_t *oldFile, const wchar_t *newFile);
|
||||
bool CopyFile_Sys(CFSTR oldFile, CFSTR newFile);
|
||||
bool MoveFile_Sys(CFSTR oldFile, CFSTR newFile);
|
||||
|
||||
HRESULT CallProgress();
|
||||
|
||||
bool IsCallbackProgressError() { return ProgressInfo.ProgressResult != S_OK; }
|
||||
};
|
||||
|
||||
HRESULT CCopyState::CallProgress()
|
||||
{
|
||||
return ProgressInfo.Progress->SetCompleted(&ProgressInfo.StartPos);
|
||||
}
|
||||
|
||||
#ifdef Z7_USE_DYN_MoveFileWithProgressW
|
||||
|
||||
CCopyState::CCopyState()
|
||||
{
|
||||
my_CopyFileExW = NULL;
|
||||
#ifndef UNDER_CE
|
||||
my_MoveFileWithProgressW = NULL;
|
||||
#endif
|
||||
#ifndef _UNICODE
|
||||
my_CopyFileExA = NULL;
|
||||
if (!g_IsNT)
|
||||
{
|
||||
my_CopyFileExA = Z7_GET_PROC_ADDRESS(
|
||||
Func_CopyFileExA, ::GetModuleHandleA("kernel32.dll"),
|
||||
"CopyFileExA");
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
const HMODULE module = ::GetModuleHandleW(
|
||||
#ifdef UNDER_CE
|
||||
L"coredll.dll"
|
||||
#else
|
||||
L"kernel32.dll"
|
||||
#endif
|
||||
);
|
||||
my_CopyFileExW = Z7_GET_PROC_ADDRESS(
|
||||
Func_CopyFileExW, module,
|
||||
"CopyFileExW");
|
||||
#ifndef UNDER_CE
|
||||
my_MoveFileWithProgressW = Z7_GET_PROC_ADDRESS(
|
||||
Func_MoveFileWithProgressW, module,
|
||||
"MoveFileWithProgressW");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* WinXP-64:
|
||||
CopyFileW(fromFile, toFile:altStream)
|
||||
OK - there are NO alt streams in fromFile
|
||||
ERROR_INVALID_PARAMETER - there are alt streams in fromFile
|
||||
*/
|
||||
|
||||
bool CCopyState::CopyFile_NT(const wchar_t *oldFile, const wchar_t *newFile)
|
||||
{
|
||||
BOOL cancelFlag = FALSE;
|
||||
#ifdef Z7_USE_DYN_MoveFileWithProgressW
|
||||
if (my_CopyFileExW)
|
||||
#endif
|
||||
return BOOLToBool(
|
||||
#ifdef Z7_USE_DYN_MoveFileWithProgressW
|
||||
my_CopyFileExW
|
||||
#else
|
||||
CopyFileExW
|
||||
#endif
|
||||
(oldFile, newFile, CopyProgressRoutine,
|
||||
&ProgressInfo, &cancelFlag, COPY_FILE_FAIL_IF_EXISTS));
|
||||
#ifdef Z7_USE_DYN_MoveFileWithProgressW
|
||||
return BOOLToBool(::CopyFileW(oldFile, newFile, TRUE));
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CCopyState::CopyFile_Sys(CFSTR oldFile, CFSTR newFile)
|
||||
{
|
||||
#ifndef _UNICODE
|
||||
if (!g_IsNT)
|
||||
{
|
||||
#ifdef Z7_USE_DYN_MoveFileWithProgressW
|
||||
if (my_CopyFileExA)
|
||||
#endif
|
||||
{
|
||||
BOOL cancelFlag = FALSE;
|
||||
if (
|
||||
#ifdef Z7_USE_DYN_MoveFileWithProgressW
|
||||
my_CopyFileExA
|
||||
#else
|
||||
CopyFileExA
|
||||
#endif
|
||||
(fs2fas(oldFile), fs2fas(newFile),
|
||||
CopyProgressRoutine, &ProgressInfo, &cancelFlag, COPY_FILE_FAIL_IF_EXISTS))
|
||||
return true;
|
||||
if (::GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
|
||||
return false;
|
||||
}
|
||||
return BOOLToBool(::CopyFile(fs2fas(oldFile), fs2fas(newFile), TRUE));
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
IF_USE_MAIN_PATH_2(oldFile, newFile)
|
||||
{
|
||||
if (CopyFile_NT(fs2us(oldFile), fs2us(newFile)))
|
||||
return true;
|
||||
}
|
||||
#ifdef Z7_LONG_PATH
|
||||
if (USE_SUPER_PATH_2)
|
||||
{
|
||||
if (IsCallbackProgressError())
|
||||
return false;
|
||||
UString superPathOld, superPathNew;
|
||||
if (!GetSuperPaths(oldFile, newFile, superPathOld, superPathNew, USE_MAIN_PATH_2))
|
||||
return false;
|
||||
if (CopyFile_NT(superPathOld, superPathNew))
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CCopyState::MoveFile_Sys(CFSTR oldFile, CFSTR newFile)
|
||||
{
|
||||
#ifndef UNDER_CE
|
||||
// if (IsItWindows2000orHigher())
|
||||
// {
|
||||
#ifdef Z7_USE_DYN_MoveFileWithProgressW
|
||||
if (my_MoveFileWithProgressW)
|
||||
#endif
|
||||
{
|
||||
IF_USE_MAIN_PATH_2(oldFile, newFile)
|
||||
{
|
||||
if (
|
||||
#ifdef Z7_USE_DYN_MoveFileWithProgressW
|
||||
my_MoveFileWithProgressW
|
||||
#else
|
||||
MoveFileWithProgressW
|
||||
#endif
|
||||
(fs2us(oldFile), fs2us(newFile), CopyProgressRoutine,
|
||||
&ProgressInfo, MOVEFILE_COPY_ALLOWED))
|
||||
return true;
|
||||
}
|
||||
#ifdef Z7_LONG_PATH
|
||||
if ((!(USE_MAIN_PATH_2) || ::GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) && USE_SUPER_PATH_2)
|
||||
{
|
||||
if (IsCallbackProgressError())
|
||||
return false;
|
||||
UString superPathOld, superPathNew;
|
||||
if (!GetSuperPaths(oldFile, newFile, superPathOld, superPathNew, USE_MAIN_PATH_2))
|
||||
return false;
|
||||
if (
|
||||
#ifdef Z7_USE_DYN_MoveFileWithProgressW
|
||||
my_MoveFileWithProgressW
|
||||
#else
|
||||
MoveFileWithProgressW
|
||||
#endif
|
||||
(superPathOld, superPathNew, CopyProgressRoutine,
|
||||
&ProgressInfo, MOVEFILE_COPY_ALLOWED))
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
if (::GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
|
||||
return false;
|
||||
}
|
||||
// }
|
||||
// else
|
||||
#endif
|
||||
return MyMoveFile(oldFile, newFile);
|
||||
}
|
||||
|
||||
static HRESULT SendMessageError(IFolderOperationsExtractCallback *callback,
|
||||
const wchar_t *message, const FString &fileName)
|
||||
{
|
||||
UString s = message;
|
||||
s += " : ";
|
||||
s += fs2us(fileName);
|
||||
return callback->ShowMessage(s);
|
||||
}
|
||||
|
||||
static HRESULT SendMessageError(IFolderOperationsExtractCallback *callback,
|
||||
const char *message, const FString &fileName)
|
||||
{
|
||||
return SendMessageError(callback, MultiByteToUnicodeString(message), fileName);
|
||||
}
|
||||
|
||||
static DWORD Return_LastError_or_FAIL()
|
||||
{
|
||||
DWORD errorCode = GetLastError();
|
||||
if (errorCode == 0)
|
||||
errorCode = (DWORD)E_FAIL;
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
static UString GetLastErrorMessage()
|
||||
{
|
||||
return NError::MyFormatMessage(Return_LastError_or_FAIL());
|
||||
}
|
||||
|
||||
HRESULT SendLastErrorMessage(IFolderOperationsExtractCallback *callback, const FString &fileName)
|
||||
{
|
||||
return SendMessageError(callback, GetLastErrorMessage(), fileName);
|
||||
}
|
||||
|
||||
static HRESULT CopyFile_Ask(
|
||||
CCopyState &state,
|
||||
const FString &srcPath,
|
||||
const CFileInfo &srcFileInfo,
|
||||
const FString &destPath)
|
||||
{
|
||||
if (CompareFileNames(destPath, srcPath) == 0)
|
||||
{
|
||||
RINOK(SendMessageError(state.Callback,
|
||||
state.MoveMode ?
|
||||
"Cannot move file onto itself" :
|
||||
"Cannot copy file onto itself"
|
||||
, destPath))
|
||||
return E_ABORT;
|
||||
}
|
||||
|
||||
Int32 writeAskResult;
|
||||
CMyComBSTR destPathResult;
|
||||
RINOK(state.Callback->AskWrite(
|
||||
fs2us(srcPath),
|
||||
BoolToInt(false),
|
||||
&srcFileInfo.MTime, &srcFileInfo.Size,
|
||||
fs2us(destPath),
|
||||
&destPathResult,
|
||||
&writeAskResult))
|
||||
|
||||
if (IntToBool(writeAskResult))
|
||||
{
|
||||
FString destPathNew = us2fs((LPCOLESTR)destPathResult);
|
||||
RINOK(state.Callback->SetCurrentFilePath(fs2us(srcPath)))
|
||||
|
||||
if (state.UseReadWriteMode)
|
||||
{
|
||||
NFsFolder::CCopyStateIO state2;
|
||||
state2.Progress = state.Callback;
|
||||
state2.DeleteSrcFile = state.MoveMode;
|
||||
state2.TotalSize = state.ProgressInfo.TotalSize;
|
||||
state2.StartPos = state.ProgressInfo.StartPos;
|
||||
|
||||
RINOK(state2.MyCopyFile(srcPath, destPathNew,
|
||||
state.IsAltStreamsDest ? INVALID_FILE_ATTRIBUTES: srcFileInfo.Attrib))
|
||||
|
||||
if (state2.ErrorFileIndex >= 0)
|
||||
{
|
||||
if (state2.ErrorMessage.IsEmpty())
|
||||
state2.ErrorMessage = GetLastErrorMessage();
|
||||
FString errorName;
|
||||
if (state2.ErrorFileIndex == 0)
|
||||
errorName = srcPath;
|
||||
else
|
||||
errorName = destPathNew;
|
||||
RINOK(SendMessageError(state.Callback, state2.ErrorMessage, errorName))
|
||||
return E_ABORT;
|
||||
}
|
||||
state.ProgressInfo.StartPos += state2.CurrentSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
state.ProgressInfo.FileSize = srcFileInfo.Size;
|
||||
bool res;
|
||||
if (state.MoveMode)
|
||||
res = state.MoveFile_Sys(srcPath, destPathNew);
|
||||
else
|
||||
res = state.CopyFile_Sys(srcPath, destPathNew);
|
||||
RINOK(state.ProgressInfo.ProgressResult)
|
||||
if (!res)
|
||||
{
|
||||
const DWORD errorCode = GetLastError();
|
||||
UString errorMessage = NError::MyFormatMessage(Return_LastError_or_FAIL());
|
||||
if (errorCode == ERROR_INVALID_PARAMETER)
|
||||
{
|
||||
NFind::CFileInfo fi;
|
||||
if (fi.Find(srcPath) &&
|
||||
fi.Size > (UInt32)(Int32)-1)
|
||||
{
|
||||
// bool isFsDetected = false;
|
||||
// if (NSystem::Is_File_LimitedBy_4GB(destPathNew, isFsDetected) || !isFsDetected)
|
||||
errorMessage += " File size exceeds 4 GB";
|
||||
}
|
||||
}
|
||||
|
||||
// GetLastError() is ERROR_REQUEST_ABORTED in case of PROGRESS_CANCEL.
|
||||
RINOK(SendMessageError(state.Callback, errorMessage, destPathNew))
|
||||
return E_ABORT;
|
||||
}
|
||||
state.ProgressInfo.StartPos += state.ProgressInfo.FileSize;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (state.ProgressInfo.TotalSize >= srcFileInfo.Size)
|
||||
{
|
||||
state.ProgressInfo.TotalSize -= srcFileInfo.Size;
|
||||
RINOK(state.ProgressInfo.Progress->SetTotal(state.ProgressInfo.TotalSize))
|
||||
}
|
||||
}
|
||||
return state.CallProgress();
|
||||
}
|
||||
|
||||
static FString CombinePath(const FString &folderPath, const FString &fileName)
|
||||
{
|
||||
FString s (folderPath);
|
||||
s.Add_PathSepar(); // FCHAR_PATH_SEPARATOR
|
||||
s += fileName;
|
||||
return s;
|
||||
}
|
||||
|
||||
static bool IsDestChild(const FString &src, const FString &dest)
|
||||
{
|
||||
unsigned len = src.Len();
|
||||
if (dest.Len() < len)
|
||||
return false;
|
||||
if (dest.Len() != len && dest[len] != FCHAR_PATH_SEPARATOR)
|
||||
return false;
|
||||
return CompareFileNames(dest.Left(len), src) == 0;
|
||||
}
|
||||
|
||||
static HRESULT CopyFolder(
|
||||
CCopyState &state,
|
||||
const FString &srcPath, // without TAIL separator
|
||||
const FString &destPath) // without TAIL separator
|
||||
{
|
||||
RINOK(state.CallProgress())
|
||||
|
||||
if (IsDestChild(srcPath, destPath))
|
||||
{
|
||||
RINOK(SendMessageError(state.Callback,
|
||||
state.MoveMode ?
|
||||
"Cannot copy folder onto itself" :
|
||||
"Cannot move folder onto itself"
|
||||
, destPath))
|
||||
return E_ABORT;
|
||||
}
|
||||
|
||||
if (state.MoveMode)
|
||||
{
|
||||
if (state.MoveFile_Sys(srcPath, destPath))
|
||||
return S_OK;
|
||||
|
||||
// MSDN: MoveFile() fails for dirs on different volumes.
|
||||
}
|
||||
|
||||
if (!CreateComplexDir(destPath))
|
||||
{
|
||||
RINOK(SendMessageError(state.Callback, "Cannot create folder", destPath))
|
||||
return E_ABORT;
|
||||
}
|
||||
|
||||
CEnumerator enumerator;
|
||||
enumerator.SetDirPrefix(CombinePath(srcPath, FString()));
|
||||
|
||||
for (;;)
|
||||
{
|
||||
NFind::CFileInfo fi;
|
||||
bool found;
|
||||
if (!enumerator.Next(fi, found))
|
||||
{
|
||||
SendLastErrorMessage(state.Callback, srcPath);
|
||||
return S_OK;
|
||||
}
|
||||
if (!found)
|
||||
break;
|
||||
const FString srcPath2 = CombinePath(srcPath, fi.Name);
|
||||
const FString destPath2 = CombinePath(destPath, fi.Name);
|
||||
if (fi.IsDir())
|
||||
{
|
||||
RINOK(CopyFolder(state, srcPath2, destPath2))
|
||||
}
|
||||
else
|
||||
{
|
||||
RINOK(CopyFile_Ask(state, srcPath2, fi, destPath2))
|
||||
}
|
||||
}
|
||||
|
||||
if (state.MoveMode)
|
||||
{
|
||||
if (!RemoveDir(srcPath))
|
||||
{
|
||||
RINOK(SendMessageError(state.Callback, "Cannot remove folder", srcPath))
|
||||
return E_ABORT;
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
Z7_COM7F_IMF(CFSFolder::CopyTo(Int32 moveMode, const UInt32 *indices, UInt32 numItems,
|
||||
Int32 /* includeAltStreams */, Int32 /* replaceAltStreamColon */,
|
||||
const wchar_t *path, IFolderOperationsExtractCallback *callback))
|
||||
{
|
||||
if (numItems == 0)
|
||||
return S_OK;
|
||||
|
||||
const FString destPath = us2fs(path);
|
||||
if (destPath.IsEmpty())
|
||||
return E_INVALIDARG;
|
||||
|
||||
const bool isAltDest = NName::IsAltPathPrefix(destPath);
|
||||
const bool isDirectPath = (!isAltDest && !IsPathSepar(destPath.Back()));
|
||||
|
||||
if (isDirectPath)
|
||||
if (numItems > 1)
|
||||
return E_INVALIDARG;
|
||||
|
||||
CFsFolderStat stat;
|
||||
stat.Progress = callback;
|
||||
|
||||
UInt32 i;
|
||||
for (i = 0; i < numItems; i++)
|
||||
{
|
||||
const UInt32 index = indices[i];
|
||||
/*
|
||||
if (index >= Files.Size())
|
||||
{
|
||||
size += Streams[index - Files.Size()].Size;
|
||||
// numFiles++;
|
||||
continue;
|
||||
}
|
||||
*/
|
||||
const CDirItem &fi = Files[index];
|
||||
if (fi.IsDir())
|
||||
{
|
||||
if (!isAltDest)
|
||||
{
|
||||
stat.Path = _path;
|
||||
stat.Path += GetRelPath(fi);
|
||||
RINOK(stat.Enumerate())
|
||||
}
|
||||
stat.NumFolders++;
|
||||
}
|
||||
else
|
||||
{
|
||||
stat.NumFiles++;
|
||||
stat.Size += fi.Size;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if (stat.NumFolders != 0 && isAltDest)
|
||||
return E_NOTIMPL;
|
||||
*/
|
||||
|
||||
RINOK(callback->SetTotal(stat.Size))
|
||||
RINOK(callback->SetNumFiles(stat.NumFiles))
|
||||
|
||||
UInt64 completedSize = 0;
|
||||
RINOK(callback->SetCompleted(&completedSize))
|
||||
|
||||
CCopyState state;
|
||||
state.ProgressInfo.TotalSize = stat.Size;
|
||||
state.ProgressInfo.StartPos = 0;
|
||||
state.ProgressInfo.Progress = callback;
|
||||
state.ProgressInfo.Init();
|
||||
state.Callback = callback;
|
||||
state.MoveMode = IntToBool(moveMode);
|
||||
state.IsAltStreamsDest = isAltDest;
|
||||
/* CopyFileW(fromFile, toFile:altStream) returns ERROR_INVALID_PARAMETER,
|
||||
if there are alt streams in fromFile.
|
||||
So we don't use CopyFileW() for alt Streams. */
|
||||
state.UseReadWriteMode = isAltDest;
|
||||
|
||||
for (i = 0; i < numItems; i++)
|
||||
{
|
||||
const UInt32 index = indices[i];
|
||||
if (index >= (UInt32)Files.Size())
|
||||
continue;
|
||||
const CDirItem &fi = Files[index];
|
||||
FString destPath2 = destPath;
|
||||
if (!isDirectPath)
|
||||
destPath2 += fi.Name;
|
||||
FString srcPath;
|
||||
GetFullPath(fi, srcPath);
|
||||
|
||||
if (fi.IsDir())
|
||||
{
|
||||
if (isAltDest)
|
||||
{
|
||||
RINOK(SendMessageError(callback, k_CannotCopyDirToAltStream, srcPath))
|
||||
}
|
||||
else
|
||||
{
|
||||
RINOK(CopyFolder(state, srcPath, destPath2))
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RINOK(CopyFile_Ask(state, srcPath, fi, destPath2))
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* we can call CopyFileSystemItems() from CDropTarget::Drop() */
|
||||
|
||||
HRESULT CopyFileSystemItems(
|
||||
const UStringVector &itemsPaths,
|
||||
const FString &destDirPrefix,
|
||||
bool moveMode,
|
||||
IFolderOperationsExtractCallback *callback)
|
||||
{
|
||||
if (itemsPaths.IsEmpty())
|
||||
return S_OK;
|
||||
|
||||
if (destDirPrefix.IsEmpty())
|
||||
return E_INVALIDARG;
|
||||
|
||||
const bool isAltDest = NName::IsAltPathPrefix(destDirPrefix);
|
||||
|
||||
CFsFolderStat stat;
|
||||
stat.Progress = callback;
|
||||
|
||||
{
|
||||
FOR_VECTOR (i, itemsPaths)
|
||||
{
|
||||
const UString &path = itemsPaths[i];
|
||||
CFileInfo fi;
|
||||
if (!fi.Find(us2fs(path)))
|
||||
continue;
|
||||
if (fi.IsDir())
|
||||
{
|
||||
if (!isAltDest)
|
||||
{
|
||||
stat.Path = us2fs(path);
|
||||
RINOK(stat.Enumerate())
|
||||
}
|
||||
stat.NumFolders++;
|
||||
}
|
||||
else
|
||||
{
|
||||
stat.NumFiles++;
|
||||
stat.Size += fi.Size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if (stat.NumFolders != 0 && isAltDest)
|
||||
return E_NOTIMPL;
|
||||
*/
|
||||
|
||||
RINOK(callback->SetTotal(stat.Size))
|
||||
// RINOK(progress->SetNumFiles(stat.NumFiles));
|
||||
|
||||
UInt64 completedSize = 0;
|
||||
RINOK(callback->SetCompleted(&completedSize))
|
||||
|
||||
CCopyState state;
|
||||
state.ProgressInfo.TotalSize = stat.Size;
|
||||
state.ProgressInfo.StartPos = 0;
|
||||
state.ProgressInfo.Progress = callback;
|
||||
state.ProgressInfo.Init();
|
||||
state.Callback = callback;
|
||||
state.MoveMode = moveMode;
|
||||
state.IsAltStreamsDest = isAltDest;
|
||||
/* CopyFileW(fromFile, toFile:altStream) returns ERROR_INVALID_PARAMETER,
|
||||
if there are alt streams in fromFile.
|
||||
So we don't use CopyFileW() for alt Streams. */
|
||||
state.UseReadWriteMode = isAltDest;
|
||||
|
||||
FOR_VECTOR (i, itemsPaths)
|
||||
{
|
||||
const UString path = itemsPaths[i];
|
||||
CFileInfo fi;
|
||||
|
||||
if (!fi.Find(us2fs(path)))
|
||||
{
|
||||
RINOK(SendMessageError(callback, "Cannot find the file", us2fs(path)))
|
||||
continue;
|
||||
}
|
||||
|
||||
FString destPath = destDirPrefix;
|
||||
destPath += fi.Name;
|
||||
|
||||
if (fi.IsDir())
|
||||
{
|
||||
if (isAltDest)
|
||||
{
|
||||
RINOK(SendMessageError(callback, k_CannotCopyDirToAltStream, us2fs(path)))
|
||||
}
|
||||
else
|
||||
{
|
||||
RINOK(CopyFolder(state, us2fs(path), destPath))
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RINOK(CopyFile_Ask(state, us2fs(path), fi, destPath))
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
/* we don't use CFSFolder::CopyFrom() because the caller of CopyFrom()
|
||||
is optimized for IFolderArchiveUpdateCallback interface,
|
||||
but we want to use IFolderOperationsExtractCallback interface instead */
|
||||
|
||||
Z7_COM7F_IMF(CFSFolder::CopyFrom(Int32 /* moveMode */, const wchar_t * /* fromFolderPath */,
|
||||
const wchar_t * const * /* itemsPaths */, UInt32 /* numItems */, IProgress * /* progress */))
|
||||
{
|
||||
/*
|
||||
Z7_DECL_CMyComPtr_QI_FROM(
|
||||
IFolderOperationsExtractCallback,
|
||||
callback, progress)
|
||||
if (!callback)
|
||||
return E_NOTIMPL;
|
||||
return CopyFileSystemItems(_path,
|
||||
moveMode, fromDirPrefix,
|
||||
itemsPaths, numItems, callback);
|
||||
*/
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
Z7_COM7F_IMF(CFSFolder::CopyFromFile(UInt32 /* index */, const wchar_t * /* fullFilePath */, IProgress * /* progress */))
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user