1 /* mkdir -- make directories
2 Copyright (C) 1990-2024 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 /* David MacKenzie <djm@ai.mit.edu> */
22 #include <sys/types.h>
23 #include <selinux/label.h>
27 #include "modechange.h"
28 #include "prog-fprintf.h"
34 /* The official name of this program (e.g., no 'g' prefix). */
35 #define PROGRAM_NAME "mkdir"
37 #define AUTHORS proper_name ("David MacKenzie")
39 static struct option
const longopts
[] =
41 {GETOPT_SELINUX_CONTEXT_OPTION_DECL
},
42 {"mode", required_argument
, nullptr, 'm'},
43 {"parents", no_argument
, nullptr, 'p'},
44 {"verbose", no_argument
, nullptr, 'v'},
45 {GETOPT_HELP_OPTION_DECL
},
46 {GETOPT_VERSION_OPTION_DECL
},
47 {nullptr, 0, nullptr, 0}
53 if (status
!= EXIT_SUCCESS
)
57 printf (_("Usage: %s [OPTION]... DIRECTORY...\n"), program_name
);
59 Create the DIRECTORY(ies), if they do not already exist.\n\
62 emit_mandatory_arg_note ();
65 -m, --mode=MODE set file mode (as in chmod), not a=rwx - umask\n\
66 -p, --parents no error if existing, make parent directories as needed,\n\
67 with their file modes unaffected by any -m option\n\
68 -v, --verbose print a message for each created directory\n\
71 -Z set SELinux security context of each created directory\n\
72 to the default type\n\
73 --context[=CTX] like -Z, or if CTX is specified then set the SELinux\n\
74 or SMACK security context to CTX\n\
76 fputs (HELP_OPTION_DESCRIPTION
, stdout
);
77 fputs (VERSION_OPTION_DESCRIPTION
, stdout
);
78 emit_ancillary_info (PROGRAM_NAME
);
83 /* Options passed to subsidiary functions. */
86 /* Function to make an ancestor, or nullptr if ancestors should not be
88 int (*make_ancestor_function
) (char const *, char const *, void *);
90 /* Umask value for when making an ancestor. */
91 mode_t umask_ancestor
;
93 /* Umask value for when making the directory itself. */
96 /* Mode for directory itself. */
99 /* File mode bits affected by MODE. */
102 /* Set the SELinux File Context. */
103 struct selabel_handle
*set_security_context
;
105 /* If not null, format to use when reporting newly made directories. */
106 char const *created_directory_format
;
109 /* Report that directory DIR was made, if OPTIONS requests this. */
111 announce_mkdir (char const *dir
, void *options
)
113 struct mkdir_options
const *o
= options
;
114 if (o
->created_directory_format
)
115 prog_fprintf (stdout
, o
->created_directory_format
, quoteaf (dir
));
118 /* Make ancestor directory DIR, whose last component is COMPONENT,
119 with options OPTIONS. Assume the working directory is COMPONENT's
120 parent. Return 0 if successful and the resulting directory is
121 readable, 1 if successful but the resulting directory is not
122 readable, -1 (setting errno) otherwise. */
124 make_ancestor (char const *dir
, char const *component
, void *options
)
126 struct mkdir_options
const *o
= options
;
128 if (o
->set_security_context
129 && defaultcon (o
->set_security_context
, component
, S_IFDIR
) < 0
130 && ! ignorable_ctx_err (errno
))
131 error (0, errno
, _("failed to set default creation context for %s"),
134 if (o
->umask_ancestor
!= o
->umask_self
)
135 umask (o
->umask_ancestor
);
136 int r
= mkdir (component
, S_IRWXUGO
);
137 if (o
->umask_ancestor
!= o
->umask_self
)
139 int mkdir_errno
= errno
;
140 umask (o
->umask_self
);
145 r
= (o
->umask_ancestor
& S_IRUSR
) != 0;
146 announce_mkdir (dir
, options
);
151 /* Process a command-line file name. */
153 process_dir (char *dir
, struct savewd
*wd
, void *options
)
155 struct mkdir_options
const *o
= options
;
157 /* If possible set context before DIR created. */
158 if (o
->set_security_context
)
160 if (! o
->make_ancestor_function
161 && defaultcon (o
->set_security_context
, dir
, S_IFDIR
) < 0
162 && ! ignorable_ctx_err (errno
))
163 error (0, errno
, _("failed to set default creation context for %s"),
167 int ret
= (make_dir_parents (dir
, wd
, o
->make_ancestor_function
, options
,
168 o
->mode
, announce_mkdir
,
169 o
->mode_bits
, (uid_t
) -1, (gid_t
) -1, true)
173 /* FIXME: Due to the current structure of make_dir_parents()
174 we don't have the facility to call defaultcon() before the
175 final component of DIR is created. So for now, create the
176 final component with the context from previous component
177 and here we set the context for the final component. */
178 if (ret
== EXIT_SUCCESS
&& o
->set_security_context
179 && o
->make_ancestor_function
)
181 if (! restorecon (o
->set_security_context
, last_component (dir
), false)
182 && ! ignorable_ctx_err (errno
))
183 error (0, errno
, _("failed to restore context for %s"),
191 main (int argc
, char **argv
)
193 char const *specified_mode
= nullptr;
195 char const *scontext
= nullptr;
196 struct mkdir_options options
;
198 options
.make_ancestor_function
= nullptr;
199 options
.mode
= S_IRWXUGO
;
200 options
.mode_bits
= 0;
201 options
.created_directory_format
= nullptr;
202 options
.set_security_context
= nullptr;
204 initialize_main (&argc
, &argv
);
205 set_program_name (argv
[0]);
206 setlocale (LC_ALL
, "");
207 bindtextdomain (PACKAGE
, LOCALEDIR
);
208 textdomain (PACKAGE
);
210 atexit (close_stdout
);
212 while ((optc
= getopt_long (argc
, argv
, "pm:vZ", longopts
, nullptr)) != -1)
217 options
.make_ancestor_function
= make_ancestor
;
220 specified_mode
= optarg
;
222 case 'v': /* --verbose */
223 options
.created_directory_format
= _("created directory %s");
226 if (is_smack_enabled ())
228 /* We don't yet support -Z to restore context with SMACK. */
231 else if (is_selinux_enabled () > 0)
237 options
.set_security_context
= selabel_open (SELABEL_CTX_FILE
,
239 if (! options
.set_security_context
)
240 error (0, errno
, _("warning: ignoring --context"));
246 _("warning: ignoring --context; "
247 "it requires an SELinux/SMACK-enabled kernel"));
250 case_GETOPT_HELP_CHAR
;
251 case_GETOPT_VERSION_CHAR (PROGRAM_NAME
, AUTHORS
);
253 usage (EXIT_FAILURE
);
259 error (0, 0, _("missing operand"));
260 usage (EXIT_FAILURE
);
263 /* FIXME: This assumes mkdir() is done in the same process.
264 If that's not always the case we would need to call this
265 like we do when options.set_security_context. */
269 if (is_smack_enabled ())
270 ret
= smack_set_label_for_self (scontext
);
272 ret
= setfscreatecon (scontext
);
275 error (EXIT_FAILURE
, errno
,
276 _("failed to set default file creation context to %s"),
281 if (options
.make_ancestor_function
|| specified_mode
)
283 mode_t umask_value
= umask (0);
284 options
.umask_ancestor
= umask_value
& ~(S_IWUSR
| S_IXUSR
);
288 struct mode_change
*change
= mode_compile (specified_mode
);
290 error (EXIT_FAILURE
, 0, _("invalid mode %s"),
291 quote (specified_mode
));
292 options
.mode
= mode_adjust (S_IRWXUGO
, true, umask_value
, change
,
294 options
.umask_self
= umask_value
& ~options
.mode
;
299 options
.mode
= S_IRWXUGO
;
300 options
.umask_self
= umask_value
;
303 umask (options
.umask_self
);
306 return savewd_process_files (argc
- optind
, argv
+ optind
,
307 process_dir
, &options
);