55
66// spell-checker:ignore (ToDO) parsemode makedev sysmacros perror IFBLK IFCHR IFIFO
77
8- use clap:: { Arg , ArgMatches , Command , value_parser} ;
8+ use clap:: { Arg , ArgAction , Command , value_parser} ;
99use libc:: { S_IFBLK , S_IFCHR , S_IFIFO , S_IRGRP , S_IROTH , S_IRUSR , S_IWGRP , S_IWOTH , S_IWUSR } ;
1010use libc:: { dev_t, mode_t} ;
1111use std:: ffi:: CString ;
@@ -20,6 +20,15 @@ const AFTER_HELP: &str = help_section!("after help", "mknod.md");
2020
2121const MODE_RW_UGO : mode_t = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH ;
2222
23+ mod options {
24+ pub const MODE : & str = "mode" ;
25+ pub const TYPE : & str = "type" ;
26+ pub const MAJOR : & str = "major" ;
27+ pub const MINOR : & str = "minor" ;
28+ pub const SELINUX : & str = "z" ;
29+ pub const CONTEXT : & str = "context" ;
30+ }
31+
2332#[ inline( always) ]
2433fn makedev ( maj : u64 , min : u64 ) -> dev_t {
2534 // pick up from <sys/sysmacros.h>
@@ -33,17 +42,30 @@ enum FileType {
3342 Fifo ,
3443}
3544
36- fn _mknod ( file_name : & str , mode : mode_t , dev : dev_t ) -> i32 {
45+ /// Configuration for directory creation.
46+ pub struct Config < ' a > {
47+ pub mode : mode_t ,
48+
49+ pub dev : dev_t ,
50+
51+ /// Set SELinux security context.
52+ pub set_selinux_context : bool ,
53+
54+ /// Specific SELinux context.
55+ pub context : Option < & ' a String > ,
56+ }
57+
58+ fn _mknod ( file_name : & str , config : Config ) -> i32 {
3759 let c_str = CString :: new ( file_name) . expect ( "Failed to convert to CString" ) ;
3860
3961 // the user supplied a mode
40- let set_umask = mode & MODE_RW_UGO != MODE_RW_UGO ;
62+ let set_umask = config . mode & MODE_RW_UGO != MODE_RW_UGO ;
4163
4264 unsafe {
4365 // store prev umask
4466 let last_umask = if set_umask { libc:: umask ( 0 ) } else { 0 } ;
4567
46- let errno = libc:: mknod ( c_str. as_ptr ( ) , mode, dev) ;
68+ let errno = libc:: mknod ( c_str. as_ptr ( ) , config . mode , config . dev ) ;
4769
4870 // set umask back to original value
4971 if set_umask {
@@ -56,16 +78,27 @@ fn _mknod(file_name: &str, mode: mode_t, dev: dev_t) -> i32 {
5678 // shows the error from the mknod syscall
5779 libc:: perror ( c_str. as_ptr ( ) ) ;
5880 }
81+
82+ // Apply SELinux context if requested
83+ #[ cfg( feature = "selinux" ) ]
84+ if config. set_selinux_context {
85+ if let Err ( e) = uucore:: selinux:: set_selinux_security_context (
86+ std:: path:: Path :: new ( file_name) ,
87+ config. context ,
88+ ) {
89+ // if it fails, delete the file
90+ let _ = std:: fs:: remove_dir ( file_name) ;
91+ eprintln ! ( "failed to set SELinux security context: {}" , e) ;
92+ return 1 ;
93+ }
94+ }
95+
5996 errno
6097 }
6198}
6299
63100#[ uucore:: main]
64101pub fn uumain ( args : impl uucore:: Args ) -> UResult < ( ) > {
65- // Linux-specific options, not implemented
66- // opts.optflag("Z", "", "set the SELinux security context to default type");
67- // opts.optopt("", "context", "like -Z, or if CTX is specified then set the SELinux or SMACK security context to CTX");
68-
69102 let matches = uu_app ( ) . try_get_matches_from ( args) ?;
70103
71104 let mode = get_mode ( & matches) . map_err ( |e| USimpleError :: new ( 1 , e) ) ?;
@@ -75,32 +108,50 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
75108 . expect ( "Missing argument 'NAME'" ) ;
76109
77110 let file_type = matches. get_one :: < FileType > ( "type" ) . unwrap ( ) ;
111+ // Extract the SELinux related flags and options
112+ let set_selinux_context = matches. get_flag ( options:: SELINUX ) ;
113+ let context = matches. get_one :: < String > ( options:: CONTEXT ) ;
114+
115+ let mut config = Config {
116+ mode,
117+ dev : 0 ,
118+ set_selinux_context : set_selinux_context || context. is_some ( ) ,
119+ context,
120+ } ;
78121
79122 if * file_type == FileType :: Fifo {
80- if matches. contains_id ( "major" ) || matches. contains_id ( "minor" ) {
123+ if matches. contains_id ( options :: MAJOR ) || matches. contains_id ( options :: MINOR ) {
81124 Err ( UUsageError :: new (
82125 1 ,
83126 "Fifos do not have major and minor device numbers." ,
84127 ) )
85128 } else {
86- let exit_code = _mknod ( file_name, S_IFIFO | mode, 0 ) ;
129+ config. mode = S_IFIFO | mode;
130+ let exit_code = _mknod ( file_name, config) ;
87131 set_exit_code ( exit_code) ;
88132 Ok ( ( ) )
89133 }
90134 } else {
91135 match (
92- matches. get_one :: < u64 > ( "major" ) ,
93- matches. get_one :: < u64 > ( "minor" ) ,
136+ matches. get_one :: < u64 > ( options :: MAJOR ) ,
137+ matches. get_one :: < u64 > ( options :: MINOR ) ,
94138 ) {
95139 ( _, None ) | ( None , _) => Err ( UUsageError :: new (
96140 1 ,
97141 "Special files require major and minor device numbers." ,
98142 ) ) ,
99143 ( Some ( & major) , Some ( & minor) ) => {
100144 let dev = makedev ( major, minor) ;
145+ config. dev = dev;
101146 let exit_code = match file_type {
102- FileType :: Block => _mknod ( file_name, S_IFBLK | mode, dev) ,
103- FileType :: Character => _mknod ( file_name, S_IFCHR | mode, dev) ,
147+ FileType :: Block => {
148+ config. mode |= S_IFBLK ;
149+ _mknod ( file_name, config)
150+ }
151+ FileType :: Character => {
152+ config. mode |= S_IFCHR ;
153+ _mknod ( file_name, config)
154+ }
104155 FileType :: Fifo => {
105156 unreachable ! ( "file_type was validated to be only block or character" )
106157 }
@@ -120,7 +171,7 @@ pub fn uu_app() -> Command {
120171 . about ( ABOUT )
121172 . infer_long_args ( true )
122173 . arg (
123- Arg :: new ( "mode" )
174+ Arg :: new ( options :: MODE )
124175 . short ( 'm' )
125176 . long ( "mode" )
126177 . value_name ( "MODE" )
@@ -134,24 +185,39 @@ pub fn uu_app() -> Command {
134185 . value_hint ( clap:: ValueHint :: AnyPath ) ,
135186 )
136187 . arg (
137- Arg :: new ( "type" )
188+ Arg :: new ( options :: TYPE )
138189 . value_name ( "TYPE" )
139190 . help ( "type of the new file (b, c, u or p)" )
140191 . required ( true )
141192 . value_parser ( parse_type) ,
142193 )
143194 . arg (
144- Arg :: new ( "major" )
145- . value_name ( " MAJOR" )
195+ Arg :: new ( options :: MAJOR )
196+ . value_name ( options :: MAJOR )
146197 . help ( "major file type" )
147198 . value_parser ( value_parser ! ( u64 ) ) ,
148199 )
149200 . arg (
150- Arg :: new ( "minor" )
151- . value_name ( " MINOR" )
201+ Arg :: new ( options :: MINOR )
202+ . value_name ( options :: MINOR )
152203 . help ( "minor file type" )
153204 . value_parser ( value_parser ! ( u64 ) ) ,
154205 )
206+ . arg (
207+ Arg :: new ( options:: SELINUX )
208+ . short ( 'Z' )
209+ . help ( "set SELinux security context of each created directory to the default type" )
210+ . action ( ArgAction :: SetTrue ) ,
211+ )
212+ . arg (
213+ Arg :: new ( options:: CONTEXT )
214+ . long ( options:: CONTEXT )
215+ . value_name ( "CTX" )
216+ . value_parser ( value_parser ! ( String ) )
217+ . num_args ( 0 ..=1 )
218+ . require_equals ( true )
219+ . help ( "like -Z, or if CTX is specified then set the SELinux or SMACK security context to CTX" )
220+ )
155221}
156222
157223fn get_mode ( matches : & ArgMatches ) -> Result < mode_t , String > {
0 commit comments