@@ -16,7 +16,7 @@ use std::path::{Path, PathBuf, StripPrefixError};
1616#[ cfg( all( unix, not( target_os = "android" ) ) ) ]
1717use uucore:: fsxattr:: copy_xattrs;
1818
19- use clap:: { Arg , ArgAction , ArgMatches , Command , builder:: ValueParser } ;
19+ use clap:: { Arg , ArgAction , ArgMatches , Command , builder:: ValueParser , value_parser } ;
2020use filetime:: FileTime ;
2121use indicatif:: { ProgressBar , ProgressStyle } ;
2222use quick_error:: ResultExt ;
@@ -311,6 +311,10 @@ pub struct Options {
311311 pub verbose : bool ,
312312 /// `-g`, `--progress`
313313 pub progress_bar : bool ,
314+ /// -Z
315+ pub set_selinux_context : bool ,
316+ // --context
317+ pub context : Option < String > ,
314318}
315319
316320impl Default for Options {
@@ -337,6 +341,8 @@ impl Default for Options {
337341 debug : false ,
338342 verbose : false ,
339343 progress_bar : false ,
344+ set_selinux_context : false ,
345+ context : None ,
340346 }
341347 }
342348}
@@ -448,6 +454,7 @@ mod options {
448454 pub const RECURSIVE : & str = "recursive" ;
449455 pub const REFLINK : & str = "reflink" ;
450456 pub const REMOVE_DESTINATION : & str = "remove-destination" ;
457+ pub const SELINUX : & str = "Z" ;
451458 pub const SPARSE : & str = "sparse" ;
452459 pub const STRIP_TRAILING_SLASHES : & str = "strip-trailing-slashes" ;
453460 pub const SYMBOLIC_LINK : & str = "symbolic-link" ;
@@ -476,6 +483,7 @@ const PRESERVE_DEFAULT_VALUES: &str = if cfg!(unix) {
476483} else {
477484 "mode,timestamp"
478485} ;
486+
479487pub fn uu_app ( ) -> Command {
480488 const MODE_ARGS : & [ & str ] = & [
481489 options:: LINK ,
@@ -709,24 +717,25 @@ pub fn uu_app() -> Command {
709717 . value_parser ( ShortcutValueParser :: new ( [ "never" , "auto" , "always" ] ) )
710718 . help ( "control creation of sparse files. See below" ) ,
711719 )
712- // TODO: implement the following args
713720 . 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" )
718724 . action ( ArgAction :: SetTrue ) ,
719725 )
720726 . arg (
721727 Arg :: new ( options:: CONTEXT )
722728 . long ( options:: CONTEXT )
723729 . value_name ( "CTX" )
730+ . value_parser ( value_parser ! ( String ) )
724731 . 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 ( "" ) ,
728738 )
729- // END TODO
730739 . arg (
731740 // The 'g' short flag is modeled after advcpmv
732741 // See this repo: https://github.com/jarun/advcpmv
@@ -739,6 +748,15 @@ pub fn uu_app() -> Command {
739748 Note: this feature is not supported by GNU coreutils.",
740749 ) ,
741750 )
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
742760 . arg (
743761 Arg :: new ( options:: PATHS )
744762 . action ( ArgAction :: Append )
@@ -971,7 +989,6 @@ impl Options {
971989 let not_implemented_opts = vec ! [
972990 #[ cfg( not( any( windows, unix) ) ) ]
973991 options:: ONE_FILE_SYSTEM ,
974- options:: CONTEXT ,
975992 #[ cfg( windows) ]
976993 options:: FORCE ,
977994 ] ;
@@ -1018,7 +1035,6 @@ impl Options {
10181035 return Err ( Error :: NotADirectory ( dir. clone ( ) ) ) ;
10191036 }
10201037 } ;
1021-
10221038 // cp follows POSIX conventions for overriding options such as "-a",
10231039 // "-d", "--preserve", and "--no-preserve". We can use clap's
10241040 // override-all behavior to achieve this, but there's a challenge: when
@@ -1101,7 +1117,7 @@ impl Options {
11011117 }
11021118 }
11031119
1104- #[ cfg( not( feature = "feat_selinux " ) ) ]
1120+ #[ cfg( not( feature = "selinux " ) ) ]
11051121 if let Preserve :: Yes { required } = attributes. context {
11061122 let selinux_disabled_error =
11071123 Error :: Error ( "SELinux was not enabled during the compile time!" . to_string ( ) ) ;
@@ -1112,6 +1128,15 @@ impl Options {
11121128 }
11131129 }
11141130
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+
11151140 let options = Self {
11161141 attributes_only : matches. get_flag ( options:: ATTRIBUTES_ONLY ) ,
11171142 copy_contents : matches. get_flag ( options:: COPY_CONTENTS ) ,
@@ -1172,6 +1197,8 @@ impl Options {
11721197 recursive,
11731198 target_dir,
11741199 progress_bar : matches. get_flag ( options:: PROGRESS_BAR ) ,
1200+ set_selinux_context : set_selinux_context || context. is_some ( ) ,
1201+ context,
11751202 } ;
11761203
11771204 Ok ( options)
@@ -1676,20 +1703,24 @@ pub(crate) fn copy_attributes(
16761703 Ok ( ( ) )
16771704 } ) ?;
16781705
1679- #[ cfg( feature = "feat_selinux " ) ]
1706+ #[ cfg( feature = "selinux " ) ]
16801707 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+ ) ) ) ;
16911723 }
1692-
16931724 Ok ( ( ) )
16941725 } ) ?;
16951726
@@ -2417,11 +2448,20 @@ fn copy_file(
24172448 // like anonymous pipes. Thus, we can't really copy its
24182449 // attributes. However, this is already handled in the stream
24192450 // copy function (see `copy_stream` under platform/linux.rs).
2420- copy_attributes ( source, dest, & options. attributes ) ?;
24212451 } else {
24222452 copy_attributes ( source, dest, & options. attributes ) ?;
24232453 }
24242454
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+
24252465 copied_files. insert (
24262466 FileInformation :: from_path ( source, options. dereference ( source_in_command_line) ) ?,
24272467 dest. to_path_buf ( ) ,
0 commit comments