ADD: Copy on write option (Linux BTRFS)

This commit is contained in:
Alexander Koblov 2021-06-19 22:33:46 +03:00
commit 5e8f3580d9
7 changed files with 117 additions and 22 deletions

View file

@ -114,6 +114,7 @@ function fpLocalTime(timer: PTime; tp: PTimeStruct): PTimeStruct;
{$IF DEFINED(LINUX)}
function fpFDataSync(fd: cint): cint;
function fpCloneFile(src_fd, dst_fd: cint): Boolean;
function fpFAllocate(fd: cint; mode: cint; offset, len: coff_t): cint;
function mbFileGetXattr(const FileName: String): TStringArray;
@ -136,6 +137,7 @@ type rlim_t = Int64;
const
{$IF DEFINED(LINUX)}
_SC_OPEN_MAX = 4;
FICLONE = $40049409;
RLIM_INFINITY = rlim_t(-1);
{$ELSEIF DEFINED(BSD)}
_SC_OPEN_MAX = 5;
@ -249,6 +251,13 @@ begin
if Result = -1 then fpseterrno(fpgetCerrno);
end;
function fpCloneFile(src_fd, dst_fd: cint): Boolean;
var
ASource: Pointer absolute src_fd;
begin
Result:= (FpIOCtl(dst_fd, FICLONE, ASource) = 0);
end;
function fpFAllocate(fd: cint; mode: cint; offset, len: coff_t): cint;
begin
Result := fallocate(fd, mode, offset, len);

View file

@ -236,6 +236,18 @@ object FileSystemCopyMoveOperationOptionsUI: TFileSystemCopyMoveOperationOptions
Caption = '&Verify'
TabOrder = 0
end
object chkCopyOnWrite: TCheckBox
AnchorSideLeft.Control = cbCopyPermissions
AnchorSideTop.Control = cbCopyPermissions
AnchorSideTop.Side = asrBottom
Left = 0
Height = 23
Top = 230
Width = 111
AllowGrayed = True
Caption = 'Copy on write'
TabOrder = 10
end
end
object gbFileTemplate: TGroupBox
AnchorSideLeft.Control = Owner
@ -338,4 +350,4 @@ object FileSystemCopyMoveOperationOptionsUI: TFileSystemCopyMoveOperationOptions
TabOrder = 1
end
end
end
end

View file

