@@ -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
@@ -498,7 +509,9 @@ 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) ) ;
514+ }
502515
503516 // Set SELinux context for directory if needed
504517 #[ cfg( feature = "selinux" ) ]
@@ -922,7 +935,9 @@ fn set_ownership_and_permissions(to: &Path, b: &Behavior) -> UResult<()> {
922935 return Err ( InstallError :: ChmodFailed ( to. to_path_buf ( ) ) . into ( ) ) ;
923936 }
924937
925- chown_optional_user_group ( to, b) ?;
938+ if !b. unprivileged {
939+ chown_optional_user_group ( to, b) ?;
940+ }
926941
927942 Ok ( ( ) )
928943}
@@ -1124,18 +1139,21 @@ fn need_copy(from: &Path, to: &Path, b: &Behavior) -> bool {
11241139 // TODO: if -P (#1809) and from/to contexts mismatch, return true.
11251140
11261141 // Check if the owner ID is specified and differs from the destination file's owner.
1127- if let Some ( owner_id) = b. owner_id {
1128- if owner_id != to_meta. uid ( ) {
1129- return true ;
1130- }
1142+ if !b. unprivileged
1143+ && let Some ( owner_id) = b. owner_id
1144+ && owner_id != to_meta. uid ( )
1145+ {
1146+ return true ;
11311147 }
11321148
11331149 // Check if the group ID is specified and differs from the destination file's group.
1134- if let Some ( group_id) = b. group_id {
1150+ if !b. unprivileged
1151+ && let Some ( group_id) = b. group_id
1152+ {
11351153 if group_id != to_meta. gid ( ) {
11361154 return true ;
11371155 }
1138- } else if needs_copy_for_ownership ( to, & to_meta) {
1156+ } else if !b . unprivileged && needs_copy_for_ownership ( to, & to_meta) {
11391157 return true ;
11401158 }
11411159
0 commit comments