@@ -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 )
@@ -2391,6 +2395,7 @@ fn calculate_dest_permissions(
23912395 options. attributes . mode ,
23922396 false ,
23932397 dest_metadata. is_none ( ) ,
2398+ false ,
23942399 source_metadata. permissions ( ) . mode ( ) ,
23952400 orig_umask,
23962401 )
@@ -2684,51 +2689,52 @@ fn is_stream(metadata: &Metadata) -> bool {
26842689}
26852690
26862691#[ cfg( unix) ]
2692+ #[ allow( clippy:: unnecessary_cast) ]
26872693/// Calculate the final permissions mode.
2688- ///
2689- /// If the dir/file was not created, then returns `None` except when preserving mode in which case
2690- /// the source mode is returned.
2691- ///
2692- /// Otherwise, see [`handle_created_mode`].
26932694fn handle_mode (
26942695 mode : Preserve ,
26952696 is_dir : bool ,
26962697 was_created : bool ,
2698+ failed_to_set_ownership : bool ,
26972699 source_mode : u32 ,
26982700 umask : u32 ,
26992701) -> Option < u32 > {
2700- if was_created {
2701- Some ( handle_created_mode ( mode, is_dir, source_mode, umask) )
2702- } else {
2703- match mode {
2704- Preserve :: Yes { required : true } => Some ( source_mode) ,
2705- _ => None ,
2706- }
2707- }
2708- }
2709-
2710- #[ cfg( unix) ]
2711- #[ inline]
2712- /// Calculate the final permissions mode when the dir/file is newly created.
2713- ///
2714- /// If no preserving mode, return 0o777(dir)/0o666(file) & !umask.
2715- /// If default, return source_mode & 0o777 & !umask.
2716- /// If preserving mode, return source_mode & 0o777.
2717- fn handle_created_mode ( mode : Preserve , is_dir : bool , source_mode : u32 , umask : u32 ) -> u32 {
2718- use libc:: { S_IRGRP , S_IROTH , S_IRUSR , S_IRWXG , S_IRWXO , S_IRWXU , S_IWGRP , S_IWOTH , S_IWUSR } ;
2719- #[ allow( clippy:: unnecessary_cast) ]
2720- const MODE_RW_UGO : u32 = ( S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH ) as u32 ;
2721- #[ allow( clippy:: unnecessary_cast) ]
2722- const S_IRWXUGO : u32 = ( S_IRWXU | S_IRWXG | S_IRWXO ) as u32 ;
2702+ match ( was_created, mode) {
2703+ // If preserving mode, return Some source_mode.
2704+ ( _, Preserve :: Yes { .. } ) => {
2705+ use libc:: { S_ISGID , S_ISUID } ;
2706+ const UID_GID_MASK : u32 = !( S_ISGID | S_ISUID ) as u32 ;
2707+
2708+ Some ( if failed_to_set_ownership {
2709+ // "Additionally, the -p option explicitly requires that all set-user-ID and
2710+ // set-group-ID permissions be discarded if any of the owner or group IDs cannot be
2711+ // set. This is to keep users from unintentionally giving away special privilege
2712+ // when copying programs."
2713+ source_mode & UID_GID_MASK
2714+ } else {
2715+ source_mode
2716+ } )
2717+ }
2718+ // If was not created (destination existed), return None
2719+ ( false , _) => None ,
2720+ // If was created (destination did not exist), return Some
2721+ // If no preserving mode, return 0o777(dir)/0o666(file) & !umask.
2722+ // If default, return source_mode & 0o777 & !umask.
2723+ ( true , Preserve :: No { explicit } ) => {
2724+ use libc:: {
2725+ S_IRGRP , S_IROTH , S_IRUSR , S_IRWXG , S_IRWXO , S_IRWXU , S_IWGRP , S_IWOTH , S_IWUSR ,
2726+ } ;
2727+ const MODE_RW_UGO : u32 =
2728+ ( S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH ) as u32 ;
2729+ const S_IRWXUGO : u32 = ( S_IRWXU | S_IRWXG | S_IRWXO ) as u32 ;
27232730
2724- if let Preserve :: No { explicit } = mode {
2725- ( if explicit {
2726- if is_dir { S_IRWXUGO } else { MODE_RW_UGO }
2727- } else {
2728- source_mode & S_IRWXUGO
2729- } ) & !umask
2730- } else {
2731- source_mode & S_IRWXUGO
2731+ let mode = if explicit {
2732+ if is_dir { S_IRWXUGO } else { MODE_RW_UGO }
2733+ } else {
2734+ source_mode & S_IRWXUGO
2735+ } ;
2736+ Some ( mode & !umask)
2737+ }
27322738 }
27332739}
27342740
0 commit comments