@@ -1693,7 +1693,7 @@ impl OverwriteMode {
16931693/// Note: ENOTSUP/EOPNOTSUPP errors are silently ignored when not required, as per GNU cp
16941694/// documentation: "Try to preserve SELinux security context and extended attributes (xattr),
16951695/// but ignore any failure to do that and print no corresponding diagnostic."
1696- fn handle_preserve < F : Fn ( ) -> CopyResult < ( ) > > ( p : Preserve , f : F ) -> CopyResult < ( ) > {
1696+ fn handle_preserve < F : FnMut ( ) -> CopyResult < ( ) > > ( p : Preserve , mut f : F ) -> CopyResult < ( ) > {
16971697 match p {
16981698 Preserve :: No { .. } => { }
16991699 Preserve :: Yes { required } => {
@@ -1789,6 +1789,8 @@ pub(crate) fn copy_attributes(
17891789 let source_metadata = fs:: symlink_metadata ( source)
17901790 . map_err ( |e| CpError :: IoErrContext ( e, context_for ( source, dest) ) ) ?;
17911791
1792+ let mut failed_to_set_ownership = false ;
1793+
17921794 // Ownership must be changed first to avoid interfering with mode change.
17931795 #[ cfg( unix) ]
17941796 handle_preserve ( attributes. ownership , || -> CopyResult < ( ) > {
@@ -1821,6 +1823,7 @@ pub(crate) fn copy_attributes(
18211823 // gnu compatibility: cp doesn't report an error if it fails to set the ownership,
18221824 // and will fall back to changing only the gid if possible.
18231825 if try_chown ( Some ( dest_uid) ) . is_err ( ) {
1826+ failed_to_set_ownership = true ;
18241827 let _ = try_chown ( None ) ;
18251828 }
18261829 Ok ( ( ) )
@@ -1872,6 +1875,7 @@ pub(crate) fn copy_attributes(
18721875 attributes. mode ,
18731876 dest. is_dir ( ) ,
18741877 was_created,
1878+ failed_to_set_ownership,
18751879 source_metadata. permissions ( ) . mode ( ) ,
18761880 orig_umask,
18771881 )
@@ -2401,6 +2405,7 @@ fn calculate_dest_permissions(
24012405 options. attributes . mode ,
24022406 false ,
24032407 dest_metadata. is_none ( ) ,
2408+ false ,
24042409 source_metadata. permissions ( ) . mode ( ) ,
24052410 orig_umask,
24062411 )
@@ -2694,51 +2699,52 @@ fn is_stream(metadata: &Metadata) -> bool {
26942699}
26952700
26962701#[ cfg( unix) ]
2702+ #[ allow( clippy:: unnecessary_cast) ]
26972703/// Calculate the final permissions mode.
2698- ///
2699- /// If the dir/file was not created, then returns `None` except when preserving mode in which case
2700- /// the source mode is returned.
2701- ///
2702- /// Otherwise, see [`handle_created_mode`].
27032704fn handle_mode (
27042705 mode : Preserve ,
27052706 is_dir : bool ,
27062707 was_created : bool ,
2708+ failed_to_set_ownership : bool ,
27072709 source_mode : u32 ,
27082710 umask : u32 ,
27092711) -> Option < u32 > {
2710- if was_created {
2711- Some ( handle_created_mode ( mode, is_dir, source_mode, umask) )
2712- } else {
2713- match mode {
2714- Preserve :: Yes { required : true } => Some ( source_mode) ,
2715- _ => None ,
2716- }
2717- }
2718- }
2719-
2720- #[ cfg( unix) ]
2721- #[ inline]
2722- /// Calculate the final permissions mode when the dir/file is newly created.
2723- ///
2724- /// If no preserving mode, return 0o777(dir)/0o666(file) & !umask.
2725- /// If default, return source_mode & 0o777 & !umask.
2726- /// If preserving mode, return source_mode & 0o777.
2727- fn handle_created_mode ( mode : Preserve , is_dir : bool , source_mode : u32 , umask : u32 ) -> u32 {
2728- use libc:: { S_IRGRP , S_IROTH , S_IRUSR , S_IRWXG , S_IRWXO , S_IRWXU , S_IWGRP , S_IWOTH , S_IWUSR } ;
2729- #[ allow( clippy:: unnecessary_cast) ]
2730- const MODE_RW_UGO : u32 = ( S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH ) as u32 ;
2731- #[ allow( clippy:: unnecessary_cast) ]
2732- const S_IRWXUGO : u32 = ( S_IRWXU | S_IRWXG | S_IRWXO ) as u32 ;
2712+ match ( was_created, mode) {
2713+ // If preserving mode, return Some source_mode.
2714+ ( _, Preserve :: Yes { .. } ) => {
2715+ use libc:: { S_ISGID , S_ISUID } ;
2716+ const UID_GID_MASK : u32 = !( S_ISGID | S_ISUID ) as u32 ;
2717+
2718+ Some ( if failed_to_set_ownership {
2719+ // "Additionally, the -p option explicitly requires that all set-user-ID and
2720+ // set-group-ID permissions be discarded if any of the owner or group IDs cannot be
2721+ // set. This is to keep users from unintentionally giving away special privilege
2722+ // when copying programs."
2723+ source_mode & UID_GID_MASK
2724+ } else {
2725+ source_mode
2726+ } )
2727+ }
2728+ // If was not created (destination existed), return None
2729+ ( false , _) => None ,
2730+ // If was created (destination did not exist), return Some
2731+ // If no preserving mode, return 0o777(dir)/0o666(file) & !umask.
2732+ // If default, return source_mode & 0o777 & !umask.
2733+ ( true , Preserve :: No { explicit } ) => {
2734+ use libc:: {
2735+ S_IRGRP , S_IROTH , S_IRUSR , S_IRWXG , S_IRWXO , S_IRWXU , S_IWGRP , S_IWOTH , S_IWUSR ,
2736+ } ;
2737+ const MODE_RW_UGO : u32 =
2738+ ( S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH ) as u32 ;
2739+ const S_IRWXUGO : u32 = ( S_IRWXU | S_IRWXG | S_IRWXO ) as u32 ;
27332740
2734- if let Preserve :: No { explicit } = mode {
2735- ( if explicit {
2736- if is_dir { S_IRWXUGO } else { MODE_RW_UGO }
2737- } else {
2738- source_mode & S_IRWXUGO
2739- } ) & !umask
2740- } else {
2741- source_mode & S_IRWXUGO
2741+ let mode = if explicit {
2742+ if is_dir { S_IRWXUGO } else { MODE_RW_UGO }
2743+ } else {
2744+ source_mode & S_IRWXUGO
2745+ } ;
2746+ Some ( mode & !umask)
2747+ }
27422748 }
27432749}
27442750
0 commit comments