瀏覽代碼

UPD: expand the applicable scope of TDarwinFileUtil.cloneFile(), macOS < 10.13 / APFS / File Size

rich2014 2 月之前
父節點
當前提交
132b74341e
共有 2 個文件被更改,包括 47 次插入13 次删除
  1. 18 9
      src/filesources/filesystem/ufilesystemutil.pas
  2. 29 4
      src/platform/unix/darwin/udarwinfileutil.pas

+ 18 - 9
src/filesources/filesystem/ufilesystemutil.pas

@@ -654,17 +654,26 @@ var
     end;
   end;
 
-begin
-{$IFDEF DARWIN}
-  if Mode = fsohcmDefault then begin
-    Result:= TDarwinFileUtil.cloneFile( SourceFile.FullPath, TargetFileName );
-    if Result then begin
-      BytesRead:= SourceFile.Size;
-      doUpdate;
-      Exit;
+  {$IFDEF DARWIN}
+  function tryCloneFirst: Boolean;
+  begin
+    Result:= False;
+    if Mode = fsohcmDefault then begin
+      Result:= TDarwinFileUtil.cloneFile( SourceFile.FullPath, TargetFileName, SourceFile.Size );
+      if Result then begin
+        BytesRead:= SourceFile.Size;
+        doUpdate;
+      end;
     end;
   end;
-{$ENDIF}
+  {$ENDIF}
+
+begin
+  {$IFDEF DARWIN}
+    Result:= tryCloneFirst;
+    if Result then
+      Exit;
+  {$ENDIF}
 
   Result := False;
 

+ 29 - 4
src/platform/unix/darwin/udarwinfileutil.pas

@@ -6,14 +6,15 @@ unit uDarwinFileUtil;
 interface
 
 uses
-  Classes, SysUtils;
+  Classes, SysUtils,
+  CocoaAll, Cocoa_Extra;
 
 type
 
   { TDarwinFileUtil }
 
   TDarwinFileUtil = class
-    class function cloneFile( const fromPath: String; const toPath: String ): Boolean;
+    class function cloneFile( const fromPath: String; const toPath: String; const size: Int64 ): Boolean;
   end;
 
 implementation
@@ -43,11 +44,35 @@ const
 
 { TDarwinFileUtil }
 
-class function TDarwinFileUtil.cloneFile( const fromPath: String; const toPath: String ): Boolean;
+// 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
-  ret:= copyfile( pchar(fromPath), pchar(toPath), nil, COPYFILE_ALL or COPYFILE_UNLINK or COPYFILE_CLONE_FORCE );
+  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;