@@ -827,7 +827,10 @@ pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
827827 Ok ( PathBuf :: from ( OsString :: from_vec ( buf) ) )
828828}
829829
830- #[ cfg( not( any( target_os = "linux" , target_os = "android" ) ) ) ]
830+ #[ cfg( not( any( target_os = "linux" ,
831+ target_os = "android" ,
832+ target_os = "macos" ,
833+ target_os = "ios" ) ) ) ]
831834pub fn copy ( from : & Path , to : & Path ) -> io:: Result < u64 > {
832835 use crate :: fs:: File ;
833836 if !from. is_file ( ) {
@@ -954,3 +957,85 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
954957 }
955958 Ok ( written)
956959}
960+
961+ #[ cfg( any( target_os = "macos" , target_os = "ios" ) ) ]
962+ pub fn copy ( from : & Path , to : & Path ) -> io:: Result < u64 > {
963+ const COPYFILE_ACL : u32 = 1 << 0 ;
964+ const COPYFILE_STAT : u32 = 1 << 1 ;
965+ const COPYFILE_XATTR : u32 = 1 << 2 ;
966+ const COPYFILE_DATA : u32 = 1 << 3 ;
967+
968+ const COPYFILE_SECURITY : u32 = COPYFILE_STAT | COPYFILE_ACL ;
969+ const COPYFILE_METADATA : u32 = COPYFILE_SECURITY | COPYFILE_XATTR ;
970+ const COPYFILE_ALL : u32 = COPYFILE_METADATA | COPYFILE_DATA ;
971+
972+ const COPYFILE_STATE_COPIED : u32 = 8 ;
973+
974+ #[ allow( non_camel_case_types) ]
975+ type copyfile_state_t = * mut libc:: c_void ;
976+ #[ allow( non_camel_case_types) ]
977+ type copyfile_flags_t = u32 ;
978+
979+ extern "C" {
980+ fn copyfile (
981+ from : * const libc:: c_char ,
982+ to : * const libc:: c_char ,
983+ state : copyfile_state_t ,
984+ flags : copyfile_flags_t ,
985+ ) -> libc:: c_int ;
986+ fn copyfile_state_alloc ( ) -> copyfile_state_t ;
987+ fn copyfile_state_free ( state : copyfile_state_t ) -> libc:: c_int ;
988+ fn copyfile_state_get (
989+ state : copyfile_state_t ,
990+ flag : u32 ,
991+ dst : * mut libc:: c_void ,
992+ ) -> libc:: c_int ;
993+ }
994+
995+ struct FreeOnDrop ( copyfile_state_t ) ;
996+ impl Drop for FreeOnDrop {
997+ fn drop ( & mut self ) {
998+ // The code below ensures that `FreeOnDrop` is never a null pointer
999+ unsafe {
1000+ // `copyfile_state_free` returns -1 if the `to` or `from` files
1001+ // cannot be closed. However, this is not considerd this an
1002+ // error.
1003+ copyfile_state_free ( self . 0 ) ;
1004+ }
1005+ }
1006+ }
1007+
1008+ if !from. is_file ( ) {
1009+ return Err ( Error :: new ( ErrorKind :: InvalidInput ,
1010+ "the source path is not an existing regular file" ) )
1011+ }
1012+
1013+ // We ensure that `FreeOnDrop` never contains a null pointer so it is
1014+ // always safe to call `copyfile_state_free`
1015+ let state = unsafe {
1016+ let state = copyfile_state_alloc ( ) ;
1017+ if state. is_null ( ) {
1018+ return Err ( crate :: io:: Error :: last_os_error ( ) ) ;
1019+ }
1020+ FreeOnDrop ( state)
1021+ } ;
1022+
1023+ cvt ( unsafe {
1024+ copyfile (
1025+ cstr ( from) ?. as_ptr ( ) ,
1026+ cstr ( to) ?. as_ptr ( ) ,
1027+ state. 0 ,
1028+ COPYFILE_ALL ,
1029+ )
1030+ } ) ?;
1031+
1032+ let mut bytes_copied: libc:: off_t = 0 ;
1033+ cvt ( unsafe {
1034+ copyfile_state_get (
1035+ state. 0 ,
1036+ COPYFILE_STATE_COPIED ,
1037+ & mut bytes_copied as * mut libc:: off_t as * mut libc:: c_void ,
1038+ )
1039+ } ) ?;
1040+ Ok ( bytes_copied as u64 )
1041+ }
0 commit comments