@@ -29,6 +29,26 @@ mod options {
2929 pub const PARENTS : & str = "parents" ;
3030 pub const VERBOSE : & str = "verbose" ;
3131 pub const DIRS : & str = "dirs" ;
32+ pub const SELINUX : & str = "z" ;
33+ pub const CONTEXT : & str = "context" ;
34+ }
35+
36+ /// Configuration for directory creation.
37+ pub struct Config < ' a > {
38+ /// Create parent directories as needed.
39+ pub recursive : bool ,
40+
41+ /// File permissions (octal).
42+ pub mode : u32 ,
43+
44+ /// Print message for each created directory.
45+ pub verbose : bool ,
46+
47+ /// Set SELinux security context.
48+ pub set_selinux_context : bool ,
49+
50+ /// Specific SELinux context.
51+ pub context : Option < & ' a String > ,
3252}
3353
3454#[ cfg( windows) ]
@@ -91,8 +111,21 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
91111 let verbose = matches. get_flag ( options:: VERBOSE ) ;
92112 let recursive = matches. get_flag ( options:: PARENTS ) ;
93113
114+ // Extract the SELinux related flags and options
115+ let set_selinux_context = matches. get_flag ( options:: SELINUX ) ;
116+ let context = matches. get_one :: < String > ( options:: CONTEXT ) ;
117+
94118 match get_mode ( & matches, mode_had_minus_prefix) {
95- Ok ( mode) => exec ( dirs, recursive, mode, verbose) ,
119+ Ok ( mode) => {
120+ let config = Config {
121+ recursive,
122+ mode,
123+ verbose,
124+ set_selinux_context : set_selinux_context || context. is_some ( ) ,
125+ context,
126+ } ;
127+ exec ( dirs, & config)
128+ }
96129 Err ( f) => Err ( USimpleError :: new ( 1 , f) ) ,
97130 }
98131}
@@ -124,6 +157,15 @@ pub fn uu_app() -> Command {
124157 . help ( "print a message for each printed directory" )
125158 . action ( ArgAction :: SetTrue ) ,
126159 )
160+ . arg (
161+ Arg :: new ( options:: SELINUX )
162+ . short ( 'Z' )
163+ . help ( "set SELinux security context of each created directory to the default type" )
164+ . action ( ArgAction :: SetTrue ) ,
165+ )
166+ . arg ( Arg :: new ( options:: CONTEXT ) . long ( options:: CONTEXT ) . value_name ( "CTX" ) . help (
167+ "like -Z, or if CTX is specified then set the SELinux or SMACK security context to CTX" ,
168+ ) )
127169 . arg (
128170 Arg :: new ( options:: DIRS )
129171 . action ( ArgAction :: Append )
@@ -137,12 +179,12 @@ pub fn uu_app() -> Command {
137179/**
138180 * Create the list of new directories
139181 */
140- fn exec ( dirs : ValuesRef < OsString > , recursive : bool , mode : u32 , verbose : bool ) -> UResult < ( ) > {
182+ fn exec ( dirs : ValuesRef < OsString > , config : & Config ) -> UResult < ( ) > {
141183 for dir in dirs {
142184 let path_buf = PathBuf :: from ( dir) ;
143185 let path = path_buf. as_path ( ) ;
144186
145- show_if_err ! ( mkdir( path, recursive , mode , verbose ) ) ;
187+ show_if_err ! ( mkdir( path, config ) ) ;
146188 }
147189 Ok ( ( ) )
148190}
@@ -160,7 +202,7 @@ fn exec(dirs: ValuesRef<OsString>, recursive: bool, mode: u32, verbose: bool) ->
160202///
161203/// To match the GNU behavior, a path with the last directory being a single dot
162204/// (like `some/path/to/.`) is created (with the dot stripped).
163- pub fn mkdir ( path : & Path , recursive : bool , mode : u32 , verbose : bool ) -> UResult < ( ) > {
205+ pub fn mkdir ( path : & Path , config : & Config ) -> UResult < ( ) > {
164206 if path. as_os_str ( ) . is_empty ( ) {
165207 return Err ( USimpleError :: new (
166208 1 ,
@@ -173,7 +215,7 @@ pub fn mkdir(path: &Path, recursive: bool, mode: u32, verbose: bool) -> UResult<
173215 // std::fs::create_dir("foo/."); fails in pure Rust
174216 let path_buf = dir_strip_dot_for_creation ( path) ;
175217 let path = path_buf. as_path ( ) ;
176- create_dir ( path, recursive , verbose , false , mode )
218+ create_dir ( path, false , config )
177219}
178220
179221#[ cfg( any( unix, target_os = "redox" ) ) ]
@@ -194,15 +236,9 @@ fn chmod(_path: &Path, _mode: u32) -> UResult<()> {
194236// Return true if the directory at `path` has been created by this call.
195237// `is_parent` argument is not used on windows
196238#[ allow( unused_variables) ]
197- fn create_dir (
198- path : & Path ,
199- recursive : bool ,
200- verbose : bool ,
201- is_parent : bool ,
202- mode : u32 ,
203- ) -> UResult < ( ) > {
239+ fn create_dir ( path : & Path , is_parent : bool , config : & Config ) -> UResult < ( ) > {
204240 let path_exists = path. exists ( ) ;
205- if path_exists && !recursive {
241+ if path_exists && !config . recursive {
206242 return Err ( USimpleError :: new (
207243 1 ,
208244 format ! ( "{}: File exists" , path. display( ) ) ,
@@ -212,9 +248,9 @@ fn create_dir(
212248 return Ok ( ( ) ) ;
213249 }
214250
215- if recursive {
251+ if config . recursive {
216252 match path. parent ( ) {
217- Some ( p) => create_dir ( p, recursive , verbose , true , mode ) ?,
253+ Some ( p) => create_dir ( p, true , config ) ?,
218254 None => {
219255 USimpleError :: new ( 1 , "failed to create whole tree" ) ;
220256 }
@@ -223,7 +259,7 @@ fn create_dir(
223259
224260 match std:: fs:: create_dir ( path) {
225261 Ok ( ( ) ) => {
226- if verbose {
262+ if config . verbose {
227263 println ! (
228264 "{}: created directory {}" ,
229265 uucore:: util_name( ) ,
@@ -233,7 +269,7 @@ fn create_dir(
233269
234270 #[ cfg( all( unix, target_os = "linux" ) ) ]
235271 let new_mode = if path_exists {
236- mode
272+ config . mode
237273 } else {
238274 // TODO: Make this macos and freebsd compatible by creating a function to get permission bits from
239275 // acl in extended attributes
@@ -242,19 +278,33 @@ fn create_dir(
242278 if is_parent {
243279 ( !mode:: get_umask ( ) & 0o777 ) | 0o300 | acl_perm_bits
244280 } else {
245- mode | acl_perm_bits
281+ config . mode | acl_perm_bits
246282 }
247283 } ;
248284 #[ cfg( all( unix, not( target_os = "linux" ) ) ) ]
249285 let new_mode = if is_parent {
250286 ( !mode:: get_umask ( ) & 0o777 ) | 0o300
251287 } else {
252- mode
288+ config . mode
253289 } ;
254290 #[ cfg( windows) ]
255- let new_mode = mode;
291+ let new_mode = config . mode ;
256292
257293 chmod ( path, new_mode) ?;
294+
295+ // Apply SELinux context if requested
296+ #[ cfg( feature = "selinux" ) ]
297+ if config. set_selinux_context && uucore:: selinux:: check_selinux_enabled ( ) . is_ok ( ) {
298+ if let Err ( e) = uucore:: selinux:: set_selinux_security_context ( path, config. context )
299+ {
300+ let _ = std:: fs:: remove_dir ( path) ;
301+ return Err ( USimpleError :: new (
302+ 1 ,
303+ format ! ( "failed to set SELinux security context: {}" , e) ,
304+ ) ) ;
305+ }
306+ }
307+
258308 Ok ( ( ) )
259309 }
260310
0 commit comments