@@ -827,27 +827,51 @@ pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
827
827
Ok ( PathBuf :: from ( OsString :: from_vec ( buf) ) )
828
828
}
829
829
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
+
830
865
#[ cfg( not( any( target_os = "linux" , target_os = "android" ) ) ) ]
831
866
pub 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) ?;
837
868
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)
845
870
}
846
871
847
872
#[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
848
873
pub fn copy ( from : & Path , to : & Path ) -> io:: Result < u64 > {
849
874
use crate :: cmp;
850
- use crate :: fs:: File ;
851
875
use crate :: sync:: atomic:: { AtomicBool , Ordering } ;
852
876
853
877
// 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> {
873
897
)
874
898
}
875
899
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) ?;
887
901
888
902
let has_copy_file_range = HAS_COPY_FILE_RANGE . load ( Ordering :: Relaxed ) ;
889
903
let mut written = 0u64 ;
@@ -893,13 +907,14 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
893
907
let copy_result = unsafe {
894
908
// We actually don't have to adjust the offsets,
895
909
// 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
+ ) )
903
918
} ;
904
919
if let Err ( ref copy_err) = copy_result {
905
920
match copy_err. raw_os_error ( ) {
@@ -917,23 +932,24 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
917
932
Ok ( ret) => written += ret as u64 ,
918
933
Err ( err) => {
919
934
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
+ }
932
949
_ => return Err ( err) ,
933
950
}
934
951
}
935
952
}
936
953
}
937
- writer. set_permissions ( perm) ?;
938
954
Ok ( written)
939
955
}
0 commit comments