@@ -16,7 +16,7 @@ use std::path::{Path, PathBuf, StripPrefixError};
16
16
#[ cfg( all( unix, not( target_os = "android" ) ) ) ]
17
17
use uucore:: fsxattr:: copy_xattrs;
18
18
19
- use clap:: { Arg , ArgAction , ArgMatches , Command , builder:: ValueParser } ;
19
+ use clap:: { Arg , ArgAction , ArgMatches , Command , builder:: ValueParser , value_parser } ;
20
20
use filetime:: FileTime ;
21
21
use indicatif:: { ProgressBar , ProgressStyle } ;
22
22
use quick_error:: ResultExt ;
@@ -311,6 +311,10 @@ pub struct Options {
311
311
pub verbose : bool ,
312
312
/// `-g`, `--progress`
313
313
pub progress_bar : bool ,
314
+ /// -Z
315
+ pub set_selinux_context : bool ,
316
+ // --context
317
+ pub context : Option < String > ,
314
318
}
315
319
316
320
impl Default for Options {
@@ -337,6 +341,8 @@ impl Default for Options {
337
341
debug : false ,
338
342
verbose : false ,
339
343
progress_bar : false ,
344
+ set_selinux_context : false ,
345
+ context : None ,
340
346
}
341
347
}
342
348
}
@@ -448,6 +454,7 @@ mod options {
448
454
pub const RECURSIVE : & str = "recursive" ;
449
455
pub const REFLINK : & str = "reflink" ;
450
456
pub const REMOVE_DESTINATION : & str = "remove-destination" ;
457
+ pub const SELINUX : & str = "Z" ;
451
458
pub const SPARSE : & str = "sparse" ;
452
459
pub const STRIP_TRAILING_SLASHES : & str = "strip-trailing-slashes" ;
453
460
pub const SYMBOLIC_LINK : & str = "symbolic-link" ;
@@ -476,6 +483,7 @@ const PRESERVE_DEFAULT_VALUES: &str = if cfg!(unix) {
476
483
} else {
477
484
"mode,timestamp"
478
485
} ;
486
+
479
487
pub fn uu_app ( ) -> Command {
480
488
const MODE_ARGS : & [ & str ] = & [
481
489
options:: LINK ,
@@ -709,24 +717,25 @@ pub fn uu_app() -> Command {
709
717
. value_parser ( ShortcutValueParser :: new ( [ "never" , "auto" , "always" ] ) )
710
718
. help ( "control creation of sparse files. See below" ) ,
711
719
)
712
- // TODO: implement the following args
713
720
. arg (
714
- Arg :: new ( options:: COPY_CONTENTS )
715
- . long ( options:: COPY_CONTENTS )
716
- . overrides_with ( options:: ATTRIBUTES_ONLY )
717
- . help ( "NotImplemented: copy contents of special files when recursive" )
721
+ Arg :: new ( options:: SELINUX )
722
+ . short ( 'Z' )
723
+ . help ( "set SELinux security context of destination file to default type" )
718
724
. action ( ArgAction :: SetTrue ) ,
719
725
)
720
726
. arg (
721
727
Arg :: new ( options:: CONTEXT )
722
728
. long ( options:: CONTEXT )
723
729
. value_name ( "CTX" )
730
+ . value_parser ( value_parser ! ( String ) )
724
731
. help (
725
- "NotImplemented: set SELinux security context of destination file to \
726
- default type",
727
- ) ,
732
+ "like -Z, or if CTX is specified then set the SELinux or SMACK security \
733
+ context to CTX",
734
+ )
735
+ . num_args ( 0 ..=1 )
736
+ . require_equals ( true )
737
+ . default_missing_value ( "" ) ,
728
738
)
729
- // END TODO
730
739
. arg (
731
740
// The 'g' short flag is modeled after advcpmv
732
741
// See this repo: https://github.com/jarun/advcpmv
@@ -739,6 +748,15 @@ pub fn uu_app() -> Command {
739
748
Note: this feature is not supported by GNU coreutils.",
740
749
) ,
741
750
)
751
+ // TODO: implement the following args
752
+ . arg (
753
+ Arg :: new ( options:: COPY_CONTENTS )
754
+ . long ( options:: COPY_CONTENTS )
755
+ . overrides_with ( options:: ATTRIBUTES_ONLY )
756
+ . help ( "NotImplemented: copy contents of special files when recursive" )
757
+ . action ( ArgAction :: SetTrue ) ,
758
+ )
759
+ // END TODO
742
760
. arg (
743
761
Arg :: new ( options:: PATHS )
744
762
. action ( ArgAction :: Append )
@@ -971,7 +989,6 @@ impl Options {
971
989
let not_implemented_opts = vec ! [
972
990
#[ cfg( not( any( windows, unix) ) ) ]
973
991
options:: ONE_FILE_SYSTEM ,
974
- options:: CONTEXT ,
975
992
#[ cfg( windows) ]
976
993
options:: FORCE ,
977
994
] ;
@@ -1018,7 +1035,6 @@ impl Options {
1018
1035
return Err ( Error :: NotADirectory ( dir. clone ( ) ) ) ;
1019
1036
}
1020
1037
} ;
1021
-
1022
1038
// cp follows POSIX conventions for overriding options such as "-a",
1023
1039
// "-d", "--preserve", and "--no-preserve". We can use clap's
1024
1040
// override-all behavior to achieve this, but there's a challenge: when
@@ -1101,7 +1117,7 @@ impl Options {
1101
1117
}
1102
1118
}
1103
1119
1104
- #[ cfg( not( feature = "feat_selinux " ) ) ]
1120
+ #[ cfg( not( feature = "selinux " ) ) ]
1105
1121
if let Preserve :: Yes { required } = attributes. context {
1106
1122
let selinux_disabled_error =
1107
1123
Error :: Error ( "SELinux was not enabled during the compile time!" . to_string ( ) ) ;
@@ -1112,6 +1128,15 @@ impl Options {
1112
1128
}
1113
1129
}
1114
1130
1131
+ // Extract the SELinux related flags and options
1132
+ let set_selinux_context = matches. get_flag ( options:: SELINUX ) ;
1133
+
1134
+ let context = if matches. contains_id ( options:: CONTEXT ) {
1135
+ matches. get_one :: < String > ( options:: CONTEXT ) . cloned ( )
1136
+ } else {
1137
+ None
1138
+ } ;
1139
+
1115
1140
let options = Self {
1116
1141
attributes_only : matches. get_flag ( options:: ATTRIBUTES_ONLY ) ,
1117
1142
copy_contents : matches. get_flag ( options:: COPY_CONTENTS ) ,
@@ -1172,6 +1197,8 @@ impl Options {
1172
1197
recursive,
1173
1198
target_dir,
1174
1199
progress_bar : matches. get_flag ( options:: PROGRESS_BAR ) ,
1200
+ set_selinux_context : set_selinux_context || context. is_some ( ) ,
1201
+ context,
1175
1202
} ;
1176
1203
1177
1204
Ok ( options)
@@ -1676,20 +1703,24 @@ pub(crate) fn copy_attributes(
1676
1703
Ok ( ( ) )
1677
1704
} ) ?;
1678
1705
1679
- #[ cfg( feature = "feat_selinux " ) ]
1706
+ #[ cfg( feature = "selinux " ) ]
1680
1707
handle_preserve ( & attributes. context , || -> CopyResult < ( ) > {
1681
- let context = selinux:: SecurityContext :: of_path ( source, false , false ) . map_err ( |e| {
1682
- format ! (
1683
- "failed to get security context of {}: {e}" ,
1684
- source. display( ) ,
1685
- )
1686
- } ) ?;
1687
- if let Some ( context) = context {
1688
- context. set_for_path ( dest, false , false ) . map_err ( |e| {
1689
- format ! ( "failed to set security context for {}: {e}" , dest. display( ) , )
1690
- } ) ?;
1708
+ // Get the source context and apply it to the destination
1709
+ if let Ok ( context) = selinux:: SecurityContext :: of_path ( source, false , false ) {
1710
+ if let Some ( context) = context {
1711
+ if let Err ( e) = context. set_for_path ( dest, false , false ) {
1712
+ return Err ( Error :: Error ( format ! (
1713
+ "failed to set security context for {}: {e}" ,
1714
+ dest. display( )
1715
+ ) ) ) ;
1716
+ }
1717
+ }
1718
+ } else {
1719
+ return Err ( Error :: Error ( format ! (
1720
+ "failed to get security context of {}" ,
1721
+ source. display( )
1722
+ ) ) ) ;
1691
1723
}
1692
-
1693
1724
Ok ( ( ) )
1694
1725
} ) ?;
1695
1726
@@ -2417,11 +2448,20 @@ fn copy_file(
2417
2448
// like anonymous pipes. Thus, we can't really copy its
2418
2449
// attributes. However, this is already handled in the stream
2419
2450
// copy function (see `copy_stream` under platform/linux.rs).
2420
- copy_attributes ( source, dest, & options. attributes ) ?;
2421
2451
} else {
2422
2452
copy_attributes ( source, dest, & options. attributes ) ?;
2423
2453
}
2424
2454
2455
+ #[ cfg( feature = "selinux" ) ]
2456
+ if options. set_selinux_context && uucore:: selinux:: is_selinux_enabled ( ) {
2457
+ // Set the given selinux permissions on the copied file.
2458
+ if let Err ( e) =
2459
+ uucore:: selinux:: set_selinux_security_context ( dest, options. context . as_ref ( ) )
2460
+ {
2461
+ return Err ( Error :: Error ( format ! ( "SELinux error: {}" , e) ) ) ;
2462
+ }
2463
+ }
2464
+
2425
2465
copied_files. insert (
2426
2466
FileInformation :: from_path ( source, options. dereference ( source_in_command_line) ) ?,
2427
2467
dest. to_path_buf ( ) ,
0 commit comments