dcdarwin.pas 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. unit DCDarwin;
  2. {$mode delphi}
  3. {$packrecords c}
  4. {$pointermath on}
  5. {$modeswitch objectivec1}
  6. interface
  7. uses
  8. Classes, SysUtils, DCBasicTypes, CocoaAll, BaseUnix;
  9. const
  10. CLOSE_RANGE_CLOEXEC = (1 << 2);
  11. function CloseRange(first: cuint; last: cuint; flags: cint): cint; cdecl;
  12. function mbFileCopyXattr(const Source, Target: String): Boolean;
  13. // MacOS File Utils
  14. function MacosFileSetCreationTime( const path:String; const birthtime:TFileTimeEx ): Boolean;
  15. type
  16. { TDarwinFileUtil }
  17. TDarwinFileUtil = class
  18. class function cloneFile( const fromPath: String; const toPath: String; const size: Int64 ): Boolean;
  19. end;
  20. implementation
  21. uses
  22. DCUnix;
  23. type
  24. proc_fdinfo = record
  25. proc_fd: Int32;
  26. proc_fdtype: UInt32;
  27. end;
  28. Pproc_fdinfo = ^proc_fdinfo;
  29. const
  30. PROC_PIDLISTFDS = 1;
  31. PROC_PIDLISTFD_SIZE = SizeOf(proc_fdinfo);
  32. const
  33. NSAppKitVersionNumber10_13 = 1561;
  34. const
  35. COPYFILE_ACL = 1 shl 0;
  36. COPYFILE_STAT = 1 shl 1;
  37. COPYFILE_XATTR = 1 shl 2;
  38. COPYFILE_DATA = 1 shl 3;
  39. COPYFILE_SECURITY = COPYFILE_STAT or COPYFILE_ACL;
  40. COPYFILE_METADATA = COPYFILE_SECURITY or COPYFILE_XATTR;
  41. COPYFILE_ALL = COPYFILE_METADATA or COPYFILE_DATA;
  42. COPYFILE_UNLINK = 1 shl 21;
  43. COPYFILE_CLONE = 1 shl 24;
  44. COPYFILE_CLONE_FORCE = 1 shl 25;
  45. type
  46. copyfile_state_t_o = record
  47. end;
  48. copyfile_state_t = ^copyfile_state_t_o;
  49. copyfile_flags_t = UInt32;
  50. function copyfile( const fromPath: pchar; const toPath: pchar; state: copyfile_state_t; flags: copyfile_flags_t ): Integer;
  51. cdecl; external name 'copyfile';
  52. function proc_pidinfo(pid: cint; flavor: cint; arg: cuint64; buffer: pointer; buffersize: cint): cint; cdecl; external 'proc';
  53. function CloseRange(first: cuint; last: cuint; flags: cint): cint; cdecl;
  54. var
  55. I: cint;
  56. Handle: cint;
  57. ProcessId: TPid;
  58. bufferSize: cint;
  59. pidInfo: Pproc_fdinfo;
  60. begin
  61. Result:= -1;
  62. ProcessId:= FpGetpid;
  63. bufferSize:= proc_pidinfo(ProcessId, PROC_PIDLISTFDS, 0, nil, 0);
  64. pidInfo:= GetMem(bufferSize);
  65. if Assigned(pidInfo) then
  66. begin
  67. bufferSize:= proc_pidinfo(ProcessId, PROC_PIDLISTFDS, 0, pidInfo, bufferSize);
  68. for I:= 0 to (bufferSize div PROC_PIDLISTFD_SIZE) - 1 do
  69. begin
  70. Handle:= pidInfo[I].proc_fd;
  71. if (Handle >= first) and (Handle <= last) then
  72. begin
  73. if (flags and CLOSE_RANGE_CLOEXEC <> 0) then
  74. FileCloseOnExec(Handle)
  75. else begin
  76. FileClose(Handle);
  77. end;
  78. end;
  79. end;
  80. Result:= 0;
  81. FreeMem(pidInfo);
  82. end;
  83. end;
  84. function mbFileCopyXattr(const Source, Target: String): Boolean;
  85. var
  86. ret: Integer;
  87. begin
  88. Writeln( '>>3> mbFileCopyXattr' );
  89. ret:= copyfile( pchar(Source), pchar(Target), nil, COPYFILE_XATTR );
  90. fpseterrno( ret );
  91. Result:= (ret=0);
  92. end;
  93. function StringToNSString(const S: String): NSString;
  94. begin
  95. Result:= NSString(NSString.stringWithUTF8String(PAnsiChar(S)));
  96. end;
  97. function MacosFileSetCreationTime( const path:String; const birthtime:TFileTimeEx ): Boolean;
  98. var
  99. seconds: Double;
  100. attrs: NSMutableDictionary;
  101. nsPath: NSString;
  102. begin
  103. Result:= true;
  104. if birthtime = TFileTimeExNull then exit;
  105. seconds:= birthtime.sec.ToDouble + birthtime.nanosec.ToDouble / (1000.0*1000.0*1000.0);
  106. attrs:= NSMutableDictionary.dictionaryWithCapacity( 1 );
  107. attrs.setValue_forKey( NSDate.dateWithTimeIntervalSince1970(seconds), NSFileCreationDate );
  108. nsPath:= StringToNSString( path );
  109. Result:= NSFileManager.defaultManager.setAttributes_ofItemAtPath_error( attrs, nsPath, nil );
  110. end;
  111. { TDarwinFileUtil }
  112. // the copyfile() api has two advantages:
  113. // 1. dramatically improve file copy speed on APFS
  114. // 2. supports copying macOS specific attributes
  115. // therefore, we should try copyfile() as much as possible on macOS
  116. class function TDarwinFileUtil.cloneFile( const fromPath: String; const toPath: String; const size: Int64 ): Boolean;
  117. const
  118. NO_CALLBACK_MAXSIZE = 20*1024*1024; // 20MB
  119. var
  120. flags: copyfile_flags_t;
  121. ret: Integer;
  122. begin
  123. Result:= False;
  124. flags:= COPYFILE_ALL;
  125. // call copyfile() when:
  126. // 1. macOS < 10.13 and filesize <= MAX_SIZE (copy only)
  127. // 2. macOS >= 10.13 and filesize > MAX_SIZE (clone only, fail fast)
  128. // 3. macOS >= 10.13 and filesize <= MAX_SIZE (try clone, then copy)
  129. if NSAppKitVersionNumber < NSAppKitVersionNumber10_13 then begin
  130. if size > NO_CALLBACK_MAXSIZE then
  131. Exit;
  132. end else begin
  133. if size > NO_CALLBACK_MAXSIZE then
  134. flags:= flags or COPYFILE_CLONE_FORCE or COPYFILE_UNLINK
  135. else
  136. flags:= flags or COPYFILE_CLONE;
  137. end;
  138. ret:= copyfile( pchar(fromPath), pchar(toPath), nil, flags );
  139. Result:= (ret=0);
  140. end;
  141. end.