|
|
@@ -1844,16 +1844,6 @@ struct TOpenRemoteFileParams
|
|
|
TOverwriteFileParams * FileParams;
|
|
|
bool Confirmed;
|
|
|
};
|
|
|
-//---------------------------------------------------------------------------
|
|
|
-struct TSinkFileParams
|
|
|
-{
|
|
|
- UnicodeString TargetDir;
|
|
|
- const TCopyParamType * CopyParam;
|
|
|
- int Params;
|
|
|
- TFileOperationProgressType * OperationProgress;
|
|
|
- bool Skipped;
|
|
|
- unsigned int Flags;
|
|
|
-};
|
|
|
//===========================================================================
|
|
|
__fastcall TSFTPFileSystem::TSFTPFileSystem(TTerminal * ATerminal,
|
|
|
TSecureShell * SecureShell):
|
|
|
@@ -5110,705 +5100,431 @@ void __fastcall TSFTPFileSystem::CopyToLocal(TStrings * FilesToCopy,
|
|
|
int Params, TFileOperationProgressType * OperationProgress,
|
|
|
TOnceDoneOperation & OnceDoneOperation)
|
|
|
{
|
|
|
- DebugAssert(FilesToCopy && OperationProgress);
|
|
|
-
|
|
|
- UnicodeString FileName;
|
|
|
- UnicodeString FullTargetDir = IncludeTrailingBackslash(TargetDir);
|
|
|
- const TRemoteFile * File;
|
|
|
- bool Success;
|
|
|
- int Index = 0;
|
|
|
- while (Index < FilesToCopy->Count && !OperationProgress->Cancel)
|
|
|
- {
|
|
|
- Success = false;
|
|
|
- FileName = FilesToCopy->Strings[Index];
|
|
|
- File = (TRemoteFile *)FilesToCopy->Objects[Index];
|
|
|
-
|
|
|
- DebugAssert(!FAvoidBusy);
|
|
|
- FAvoidBusy = true;
|
|
|
-
|
|
|
- try
|
|
|
- {
|
|
|
- try
|
|
|
- {
|
|
|
- SFTPSinkRobust(LocalCanonify(FileName), File, FullTargetDir, CopyParam,
|
|
|
- Params, OperationProgress, tfFirstLevel);
|
|
|
- Success = true;
|
|
|
- }
|
|
|
- catch(EScpSkipFile & E)
|
|
|
- {
|
|
|
- TSuspendFileOperationProgress Suspend(OperationProgress);
|
|
|
- if (!FTerminal->HandleException(&E))
|
|
|
- {
|
|
|
- throw;
|
|
|
- }
|
|
|
- }
|
|
|
- catch(...)
|
|
|
- {
|
|
|
- // TODO: remove the block?
|
|
|
- throw;
|
|
|
- }
|
|
|
- }
|
|
|
- __finally
|
|
|
- {
|
|
|
- FAvoidBusy = false;
|
|
|
- OperationProgress->Finish(FileName, Success, OnceDoneOperation);
|
|
|
- }
|
|
|
- Index++;
|
|
|
- }
|
|
|
+ TAutoFlag AvoidBusyFlag(FAvoidBusy);
|
|
|
+ FTerminal->DoCopyToLocal(FilesToCopy, TargetDir, CopyParam, Params, OperationProgress, tfNone, OnceDoneOperation);
|
|
|
}
|
|
|
//---------------------------------------------------------------------------
|
|
|
-void __fastcall TSFTPFileSystem::SFTPSinkRobust(const UnicodeString FileName,
|
|
|
- const TRemoteFile * File, const UnicodeString TargetDir,
|
|
|
- const TCopyParamType * CopyParam, int Params,
|
|
|
- TFileOperationProgressType * OperationProgress, unsigned int Flags)
|
|
|
+void __fastcall TSFTPFileSystem::DirectorySunk(
|
|
|
+ const UnicodeString & DestFullName, const TRemoteFile * File, const TCopyParamType * CopyParam)
|
|
|
{
|
|
|
- // the same in TFTPFileSystem
|
|
|
-
|
|
|
- TDownloadSessionAction Action(FTerminal->ActionLog);
|
|
|
- TRobustOperationLoop RobustLoop(FTerminal, OperationProgress);
|
|
|
-
|
|
|
- do
|
|
|
+ if (CopyParam->PreserveTime && CopyParam->PreserveTimeDirs)
|
|
|
{
|
|
|
- bool ChildError = false;
|
|
|
- try
|
|
|
- {
|
|
|
- SFTPSink(FileName, File, TargetDir, CopyParam, Params, OperationProgress,
|
|
|
- Flags, Action, ChildError);
|
|
|
- }
|
|
|
- catch(Exception & E)
|
|
|
+ // FILE_FLAG_BACKUP_SEMANTICS is needed to "open" directory
|
|
|
+ HANDLE LocalHandle =
|
|
|
+ CreateFile(
|
|
|
+ ApiPath(DestFullName).c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
|
|
|
+ FILE_FLAG_BACKUP_SEMANTICS, 0);
|
|
|
+
|
|
|
+ if (LocalHandle == INVALID_HANDLE_VALUE)
|
|
|
{
|
|
|
- if (!RobustLoop.TryReopen(E))
|
|
|
- {
|
|
|
- if (!ChildError)
|
|
|
- {
|
|
|
- FTerminal->RollbackAction(Action, OperationProgress, &E);
|
|
|
- }
|
|
|
- throw;
|
|
|
- }
|
|
|
+ int SetFileTimeError = GetLastError();
|
|
|
+ FTerminal->LogEvent(
|
|
|
+ FORMAT(L"Preserving directory timestamp failed, ignoring: %s", (SysErrorMessageForError(SetFileTimeError))));
|
|
|
}
|
|
|
-
|
|
|
- if (RobustLoop.ShouldRetry())
|
|
|
+ else
|
|
|
{
|
|
|
- OperationProgress->RollbackTransfer();
|
|
|
- Action.Restart();
|
|
|
- DebugAssert(File != NULL);
|
|
|
- if (!File->IsDirectory)
|
|
|
- {
|
|
|
- // prevent overwrite and resume confirmations
|
|
|
- Params |= cpNoConfirmation;
|
|
|
- }
|
|
|
+ FTerminal->UpdateTargetTime(LocalHandle, File->Modification, FTerminal->SessionData->DSTMode);
|
|
|
+ CloseHandle(LocalHandle);
|
|
|
}
|
|
|
}
|
|
|
- while (RobustLoop.Retry());
|
|
|
}
|
|
|
//---------------------------------------------------------------------------
|
|
|
-void __fastcall TSFTPFileSystem::SFTPSink(const UnicodeString FileName,
|
|
|
- const TRemoteFile * File, const UnicodeString TargetDir,
|
|
|
- const TCopyParamType * CopyParam, int Params,
|
|
|
- TFileOperationProgressType * OperationProgress, unsigned int Flags,
|
|
|
- TDownloadSessionAction & Action, bool & ChildError)
|
|
|
+void __fastcall TSFTPFileSystem::Sink(
|
|
|
+ const UnicodeString & FileName, const TRemoteFile * File,
|
|
|
+ const UnicodeString & TargetDir, UnicodeString & DestFileName, int Attrs,
|
|
|
+ const TCopyParamType * CopyParam, int Params, TFileOperationProgressType * OperationProgress,
|
|
|
+ unsigned int /*Flags*/, TDownloadSessionAction & Action)
|
|
|
{
|
|
|
-
|
|
|
- Action.FileName(FileName);
|
|
|
-
|
|
|
- UnicodeString OnlyFileName = UnixExtractFileName(FileName);
|
|
|
-
|
|
|
- TFileMasks::TParams MaskParams;
|
|
|
- DebugAssert(File);
|
|
|
- MaskParams.Size = File->Size;
|
|
|
- MaskParams.Modification = File->Modification;
|
|
|
-
|
|
|
- UnicodeString BaseFileName = FTerminal->GetBaseFileName(FileName);
|
|
|
- if (!CopyParam->AllowTransfer(BaseFileName, osRemote, File->IsDirectory, MaskParams))
|
|
|
- {
|
|
|
- FTerminal->LogEvent(FORMAT(L"File \"%s\" excluded from transfer", (FileName)));
|
|
|
- THROW_SKIP_FILE_NULL;
|
|
|
- }
|
|
|
-
|
|
|
- if (CopyParam->SkipTransfer(FileName, File->IsDirectory))
|
|
|
- {
|
|
|
- OperationProgress->AddSkippedFileSize(File->Size);
|
|
|
- THROW_SKIP_FILE_NULL;
|
|
|
- }
|
|
|
-
|
|
|
- FTerminal->LogFileDetails(FileName, File->Modification, File->Size);
|
|
|
-
|
|
|
- OperationProgress->SetFile(FileName);
|
|
|
-
|
|
|
- UnicodeString DestFileName =
|
|
|
- FTerminal->ChangeFileName(
|
|
|
- CopyParam, OnlyFileName, osRemote, FLAGSET(Flags, tfFirstLevel));
|
|
|
+ // resume has no sense for temporary downloads
|
|
|
+ bool ResumeAllowed =
|
|
|
+ FLAGCLEAR(Params, cpTemporary) &&
|
|
|
+ !OperationProgress->AsciiTransfer &&
|
|
|
+ CopyParam->AllowResume(OperationProgress->TransferSize);
|
|
|
+
|
|
|
+ HANDLE LocalHandle = NULL;
|
|
|
+ TStream * FileStream = NULL;
|
|
|
+ bool DeleteLocalFile = false;
|
|
|
+ RawByteString RemoteHandle;
|
|
|
UnicodeString DestFullName = TargetDir + DestFileName;
|
|
|
+ UnicodeString LocalFileName = DestFullName;
|
|
|
+ TSFTPOverwriteMode OverwriteMode = omOverwrite;
|
|
|
|
|
|
- if (File->IsDirectory)
|
|
|
+ try
|
|
|
{
|
|
|
- Action.Cancel();
|
|
|
- if (FTerminal->CanRecurseToDirectory(File))
|
|
|
- {
|
|
|
- FILE_OPERATION_LOOP_BEGIN
|
|
|
- {
|
|
|
- int Attrs = FileGetAttrFix(ApiPath(DestFullName));
|
|
|
- if ((Attrs & faDirectory) == 0) EXCEPTION;
|
|
|
- }
|
|
|
- FILE_OPERATION_LOOP_END(FMTLOAD(NOT_DIRECTORY_ERROR, (DestFullName)));
|
|
|
+ bool ResumeTransfer = false;
|
|
|
+ UnicodeString DestPartialFullName;
|
|
|
|
|
|
- FILE_OPERATION_LOOP_BEGIN
|
|
|
- {
|
|
|
- THROWOSIFFALSE(ForceDirectories(ApiPath(DestFullName)));
|
|
|
- }
|
|
|
- FILE_OPERATION_LOOP_END(FMTLOAD(CREATE_DIR_ERROR, (DestFullName)));
|
|
|
+ if (ResumeAllowed)
|
|
|
+ {
|
|
|
+ DestPartialFullName = DestFullName + FTerminal->Configuration->PartialExt;
|
|
|
+ LocalFileName = DestPartialFullName;
|
|
|
|
|
|
- if (FLAGCLEAR(Params, cpNoRecurse))
|
|
|
+ FTerminal->LogEvent(L"Checking existence of partially transferred file.");
|
|
|
+ if (FileExists(ApiPath(DestPartialFullName)))
|
|
|
{
|
|
|
- TSinkFileParams SinkFileParams;
|
|
|
- SinkFileParams.TargetDir = IncludeTrailingBackslash(DestFullName);
|
|
|
- SinkFileParams.CopyParam = CopyParam;
|
|
|
- SinkFileParams.Params = Params;
|
|
|
- SinkFileParams.OperationProgress = OperationProgress;
|
|
|
- SinkFileParams.Skipped = false;
|
|
|
- SinkFileParams.Flags = Flags & ~tfFirstLevel;
|
|
|
-
|
|
|
- FTerminal->ProcessDirectory(FileName, SFTPSinkFile, &SinkFileParams);
|
|
|
+ FTerminal->LogEvent(L"Partially transferred file exists.");
|
|
|
+ __int64 ResumeOffset;
|
|
|
+ FTerminal->OpenLocalFile(DestPartialFullName, GENERIC_WRITE,
|
|
|
+ NULL, &LocalHandle, NULL, NULL, NULL, &ResumeOffset);
|
|
|
|
|
|
- if (CopyParam->PreserveTime && CopyParam->PreserveTimeDirs)
|
|
|
+ bool PartialBiggerThanSource = (ResumeOffset > OperationProgress->TransferSize);
|
|
|
+ if (FLAGCLEAR(Params, cpNoConfirmation))
|
|
|
{
|
|
|
- FTerminal->LogEvent(FORMAT(L"Preserving directory timestamp [%s]",
|
|
|
- (StandardTimestamp(File->Modification))));
|
|
|
- int SetFileTimeError = ERROR_SUCCESS;
|
|
|
- // FILE_FLAG_BACKUP_SEMANTICS is needed to "open" directory
|
|
|
- HANDLE LocalHandle = CreateFile(ApiPath(DestFullName).c_str(), GENERIC_WRITE,
|
|
|
- FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
|
|
|
- if (LocalHandle == INVALID_HANDLE_VALUE)
|
|
|
- {
|
|
|
- SetFileTimeError = GetLastError();
|
|
|
- }
|
|
|
- else
|
|
|
+ ResumeTransfer = SFTPConfirmResume(DestFileName, PartialBiggerThanSource, OperationProgress);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ ResumeTransfer = !PartialBiggerThanSource;
|
|
|
+ if (!ResumeTransfer)
|
|
|
{
|
|
|
- FILETIME AcTime = DateTimeToFileTime(File->LastAccess, FTerminal->SessionData->DSTMode);
|
|
|
- FILETIME WrTime = DateTimeToFileTime(File->Modification, FTerminal->SessionData->DSTMode);
|
|
|
- if (!SetFileTime(LocalHandle, NULL, &AcTime, &WrTime))
|
|
|
- {
|
|
|
- SetFileTimeError = GetLastError();
|
|
|
- }
|
|
|
- CloseHandle(LocalHandle);
|
|
|
+ FTerminal->LogEvent(L"Partially transferred file is bigger that original file.");
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- if (SetFileTimeError != ERROR_SUCCESS)
|
|
|
+ if (!ResumeTransfer)
|
|
|
+ {
|
|
|
+ CloseHandle(LocalHandle);
|
|
|
+ LocalHandle = NULL;
|
|
|
+ FILE_OPERATION_LOOP_BEGIN
|
|
|
{
|
|
|
- FTerminal->LogEvent(FORMAT(L"Preserving timestamp failed, ignoring: %s",
|
|
|
- (SysErrorMessageForError(SetFileTimeError))));
|
|
|
+ THROWOSIFFALSE(Sysutils::DeleteFile(ApiPath(DestPartialFullName)));
|
|
|
}
|
|
|
+ FILE_OPERATION_LOOP_END(FMTLOAD(DELETE_LOCAL_FILE_ERROR, (DestPartialFullName)));
|
|
|
}
|
|
|
-
|
|
|
- // Do not delete directory if some of its files were skip.
|
|
|
- // Throw "skip file" for the directory to avoid attempt to deletion
|
|
|
- // of any parent directory
|
|
|
- if ((Params & cpDelete) && SinkFileParams.Skipped)
|
|
|
+ else
|
|
|
{
|
|
|
- THROW_SKIP_FILE_NULL;
|
|
|
+ FTerminal->LogEvent(L"Resuming file transfer.");
|
|
|
+ FileSeek((THandle)LocalHandle, ResumeOffset, 0);
|
|
|
+ OperationProgress->AddResumed(ResumeOffset);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ OperationProgress->Progress();
|
|
|
}
|
|
|
- else
|
|
|
+
|
|
|
+ // first open source file, not to loose the destination file,
|
|
|
+ // if we cannot open the source one in the first place
|
|
|
+ FTerminal->LogEvent(L"Opening remote file.");
|
|
|
+ FILE_OPERATION_LOOP_BEGIN
|
|
|
{
|
|
|
- FTerminal->LogEvent(FORMAT(L"Skipping symlink to directory \"%s\".", (FileName)));
|
|
|
+ int OpenType = SSH_FXF_READ;
|
|
|
+ if ((FVersion >= 4) && OperationProgress->AsciiTransfer)
|
|
|
+ {
|
|
|
+ OpenType |= SSH_FXF_TEXT;
|
|
|
+ }
|
|
|
+ RemoteHandle = SFTPOpenRemoteFile(FileName, OpenType);
|
|
|
+ OperationProgress->Progress();
|
|
|
}
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- FTerminal->LogEvent(FORMAT(L"Copying \"%s\" to local directory started.", (FileName)));
|
|
|
-
|
|
|
- UnicodeString DestPartialFullName;
|
|
|
- bool ResumeAllowed;
|
|
|
- bool ResumeTransfer = false;
|
|
|
- __int64 ResumeOffset;
|
|
|
+ FILE_OPERATION_LOOP_END(FMTLOAD(SFTP_OPEN_FILE_ERROR, (FileName)));
|
|
|
|
|
|
- // Will we use ASCII of BINARY file transfer?
|
|
|
- OperationProgress->SetAsciiTransfer(
|
|
|
- CopyParam->UseAsciiTransfer(BaseFileName, osRemote, MaskParams));
|
|
|
- FTerminal->LogEvent(UnicodeString((OperationProgress->AsciiTransfer ? L"Ascii" : L"Binary")) +
|
|
|
- " transfer mode selected.");
|
|
|
+ TSFTPPacket RemoteFilePacket(SSH_FXP_FSTAT);
|
|
|
+ RemoteFilePacket.AddString(RemoteHandle);
|
|
|
+ SendCustomReadFile(&RemoteFilePacket, &RemoteFilePacket, SSH_FILEXFER_ATTR_MODIFYTIME);
|
|
|
+ ReceiveResponse(&RemoteFilePacket, &RemoteFilePacket);
|
|
|
+ OperationProgress->Progress();
|
|
|
|
|
|
- // Suppose same data size to transfer as to write
|
|
|
- // (not true with ASCII transfer)
|
|
|
- OperationProgress->SetTransferSize(File->Size);
|
|
|
- OperationProgress->SetLocalSize(OperationProgress->TransferSize);
|
|
|
-
|
|
|
- // resume has no sense for temporary downloads
|
|
|
- ResumeAllowed = ((Params & cpTemporary) == 0) &&
|
|
|
- !OperationProgress->AsciiTransfer &&
|
|
|
- CopyParam->AllowResume(OperationProgress->TransferSize);
|
|
|
-
|
|
|
- int Attrs;
|
|
|
- FILE_OPERATION_LOOP_BEGIN
|
|
|
+ TDateTime Modification = File->Modification; // fallback
|
|
|
+ // ignore errors
|
|
|
+ if (RemoteFilePacket.Type == SSH_FXP_ATTRS)
|
|
|
{
|
|
|
- Attrs = FileGetAttrFix(ApiPath(DestFullName));
|
|
|
- if ((Attrs >= 0) && (Attrs & faDirectory)) EXCEPTION;
|
|
|
+ // load file, avoid completion (resolving symlinks) as we do not need that
|
|
|
+ std::unique_ptr<TRemoteFile> AFile(
|
|
|
+ LoadFile(&RemoteFilePacket, NULL, UnixExtractFileName(FileName), NULL, false));
|
|
|
+ if (AFile->Modification != TDateTime())
|
|
|
+ {
|
|
|
+ Modification = File->Modification;
|
|
|
+ }
|
|
|
}
|
|
|
- FILE_OPERATION_LOOP_END(FMTLOAD(NOT_FILE_ERROR, (DestFullName)));
|
|
|
|
|
|
- HANDLE LocalHandle = NULL;
|
|
|
- TStream * FileStream = NULL;
|
|
|
- bool DeleteLocalFile = false;
|
|
|
- RawByteString RemoteHandle;
|
|
|
- UnicodeString LocalFileName = DestFullName;
|
|
|
- TSFTPOverwriteMode OverwriteMode = omOverwrite;
|
|
|
- UnicodeString ExpandedDestFullName;
|
|
|
-
|
|
|
- try
|
|
|
+ if ((Attrs >= 0) && !ResumeTransfer)
|
|
|
{
|
|
|
- if (ResumeAllowed)
|
|
|
+ __int64 DestFileSize;
|
|
|
+ __int64 MTime;
|
|
|
+ FTerminal->OpenLocalFile(
|
|
|
+ DestFullName, GENERIC_WRITE, NULL, &LocalHandle, NULL, &MTime, NULL, &DestFileSize, false);
|
|
|
+
|
|
|
+ FTerminal->LogEvent(L"Confirming overwriting of file.");
|
|
|
+ TOverwriteFileParams FileParams;
|
|
|
+ FileParams.SourceSize = OperationProgress->TransferSize;
|
|
|
+ FileParams.SourceTimestamp = Modification;
|
|
|
+ FileParams.DestTimestamp = UnixToDateTime(MTime,
|
|
|
+ FTerminal->SessionData->DSTMode);
|
|
|
+ FileParams.DestSize = DestFileSize;
|
|
|
+ UnicodeString PrevDestFileName = DestFileName;
|
|
|
+ SFTPConfirmOverwrite(FileName, DestFileName, CopyParam, Params, OperationProgress, OverwriteMode, &FileParams);
|
|
|
+ if (PrevDestFileName != DestFileName)
|
|
|
{
|
|
|
+ DestFullName = TargetDir + DestFileName;
|
|
|
DestPartialFullName = DestFullName + FTerminal->Configuration->PartialExt;
|
|
|
- LocalFileName = DestPartialFullName;
|
|
|
-
|
|
|
- FTerminal->LogEvent(L"Checking existence of partially transferred file.");
|
|
|
- if (FileExists(ApiPath(DestPartialFullName)))
|
|
|
+ if (ResumeAllowed)
|
|
|
{
|
|
|
- FTerminal->LogEvent(L"Partially transferred file exists.");
|
|
|
- FTerminal->OpenLocalFile(DestPartialFullName, GENERIC_WRITE,
|
|
|
- NULL, &LocalHandle, NULL, NULL, NULL, &ResumeOffset);
|
|
|
-
|
|
|
- bool PartialBiggerThanSource = (ResumeOffset > OperationProgress->TransferSize);
|
|
|
- if (FLAGCLEAR(Params, cpNoConfirmation))
|
|
|
+ if (FileExists(ApiPath(DestPartialFullName)))
|
|
|
{
|
|
|
- ResumeTransfer = SFTPConfirmResume(DestFileName,
|
|
|
- PartialBiggerThanSource, OperationProgress);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- ResumeTransfer = !PartialBiggerThanSource;
|
|
|
- if (!ResumeTransfer)
|
|
|
- {
|
|
|
- FTerminal->LogEvent(L"Partially transferred file is bigger that original file.");
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (!ResumeTransfer)
|
|
|
- {
|
|
|
- CloseHandle(LocalHandle);
|
|
|
- LocalHandle = NULL;
|
|
|
FILE_OPERATION_LOOP_BEGIN
|
|
|
{
|
|
|
THROWOSIFFALSE(Sysutils::DeleteFile(ApiPath(DestPartialFullName)));
|
|
|
}
|
|
|
FILE_OPERATION_LOOP_END(FMTLOAD(DELETE_LOCAL_FILE_ERROR, (DestPartialFullName)));
|
|
|
}
|
|
|
- else
|
|
|
- {
|
|
|
- FTerminal->LogEvent(L"Resuming file transfer.");
|
|
|
- FileSeek((THandle)LocalHandle, ResumeOffset, 0);
|
|
|
- OperationProgress->AddResumed(ResumeOffset);
|
|
|
- }
|
|
|
+ LocalFileName = DestPartialFullName;
|
|
|
}
|
|
|
-
|
|
|
- OperationProgress->Progress();
|
|
|
- }
|
|
|
-
|
|
|
- // first open source file, not to loose the destination file,
|
|
|
- // if we cannot open the source one in the first place
|
|
|
- FTerminal->LogEvent(L"Opening remote file.");
|
|
|
- FILE_OPERATION_LOOP_BEGIN
|
|
|
- {
|
|
|
- int OpenType = SSH_FXF_READ;
|
|
|
- if ((FVersion >= 4) && OperationProgress->AsciiTransfer)
|
|
|
+ else
|
|
|
{
|
|
|
- OpenType |= SSH_FXF_TEXT;
|
|
|
+ LocalFileName = DestFullName;
|
|
|
}
|
|
|
- RemoteHandle = SFTPOpenRemoteFile(FileName, OpenType);
|
|
|
- OperationProgress->Progress();
|
|
|
}
|
|
|
- FILE_OPERATION_LOOP_END(FMTLOAD(SFTP_OPEN_FILE_ERROR, (FileName)));
|
|
|
-
|
|
|
- TDateTime Modification;
|
|
|
- FILETIME AcTime;
|
|
|
- FILETIME WrTime;
|
|
|
|
|
|
- TSFTPPacket RemoteFilePacket(SSH_FXP_FSTAT);
|
|
|
- RemoteFilePacket.AddString(RemoteHandle);
|
|
|
- SendCustomReadFile(&RemoteFilePacket, &RemoteFilePacket,
|
|
|
- SSH_FILEXFER_ATTR_MODIFYTIME);
|
|
|
- ReceiveResponse(&RemoteFilePacket, &RemoteFilePacket);
|
|
|
- OperationProgress->Progress();
|
|
|
-
|
|
|
- const TRemoteFile * AFile = NULL;
|
|
|
- try
|
|
|
+ if (OverwriteMode == omOverwrite)
|
|
|
{
|
|
|
- // ignore errors
|
|
|
- if (RemoteFilePacket.Type == SSH_FXP_ATTRS)
|
|
|
+ // is NULL when overwriting read-only file
|
|
|
+ if (LocalHandle)
|
|
|
{
|
|
|
- // load file, avoid completion (resolving symlinks) as we do not need that
|
|
|
- AFile = LoadFile(&RemoteFilePacket, NULL, UnixExtractFileName(FileName),
|
|
|
- NULL, false);
|
|
|
+ CloseHandle(LocalHandle);
|
|
|
+ LocalHandle = NULL;
|
|
|
}
|
|
|
-
|
|
|
- Modification =
|
|
|
- (AFile != NULL) && (AFile->Modification != TDateTime()) ? AFile->Modification : File->Modification;
|
|
|
- TDateTime LastAccess =
|
|
|
- (AFile != NULL) && (AFile->LastAccess != TDateTime()) ? AFile->LastAccess : File->LastAccess;
|
|
|
- AcTime = DateTimeToFileTime(LastAccess, FTerminal->SessionData->DSTMode);
|
|
|
- WrTime = DateTimeToFileTime(Modification, FTerminal->SessionData->DSTMode);
|
|
|
}
|
|
|
- __finally
|
|
|
- {
|
|
|
- delete AFile;
|
|
|
- }
|
|
|
-
|
|
|
- if ((Attrs >= 0) && !ResumeTransfer)
|
|
|
+ else
|
|
|
{
|
|
|
- __int64 DestFileSize;
|
|
|
- __int64 MTime;
|
|
|
- FTerminal->OpenLocalFile(DestFullName, GENERIC_WRITE,
|
|
|
- NULL, &LocalHandle, NULL, &MTime, NULL, &DestFileSize, false);
|
|
|
-
|
|
|
- FTerminal->LogEvent(L"Confirming overwriting of file.");
|
|
|
- TOverwriteFileParams FileParams;
|
|
|
- FileParams.SourceSize = OperationProgress->TransferSize;
|
|
|
- FileParams.SourceTimestamp = Modification;
|
|
|
- FileParams.DestTimestamp = UnixToDateTime(MTime,
|
|
|
- FTerminal->SessionData->DSTMode);
|
|
|
- FileParams.DestSize = DestFileSize;
|
|
|
- UnicodeString PrevDestFileName = DestFileName;
|
|
|
- SFTPConfirmOverwrite(FileName, DestFileName, CopyParam, Params, OperationProgress, OverwriteMode, &FileParams);
|
|
|
- if (PrevDestFileName != DestFileName)
|
|
|
+ // is NULL when overwriting read-only file, so following will
|
|
|
+ // probably fail anyway
|
|
|
+ if (LocalHandle == NULL)
|
|
|
{
|
|
|
- DestFullName = TargetDir + DestFileName;
|
|
|
- DestPartialFullName = DestFullName + FTerminal->Configuration->PartialExt;
|
|
|
- if (ResumeAllowed)
|
|
|
- {
|
|
|
- if (FileExists(ApiPath(DestPartialFullName)))
|
|
|
- {
|
|
|
- FILE_OPERATION_LOOP_BEGIN
|
|
|
- {
|
|
|
- THROWOSIFFALSE(Sysutils::DeleteFile(ApiPath(DestPartialFullName)));
|
|
|
- }
|
|
|
- FILE_OPERATION_LOOP_END(FMTLOAD(DELETE_LOCAL_FILE_ERROR, (DestPartialFullName)));
|
|
|
- }
|
|
|
- LocalFileName = DestPartialFullName;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- LocalFileName = DestFullName;
|
|
|
- }
|
|
|
+ FTerminal->OpenLocalFile(DestFullName, GENERIC_WRITE, NULL, &LocalHandle, NULL, NULL, NULL, NULL);
|
|
|
}
|
|
|
-
|
|
|
- if (OverwriteMode == omOverwrite)
|
|
|
+ ResumeAllowed = false;
|
|
|
+ FileSeek((THandle)LocalHandle, DestFileSize, 0);
|
|
|
+ if (OverwriteMode == omAppend)
|
|
|
{
|
|
|
- // is NULL when overwriting read-only file
|
|
|
- if (LocalHandle)
|
|
|
- {
|
|
|
- CloseHandle(LocalHandle);
|
|
|
- LocalHandle = NULL;
|
|
|
- }
|
|
|
+ FTerminal->LogEvent(L"Appending to file.");
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- // is NULL when overwriting read-only file, so following will
|
|
|
- // probably fail anyway
|
|
|
- if (LocalHandle == NULL)
|
|
|
- {
|
|
|
- FTerminal->OpenLocalFile(DestFullName, GENERIC_WRITE,
|
|
|
- NULL, &LocalHandle, NULL, NULL, NULL, NULL);
|
|
|
- }
|
|
|
- ResumeAllowed = false;
|
|
|
- FileSeek((THandle)LocalHandle, DestFileSize, 0);
|
|
|
- if (OverwriteMode == omAppend)
|
|
|
- {
|
|
|
- FTerminal->LogEvent(L"Appending to file.");
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- FTerminal->LogEvent(L"Resuming file transfer (append style).");
|
|
|
- DebugAssert(OverwriteMode == omResume);
|
|
|
- OperationProgress->AddResumed(DestFileSize);
|
|
|
- }
|
|
|
+ FTerminal->LogEvent(L"Resuming file transfer (append style).");
|
|
|
+ DebugAssert(OverwriteMode == omResume);
|
|
|
+ OperationProgress->AddResumed(DestFileSize);
|
|
|
}
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- ExpandedDestFullName = ExpandUNCFileName(DestFullName);
|
|
|
- Action.Destination(ExpandedDestFullName);
|
|
|
+ Action.Destination(ExpandUNCFileName(DestFullName));
|
|
|
|
|
|
- // if not already opened (resume, append...), create new empty file
|
|
|
- if (!LocalHandle)
|
|
|
+ // if not already opened (resume, append...), create new empty file
|
|
|
+ if (!LocalHandle)
|
|
|
+ {
|
|
|
+ if (!FTerminal->CreateLocalFile(LocalFileName, OperationProgress,
|
|
|
+ &LocalHandle, FLAGSET(Params, cpNoConfirmation)))
|
|
|
{
|
|
|
- if (!FTerminal->CreateLocalFile(LocalFileName, OperationProgress,
|
|
|
- &LocalHandle, FLAGSET(Params, cpNoConfirmation)))
|
|
|
- {
|
|
|
- THROW_SKIP_FILE_NULL;
|
|
|
- }
|
|
|
+ THROW_SKIP_FILE_NULL;
|
|
|
}
|
|
|
- DebugAssert(LocalHandle);
|
|
|
+ }
|
|
|
+ DebugAssert(LocalHandle);
|
|
|
|
|
|
- DeleteLocalFile = true;
|
|
|
+ DeleteLocalFile = true;
|
|
|
|
|
|
- FileStream = new TSafeHandleStream((THandle)LocalHandle);
|
|
|
+ FileStream = new TSafeHandleStream((THandle)LocalHandle);
|
|
|
|
|
|
- // at end of this block queue is discarded
|
|
|
+ // at end of this block queue is discarded
|
|
|
+ {
|
|
|
+ TSFTPDownloadQueue Queue(this);
|
|
|
+ try
|
|
|
{
|
|
|
- TSFTPDownloadQueue Queue(this);
|
|
|
- try
|
|
|
- {
|
|
|
- TSFTPPacket DataPacket;
|
|
|
+ TSFTPPacket DataPacket;
|
|
|
|
|
|
- int QueueLen = int(File->Size / DownloadBlockSize(OperationProgress)) + 1;
|
|
|
- if ((QueueLen > FTerminal->SessionData->SFTPDownloadQueue) ||
|
|
|
- (QueueLen < 0))
|
|
|
+ int QueueLen = int(File->Size / DownloadBlockSize(OperationProgress)) + 1;
|
|
|
+ if ((QueueLen > FTerminal->SessionData->SFTPDownloadQueue) ||
|
|
|
+ (QueueLen < 0))
|
|
|
+ {
|
|
|
+ QueueLen = FTerminal->SessionData->SFTPDownloadQueue;
|
|
|
+ }
|
|
|
+ if (QueueLen < 1)
|
|
|
+ {
|
|
|
+ QueueLen = 1;
|
|
|
+ }
|
|
|
+ Queue.Init(QueueLen, RemoteHandle, OperationProgress->TransferredSize, OperationProgress);
|
|
|
+
|
|
|
+ bool Eof = false;
|
|
|
+ bool PrevIncomplete = false;
|
|
|
+ int GapFillCount = 0;
|
|
|
+ int GapCount = 0;
|
|
|
+ unsigned long Missing = 0;
|
|
|
+ unsigned long DataLen = 0;
|
|
|
+ unsigned long BlockSize;
|
|
|
+ bool ConvertToken = false;
|
|
|
+
|
|
|
+ while (!Eof)
|
|
|
+ {
|
|
|
+ if (Missing > 0)
|
|
|
{
|
|
|
- QueueLen = FTerminal->SessionData->SFTPDownloadQueue;
|
|
|
+ Queue.InitFillGapRequest(OperationProgress->TransferredSize, Missing, &DataPacket);
|
|
|
+ GapFillCount++;
|
|
|
+ SendPacketAndReceiveResponse(&DataPacket, &DataPacket, SSH_FXP_DATA, asEOF);
|
|
|
}
|
|
|
- if (QueueLen < 1)
|
|
|
+ else
|
|
|
{
|
|
|
- QueueLen = 1;
|
|
|
+ Queue.ReceivePacket(&DataPacket, BlockSize);
|
|
|
}
|
|
|
- Queue.Init(QueueLen, RemoteHandle, OperationProgress->TransferredSize,
|
|
|
- OperationProgress);
|
|
|
-
|
|
|
- bool Eof = false;
|
|
|
- bool PrevIncomplete = false;
|
|
|
- int GapFillCount = 0;
|
|
|
- int GapCount = 0;
|
|
|
- unsigned long Missing = 0;
|
|
|
- unsigned long DataLen = 0;
|
|
|
- unsigned long BlockSize;
|
|
|
- bool ConvertToken = false;
|
|
|
-
|
|
|
- while (!Eof)
|
|
|
+
|
|
|
+ if (DataPacket.Type == SSH_FXP_STATUS)
|
|
|
{
|
|
|
- if (Missing > 0)
|
|
|
- {
|
|
|
- Queue.InitFillGapRequest(OperationProgress->TransferredSize, Missing,
|
|
|
- &DataPacket);
|
|
|
- GapFillCount++;
|
|
|
- SendPacketAndReceiveResponse(&DataPacket, &DataPacket,
|
|
|
- SSH_FXP_DATA, asEOF);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- Queue.ReceivePacket(&DataPacket, BlockSize);
|
|
|
- }
|
|
|
+ // must be SSH_FX_EOF, any other status packet would raise exception
|
|
|
+ Eof = true;
|
|
|
+ // close file right away, before waiting for pending responses
|
|
|
+ SFTPCloseRemote(RemoteHandle, DestFileName, OperationProgress, true, true, NULL);
|
|
|
+ RemoteHandle = L""; // do not close file again in __finally block
|
|
|
+ }
|
|
|
|
|
|
- if (DataPacket.Type == SSH_FXP_STATUS)
|
|
|
+ if (!Eof)
|
|
|
+ {
|
|
|
+ if ((Missing == 0) && PrevIncomplete)
|
|
|
{
|
|
|
- // must be SSH_FX_EOF, any other status packet would raise exception
|
|
|
- Eof = true;
|
|
|
- // close file right away, before waiting for pending responses
|
|
|
- SFTPCloseRemote(RemoteHandle, DestFileName, OperationProgress,
|
|
|
- true, true, NULL);
|
|
|
- RemoteHandle = L""; // do not close file again in __finally block
|
|
|
+ // This can happen only if last request returned less bytes
|
|
|
+ // than expected, but exactly number of bytes missing to last
|
|
|
+ // known file size, but actually EOF was not reached.
|
|
|
+ // Can happen only when filesize has changed since directory
|
|
|
+ // listing and server returns less bytes than requested and
|
|
|
+ // file has some special file size.
|
|
|
+ FTerminal->LogEvent(FORMAT(
|
|
|
+ L"Received incomplete data packet before end of file, offset: %s, size: %d, requested: %d",
|
|
|
+ (IntToStr(OperationProgress->TransferredSize), int(DataLen), int(BlockSize))));
|
|
|
+ FTerminal->TerminalError(NULL, LoadStr(SFTP_INCOMPLETE_BEFORE_EOF));
|
|
|
}
|
|
|
|
|
|
- if (!Eof)
|
|
|
- {
|
|
|
- if ((Missing == 0) && PrevIncomplete)
|
|
|
- {
|
|
|
- // This can happen only if last request returned less bytes
|
|
|
- // than expected, but exactly number of bytes missing to last
|
|
|
- // known file size, but actually EOF was not reached.
|
|
|
- // Can happen only when filesize has changed since directory
|
|
|
- // listing and server returns less bytes than requested and
|
|
|
- // file has some special file size.
|
|
|
- FTerminal->LogEvent(FORMAT(
|
|
|
- L"Received incomplete data packet before end of file, "
|
|
|
- "offset: %s, size: %d, requested: %d",
|
|
|
- (IntToStr(OperationProgress->TransferredSize), int(DataLen),
|
|
|
- int(BlockSize))));
|
|
|
- FTerminal->TerminalError(NULL, LoadStr(SFTP_INCOMPLETE_BEFORE_EOF));
|
|
|
- }
|
|
|
-
|
|
|
- // Buffer for one block of data
|
|
|
- TFileBuffer BlockBuf;
|
|
|
+ // Buffer for one block of data
|
|
|
+ TFileBuffer BlockBuf;
|
|
|
|
|
|
- DataLen = DataPacket.GetCardinal();
|
|
|
+ DataLen = DataPacket.GetCardinal();
|
|
|
|
|
|
- PrevIncomplete = false;
|
|
|
- if (Missing > 0)
|
|
|
- {
|
|
|
- DebugAssert(DataLen <= Missing);
|
|
|
- Missing -= DataLen;
|
|
|
- }
|
|
|
- else if (DataLen < BlockSize)
|
|
|
+ PrevIncomplete = false;
|
|
|
+ if (Missing > 0)
|
|
|
+ {
|
|
|
+ DebugAssert(DataLen <= Missing);
|
|
|
+ Missing -= DataLen;
|
|
|
+ }
|
|
|
+ else if (DataLen < BlockSize)
|
|
|
+ {
|
|
|
+ if (OperationProgress->TransferredSize + DataLen !=
|
|
|
+ OperationProgress->TransferSize)
|
|
|
{
|
|
|
- if (OperationProgress->TransferredSize + DataLen !=
|
|
|
- OperationProgress->TransferSize)
|
|
|
- {
|
|
|
- // with native text transfer mode (SFTP>=4), do not bother about
|
|
|
- // getting less than requested, read offset is ignored anyway
|
|
|
- if ((FVersion < 4) || !OperationProgress->AsciiTransfer)
|
|
|
- {
|
|
|
- GapCount++;
|
|
|
- Missing = BlockSize - DataLen;
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
+ // with native text transfer mode (SFTP>=4), do not bother about
|
|
|
+ // getting less than requested, read offset is ignored anyway
|
|
|
+ if ((FVersion < 4) || !OperationProgress->AsciiTransfer)
|
|
|
{
|
|
|
- PrevIncomplete = true;
|
|
|
+ GapCount++;
|
|
|
+ Missing = BlockSize - DataLen;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- DebugAssert(DataLen <= BlockSize);
|
|
|
- BlockBuf.Insert(0, reinterpret_cast<const char *>(DataPacket.GetNextData(DataLen)), DataLen);
|
|
|
- DataPacket.DataConsumed(DataLen);
|
|
|
- OperationProgress->AddTransferred(DataLen);
|
|
|
-
|
|
|
- if ((FVersion >= 6) && DataPacket.CanGetBool() && (Missing == 0))
|
|
|
+ else
|
|
|
{
|
|
|
- Eof = DataPacket.GetBool();
|
|
|
+ PrevIncomplete = true;
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- if (OperationProgress->AsciiTransfer)
|
|
|
- {
|
|
|
- DebugAssert(!ResumeTransfer && !ResumeAllowed);
|
|
|
+ DebugAssert(DataLen <= BlockSize);
|
|
|
+ BlockBuf.Insert(0, reinterpret_cast<const char *>(DataPacket.GetNextData(DataLen)), DataLen);
|
|
|
+ DataPacket.DataConsumed(DataLen);
|
|
|
+ OperationProgress->AddTransferred(DataLen);
|
|
|
|
|
|
- unsigned int PrevBlockSize = BlockBuf.Size;
|
|
|
- BlockBuf.Convert(GetEOL(), FTerminal->Configuration->LocalEOLType, 0, ConvertToken);
|
|
|
- OperationProgress->SetLocalSize(
|
|
|
- OperationProgress->LocalSize - PrevBlockSize + BlockBuf.Size);
|
|
|
- }
|
|
|
+ if ((FVersion >= 6) && DataPacket.CanGetBool() && (Missing == 0))
|
|
|
+ {
|
|
|
+ Eof = DataPacket.GetBool();
|
|
|
+ }
|
|
|
|
|
|
- FILE_OPERATION_LOOP_BEGIN
|
|
|
- {
|
|
|
- BlockBuf.WriteToStream(FileStream, BlockBuf.Size);
|
|
|
- }
|
|
|
- FILE_OPERATION_LOOP_END(FMTLOAD(WRITE_ERROR, (LocalFileName)));
|
|
|
+ if (OperationProgress->AsciiTransfer)
|
|
|
+ {
|
|
|
+ DebugAssert(!ResumeTransfer && !ResumeAllowed);
|
|
|
|
|
|
- OperationProgress->AddLocallyUsed(BlockBuf.Size);
|
|
|
+ unsigned int PrevBlockSize = BlockBuf.Size;
|
|
|
+ BlockBuf.Convert(GetEOL(), FTerminal->Configuration->LocalEOLType, 0, ConvertToken);
|
|
|
+ OperationProgress->SetLocalSize(OperationProgress->LocalSize - PrevBlockSize + BlockBuf.Size);
|
|
|
}
|
|
|
|
|
|
- if (OperationProgress->Cancel != csContinue)
|
|
|
+ FILE_OPERATION_LOOP_BEGIN
|
|
|
{
|
|
|
- if (OperationProgress->ClearCancelFile())
|
|
|
- {
|
|
|
- THROW_SKIP_FILE_NULL;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- Abort();
|
|
|
- }
|
|
|
+ BlockBuf.WriteToStream(FileStream, BlockBuf.Size);
|
|
|
}
|
|
|
- };
|
|
|
+ FILE_OPERATION_LOOP_END(FMTLOAD(WRITE_ERROR, (LocalFileName)));
|
|
|
|
|
|
- if (GapCount > 0)
|
|
|
- {
|
|
|
- FTerminal->LogEvent(FORMAT(
|
|
|
- L"%d requests to fill %d data gaps were issued.",
|
|
|
- (GapFillCount, GapCount)));
|
|
|
+ OperationProgress->AddLocallyUsed(BlockBuf.Size);
|
|
|
}
|
|
|
- }
|
|
|
- __finally
|
|
|
- {
|
|
|
- Queue.DisposeSafe();
|
|
|
- }
|
|
|
- // queue is discarded here
|
|
|
- }
|
|
|
|
|
|
- if (CopyParam->PreserveTime)
|
|
|
- {
|
|
|
- FTerminal->LogEvent(FORMAT(L"Preserving timestamp [%s]",
|
|
|
- (StandardTimestamp(Modification))));
|
|
|
- SetFileTime(LocalHandle, NULL, &AcTime, &WrTime);
|
|
|
- }
|
|
|
-
|
|
|
- CloseHandle(LocalHandle);
|
|
|
- LocalHandle = NULL;
|
|
|
-
|
|
|
- if (ResumeAllowed)
|
|
|
- {
|
|
|
- FILE_OPERATION_LOOP_BEGIN
|
|
|
- {
|
|
|
- if (FileExists(ApiPath(DestFullName)))
|
|
|
+ if (OperationProgress->Cancel != csContinue)
|
|
|
{
|
|
|
- DeleteFileChecked(DestFullName);
|
|
|
+ if (OperationProgress->ClearCancelFile())
|
|
|
+ {
|
|
|
+ THROW_SKIP_FILE_NULL;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ Abort();
|
|
|
+ }
|
|
|
}
|
|
|
- THROWOSIFFALSE(Sysutils::RenameFile(DestPartialFullName, DestFullName));
|
|
|
- }
|
|
|
- FILE_OPERATION_LOOP_END(
|
|
|
- FMTLOAD(RENAME_AFTER_RESUME_ERROR,
|
|
|
- (ExtractFileName(DestPartialFullName), DestFileName)));
|
|
|
- }
|
|
|
-
|
|
|
- DeleteLocalFile = false;
|
|
|
+ };
|
|
|
|
|
|
- if (Attrs == -1)
|
|
|
- {
|
|
|
- Attrs = faArchive;
|
|
|
- }
|
|
|
- int NewAttrs = CopyParam->LocalFileAttrs(*File->Rights);
|
|
|
- if ((NewAttrs & Attrs) != NewAttrs)
|
|
|
- {
|
|
|
- FILE_OPERATION_LOOP_BEGIN
|
|
|
+ if (GapCount > 0)
|
|
|
{
|
|
|
- THROWOSIFFALSE(FileSetAttr(ApiPath(DestFullName), Attrs | NewAttrs) == 0);
|
|
|
+ FTerminal->LogEvent(FORMAT(L"%d requests to fill %d data gaps were issued.", (GapFillCount, GapCount)));
|
|
|
}
|
|
|
- FILE_OPERATION_LOOP_END(FMTLOAD(CANT_SET_ATTRS, (DestFullName)));
|
|
|
}
|
|
|
+ __finally
|
|
|
+ {
|
|
|
+ Queue.DisposeSafe();
|
|
|
+ }
|
|
|
+ // queue is discarded here
|
|
|
+ }
|
|
|
|
|
|
+ if (CopyParam->PreserveTime)
|
|
|
+ {
|
|
|
+ FTerminal->UpdateTargetTime(LocalHandle, Modification, FTerminal->SessionData->DSTMode);
|
|
|
}
|
|
|
- __finally
|
|
|
+
|
|
|
+ CloseHandle(LocalHandle);
|
|
|
+ LocalHandle = NULL;
|
|
|
+
|
|
|
+ if (ResumeAllowed)
|
|
|
{
|
|
|
- if (LocalHandle) CloseHandle(LocalHandle);
|
|
|
- if (FileStream) delete FileStream;
|
|
|
- if (DeleteLocalFile && (!ResumeAllowed || OperationProgress->LocallyUsed == 0) &&
|
|
|
- (OverwriteMode == omOverwrite))
|
|
|
+ FILE_OPERATION_LOOP_BEGIN
|
|
|
{
|
|
|
- FILE_OPERATION_LOOP_BEGIN
|
|
|
+ if (FileExists(ApiPath(DestFullName)))
|
|
|
{
|
|
|
- THROWOSIFFALSE(Sysutils::DeleteFile(ApiPath(LocalFileName)));
|
|
|
+ DeleteFileChecked(DestFullName);
|
|
|
}
|
|
|
- FILE_OPERATION_LOOP_END(FMTLOAD(DELETE_LOCAL_FILE_ERROR, (LocalFileName)));
|
|
|
- }
|
|
|
-
|
|
|
- // if the transfer was finished, the file is closed already
|
|
|
- if (FTerminal->Active && !RemoteHandle.IsEmpty())
|
|
|
- {
|
|
|
- // do not wait for response
|
|
|
- SFTPCloseRemote(RemoteHandle, DestFileName, OperationProgress,
|
|
|
- true, true, NULL);
|
|
|
+ THROWOSIFFALSE(Sysutils::RenameFile(DestPartialFullName, DestFullName));
|
|
|
}
|
|
|
+ FILE_OPERATION_LOOP_END(FMTLOAD(RENAME_AFTER_RESUME_ERROR, (ExtractFileName(DestPartialFullName), DestFileName)));
|
|
|
}
|
|
|
|
|
|
- FTerminal->LogFileDone(OperationProgress, ExpandedDestFullName);
|
|
|
- }
|
|
|
+ DeleteLocalFile = false;
|
|
|
+
|
|
|
+ FTerminal->UpdateTargetAttrs(DestFullName, File, CopyParam, Attrs);
|
|
|
|
|
|
- if (Params & cpDelete)
|
|
|
- {
|
|
|
- DebugAssert(FLAGCLEAR(Params, cpNoRecurse));
|
|
|
- ChildError = true;
|
|
|
- // If file is directory, do not delete it recursively, because it should be
|
|
|
- // empty already. If not, it should not be deleted (some files were
|
|
|
- // skipped or some new files were copied to it, while we were downloading)
|
|
|
- int Params = dfNoRecursive;
|
|
|
- FTerminal->DeleteFile(FileName, File, &Params);
|
|
|
- ChildError = false;
|
|
|
- }
|
|
|
-}
|
|
|
-//---------------------------------------------------------------------------
|
|
|
-void __fastcall TSFTPFileSystem::SFTPSinkFile(UnicodeString FileName,
|
|
|
- const TRemoteFile * File, void * Param)
|
|
|
-{
|
|
|
- TSinkFileParams * Params = (TSinkFileParams *)Param;
|
|
|
- DebugAssert(Params->OperationProgress);
|
|
|
- try
|
|
|
- {
|
|
|
- SFTPSinkRobust(FileName, File, Params->TargetDir, Params->CopyParam,
|
|
|
- Params->Params, Params->OperationProgress, Params->Flags);
|
|
|
}
|
|
|
- catch(EScpSkipFile & E)
|
|
|
+ __finally
|
|
|
{
|
|
|
- TFileOperationProgressType * OperationProgress = Params->OperationProgress;
|
|
|
+ if (LocalHandle)
|
|
|
+ {
|
|
|
+ CloseHandle(LocalHandle);
|
|
|
+ }
|
|
|
|
|
|
- Params->Skipped = true;
|
|
|
+ if (FileStream != NULL)
|
|
|
+ {
|
|
|
+ delete FileStream;
|
|
|
+ }
|
|
|
|
|
|
+ if (DeleteLocalFile && (!ResumeAllowed || OperationProgress->LocallyUsed == 0) &&
|
|
|
+ (OverwriteMode == omOverwrite))
|
|
|
{
|
|
|
- TSuspendFileOperationProgress Suspend(OperationProgress);
|
|
|
- if (!FTerminal->HandleException(&E))
|
|
|
+ FILE_OPERATION_LOOP_BEGIN
|
|
|
{
|
|
|
- throw;
|
|
|
+ THROWOSIFFALSE(Sysutils::DeleteFile(ApiPath(LocalFileName)));
|
|
|
}
|
|
|
+ FILE_OPERATION_LOOP_END(FMTLOAD(DELETE_LOCAL_FILE_ERROR, (LocalFileName)));
|
|
|
}
|
|
|
|
|
|
- if (OperationProgress->Cancel)
|
|
|
+ // if the transfer was finished, the file is closed already
|
|
|
+ if (FTerminal->Active && !RemoteHandle.IsEmpty())
|
|
|
{
|
|
|
- Abort();
|
|
|
+ // do not wait for response
|
|
|
+ SFTPCloseRemote(RemoteHandle, DestFileName, OperationProgress, true, true, NULL);
|
|
|
}
|
|
|
}
|
|
|
}
|