summaryrefslogtreecommitdiff
path: root/UCoreWorkers.pas
diff options
context:
space:
mode:
authorTomas Bzatek <tbzatek@redhat.com>2024-10-25 11:32:12 +0200
committerTomas Bzatek <tbzatek@redhat.com>2024-10-25 11:32:12 +0200
commitd08636bdad4f36431bfdf07e8400d7188a2df781 (patch)
tree841aef1904f9c87ee02e1a730ef488cc75215ec6 /UCoreWorkers.pas
parent741e1499ed55e20c136298ced705dcbb59915117 (diff)
downloadtuxcmd-d08636bdad4f36431bfdf07e8400d7188a2df781.tar.xz
Rework file copy/open-read-write-close data handlingv0.6.83
Stick to the POSIX open(2), read(2), write(2), close(2) return values semantics, apply on the VFS interface. Also handle short reads and writes that are common for some gvfs backends. This makes cross-VFS copy work.
Diffstat (limited to 'UCoreWorkers.pas')
-rw-r--r--UCoreWorkers.pas71
1 files changed, 39 insertions, 32 deletions
diff --git a/UCoreWorkers.pas b/UCoreWorkers.pas
index 507e2b7..75a9453 100644
--- a/UCoreWorkers.pas
+++ b/UCoreWorkers.pas
@@ -1019,11 +1019,12 @@ var DefResponse: integer; // Global variables for this function
var fsrc, fdst: TEngineFileDes;
BSize: integer;
Buffer: Pointer;
- BytesDone, BytesRead, BytesWritten: Int64;
+ BytesDone, BytesRead, BytesWritten, BytesRemaining: Int64;
LocalError: PGError;
begin
Result := False;
LocalError := nil;
+ BytesDone := 0;
DebugMsg(['ManualCopyFile: ', SourceFile, ' ---> ', DestFile]);
fsrc := SrcEngine.OpenFile(SourceFile, omRead, @LocalError);
if fsrc = nil then begin
@@ -1042,7 +1043,6 @@ var DefResponse: integer; // Global variables for this function
Exit;
end;
- BytesDone := 0;
BSize := DestEngine.GetBlockSize;
Buffer := malloc(BSize);
if Buffer = nil then begin
@@ -1050,60 +1050,66 @@ var DefResponse: integer; // Global variables for this function
CopyFilesWorker_ProgressFunc(Self, 0, Error^); // Memory allocation failed
Exit;
end;
- memset(Buffer, 0, BSize);
- BytesWritten := 0;
repeat
+ // Read block
BytesRead := SrcEngine.ReadFile(fsrc, Buffer, BSize, @LocalError);
- if (BytesRead = 0) and (Error^ <> nil) then begin
+ if BytesRead < 0 then begin
g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_SOURCE_READ), LocalError^.message);
g_error_free(LocalError);
LocalError := nil;
- Result := CopyFilesWorker_ProgressFunc(Self, BytesWritten, Error^); // Cannot read from source file
+ Result := CopyFilesWorker_ProgressFunc(Self, BytesDone, Error^); // Cannot read from source file
if Result then begin
g_error_free(Error^);
Error^ := nil;
Continue;
end else Break;
end;
+
+ // Write block
if BytesRead > 0 then begin
- BytesWritten := DestEngine.WriteFile(fdst, Buffer, BytesRead, @LocalError);
- if (BytesWritten < BytesRead) then begin
+ BytesRemaining := BytesRead;
+ repeat
+ BytesWritten := DestEngine.WriteFile(fdst, Buffer + (BytesRead - BytesRemaining), BytesRemaining, @LocalError);
+ if BytesWritten > 0 then
+ BytesRemaining := BytesRemaining - BytesWritten;
+ until (BytesRemaining = 0) or (BytesWritten <= 0);
+ if BytesWritten < 0 then begin
g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_TARGET_WRITE), LocalError^.message);
g_error_free(LocalError);
LocalError := nil;
- Result := CopyFilesWorker_ProgressFunc(Self, BytesWritten, Error^); // Cannot write to target file
- if Result then begin
- g_error_free(Error^);
- Error^ := nil;
- Continue;
- end else Break;
+ Result := False;
+ CopyFilesWorker_ProgressFunc(Self, BytesDone, Error^); // Cannot write to target file
+ Break;
end;
end;
+ // BytesRead == 0 means EOF
BytesDone := BytesDone + BytesRead;
Result := CopyFilesWorker_ProgressFunc(Self, BytesDone, nil);
if not Result then Break;
- until (BytesRead = 0) or (BytesWritten < BytesRead);
+ until BytesRead <= 0;
libc_free(Buffer);
- if not DestEngine.CloseFile(fdst, @LocalError) then begin
- g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_TARGET_CLOSE), LocalError^.message);
- g_error_free(LocalError);
- Result := False;
- SrcEngine.CloseFile(fsrc, nil);
- CopyFilesWorker_ProgressFunc(Self, BytesDone, Error^); // Cannot close target file
- Exit;
- end;
- if not SrcEngine.CloseFile(fsrc, @LocalError) then begin
- g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_SOURCE_CLOSE), LocalError^.message);
- g_error_free(LocalError);
- Result := CopyFilesWorker_ProgressFunc(Self, BytesDone, Error^); // Cannot close source file
+ if not DestEngine.CloseFile(fdst, @LocalError) then
if Result then begin
- // user has chosen to ignore the error
- g_error_free(Error^);
- Error^ := nil;
- end else Exit;
- end;
+ g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_TARGET_CLOSE), LocalError^.message);
+ g_error_free(LocalError);
+ Result := False;
+ SrcEngine.CloseFile(fsrc, nil);
+ CopyFilesWorker_ProgressFunc(Self, BytesDone, Error^); // Cannot close target file
+ Exit;
+ end;
+ if not SrcEngine.CloseFile(fsrc, @LocalError) then
+ if Result then begin
+ g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_SOURCE_CLOSE), LocalError^.message);
+ g_error_free(LocalError);
+ Result := CopyFilesWorker_ProgressFunc(Self, BytesDone, Error^); // Cannot close source file
+ if Result then begin
+ // user has chosen to ignore the error
+ g_error_free(Error^);
+ Error^ := nil;
+ end else Exit;
+ end;
end;
// Returns True if the file was successfully copied and will be deleted on move
@@ -1120,6 +1126,7 @@ var DefResponse: integer; // Global variables for this function
then Result := DestEngine.CopyFileIn(SourceFile, DestFile, Append, @CopyFilesWorker_ProgressFunc, Self, Error)
else
+ // TODO: check for reported Append capability and fall back to manual copy if needed
// from local engine to VFS engine
if (SrcEngine is TLocalTreeEngine) and (DestEngine is TVFSEngine) then
begin