@@ -62,6 +62,7 @@ pub struct Behavior {
6262 preserve_context : bool ,
6363 context : Option < String > ,
6464 default_context : bool ,
65+ unprivileged : bool ,
6566}
6667
6768#[ derive( Error , Debug ) ]
@@ -163,6 +164,7 @@ static OPT_VERBOSE: &str = "verbose";
163164static OPT_PRESERVE_CONTEXT : & str = "preserve-context" ;
164165static OPT_CONTEXT : & str = "context" ;
165166static OPT_DEFAULT_CONTEXT : & str = "default-context" ;
167+ static OPT_UNPRIVILEGED : & str = "unprivileged" ;
166168
167169static ARG_FILES : & str = "files" ;
168170
@@ -317,6 +319,13 @@ pub fn uu_app() -> Command {
317319 . value_hint ( clap:: ValueHint :: AnyPath )
318320 . value_parser ( clap:: value_parser!( OsString ) ) ,
319321 )
322+ . arg (
323+ Arg :: new ( OPT_UNPRIVILEGED )
324+ . short ( 'U' )
325+ . long ( OPT_UNPRIVILEGED )
326+ . help ( translate ! ( "install-help-unprivileged" ) )
327+ . action ( ArgAction :: SetTrue ) ,
328+ )
320329}
321330
322331/// Determine behavior, given command line arguments.
@@ -416,6 +425,7 @@ fn behavior(matches: &ArgMatches) -> UResult<Behavior> {
416425
417426 let context = matches. get_one :: < String > ( OPT_CONTEXT ) . cloned ( ) ;
418427 let default_context = matches. get_flag ( OPT_DEFAULT_CONTEXT ) ;
428+ let unprivileged = matches. get_flag ( OPT_UNPRIVILEGED ) ;
419429
420430 Ok ( Behavior {
421431 main_function,
@@ -439,6 +449,7 @@ fn behavior(matches: &ArgMatches) -> UResult<Behavior> {
439449 preserve_context : matches. get_flag ( OPT_PRESERVE_CONTEXT ) ,
440450 context,
441451 default_context,
452+ unprivileged,
442453 } )
443454}
444455
@@ -479,7 +490,7 @@ fn directory(paths: &[OsString], b: &Behavior) -> UResult<()> {
479490
480491 // Set SELinux context for all created directories if needed
481492 #[ cfg( feature = "selinux" ) ]
482- if b . context . is_some ( ) || b . default_context {
493+ if should_set_selinux_context ( b ) {
483494 let context = get_context_for_selinux ( b) ;
484495 set_selinux_context_for_directories_install ( path_to_create. as_path ( ) , context) ;
485496 }
@@ -498,15 +509,17 @@ fn directory(paths: &[OsString], b: &Behavior) -> UResult<()> {
498509 continue ;
499510 }
500511
501- show_if_err ! ( chown_optional_user_group( path, b) ) ;
512+ if !b. unprivileged {
513+ show_if_err ! ( chown_optional_user_group( path, b) ) ;
502514
503- // Set SELinux context for directory if needed
504- #[ cfg( feature = "selinux" ) ]
505- if b. default_context {
506- show_if_err ! ( set_selinux_default_context( path) ) ;
507- } else if b. context . is_some ( ) {
508- let context = get_context_for_selinux ( b) ;
509- show_if_err ! ( set_selinux_security_context( path, context) ) ;
515+ // Set SELinux context for directory if needed
516+ #[ cfg( feature = "selinux" ) ]
517+ if b. default_context {
518+ show_if_err ! ( set_selinux_default_context( path) ) ;
519+ } else if b. context . is_some ( ) {
520+ let context = get_context_for_selinux ( b) ;
521+ show_if_err ! ( set_selinux_security_context( path, context) ) ;
522+ }
510523 }
511524 }
512525 // If the exit code was set, or show! has been called at least once
@@ -628,7 +641,7 @@ fn standard(mut paths: Vec<OsString>, b: &Behavior) -> UResult<()> {
628641
629642 // Set SELinux context for all created directories if needed
630643 #[ cfg( feature = "selinux" ) ]
631- if b . context . is_some ( ) || b . default_context {
644+ if should_set_selinux_context ( b ) {
632645 let context = get_context_for_selinux ( b) ;
633646 set_selinux_context_for_directories_install ( to_create, context) ;
634647 }
@@ -918,7 +931,9 @@ fn set_ownership_and_permissions(to: &Path, b: &Behavior) -> UResult<()> {
918931 return Err ( InstallError :: ChmodFailed ( to. to_path_buf ( ) ) . into ( ) ) ;
919932 }
920933
921- chown_optional_user_group ( to, b) ?;
934+ if !b. unprivileged {
935+ chown_optional_user_group ( to, b) ?;
936+ }
922937
923938 Ok ( ( ) )
924939}
@@ -984,16 +999,18 @@ fn copy(from: &Path, to: &Path, b: &Behavior) -> UResult<()> {
984999 }
9851000
9861001 #[ cfg( feature = "selinux" ) ]
987- if b. preserve_context {
988- uucore:: selinux:: preserve_security_context ( from, to)
989- . map_err ( |e| InstallError :: SelinuxContextFailed ( e. to_string ( ) ) ) ?;
990- } else if b. default_context {
991- set_selinux_default_context ( to)
992- . map_err ( |e| InstallError :: SelinuxContextFailed ( e. to_string ( ) ) ) ?;
993- } else if b. context . is_some ( ) {
994- let context = get_context_for_selinux ( b) ;
995- set_selinux_security_context ( to, context)
996- . map_err ( |e| InstallError :: SelinuxContextFailed ( e. to_string ( ) ) ) ?;
1002+ if !b. unprivileged {
1003+ if b. preserve_context {
1004+ uucore:: selinux:: preserve_security_context ( from, to)
1005+ . map_err ( |e| InstallError :: SelinuxContextFailed ( e. to_string ( ) ) ) ?;
1006+ } else if b. default_context {
1007+ set_selinux_default_context ( to)
1008+ . map_err ( |e| InstallError :: SelinuxContextFailed ( e. to_string ( ) ) ) ?;
1009+ } else if b. context . is_some ( ) {
1010+ let context = get_context_for_selinux ( b) ;
1011+ set_selinux_security_context ( to, context)
1012+ . map_err ( |e| InstallError :: SelinuxContextFailed ( e. to_string ( ) ) ) ?;
1013+ }
9971014 }
9981015
9991016 if b. verbose {
@@ -1022,6 +1039,11 @@ fn get_context_for_selinux(b: &Behavior) -> Option<&String> {
10221039 }
10231040}
10241041
1042+ #[ cfg( feature = "selinux" ) ]
1043+ fn should_set_selinux_context ( b : & Behavior ) -> bool {
1044+ !b. unprivileged && ( b. context . is_some ( ) || b. default_context )
1045+ }
1046+
10251047/// Check if a file needs to be copied due to ownership differences when no explicit group is specified.
10261048/// Returns true if the destination file's ownership would differ from what it should be after installation.
10271049fn needs_copy_for_ownership ( to : & Path , to_meta : & fs:: Metadata ) -> bool {
@@ -1113,25 +1135,25 @@ fn need_copy(from: &Path, to: &Path, b: &Behavior) -> bool {
11131135 }
11141136
11151137 #[ cfg( feature = "selinux" ) ]
1116- if b. preserve_context && contexts_differ ( from, to) {
1138+ if !b . unprivileged && b. preserve_context && contexts_differ ( from, to) {
11171139 return true ;
11181140 }
11191141
11201142 // TODO: if -P (#1809) and from/to contexts mismatch, return true.
11211143
11221144 // Check if the owner ID is specified and differs from the destination file's owner.
11231145 if let Some ( owner_id) = b. owner_id {
1124- if owner_id != to_meta. uid ( ) {
1146+ if !b . unprivileged && owner_id != to_meta. uid ( ) {
11251147 return true ;
11261148 }
11271149 }
11281150
11291151 // Check if the group ID is specified and differs from the destination file's group.
11301152 if let Some ( group_id) = b. group_id {
1131- if group_id != to_meta. gid ( ) {
1153+ if !b . unprivileged && group_id != to_meta. gid ( ) {
11321154 return true ;
11331155 }
1134- } else if needs_copy_for_ownership ( to, & to_meta) {
1156+ } else if !b . unprivileged && needs_copy_for_ownership ( to, & to_meta) {
11351157 return true ;
11361158 }
11371159
0 commit comments