| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 |
- unit DCDarwin;
- {$mode delphi}
- {$packrecords c}
- {$pointermath on}
- {$modeswitch objectivec1}
- interface
- uses
- Classes, SysUtils, DCBasicTypes, CocoaAll, BaseUnix;
- const
- CLOSE_RANGE_CLOEXEC = (1 << 2);
- function CloseRange(first: cuint; last: cuint; flags: cint): cint; cdecl;
- function mbFileCopyXattr(const Source, Target: String): Boolean;
- // MacOS File Utils
- function MacosFileSetCreationTime( const path:String; const birthtime:TFileTimeEx ): Boolean;
- type
- { TDarwinFileUtil }
- TDarwinFileUtil = class
- class function cloneFile( const fromPath: String; const toPath: String; const size: Int64 ): Boolean;
- end;
- implementation
- uses
- DCUnix;
- type
- proc_fdinfo = record
- proc_fd: Int32;
- proc_fdtype: UInt32;
- end;
- Pproc_fdinfo = ^proc_fdinfo;
- const
- PROC_PIDLISTFDS = 1;
- PROC_PIDLISTFD_SIZE = SizeOf(proc_fdinfo);
- const
- NSAppKitVersionNumber10_13 = 1561;
- const
- COPYFILE_ACL = 1 shl 0;
- COPYFILE_STAT = 1 shl 1;
- COPYFILE_XATTR = 1 shl 2;
- COPYFILE_DATA = 1 shl 3;
- COPYFILE_SECURITY = COPYFILE_STAT or COPYFILE_ACL;
- COPYFILE_METADATA = COPYFILE_SECURITY or COPYFILE_XATTR;
- COPYFILE_ALL = COPYFILE_METADATA or COPYFILE_DATA;
- COPYFILE_UNLINK = 1 shl 21;
- COPYFILE_CLONE = 1 shl 24;
- COPYFILE_CLONE_FORCE = 1 shl 25;
- type
- copyfile_state_t_o = record
- end;
- copyfile_state_t = ^copyfile_state_t_o;
- copyfile_flags_t = UInt32;
- function copyfile( const fromPath: pchar; const toPath: pchar; state: copyfile_state_t; flags: copyfile_flags_t ): Integer;
- cdecl; external name 'copyfile';
- function proc_pidinfo(pid: cint; flavor: cint; arg: cuint64; buffer: pointer; buffersize: cint): cint; cdecl; external 'proc';
- function CloseRange(first: cuint; last: cuint; flags: cint): cint; cdecl;
- var
- I: cint;
- Handle: cint;
- ProcessId: TPid;
- bufferSize: cint;
- pidInfo: Pproc_fdinfo;
- begin
- Result:= -1;
- ProcessId:= FpGetpid;
- bufferSize:= proc_pidinfo(ProcessId, PROC_PIDLISTFDS, 0, nil, 0);
- pidInfo:= GetMem(bufferSize);
- if Assigned(pidInfo) then
- begin
- bufferSize:= proc_pidinfo(ProcessId, PROC_PIDLISTFDS, 0, pidInfo, bufferSize);
- for I:= 0 to (bufferSize div PROC_PIDLISTFD_SIZE) - 1 do
- begin
- Handle:= pidInfo[I].proc_fd;
- if (Handle >= first) and (Handle <= last) then
- begin
- if (flags and CLOSE_RANGE_CLOEXEC <> 0) then
- FileCloseOnExec(Handle)
- else begin
- FileClose(Handle);
- end;
- end;
- end;
- Result:= 0;
- FreeMem(pidInfo);
- end;
- end;
- function mbFileCopyXattr(const Source, Target: String): Boolean;
- var
- ret: Integer;
- begin
- Writeln( '>>3> mbFileCopyXattr' );
- ret:= copyfile( pchar(Source), pchar(Target), nil, COPYFILE_XATTR );
- fpseterrno( ret );
- Result:= (ret=0);
- end;
- function StringToNSString(const S: String): NSString;
- begin
- Result:= NSString(NSString.stringWithUTF8String(PAnsiChar(S)));
- end;
- function MacosFileSetCreationTime( const path:String; const birthtime:TFileTimeEx ): Boolean;
- var
- seconds: Double;
- attrs: NSMutableDictionary;
- nsPath: NSString;
- begin
- Result:= true;
- if birthtime = TFileTimeExNull then exit;
- seconds:= birthtime.sec.ToDouble + birthtime.nanosec.ToDouble / (1000.0*1000.0*1000.0);
- attrs:= NSMutableDictionary.dictionaryWithCapacity( 1 );
- attrs.setValue_forKey( NSDate.dateWithTimeIntervalSince1970(seconds), NSFileCreationDate );
- nsPath:= StringToNSString( path );
- Result:= NSFileManager.defaultManager.setAttributes_ofItemAtPath_error( attrs, nsPath, nil );
- end;
- { TDarwinFileUtil }
- // the copyfile() api has two advantages:
- // 1. dramatically improve file copy speed on APFS
- // 2. supports copying macOS specific attributes
- // therefore, we should try copyfile() as much as possible on macOS
- class function TDarwinFileUtil.cloneFile( const fromPath: String; const toPath: String; const size: Int64 ): Boolean;
- const
- NO_CALLBACK_MAXSIZE = 20*1024*1024; // 20MB
- var
- flags: copyfile_flags_t;
- ret: Integer;
- begin
- Result:= False;
- flags:= COPYFILE_ALL;
- // call copyfile() when:
- // 1. macOS < 10.13 and filesize <= MAX_SIZE (copy only)
- // 2. macOS >= 10.13 and filesize > MAX_SIZE (clone only, fail fast)
- // 3. macOS >= 10.13 and filesize <= MAX_SIZE (try clone, then copy)
- if NSAppKitVersionNumber < NSAppKitVersionNumber10_13 then begin
- if size > NO_CALLBACK_MAXSIZE then
- Exit;
- end else begin
- if size > NO_CALLBACK_MAXSIZE then
- flags:= flags or COPYFILE_CLONE_FORCE or COPYFILE_UNLINK
- else
- flags:= flags or COPYFILE_CLONE;
- end;
- ret:= copyfile( pchar(fromPath), pchar(toPath), nil, flags );
- Result:= (ret=0);
- end;
- end.
|