@ -1,20 +1,21 @@
{"version":1,"strings":[
{"hash":73409731,"name":"tfilesystemcopymoveoperationoptionsui.lblfileexists.caption","sourcebytes":[87,104,101,110,32,38,102,105,108,101,32,101,120,105,115,116,115],"value":"When &file exists"},
{"hash":219592963,"name":"tfilesystemcopymoveoperationoptionsui.lbldirectoryexists.caption","sourcebytes":[87,104,101,110,32,100,105,114,38,101,99,116,111,114,121,32,101,120,105,115,116,115],"value":"When dir&ectory exists"},
{"hash":46399081,"name":"tfilesystemcopymoveoperationoptionsui.lblsetpropertyerror.caption","sourcebytes":[87,104,101,110,32,99,97,38,110,110,111,116,32,115,101,116,32,112,114,111,112,101,114,116,121],"value":"When ca&nnot set property"},
{"hash":171515630,"name":"tfilesystemcopymoveoperationoptionsui.cmbsetpropertyerror.hint","sourcebytes":[87,104,97,116,32,116,111,32,100,111,32,119,104,101,110,32,99,97,110,110,111,116,32,115,101,116,32,102,105,108,101,32,116,105,109,101,44,32,97,116,116,114,105,98,117,116,101,115,44,32,101,116,99,46],"value":"What to do when cannot set file time, attributes, etc."},
{"hash":250179989,"name":"tfilesystemcopymoveoperationoptionsui.cbcheckfreespace.caption","sourcebytes":[67,38,104,101,99,107,32,102,114,101,101,32,115,112,97,99,101],"value":"C&heck free space"},
{"hash":76535811,"name":"tfilesystemcopymoveoperationoptionsui.cbfollowlinks.caption","sourcebytes":[70,111,38,108,108,111,119,32,108,105,110,107,115],"value":"Fo&llow links"},
{"hash":28881891,"name":"tfilesystemcopymoveoperationoptionsui.cbcorrectlinks.caption","sourcebytes":[67,111,114,114,101,99,116,32,108,105,110,38,107,115],"value":"Correct lin&ks"},
{"hash":88860499,"name":"tfilesystemcopymoveoperationoptionsui.cbcopyattributes.caption","sourcebytes":[67,111,112,38,121,32,97,116,116,114,105,98,117,116,101,115],"value":"Cop&y attributes"},
{"hash":193111863,"name":"tfilesystemcopymoveoperationoptionsui.cbdropreadonlyflag.caption","sourcebytes":[68,114,111,112,32,114,101,97,100,111,110,108,121,32,102,108,97,38,103],"value":"Drop readonly fla&g"},
{"hash":231770837,"name":"tfilesystemcopymoveoperationoptionsui.cbcopytime.caption","sourcebytes":[67,111,112,121,32,100,38,97,116,101,47,116,105,109,101],"value":"Copy d&ate/time"},
{"hash":58641088,"name":"tfilesystemcopymoveoperationoptionsui.cbcopyownership.caption","sourcebytes":[67,111,112,121,32,111,38,119,110,101,114,115,104,105,112],"value":"Copy o&wnership"},
{"hash":263433445,"name":"tfilesystemcopymoveoperationoptionsui.cbreservespace.caption","sourcebytes":[38,82,101,115,101,114,118,101,32,115,112,97,99,101],"value":"&Reserve space"},
{"hash":37050051,"name":"tfilesystemcopymoveoperationoptionsui.cbcopypermissions.caption","sourcebytes":[67,111,112,121,32,38,112,101,114,109,105,115,115,105,111,110,115],"value":"Copy &permissions"},
{"hash":197955577,"name":"tfilesystemcopymoveoperationoptionsui.chkverify.caption","sourcebytes":[38,86,101,114,105,102,121],"value":"&Verify"},
{"hash":150587493,"name":"tfilesystemcopymoveoperationoptionsui.gbfiletemplate.caption","sourcebytes":[85,115,101,32,102,105,108,101,32,116,101,109,112,108,97,116,101],"value":"Use file template"},
{"hash":24093710,"name":"tfilesystemcopymoveoperationoptionsui.btnsearchtemplate.hint","sourcebytes":[67,104,111,111,115,101,32,116,101,109,112,108,97,116,101,46,46,46],"value":"Choose template..."},
{"hash":121893262,"name":"tfilesystemcopymoveoperationoptionsui.lbltemplatename.caption","sourcebytes":[60,110,111,32,116,101,109,112,108,97,116,101,62],"value":"<no template>"},
{"hash":190840595,"name":"tfilesystemcopymoveoperationoptionsui.cbexcludeemptydirectories.caption","sourcebytes":[69,38,120,99,108,117,100,101,32,101,109,112,116,121,32,100,105,114,101,99,116,111,114,105,101,115],"value":"E&xclude empty directories"}
]}
{"version":1,"strings":[
{"hash":73409731,"name":"tfilesystemcopymoveoperationoptionsui.lblfileexists.caption","sourcebytes":[87,104,101,110,32,38,102,105,108,101,32,101,120,105,115,116,115],"value":"When &file exists"},
{"hash":219592963,"name":"tfilesystemcopymoveoperationoptionsui.lbldirectoryexists.caption","sourcebytes":[87,104,101,110,32,100,105,114,38,101,99,116,111,114,121,32,101,120,105,115,116,115],"value":"When dir&ectory exists"},
{"hash":46399081,"name":"tfilesystemcopymoveoperationoptionsui.lblsetpropertyerror.caption","sourcebytes":[87,104,101,110,32,99,97,38,110,110,111,116,32,115,101,116,32,112,114,111,112,101,114,116,121],"value":"When ca&nnot set property"},
{"hash":171515630,"name":"tfilesystemcopymoveoperationoptionsui.cmbsetpropertyerror.hint","sourcebytes":[87,104,97,116,32,116,111,32,100,111,32,119,104,101,110,32,99,97,110,110,111,116,32,115,101,116,32,102,105,108,101,32,116,105,109,101,44,32,97,116,116,114,105,98,117,116,101,115,44,32,101,116,99,46],"value":"What to do when cannot set file time, attributes, etc."},
{"hash":250179989,"name":"tfilesystemcopymoveoperationoptionsui.cbcheckfreespace.caption","sourcebytes":[67,38,104,101,99,107,32,102,114,101,101,32,115,112,97,99,101],"value":"C&heck free space"},
{"hash":76535811,"name":"tfilesystemcopymoveoperationoptionsui.cbfollowlinks.caption","sourcebytes":[70,111,38,108,108,111,119,32,108,105,110,107,115],"value":"Fo&llow links"},
{"hash":28881891,"name":"tfilesystemcopymoveoperationoptionsui.cbcorrectlinks.caption","sourcebytes":[67,111,114,114,101,99,116,32,108,105,110,38,107,115],"value":"Correct lin&ks"},
{"hash":88860499,"name":"tfilesystemcopymoveoperationoptionsui.cbcopyattributes.caption","sourcebytes":[67,111,112,38,121,32,97,116,116,114,105,98,117,116,101,115],"value":"Cop&y attributes"},
{"hash":193111863,"name":"tfilesystemcopymoveoperationoptionsui.cbdropreadonlyflag.caption","sourcebytes":[68,114,111,112,32,114,101,97,100,111,110,108,121,32,102,108,97,38,103],"value":"Drop readonly fla&g"},
{"hash":231770837,"name":"tfilesystemcopymoveoperationoptionsui.cbcopytime.caption","sourcebytes":[67,111,112,121,32,100,38,97,116,101,47,116,105,109,101],"value":"Copy d&ate/time"},
{"hash":58641088,"name":"tfilesystemcopymoveoperationoptionsui.cbcopyownership.caption","sourcebytes":[67,111,112,121,32,111,38,119,110,101,114,115,104,105,112],"value":"Copy o&wnership"},
{"hash":263433445,"name":"tfilesystemcopymoveoperationoptionsui.cbreservespace.caption","sourcebytes":[38,82,101,115,101,114,118,101,32,115,112,97,99,101],"value":"&Reserve space"},
{"hash":37050051,"name":"tfilesystemcopymoveoperationoptionsui.cbcopypermissions.caption","sourcebytes":[67,111,112,121,32,38,112,101,114,109,105,115,115,105,111,110,115],"value":"Copy &permissions"},
{"hash":197955577,"name":"tfilesystemcopymoveoperationoptionsui.chkverify.caption","sourcebytes":[38,86,101,114,105,102,121],"value":"&Verify"},
{"hash":169428869,"name":"tfilesystemcopymoveoperationoptionsui.chkcopyonwrite.caption","sourcebytes":[67,111,112,121,32,111,110,32,119,114,105,116,101],"value":"Copy on write"},
{"hash":150587493,"name":"tfilesystemcopymoveoperationoptionsui.gbfiletemplate.caption","sourcebytes":[85,115,101,32,102,105,108,101,32,116,101,109,112,108,97,116,101],"value":"Use file template"},
{"hash":24093710,"name":"tfilesystemcopymoveoperationoptionsui.btnsearchtemplate.hint","sourcebytes":[67,104,111,111,115,101,32,116,101,109,112,108,97,116,101,46,46,46],"value":"Choose template..."},
{"hash":121893262,"name":"tfilesystemcopymoveoperationoptionsui.lbltemplatename.caption","sourcebytes":[60,110,111,32,116,101,109,112,108,97,116,101,62],"value":"<no template>"},
{"hash":190840595,"name":"tfilesystemcopymoveoperationoptionsui.cbexcludeemptydirectories.caption","sourcebytes":[69,38,120,99,108,117,100,101,32,101,109,112,116,121,32,100,105,114,101,99,116,111,114,105,101,115],"value":"E&xclude empty directories"}
]}

