@@ -14,10 +14,19 @@ interface
1414 CLOSE_RANGE_CLOEXEC = (1 << 2 );
1515
1616function CloseRange (first: cuint; last: cuint; flags: cint): cint; cdecl;
17+ function mbFileCopyXattr (const Source, Target: String): Boolean;
1718
1819// MacOS File Utils
1920function MacosFileSetCreationTime ( const path:String; const birthtime:TFileTimeEx ): Boolean;
2021
22+ type
23+
24+ { TDarwinFileUtil }
25+
26+ TDarwinFileUtil = class
27+ class function cloneFile ( const fromPath: String; const toPath: String; const size: Int64 ): Boolean;
28+ end ;
29+
2130implementation
2231
2332uses
@@ -34,6 +43,32 @@ proc_fdinfo = record
3443 PROC_PIDLISTFDS = 1 ;
3544 PROC_PIDLISTFD_SIZE = SizeOf(proc_fdinfo);
3645
46+ const
47+ NSAppKitVersionNumber10_13 = 1561 ;
48+
49+ const
50+ COPYFILE_ACL = 1 shl 0 ;
51+ COPYFILE_STAT = 1 shl 1 ;
52+ COPYFILE_XATTR = 1 shl 2 ;
53+ COPYFILE_DATA = 1 shl 3 ;
54+
55+ COPYFILE_SECURITY = COPYFILE_STAT or COPYFILE_ACL;
56+ COPYFILE_METADATA = COPYFILE_SECURITY or COPYFILE_XATTR;
57+ COPYFILE_ALL = COPYFILE_METADATA or COPYFILE_DATA;
58+
59+ COPYFILE_UNLINK = 1 shl 21 ;
60+ COPYFILE_CLONE = 1 shl 24 ;
61+ COPYFILE_CLONE_FORCE = 1 shl 25 ;
62+
63+ type
64+ copyfile_state_t_o = record
65+ end ;
66+ copyfile_state_t = ^copyfile_state_t_o;
67+ copyfile_flags_t = UInt32;
68+
69+ function copyfile ( const fromPath: pchar; const toPath: pchar; state: copyfile_state_t; flags: copyfile_flags_t ): Integer;
70+ cdecl; external name ' copyfile' ;
71+
3772function proc_pidinfo (pid: cint; flavor: cint; arg: cuint64; buffer: pointer; buffersize: cint): cint; cdecl; external ' proc' ;
3873
3974function CloseRange (first: cuint; last: cuint; flags: cint): cint; cdecl;
@@ -68,6 +103,16 @@ function CloseRange(first: cuint; last: cuint; flags: cint): cint; cdecl;
68103 end ;
69104end ;
70105
106+ function mbFileCopyXattr (const Source, Target: String): Boolean;
107+ var
108+ ret: Integer;
109+ begin
110+ Writeln( ' >>3> mbFileCopyXattr' );
111+ ret:= copyfile( pchar(Source), pchar(Target), nil , COPYFILE_XATTR );
112+ fpseterrno( ret );
113+ Result:= (ret=0 );
114+ end ;
115+
71116function StringToNSString (const S: String): NSString;
72117begin
73118 Result:= NSString(NSString.stringWithUTF8String(PAnsiChar(S)));
@@ -88,5 +133,39 @@ function MacosFileSetCreationTime( const path:String; const birthtime:TFileTimeE
88133 Result:= NSFileManager.defaultManager.setAttributes_ofItemAtPath_error( attrs, nsPath, nil );
89134end ;
90135
136+ { TDarwinFileUtil }
137+
138+ // the copyfile() api has two advantages:
139+ // 1. dramatically improve file copy speed on APFS
140+ // 2. supports copying macOS specific attributes
141+ // therefore, we should try copyfile() as much as possible on macOS
142+ class function TDarwinFileUtil.cloneFile ( const fromPath: String; const toPath: String; const size: Int64 ): Boolean;
143+ const
144+ NO_CALLBACK_MAXSIZE = 20 *1024 *1024 ; // 20MB
145+ var
146+ flags: copyfile_flags_t;
147+ ret: Integer;
148+ begin
149+ Result:= False;
150+ flags:= COPYFILE_ALL;
151+
152+ // call copyfile() when:
153+ // 1. macOS < 10.13 and filesize <= MAX_SIZE (copy only)
154+ // 2. macOS >= 10.13 and filesize > MAX_SIZE (clone only, fail fast)
155+ // 3. macOS >= 10.13 and filesize <= MAX_SIZE (try clone, then copy)
156+ if NSAppKitVersionNumber < NSAppKitVersionNumber10_13 then begin
157+ if size > NO_CALLBACK_MAXSIZE then
158+ Exit;
159+ end else begin
160+ if size > NO_CALLBACK_MAXSIZE then
161+ flags:= flags or COPYFILE_CLONE_FORCE or COPYFILE_UNLINK
162+ else
163+ flags:= flags or COPYFILE_CLONE;
164+ end ;
165+
166+ ret:= copyfile( pchar(fromPath), pchar(toPath), nil , flags );
167+ Result:= (ret=0 );
168+ end ;
169+
91170end .
92171
0 commit comments