@@ -827,13 +827,14 @@ 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" ) ) ) ]
831- pub fn copy ( from : & Path , to : & Path ) -> io:: Result < u64 > {
830+ fn open_and_set_permissions (
831+ from : & Path ,
832+ to : & Path ,
833+ ) -> io:: Result < ( crate :: fs:: File , crate :: fs:: File , u64 ) > {
832834 use crate :: fs:: { File , OpenOptions } ;
833835 use crate :: os:: unix:: fs:: { OpenOptionsExt , PermissionsExt } ;
834836
835- let mut reader = File :: open ( from) ?;
836-
837+ let reader = File :: open ( from) ?;
837838 let ( perm, len) = {
838839 let metadata = reader. metadata ( ) ?;
839840 if !metadata. is_file ( ) {
@@ -844,30 +845,33 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
844845 }
845846 ( metadata. permissions ( ) , metadata. len ( ) )
846847 } ;
847-
848- let mut writer = OpenOptions :: new ( )
848+ let writer = OpenOptions :: new ( )
849849 // create the file with the correct mode right away
850850 . mode ( perm. mode ( ) )
851851 . write ( true )
852852 . create ( true )
853853 . truncate ( true )
854854 . open ( to) ?;
855-
856855 let writer_metadata = writer. metadata ( ) ?;
857856 if writer_metadata. is_file ( ) {
858857 // Set the correct file permissions, in case the file already existed.
859858 // Don't set the permissions on already existing non-files like
860859 // pipes/FIFOs or device nodes.
861860 writer. set_permissions ( perm) ?;
862861 }
862+ Ok ( ( reader, writer, len) )
863+ }
864+
865+ #[ cfg( not( any( target_os = "linux" , target_os = "android" ) ) ) ]
866+ pub fn copy ( from : & Path , to : & Path ) -> io:: Result < u64 > {
867+ let ( mut reader, mut writer, _) = open_and_set_permissions ( from, to) ?;
863868
864869 io:: copy ( & mut reader, & mut writer)
865870}
866871
867872#[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
868873pub fn copy ( from : & Path , to : & Path ) -> io:: Result < u64 > {
869874 use crate :: cmp;
870- use crate :: fs:: File ;
871875 use crate :: sync:: atomic:: { AtomicBool , Ordering } ;
872876
873877 // Kernel prior to 4.5 don't have copy_file_range
@@ -893,17 +897,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
893897 )
894898 }
895899
896- if !from. is_file ( ) {
897- return Err ( Error :: new ( ErrorKind :: InvalidInput ,
898- "the source path is not an existing regular file" ) )
899- }
900-
901- let mut reader = File :: open ( from) ?;
902- let mut writer = File :: create ( to) ?;
903- let ( perm, len) = {
904- let metadata = reader. metadata ( ) ?;
905- ( metadata. permissions ( ) , metadata. size ( ) )
906- } ;
900+ let ( mut reader, mut writer, len) = open_and_set_permissions ( from, to) ?;
907901
908902 let has_copy_file_range = HAS_COPY_FILE_RANGE . load ( Ordering :: Relaxed ) ;
909903 let mut written = 0u64 ;
@@ -913,13 +907,14 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
913907 let copy_result = unsafe {
914908 // We actually don't have to adjust the offsets,
915909 // because copy_file_range adjusts the file offset automatically
916- cvt ( copy_file_range ( reader. as_raw_fd ( ) ,
917- ptr:: null_mut ( ) ,
918- writer. as_raw_fd ( ) ,
919- ptr:: null_mut ( ) ,
920- bytes_to_copy,
921- 0 )
922- )
910+ cvt ( copy_file_range (
911+ reader. as_raw_fd ( ) ,
912+ ptr:: null_mut ( ) ,
913+ writer. as_raw_fd ( ) ,
914+ ptr:: null_mut ( ) ,
915+ bytes_to_copy,
916+ 0 ,
917+ ) )
923918 } ;
924919 if let Err ( ref copy_err) = copy_result {
925920 match copy_err. raw_os_error ( ) {
@@ -937,23 +932,24 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
937932 Ok ( ret) => written += ret as u64 ,
938933 Err ( err) => {
939934 match err. raw_os_error ( ) {
940- Some ( os_err) if os_err == libc:: ENOSYS
941- || os_err == libc:: EXDEV
942- || os_err == libc:: EPERM => {
943- // Try fallback io::copy if either:
944- // - Kernel version is < 4.5 (ENOSYS)
945- // - Files are mounted on different fs (EXDEV)
946- // - copy_file_range is disallowed, for example by seccomp (EPERM)
947- assert_eq ! ( written, 0 ) ;
948- let ret = io:: copy ( & mut reader, & mut writer) ?;
949- writer. set_permissions ( perm) ?;
950- return Ok ( ret)
951- } ,
935+ Some ( os_err)
936+ if os_err == libc:: ENOSYS
937+ || os_err == libc:: EXDEV
938+ || os_err == libc:: EINVAL
939+ || os_err == libc:: EPERM =>
940+ {
941+ // Try fallback io::copy if either:
942+ // - Kernel version is < 4.5 (ENOSYS)
943+ // - Files are mounted on different fs (EXDEV)
944+ // - copy_file_range is disallowed, for example by seccomp (EPERM)
945+ // - copy_file_range cannot be used with pipes or device nodes (EINVAL)
946+ assert_eq ! ( written, 0 ) ;
947+ return io:: copy ( & mut reader, & mut writer) ;
948+ }
952949 _ => return Err ( err) ,
953950 }
954951 }
955952 }
956953 }
957- writer. set_permissions ( perm) ?;
958954 Ok ( written)
959955}
0 commit comments