@@ -25,6 +25,8 @@ use uucore::fs::dir_strip_dot_for_creation;
25
25
use uucore:: mode:: get_umask;
26
26
use uucore:: perms:: { Verbosity , VerbosityLevel , wrap_chown} ;
27
27
use uucore:: process:: { getegid, geteuid} ;
28
+ #[ cfg( feature = "selinux" ) ]
29
+ use uucore:: selinux:: { contexts_differ, set_selinux_security_context} ;
28
30
use uucore:: { format_usage, help_about, help_usage, show, show_error, show_if_err} ;
29
31
30
32
#[ cfg( unix) ]
@@ -51,13 +53,12 @@ pub struct Behavior {
51
53
create_leading : bool ,
52
54
target_dir : Option < String > ,
53
55
no_target_dir : bool ,
56
+ preserve_context : bool ,
57
+ context : Option < String > ,
54
58
}
55
59
56
60
#[ derive( Error , Debug ) ]
57
61
enum InstallError {
58
- #[ error( "Unimplemented feature: {0}" ) ]
59
- Unimplemented ( String ) ,
60
-
61
62
#[ error( "{} with -d requires at least one argument." , uucore:: util_name( ) ) ]
62
63
DirNeedsArg ,
63
64
@@ -108,14 +109,15 @@ enum InstallError {
108
109
109
110
#[ error( "extra operand {}\n {}" , . 0 . quote( ) , . 1 . quote( ) ) ]
110
111
ExtraOperand ( String , String ) ,
112
+
113
+ #[ cfg( feature = "selinux" ) ]
114
+ #[ error( "{}" , . 0 ) ]
115
+ SelinuxContextFailed ( String ) ,
111
116
}
112
117
113
118
impl UError for InstallError {
114
119
fn code ( & self ) -> i32 {
115
- match self {
116
- Self :: Unimplemented ( _) => 2 ,
117
- _ => 1 ,
118
- }
120
+ 1
119
121
}
120
122
121
123
fn usage ( & self ) -> bool {
@@ -172,8 +174,6 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
172
174
. map ( |v| v. map ( ToString :: to_string) . collect ( ) )
173
175
. unwrap_or_default ( ) ;
174
176
175
- check_unimplemented ( & matches) ?;
176
-
177
177
let behavior = behavior ( & matches) ?;
178
178
179
179
match behavior. main_function {
@@ -295,21 +295,20 @@ pub fn uu_app() -> Command {
295
295
. action ( ArgAction :: SetTrue ) ,
296
296
)
297
297
. arg (
298
- // TODO implement flag
299
298
Arg :: new ( OPT_PRESERVE_CONTEXT )
300
299
. short ( 'P' )
301
300
. long ( OPT_PRESERVE_CONTEXT )
302
- . help ( "(unimplemented) preserve security context" )
301
+ . help ( "preserve security context" )
303
302
. action ( ArgAction :: SetTrue ) ,
304
303
)
305
304
. arg (
306
- // TODO implement flag
307
305
Arg :: new ( OPT_CONTEXT )
308
306
. short ( 'Z' )
309
307
. long ( OPT_CONTEXT )
310
- . help ( "(unimplemented) set security context of files and directories" )
308
+ . help ( "set security context of files and directories" )
311
309
. value_name ( "CONTEXT" )
312
- . action ( ArgAction :: SetTrue ) ,
310
+ . value_parser ( clap:: value_parser!( String ) )
311
+ . num_args ( 0 ..=1 ) ,
313
312
)
314
313
. arg (
315
314
Arg :: new ( ARG_FILES )
@@ -319,25 +318,6 @@ pub fn uu_app() -> Command {
319
318
)
320
319
}
321
320
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
-
341
321
/// Determine behavior, given command line arguments.
342
322
///
343
323
/// If successful, returns a filled-out Behavior struct.
@@ -415,6 +395,8 @@ fn behavior(matches: &ArgMatches) -> UResult<Behavior> {
415
395
}
416
396
} ;
417
397
398
+ let context = matches. get_one :: < String > ( OPT_CONTEXT ) . cloned ( ) ;
399
+
418
400
Ok ( Behavior {
419
401
main_function,
420
402
specified_mode,
@@ -435,6 +417,8 @@ fn behavior(matches: &ArgMatches) -> UResult<Behavior> {
435
417
create_leading : matches. get_flag ( OPT_CREATE_LEADING ) ,
436
418
target_dir,
437
419
no_target_dir,
420
+ preserve_context : matches. get_flag ( OPT_PRESERVE_CONTEXT ) ,
421
+ context,
438
422
} )
439
423
}
440
424
@@ -485,6 +469,10 @@ fn directory(paths: &[String], b: &Behavior) -> UResult<()> {
485
469
}
486
470
487
471
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) ) ;
488
476
}
489
477
// If the exit code was set, or show! has been called at least once
490
478
// (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<()> {
941
929
preserve_timestamps ( from, to) ?;
942
930
}
943
931
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
+
944
940
if b. verbose {
945
941
print ! ( "{} -> {}" , from. quote( ) , to. quote( ) ) ;
946
942
match backup_path {
@@ -1012,6 +1008,11 @@ fn need_copy(from: &Path, to: &Path, b: &Behavior) -> UResult<bool> {
1012
1008
return Ok ( true ) ;
1013
1009
}
1014
1010
1011
+ #[ cfg( feature = "selinux" ) ]
1012
+ if b. preserve_context && contexts_differ ( from, to) {
1013
+ return Ok ( true ) ;
1014
+ }
1015
+
1015
1016
// TODO: if -P (#1809) and from/to contexts mismatch, return true.
1016
1017
1017
1018
// 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> {
1042
1043
1043
1044
Ok ( false )
1044
1045
}
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