@@ -827,30 +827,54 @@ 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 , crate :: fs:: Metadata ) > {
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, writer_metadata) )
863
+ }
864
+
830
865
#[ cfg( not( any( target_os = "linux" ,
831
866
target_os = "android" ,
832
867
target_os = "macos" ,
833
868
target_os = "ios" ) ) ) ]
834
869
pub fn copy ( from : & Path , to : & Path ) -> io:: Result < u64 > {
835
- use crate :: fs:: File ;
836
- if !from. is_file ( ) {
837
- return Err ( Error :: new ( ErrorKind :: InvalidInput ,
838
- "the source path is not an existing regular file" ) )
839
- }
870
+ let ( mut reader, mut writer, _, _) = open_and_set_permissions ( from, to) ?;
840
871
841
- let mut reader = File :: open ( from) ?;
842
- let mut writer = File :: create ( to) ?;
843
- let perm = reader. metadata ( ) ?. permissions ( ) ;
844
-
845
- let ret = io:: copy ( & mut reader, & mut writer) ?;
846
- writer. set_permissions ( perm) ?;
847
- Ok ( ret)
872
+ io:: copy ( & mut reader, & mut writer)
848
873
}
849
874
850
875
#[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
851
876
pub fn copy ( from : & Path , to : & Path ) -> io:: Result < u64 > {
852
877
use crate :: cmp;
853
- use crate :: fs:: File ;
854
878
use crate :: sync:: atomic:: { AtomicBool , Ordering } ;
855
879
856
880
// Kernel prior to 4.5 don't have copy_file_range
@@ -876,17 +900,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
876
900
)
877
901
}
878
902
879
- if !from. is_file ( ) {
880
- return Err ( Error :: new ( ErrorKind :: InvalidInput ,
881
- "the source path is not an existing regular file" ) )
882
- }
883
-
884
- let mut reader = File :: open ( from) ?;
885
- let mut writer = File :: create ( to) ?;
886
- let ( perm, len) = {
887
- let metadata = reader. metadata ( ) ?;
888
- ( metadata. permissions ( ) , metadata. size ( ) )
889
- } ;
903
+ let ( mut reader, mut writer, len, _) = open_and_set_permissions ( from, to) ?;
890
904
891
905
let has_copy_file_range = HAS_COPY_FILE_RANGE . load ( Ordering :: Relaxed ) ;
892
906
let mut written = 0u64 ;
@@ -896,13 +910,14 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
896
910
let copy_result = unsafe {
897
911
// We actually don't have to adjust the offsets,
898
912
// because copy_file_range adjusts the file offset automatically
899
- cvt ( copy_file_range ( reader. as_raw_fd ( ) ,
900
- ptr:: null_mut ( ) ,
901
- writer. as_raw_fd ( ) ,
902
- ptr:: null_mut ( ) ,
903
- bytes_to_copy,
904
- 0 )
905
- )
913
+ cvt ( copy_file_range (
914
+ reader. as_raw_fd ( ) ,
915
+ ptr:: null_mut ( ) ,
916
+ writer. as_raw_fd ( ) ,
917
+ ptr:: null_mut ( ) ,
918
+ bytes_to_copy,
919
+ 0 ,
920
+ ) )
906
921
} ;
907
922
if let Err ( ref copy_err) = copy_result {
908
923
match copy_err. raw_os_error ( ) {
@@ -920,24 +935,25 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
920
935
Ok ( ret) => written += ret as u64 ,
921
936
Err ( err) => {
922
937
match err. raw_os_error ( ) {
923
- Some ( os_err) if os_err == libc:: ENOSYS
924
- || os_err == libc:: EXDEV
925
- || os_err == libc:: EPERM => {
926
- // Try fallback io::copy if either:
927
- // - Kernel version is < 4.5 (ENOSYS)
928
- // - Files are mounted on different fs (EXDEV)
929
- // - copy_file_range is disallowed, for example by seccomp (EPERM)
930
- assert_eq ! ( written, 0 ) ;
931
- let ret = io:: copy ( & mut reader, & mut writer) ?;
932
- writer. set_permissions ( perm) ?;
933
- return Ok ( ret)
934
- } ,
938
+ Some ( os_err)
939
+ if os_err == libc:: ENOSYS
940
+ || os_err == libc:: EXDEV
941
+ || os_err == libc:: EINVAL
942
+ || os_err == libc:: EPERM =>
943
+ {
944
+ // Try fallback io::copy if either:
945
+ // - Kernel version is < 4.5 (ENOSYS)
946
+ // - Files are mounted on different fs (EXDEV)
947
+ // - copy_file_range is disallowed, for example by seccomp (EPERM)
948
+ // - copy_file_range cannot be used with pipes or device nodes (EINVAL)
949
+ assert_eq ! ( written, 0 ) ;
950
+ return io:: copy ( & mut reader, & mut writer) ;
951
+ }
935
952
_ => return Err ( err) ,
936
953
}
937
954
}
938
955
}
939
956
}
940
- writer. set_permissions ( perm) ?;
941
957
Ok ( written)
942
958
}
943
959
@@ -960,9 +976,9 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
960
976
type copyfile_flags_t = u32 ;
961
977
962
978
extern "C" {
963
- fn copyfile (
964
- from : * const libc:: c_char ,
965
- to : * const libc:: c_char ,
979
+ fn fcopyfile (
980
+ from : libc:: c_int ,
981
+ to : libc:: c_int ,
966
982
state : copyfile_state_t ,
967
983
flags : copyfile_flags_t ,
968
984
) -> libc:: c_int ;
@@ -988,10 +1004,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
988
1004
}
989
1005
}
990
1006
991
- if !from. is_file ( ) {
992
- return Err ( Error :: new ( ErrorKind :: InvalidInput ,
993
- "the source path is not an existing regular file" ) )
994
- }
1007
+ let ( reader, writer, _, writer_metadata) = open_and_set_permissions ( from, to) ?;
995
1008
996
1009
// We ensure that `FreeOnDrop` never contains a null pointer so it is
997
1010
// always safe to call `copyfile_state_free`
@@ -1003,12 +1016,18 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
1003
1016
FreeOnDrop ( state)
1004
1017
} ;
1005
1018
1019
+ let flags = if writer_metadata. is_file ( ) {
1020
+ COPYFILE_ALL
1021
+ } else {
1022
+ COPYFILE_DATA
1023
+ } ;
1024
+
1006
1025
cvt ( unsafe {
1007
- copyfile (
1008
- cstr ( from ) ? . as_ptr ( ) ,
1009
- cstr ( to ) ? . as_ptr ( ) ,
1026
+ fcopyfile (
1027
+ reader . as_raw_fd ( ) ,
1028
+ writer . as_raw_fd ( ) ,
1010
1029
state. 0 ,
1011
- COPYFILE_ALL ,
1030
+ flags ,
1012
1031
)
1013
1032
} ) ?;
1014
1033
0 commit comments