@@ -827,27 +827,51 @@ pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
827827 Ok ( PathBuf :: from ( OsString :: from_vec ( buf) ) )
828828}
829829
830+ fn open_and_set_permissions (
831+ from : & Path ,
832+ to : & Path ,
833+ ) -> io:: Result < ( crate :: fs:: File , crate :: fs:: File , u64 ) > {
834+ use crate :: fs:: { File , OpenOptions } ;
835+ use crate :: os:: unix:: fs:: { OpenOptionsExt , PermissionsExt } ;
836+
837+ let reader = File :: open ( from) ?;
838+ let ( perm, len) = {
839+ let metadata = reader. metadata ( ) ?;
840+ if !metadata. is_file ( ) {
841+ return Err ( Error :: new (
842+ ErrorKind :: InvalidInput ,
843+ "the source path is not an existing regular file" ,
844+ ) ) ;
845+ }
846+ ( metadata. permissions ( ) , metadata. len ( ) )
847+ } ;
848+ let writer = OpenOptions :: new ( )
849+ // create the file with the correct mode right away
850+ . mode ( perm. mode ( ) )
851+ . write ( true )
852+ . create ( true )
853+ . truncate ( true )
854+ . open ( to) ?;
855+ let writer_metadata = writer. metadata ( ) ?;
856+ if writer_metadata. is_file ( ) {
857+ // Set the correct file permissions, in case the file already existed.
858+ // Don't set the permissions on already existing non-files like
859+ // pipes/FIFOs or device nodes.
860+ writer. set_permissions ( perm) ?;
861+ }
862+ Ok ( ( reader, writer, len) )
863+ }
864+
830865#[ cfg( not( any( target_os = "linux" , target_os = "android" ) ) ) ]
831866pub fn copy ( from : & Path , to : & Path ) -> io:: Result < u64 > {
832- use crate :: fs:: File ;
833- if !from. is_file ( ) {
834- return Err ( Error :: new ( ErrorKind :: InvalidInput ,
835- "the source path is not an existing regular file" ) )
836- }
867+ let ( mut reader, mut writer, _) = open_and_set_permissions ( from, to) ?;
837868
838- let mut reader = File :: open ( from) ?;
839- let mut writer = File :: create ( to) ?;
840- let perm = reader. metadata ( ) ?. permissions ( ) ;
841-
842- let ret = io:: copy ( & mut reader, & mut writer) ?;
843- writer. set_permissions ( perm) ?;
844- Ok ( ret)
869+ io:: copy ( & mut reader, & mut writer)
845870}
846871
847872#[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
848873pub fn copy ( from : & Path , to : & Path ) -> io:: Result < u64 > {
849874 use crate :: cmp;
850- use crate :: fs:: File ;
851875 use crate :: sync:: atomic:: { AtomicBool , Ordering } ;
852876
853877 // Kernel prior to 4.5 don't have copy_file_range
@@ -873,17 +897,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
873897 )
874898 }
875899
876- if !from. is_file ( ) {
877- return Err ( Error :: new ( ErrorKind :: InvalidInput ,
878- "the source path is not an existing regular file" ) )
879- }
880-
881- let mut reader = File :: open ( from) ?;
882- let mut writer = File :: create ( to) ?;
883- let ( perm, len) = {
884- let metadata = reader. metadata ( ) ?;
885- ( metadata. permissions ( ) , metadata. size ( ) )
886- } ;
900+ let ( mut reader, mut writer, len) = open_and_set_permissions ( from, to) ?;
887901
888902 let has_copy_file_range = HAS_COPY_FILE_RANGE . load ( Ordering :: Relaxed ) ;
889903 let mut written = 0u64 ;
@@ -893,13 +907,14 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
893907 let copy_result = unsafe {
894908 // We actually don't have to adjust the offsets,
895909 // because copy_file_range adjusts the file offset automatically
896- cvt ( copy_file_range ( reader. as_raw_fd ( ) ,
897- ptr:: null_mut ( ) ,
898- writer. as_raw_fd ( ) ,
899- ptr:: null_mut ( ) ,
900- bytes_to_copy,
901- 0 )
902- )
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+ ) )
903918 } ;
904919 if let Err ( ref copy_err) = copy_result {
905920 match copy_err. raw_os_error ( ) {
@@ -917,23 +932,24 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
917932 Ok ( ret) => written += ret as u64 ,
918933 Err ( err) => {
919934 match err. raw_os_error ( ) {
920- Some ( os_err) if os_err == libc:: ENOSYS
921- || os_err == libc:: EXDEV
922- || os_err == libc:: EPERM => {
923- // Try fallback io::copy if either:
924- // - Kernel version is < 4.5 (ENOSYS)
925- // - Files are mounted on different fs (EXDEV)
926- // - copy_file_range is disallowed, for example by seccomp (EPERM)
927- assert_eq ! ( written, 0 ) ;
928- let ret = io:: copy ( & mut reader, & mut writer) ?;
929- writer. set_permissions ( perm) ?;
930- return Ok ( ret)
931- } ,
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+ }
932949 _ => return Err ( err) ,
933950 }
934951 }
935952 }
936953 }
937- writer. set_permissions ( perm) ?;
938954 Ok ( written)
939955}
0 commit comments