@@ -823,24 +823,28 @@ pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
823
823
Ok ( PathBuf :: from ( OsString :: from_vec ( buf) ) )
824
824
}
825
825
826
- fn open_and_set_permissions (
827
- from : & Path ,
826
+ fn open_from ( from : & Path ) -> io:: Result < ( crate :: fs:: File , crate :: fs:: Metadata ) > {
827
+ use crate :: fs:: File ;
828
+
829
+ let reader = File :: open ( from) ?;
830
+ let metadata = reader. metadata ( ) ?;
831
+ if !metadata. is_file ( ) {
832
+ return Err ( Error :: new (
833
+ ErrorKind :: InvalidInput ,
834
+ "the source path is not an existing regular file" ,
835
+ ) ) ;
836
+ }
837
+ Ok ( ( reader, metadata) )
838
+ }
839
+
840
+ fn open_to_and_set_permissions (
828
841
to : & Path ,
829
- ) -> io:: Result < ( crate :: fs:: File , crate :: fs:: File , u64 , crate :: fs:: Metadata ) > {
830
- use crate :: fs:: { File , OpenOptions } ;
842
+ reader_metadata : crate :: fs:: Metadata ,
843
+ ) -> io:: Result < ( crate :: fs:: File , crate :: fs:: Metadata ) > {
844
+ use crate :: fs:: OpenOptions ;
831
845
use crate :: os:: unix:: fs:: { OpenOptionsExt , PermissionsExt } ;
832
846
833
- let reader = File :: open ( from) ?;
834
- let ( perm, len) = {
835
- let metadata = reader. metadata ( ) ?;
836
- if !metadata. is_file ( ) {
837
- return Err ( Error :: new (
838
- ErrorKind :: InvalidInput ,
839
- "the source path is not an existing regular file" ,
840
- ) ) ;
841
- }
842
- ( metadata. permissions ( ) , metadata. len ( ) )
843
- } ;
847
+ let perm = reader_metadata. permissions ( ) ;
844
848
let writer = OpenOptions :: new ( )
845
849
// create the file with the correct mode right away
846
850
. mode ( perm. mode ( ) )
@@ -855,15 +859,16 @@ fn open_and_set_permissions(
855
859
// pipes/FIFOs or device nodes.
856
860
writer. set_permissions ( perm) ?;
857
861
}
858
- Ok ( ( reader , writer, len , writer_metadata) )
862
+ Ok ( ( writer, writer_metadata) )
859
863
}
860
864
861
865
#[ cfg( not( any( target_os = "linux" ,
862
866
target_os = "android" ,
863
867
target_os = "macos" ,
864
868
target_os = "ios" ) ) ) ]
865
869
pub fn copy ( from : & Path , to : & Path ) -> io:: Result < u64 > {
866
- let ( mut reader, mut writer, _, _) = open_and_set_permissions ( from, to) ?;
870
+ let ( mut reader, reader_metadata) = open_from ( from) ?;
871
+ let ( mut writer, _) = open_to_and_set_permissions ( to, reader_metadata) ?;
867
872
868
873
io:: copy ( & mut reader, & mut writer)
869
874
}
@@ -896,7 +901,9 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
896
901
)
897
902
}
898
903
899
- let ( mut reader, mut writer, len, _) = open_and_set_permissions ( from, to) ?;
904
+ let ( mut reader, reader_metadata) = open_from ( from) ?;
905
+ let len = reader_metadata. len ( ) ;
906
+ let ( mut writer, _) = open_to_and_set_permissions ( to, reader_metadata) ?;
900
907
901
908
let has_copy_file_range = HAS_COPY_FILE_RANGE . load ( Ordering :: Relaxed ) ;
902
909
let mut written = 0u64 ;
@@ -955,6 +962,8 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
955
962
956
963
#[ cfg( any( target_os = "macos" , target_os = "ios" ) ) ]
957
964
pub fn copy ( from : & Path , to : & Path ) -> io:: Result < u64 > {
965
+ use crate :: sync:: atomic:: { AtomicBool , Ordering } ;
966
+
958
967
const COPYFILE_ACL : u32 = 1 << 0 ;
959
968
const COPYFILE_STAT : u32 = 1 << 1 ;
960
969
const COPYFILE_XATTR : u32 = 1 << 2 ;
@@ -1000,7 +1009,48 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
1000
1009
}
1001
1010
}
1002
1011
1003
- let ( reader, writer, _, writer_metadata) = open_and_set_permissions ( from, to) ?;
1012
+ // MacOS prior to 10.12 don't support `fclonefileat`
1013
+ // We store the availability in a global to avoid unnecessary syscalls
1014
+ static HAS_FCLONEFILEAT : AtomicBool = AtomicBool :: new ( true ) ;
1015
+ syscall ! {
1016
+ fn fclonefileat(
1017
+ srcfd: libc:: c_int,
1018
+ dst_dirfd: libc:: c_int,
1019
+ dst: * const libc:: c_char,
1020
+ flags: libc:: c_int
1021
+ ) -> libc:: c_int
1022
+ }
1023
+
1024
+ let ( reader, reader_metadata) = open_from ( from) ?;
1025
+
1026
+ // Opportunistically attempt to create a copy-on-write clone of `from`
1027
+ // using `fclonefileat`.
1028
+ if HAS_FCLONEFILEAT . load ( Ordering :: Relaxed ) {
1029
+ let to = cstr ( to) ?;
1030
+ let clonefile_result = cvt ( unsafe {
1031
+ fclonefileat (
1032
+ reader. as_raw_fd ( ) ,
1033
+ libc:: AT_FDCWD ,
1034
+ to. as_ptr ( ) ,
1035
+ 0 ,
1036
+ )
1037
+ } ) ;
1038
+ match clonefile_result {
1039
+ Ok ( _) => return Ok ( reader_metadata. len ( ) ) ,
1040
+ Err ( err) => match err. raw_os_error ( ) {
1041
+ // `fclonefileat` will fail on non-APFS volumes, if the
1042
+ // destination already exists, or if the source and destination
1043
+ // are on different devices. In all these cases `fcopyfile`
1044
+ // should succeed.
1045
+ Some ( libc:: ENOTSUP ) | Some ( libc:: EEXIST ) | Some ( libc:: EXDEV ) => ( ) ,
1046
+ Some ( libc:: ENOSYS ) => HAS_FCLONEFILEAT . store ( false , Ordering :: Relaxed ) ,
1047
+ _ => return Err ( err) ,
1048
+ }
1049
+ }
1050
+ }
1051
+
1052
+ // Fall back to using `fcopyfile` if `fclonefileat` does not succeed.
1053
+ let ( writer, writer_metadata) = open_to_and_set_permissions ( to, reader_metadata) ?;
1004
1054
1005
1055
// We ensure that `FreeOnDrop` never contains a null pointer so it is
1006
1056
// always safe to call `copyfile_state_free`
0 commit comments