@@ -25,6 +25,8 @@ use uucore::fs::dir_strip_dot_for_creation;
2525use uucore:: mode:: get_umask;
2626use uucore:: perms:: { Verbosity , VerbosityLevel , wrap_chown} ;
2727use uucore:: process:: { getegid, geteuid} ;
28+ #[ cfg( feature = "selinux" ) ]
29+ use uucore:: selinux:: { contexts_differ, set_selinux_security_context} ;
2830use uucore:: { format_usage, help_about, help_usage, show, show_error, show_if_err} ;
2931
3032#[ cfg( unix) ]
@@ -51,13 +53,12 @@ pub struct Behavior {
5153 create_leading : bool ,
5254 target_dir : Option < String > ,
5355 no_target_dir : bool ,
56+ preserve_context : bool ,
57+ context : Option < String > ,
5458}
5559
5660#[ derive( Error , Debug ) ]
5761enum InstallError {
58- #[ error( "Unimplemented feature: {0}" ) ]
59- Unimplemented ( String ) ,
60-
6162 #[ error( "{} with -d requires at least one argument." , uucore:: util_name( ) ) ]
6263 DirNeedsArg ,
6364
@@ -108,14 +109,15 @@ enum InstallError {
108109
109110 #[ error( "extra operand {}\n {}" , . 0 . quote( ) , . 1 . quote( ) ) ]
110111 ExtraOperand ( String , String ) ,
112+
113+ #[ cfg( feature = "selinux" ) ]
114+ #[ error( "{}" , . 0 ) ]
115+ SelinuxContextFailed ( String ) ,
111116}
112117
113118impl UError for InstallError {
114119 fn code ( & self ) -> i32 {
115- match self {
116- Self :: Unimplemented ( _) => 2 ,
117- _ => 1 ,
118- }
120+ 1
119121 }
120122
121123 fn usage ( & self ) -> bool {
@@ -172,8 +174,6 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
172174 . map ( |v| v. map ( ToString :: to_string) . collect ( ) )
173175 . unwrap_or_default ( ) ;
174176
175- check_unimplemented ( & matches) ?;
176-
177177 let behavior = behavior ( & matches) ?;
178178
179179 match behavior. main_function {
@@ -295,21 +295,20 @@ pub fn uu_app() -> Command {
295295 . action ( ArgAction :: SetTrue ) ,
296296 )
297297 . arg (
298- // TODO implement flag
299298 Arg :: new ( OPT_PRESERVE_CONTEXT )
300299 . short ( 'P' )
301300 . long ( OPT_PRESERVE_CONTEXT )
302- . help ( "(unimplemented) preserve security context" )
301+ . help ( "preserve security context" )
303302 . action ( ArgAction :: SetTrue ) ,
304303 )
305304 . arg (
306- // TODO implement flag
307305 Arg :: new ( OPT_CONTEXT )
308306 . short ( 'Z' )
309307 . long ( OPT_CONTEXT )
310- . help ( "(unimplemented) set security context of files and directories" )
308+ . help ( "set security context of files and directories" )
311309 . value_name ( "CONTEXT" )
312- . action ( ArgAction :: SetTrue ) ,
310+ . value_parser ( clap:: value_parser!( String ) )
311+ . num_args ( 0 ..=1 ) ,
313312 )
314313 . arg (
315314 Arg :: new ( ARG_FILES )
@@ -319,25 +318,6 @@ pub fn uu_app() -> Command {
319318 )
320319}
321320
322- /// Check for unimplemented command line arguments.
323- ///
324- /// Either return the degenerate Ok value, or an Err with string.
325- ///
326- /// # Errors
327- ///
328- /// Error datum is a string of the unimplemented argument.
329- ///
330- ///
331- fn check_unimplemented ( matches : & ArgMatches ) -> UResult < ( ) > {
332- if matches. get_flag ( OPT_PRESERVE_CONTEXT ) {
333- Err ( InstallError :: Unimplemented ( String :: from ( "--preserve-context, -P" ) ) . into ( ) )
334- } else if matches. get_flag ( OPT_CONTEXT ) {
335- Err ( InstallError :: Unimplemented ( String :: from ( "--context, -Z" ) ) . into ( ) )
336- } else {
337- Ok ( ( ) )
338- }
339- }
340-
341321/// Determine behavior, given command line arguments.
342322///
343323/// If successful, returns a filled-out Behavior struct.
@@ -415,6 +395,8 @@ fn behavior(matches: &ArgMatches) -> UResult<Behavior> {
415395 }
416396 } ;
417397
398+ let context = matches. get_one :: < String > ( OPT_CONTEXT ) . cloned ( ) ;
399+
418400 Ok ( Behavior {
419401 main_function,
420402 specified_mode,
@@ -435,6 +417,8 @@ fn behavior(matches: &ArgMatches) -> UResult<Behavior> {
435417 create_leading : matches. get_flag ( OPT_CREATE_LEADING ) ,
436418 target_dir,
437419 no_target_dir,
420+ preserve_context : matches. get_flag ( OPT_PRESERVE_CONTEXT ) ,
421+ context,
438422 } )
439423}
440424
@@ -485,6 +469,10 @@ fn directory(paths: &[String], b: &Behavior) -> UResult<()> {
485469 }
486470
487471 show_if_err ! ( chown_optional_user_group( path, b) ) ;
472+
473+ // Set SELinux context for directory if needed
474+ #[ cfg( feature = "selinux" ) ]
475+ show_if_err ! ( set_selinux_context( path, b) ) ;
488476 }
489477 // If the exit code was set, or show! has been called at least once
490478 // (which sets the exit code as well), function execution will end after
@@ -941,6 +929,14 @@ fn copy(from: &Path, to: &Path, b: &Behavior) -> UResult<()> {
941929 preserve_timestamps ( from, to) ?;
942930 }
943931
932+ #[ cfg( feature = "selinux" ) ]
933+ if b. preserve_context {
934+ uucore:: selinux:: preserve_security_context ( from, to)
935+ . map_err ( |e| InstallError :: SelinuxContextFailed ( e. to_string ( ) ) ) ?;
936+ } else if b. context . is_some ( ) {
937+ set_selinux_context ( to, b) ?;
938+ }
939+
944940 if b. verbose {
945941 print ! ( "{} -> {}" , from. quote( ) , to. quote( ) ) ;
946942 match backup_path {
@@ -1012,6 +1008,11 @@ fn need_copy(from: &Path, to: &Path, b: &Behavior) -> UResult<bool> {
10121008 return Ok ( true ) ;
10131009 }
10141010
1011+ #[ cfg( feature = "selinux" ) ]
1012+ if b. preserve_context && contexts_differ ( from, to) {
1013+ return Ok ( true ) ;
1014+ }
1015+
10151016 // TODO: if -P (#1809) and from/to contexts mismatch, return true.
10161017
10171018 // Check if the owner ID is specified and differs from the destination file's owner.
@@ -1042,3 +1043,13 @@ fn need_copy(from: &Path, to: &Path, b: &Behavior) -> UResult<bool> {
10421043
10431044 Ok ( false )
10441045}
1046+
1047+ #[ cfg( feature = "selinux" ) ]
1048+ fn set_selinux_context ( path : & Path , behavior : & Behavior ) -> UResult < ( ) > {
1049+ if !behavior. preserve_context && behavior. context . is_some ( ) {
1050+ // Use the provided context set by -Z/--context
1051+ set_selinux_security_context ( path, behavior. context . as_ref ( ) )
1052+ . map_err ( |e| InstallError :: SelinuxContextFailed ( e. to_string ( ) ) ) ?;
1053+ }
1054+ Ok ( ( ) )
1055+ }
0 commit comments