View file

@ -27,6 +27,7 @@ type
cbExcludeEmptyDirectories: TCheckBox;
cbReserveSpace: TCheckBox;
cbCopyPermissions: TCheckBox;
chkCopyOnWrite: TCheckBox;
chkVerify: TCheckBox;
cmbDirectoryExists: TComboBoxAutoWidth;
cmbFileExists: TComboBoxAutoWidth;
@ -120,6 +121,10 @@ begin
cbCopyPermissions.Visible := True;
{$ENDIF}
{$IFNDEF LINUX}
chkCopyOnWrite.Visible := False;
{$ENDIF}
if Assigned(FileCopyEx) then
begin
cbCopyTime.Visible:= False;
@ -151,6 +156,12 @@ begin
fsoospeIgnoreErrors : cmbSetPropertyError.ItemIndex := 2;
end;
case gOperationOptionCopyOnWrite of
fsoogNone : chkCopyOnWrite.State:= cbGrayed;
fsoogYes : chkCopyOnWrite.State:= cbChecked;
fsoogNo : chkCopyOnWrite.State:= cbUnchecked;
end;
cbCopyAttributes.Checked := gOperationOptionCopyAttributes;
cbCopyTime.Checked := gOperationOptionCopyTime;
cbCopyOwnership.Checked := gOperationOptionCopyOwnership;
@ -194,6 +205,11 @@ begin
1: gOperationOptionSetPropertyError := fsoospeDontSet;
2: gOperationOptionSetPropertyError := fsoospeIgnoreErrors;
end;
case chkCopyOnWrite.State of
cbGrayed : gOperationOptionCopyOnWrite := fsoogNone;
cbChecked : gOperationOptionCopyOnWrite := fsoogYes;
cbUnchecked : gOperationOptionCopyOnWrite := fsoogNo;
end;
gOperationOptionVerify := chkVerify.Checked;
gOperationOptionCopyAttributes := cbCopyAttributes.Checked;
@ -248,6 +264,11 @@ begin
cbUnchecked: SymLinkOption := fsooslDontFollow;
cbGrayed : SymLinkOption := fsooslNone;
end;
case chkCopyOnWrite.State of
cbGrayed : CopyOnWrite := fsoogNone;
cbChecked : CopyOnWrite := fsoogYes;
cbUnchecked : CopyOnWrite := fsoogNo;
end;
Options := CopyAttributesOptions;
SetCopyOption(Options, caoCopyAttributes, cbCopyAttributes.Checked);
SetCopyOption(Options, caoCopyTime, cbCopyTime.Checked);

