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>
@@ -38,18 +47,33 @@ enum FileType {
3847 Fifo ,
3948}
4049
50+ /// Configuration for directory creation.
51+ pub struct Config < ' a > {
52+ /// Mode
53+ pub mode : mode_t ,
54+
55+ /// dev
56+ pub dev : dev_t ,
57+
58+ /// Set SELinux security context.
59+ pub set_selinux_context : bool ,
60+
61+ /// Specific SELinux context.
62+ pub context : Option < & ' a String > ,
63+ }
64+
4165#[ cfg( unix) ]
42- fn _mknod ( file_name : & str , mode : mode_t , dev : dev_t ) -> i32 {
66+ fn _mknod ( file_name : & str , config : Config ) -> i32 {
4367 let c_str = CString :: new ( file_name) . expect ( "Failed to convert to CString" ) ;
4468
4569 // the user supplied a mode
46- let set_umask = mode & MODE_RW_UGO != MODE_RW_UGO ;
70+ let set_umask = config . mode & MODE_RW_UGO != MODE_RW_UGO ;
4771
4872 unsafe {
4973 // store prev umask
5074 let last_umask = if set_umask { libc:: umask ( 0 ) } else { 0 } ;
5175
52- let errno = libc:: mknod ( c_str. as_ptr ( ) , mode, dev) ;
76+ let errno = libc:: mknod ( c_str. as_ptr ( ) , config . mode , config . dev ) ;
5377
5478 // set umask back to original value
5579 if set_umask {
@@ -62,16 +86,27 @@ fn _mknod(file_name: &str, mode: mode_t, dev: dev_t) -> i32 {
6286 // shows the error from the mknod syscall
6387 libc:: perror ( c_str. as_ptr ( ) ) ;
6488 }
89+
90+ // Apply SELinux context if requested
91+ #[ cfg( feature = "selinux" ) ]
92+ if config. set_selinux_context {
93+ if let Err ( e) = uucore:: selinux:: set_selinux_security_context (
94+ std:: path:: Path :: new ( file_name) ,
95+ config. context ,
96+ ) {
97+ // if it fails, delete the file
98+ let _ = std:: fs:: remove_dir ( file_name) ;
99+ eprintln ! ( "failed to set SELinux security context: {}" , e) ;
100+ return 1 ;
101+ }
102+ }
103+
65104 errno
66105 }
67106}
68107
69108#[ uucore:: main]
70109pub fn uumain ( args : impl uucore:: Args ) -> UResult < ( ) > {
71- // Linux-specific options, not implemented
72- // opts.optflag("Z", "", "set the SELinux security context to default type");
73- // opts.optopt("", "context", "like -Z, or if CTX is specified then set the SELinux or SMACK security context to CTX");
74-
75110 let matches = uu_app ( ) . try_get_matches_from ( args) ?;
76111
77112 let mode = get_mode ( & matches) . map_err ( |e| USimpleError :: new ( 1 , e) ) ?;
@@ -81,32 +116,50 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
81116 . expect ( "Missing argument 'NAME'" ) ;
82117
83118 let file_type = matches. get_one :: < FileType > ( "type" ) . unwrap ( ) ;
119+ // Extract the SELinux related flags and options
120+ let set_selinux_context = matches. get_flag ( options:: SELINUX ) ;
121+ let context = matches. get_one :: < String > ( options:: CONTEXT ) ;
122+
123+ let mut config = Config {
124+ mode,
125+ dev : 0 ,
126+ set_selinux_context : set_selinux_context || context. is_some ( ) ,
127+ context,
128+ } ;
84129
85130 if * file_type == FileType :: Fifo {
86- if matches. contains_id ( "major" ) || matches. contains_id ( "minor" ) {
131+ if matches. contains_id ( options :: MAJOR ) || matches. contains_id ( options :: MINOR ) {
87132 Err ( UUsageError :: new (
88133 1 ,
89134 "Fifos do not have major and minor device numbers." ,
90135 ) )
91136 } else {
92- let exit_code = _mknod ( file_name, S_IFIFO | mode, 0 ) ;
137+ config. mode = S_IFIFO | mode;
138+ let exit_code = _mknod ( file_name, config) ;
93139 set_exit_code ( exit_code) ;
94140 Ok ( ( ) )
95141 }
96142 } else {
97143 match (
98- matches. get_one :: < u64 > ( "major" ) ,
99- matches. get_one :: < u64 > ( "minor" ) ,
144+ matches. get_one :: < u64 > ( options :: MAJOR ) ,
145+ matches. get_one :: < u64 > ( options :: MINOR ) ,
100146 ) {
101147 ( _, None ) | ( None , _) => Err ( UUsageError :: new (
102148 1 ,
103149 "Special files require major and minor device numbers." ,
104150 ) ) ,
105151 ( Some ( & major) , Some ( & minor) ) => {
106152 let dev = makedev ( major, minor) ;
153+ config. dev = dev;
107154 let exit_code = match file_type {
108- FileType :: Block => _mknod ( file_name, S_IFBLK | mode, dev) ,
109- FileType :: Character => _mknod ( file_name, S_IFCHR | mode, dev) ,
155+ FileType :: Block => {
156+ config. mode |= S_IFBLK ;
157+ _mknod ( file_name, config)
158+ }
159+ FileType :: Character => {
160+ config. mode |= S_IFCHR ;
161+ _mknod ( file_name, config)
162+ }
110163 FileType :: Fifo => {
111164 unreachable ! ( "file_type was validated to be only block or character" )
112165 }
@@ -126,7 +179,7 @@ pub fn uu_app() -> Command {
126179 . about ( ABOUT )
127180 . infer_long_args ( true )
128181 . arg (
129- Arg :: new ( "mode" )
182+ Arg :: new ( options :: MODE )
130183 . short ( 'm' )
131184 . long ( "mode" )
132185 . value_name ( "MODE" )
@@ -140,24 +193,33 @@ pub fn uu_app() -> Command {
140193 . value_hint ( clap:: ValueHint :: AnyPath ) ,
141194 )
142195 . arg (
143- Arg :: new ( "type" )
196+ Arg :: new ( options :: TYPE )
144197 . value_name ( "TYPE" )
145198 . help ( "type of the new file (b, c, u or p)" )
146199 . required ( true )
147200 . value_parser ( parse_type) ,
148201 )
149202 . arg (
150- Arg :: new ( "major" )
151- . value_name ( " MAJOR" )
203+ Arg :: new ( options :: MAJOR )
204+ . value_name ( options :: MAJOR )
152205 . help ( "major file type" )
153206 . value_parser ( value_parser ! ( u64 ) ) ,
154207 )
155208 . arg (
156- Arg :: new ( "minor" )
157- . value_name ( " MINOR" )
209+ Arg :: new ( options :: MINOR )
210+ . value_name ( options :: MINOR )
158211 . help ( "minor file type" )
159212 . value_parser ( value_parser ! ( u64 ) ) ,
160213 )
214+ . arg (
215+ Arg :: new ( options:: SELINUX )
216+ . short ( 'Z' )
217+ . help ( "set SELinux security context of each created directory to the default type" )
218+ . action ( ArgAction :: SetTrue ) ,
219+ )
220+ . arg ( Arg :: new ( options:: CONTEXT ) . long ( options:: CONTEXT ) . value_name ( "CTX" ) . help (
221+ "like -Z, or if CTX is specified then set the SELinux or SMACK security context to CTX" ,
222+ ) )
161223}
162224
163225fn get_mode ( matches : & ArgMatches ) -> Result < mode_t , String > {
0 commit comments