View file

@ -26,6 +26,7 @@ type
FOperationHelper: TFileSystemOperationHelper;
FExcludeEmptyTemplateDirectories: Boolean;
FSearchTemplate: TSearchTemplate;
FCopyOnWrite: TFileSourceOperationOptionGeneral;
FSetPropertyError: TFileSourceOperationOptionSetPropertyError;
FSourceFilesTree: TFileTree; // source files including all files/dirs in subdirectories
FStatistics: TFileSourceCopyOperationStatistics; // local copy of statistics
@ -59,6 +60,7 @@ type
property SkipAllBigFiles: Boolean read FSkipAllBigFiles write FSkipAllBigFiles;
property AutoRenameItSelf: Boolean read FAutoRenameItSelf write FAutoRenameItSelf;
property CorrectSymLinks: Boolean read FCorrectSymLinks write FCorrectSymLinks;
property CopyOnWrite: TFileSourceOperationOptionGeneral read FCopyOnWrite write FCopyOnWrite;
property SetPropertyError: TFileSourceOperationOptionSetPropertyError read FSetPropertyError write FSetPropertyError;
property ExcludeEmptyTemplateDirectories: Boolean read FExcludeEmptyTemplateDirectories write FExcludeEmptyTemplateDirectories;
{en
@ -116,6 +118,7 @@ begin
inherited Create(aSourceFileSource, aTargetFileSource, theSourceFiles, aTargetPath);
// Here we can read global settings if there are any
FCopyOnWrite := gOperationOptionCopyOnWrite;
FFileExistsOption := gOperationOptionFileExists;
FDirExistsOption := gOperationOptionDirectoryExists;
@ -182,6 +185,7 @@ begin
FOperationHelper.Verify := FVerify;
FOperationHelper.RenameMask := RenameMask;
FOperationHelper.CopyOnWrite := FCopyOnWrite;
FOperationHelper.ReserveSpace := FReserveSpace;
FOperationHelper.CheckFreeSpace := CheckFreeSpace;
FOperationHelper.CopyAttributesOptions := CopyAttributesOptions;

View file

@ -84,6 +84,7 @@ type
FCorrectSymLinks: Boolean;
FCopyAttributesOptions: TCopyAttributesOptions;
FMaxPathOption: TFileSourceOperationUIResponse;
FCopyOnWrite: TFileSourceOperationOptionGeneral;
FDeleteFileOption: TFileSourceOperationUIResponse;
FFileExistsOption: TFileSourceOperationOptionFileExists;
FDirExistsOption: TFileSourceOperationOptionDirectoryExists;
@ -146,6 +147,7 @@ type
procedure ProcessTree(aFileTree: TFileTree);
property Verify: Boolean read FVerify write FVerify;
property CopyOnWrite: TFileSourceOperationOptionGeneral read FCopyOnWrite write FCopyOnWrite;
property FileExistsOption: TFileSourceOperationOptionFileExists read FFileExistsOption write FFileExistsOption;
property DirExistsOption: TFileSourceOperationOptionDirectoryExists read FDirExistsOption write FDirExistsOption;
property CheckFreeSpace: Boolean read FCheckFreeSpace write FCheckFreeSpace;
@ -165,7 +167,7 @@ uses
DCBasicTypes, uFileSource, uFileSystemFileSource, uFileProperty, uAdministrator,
StrUtils, DCDateTimeUtils, uShowMsg, Forms, LazUTF8, uHash, uFileCopyEx, SysConst
{$IFDEF UNIX}
, BaseUnix
, BaseUnix, DCUnix
{$ENDIF}
;
@ -692,6 +694,48 @@ begin
if not Assigned(SourceFileStream) then
Exit;
{$IF DEFINED(LINUX)}
if (Mode = fsohcmDefault) and ((FCopyOnWrite <> fsoogNo) or (FMode = fsohmMove)) then
begin
bRetryWrite:= FReserveSpace;
FReserveSpace:= False;
OpenTargetFile;
if not Assigned(TargetFileStream) then
Exit;
Result:= fpCloneFile(SourceFileStream.Handle, TargetFileStream.Handle);
if Result then
begin
FreeAndNil(TargetFileStream);
CopyProperties(SourceFile, TargetFileName);
Exit;
end
else if (FCopyOnWrite = fsoogYes) then
begin
bDeleteFile := True;
if FSkipCopyError then Exit;
case AskQuestion('',
Format(rsMsgErrCannotCopyFile, [WrapTextSimple(SourceFile.FullPath, 64), WrapTextSimple(TargetFileName, 64)]) +
LineEnding + LineEnding + mbSysErrorMessage,
[fsourSkip, fsourSkipAll, fsourAbort],
fsourSkip, fsourAbort) of
fsourAbort:
AbortOperation;
fsourSkipAll:
FSkipCopyError := True;
end; // case
Exit;
end;
if bRetryWrite then
begin
TargetFileStream.Size:= SourceFileStream.Size;
TargetFileStream.Seek(0, fsFromBeginning);
end;
end else
{$ENDIF}
OpenTargetFile;
if not Assigned(TargetFileStream) then
Exit;

View file

@ -579,6 +579,7 @@ var
{Copy/Move operation options}
gOperationOptionSymLinks: TFileSourceOperationOptionSymLink;
gOperationOptionCorrectLinks: Boolean;
gOperationOptionCopyOnWrite: TFileSourceOperationOptionGeneral;
gOperationOptionFileExists: TFileSourceOperationOptionFileExists;
gOperationOptionDirectoryExists: TFileSourceOperationOptionDirectoryExists;
gOperationOptionSetPropertyError: TFileSourceOperationOptionSetPropertyError;
@ -1830,6 +1831,7 @@ begin
// Operations options
gOperationOptionSymLinks := fsooslNone;
gOperationOptionCorrectLinks := False;
gOperationOptionCopyOnWrite := fsoogNo;
gOperationOptionFileExists := fsoofeNone;
gOperationOptionDirectoryExists := fsoodeNone;
gOperationOptionSetPropertyError := fsoospeNone;
@ -2827,6 +2829,7 @@ begin
begin
gOperationOptionSymLinks := TFileSourceOperationOptionSymLink(GetValue(SubNode, 'Symlink', Integer(gOperationOptionSymLinks)));
gOperationOptionCorrectLinks := GetValue(SubNode, 'CorrectLinks', gOperationOptionCorrectLinks);
gOperationOptionCopyOnWrite := TFileSourceOperationOptionGeneral(GetValue(SubNode, 'CopyOnWrite', Integer(gOperationOptionCopyOnWrite)));
gOperationOptionFileExists := TFileSourceOperationOptionFileExists(GetValue(SubNode, 'FileExists', Integer(gOperationOptionFileExists)));
gOperationOptionDirectoryExists := TFileSourceOperationOptionDirectoryExists(GetValue(SubNode, 'DirectoryExists', Integer(gOperationOptionDirectoryExists)));
gOperationOptionSetPropertyError := TFileSourceOperationOptionSetPropertyError(GetValue(SubNode, 'SetPropertyError', Integer(gOperationOptionSetPropertyError)));
@ -3504,6 +3507,7 @@ begin
SubNode := FindNode(Node, 'Options', True);
SetValue(SubNode, 'Symlink', Integer(gOperationOptionSymLinks));
SetValue(SubNode, 'CorrectLinks', gOperationOptionCorrectLinks);
SetValue(SubNode, 'CopyOnWrite', Integer(gOperationOptionCopyOnWrite));
SetValue(SubNode, 'FileExists', Integer(gOperationOptionFileExists));
SetValue(SubNode, 'DirectoryExists', Integer(gOperationOptionDirectoryExists));
SetValue(SubNode, 'SetPropertyError', Integer(gOperationOptionSetPropertyError));