maint: prefer C23-style nullptr
[coreutils.git] / src / mkdir.c
blob4fd54af1dd592d4d73684bdf2ec415ed805685b7
1 /* mkdir -- make directories
2 Copyright (C) 1990-2023 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> */
19 #include <config.h>
20 #include <stdio.h>
21 #include <getopt.h>
22 #include <sys/types.h>
23 #include <selinux/label.h>
25 #include "system.h"
26 #include "die.h"
27 #include "error.h"
28 #include "mkdir-p.h"
29 #include "modechange.h"
30 #include "prog-fprintf.h"
31 #include "quote.h"
32 #include "savewd.h"
33 #include "selinux.h"
34 #include "smack.h"
36 /* The official name of this program (e.g., no 'g' prefix). */
37 #define PROGRAM_NAME "mkdir"
39 #define AUTHORS proper_name ("David MacKenzie")
41 static struct option const longopts[] =
43 {GETOPT_SELINUX_CONTEXT_OPTION_DECL},
44 {"mode", required_argument, nullptr, 'm'},
45 {"parents", no_argument, nullptr, 'p'},
46 {"verbose", no_argument, nullptr, 'v'},
47 {GETOPT_HELP_OPTION_DECL},
48 {GETOPT_VERSION_OPTION_DECL},
49 {nullptr, 0, nullptr, 0}
52 void
53 usage (int status)
55 if (status != EXIT_SUCCESS)
56 emit_try_help ();
57 else
59 printf (_("Usage: %s [OPTION]... DIRECTORY...\n"), program_name);
60 fputs (_("\
61 Create the DIRECTORY(ies), if they do not already exist.\n\
62 "), stdout);
64 emit_mandatory_arg_note ();
66 fputs (_("\
67 -m, --mode=MODE set file mode (as in chmod), not a=rwx - umask\n\
68 -p, --parents no error if existing, make parent directories as needed,\n\
69 with their file modes unaffected by any -m option.\n\
70 -v, --verbose print a message for each created directory\n\
71 "), stdout);
72 fputs (_("\
73 -Z set SELinux security context of each created directory\n\
74 to the default type\n\
75 --context[=CTX] like -Z, or if CTX is specified then set the SELinux\n\
76 or SMACK security context to CTX\n\
77 "), stdout);
78 fputs (HELP_OPTION_DESCRIPTION, stdout);
79 fputs (VERSION_OPTION_DESCRIPTION, stdout);
80 emit_ancillary_info (PROGRAM_NAME);
82 exit (status);
85 /* Options passed to subsidiary functions. */
86 struct mkdir_options
88 /* Function to make an ancestor, or nullptr if ancestors should not be
89 made. */
90 int (*make_ancestor_function) (char const *, char const *, void *);
92 /* Umask value for when making an ancestor. */
93 mode_t umask_ancestor;
95 /* Umask value for when making the directory itself. */
96 mode_t umask_self;
98 /* Mode for directory itself. */
99 mode_t mode;
101 /* File mode bits affected by MODE. */
102 mode_t mode_bits;
104 /* Set the SELinux File Context. */
105 struct selabel_handle *set_security_context;
107 /* If not null, format to use when reporting newly made directories. */
108 char const *created_directory_format;
111 /* Report that directory DIR was made, if OPTIONS requests this. */
112 static void
113 announce_mkdir (char const *dir, void *options)
115 struct mkdir_options const *o = options;
116 if (o->created_directory_format)
117 prog_fprintf (stdout, o->created_directory_format, quoteaf (dir));
120 /* Make ancestor directory DIR, whose last component is COMPONENT,
121 with options OPTIONS. Assume the working directory is COMPONENT's
122 parent. Return 0 if successful and the resulting directory is
123 readable, 1 if successful but the resulting directory is not
124 readable, -1 (setting errno) otherwise. */
125 static int
126 make_ancestor (char const *dir, char const *component, void *options)
128 struct mkdir_options const *o = options;
130 if (o->set_security_context
131 && defaultcon (o->set_security_context, component, S_IFDIR) < 0
132 && ! ignorable_ctx_err (errno))
133 error (0, errno, _("failed to set default creation context for %s"),
134 quoteaf (dir));
136 if (o->umask_ancestor != o->umask_self)
137 umask (o->umask_ancestor);
138 int r = mkdir (component, S_IRWXUGO);
139 if (o->umask_ancestor != o->umask_self)
141 int mkdir_errno = errno;
142 umask (o->umask_self);
143 errno = mkdir_errno;
145 if (r == 0)
147 r = (o->umask_ancestor & S_IRUSR) != 0;
148 announce_mkdir (dir, options);
150 return r;
153 /* Process a command-line file name. */
154 static int
155 process_dir (char *dir, struct savewd *wd, void *options)
157 struct mkdir_options const *o = options;
159 /* If possible set context before DIR created. */
160 if (o->set_security_context)
162 if (! o->make_ancestor_function
163 && defaultcon (o->set_security_context, dir, S_IFDIR) < 0
164 && ! ignorable_ctx_err (errno))
165 error (0, errno, _("failed to set default creation context for %s"),
166 quoteaf (dir));
169 int ret = (make_dir_parents (dir, wd, o->make_ancestor_function, options,
170 o->mode, announce_mkdir,
171 o->mode_bits, (uid_t) -1, (gid_t) -1, true)
172 ? EXIT_SUCCESS
173 : EXIT_FAILURE);
175 /* FIXME: Due to the current structure of make_dir_parents()
176 we don't have the facility to call defaultcon() before the
177 final component of DIR is created. So for now, create the
178 final component with the context from previous component
179 and here we set the context for the final component. */
180 if (ret == EXIT_SUCCESS && o->set_security_context
181 && o->make_ancestor_function)
183 if (! restorecon (o->set_security_context, last_component (dir), false)
184 && ! ignorable_ctx_err (errno))
185 error (0, errno, _("failed to restore context for %s"),
186 quoteaf (dir));
189 return ret;
193 main (int argc, char **argv)
195 char const *specified_mode = nullptr;
196 int optc;
197 char const *scontext = nullptr;
198 struct mkdir_options options;
200 options.make_ancestor_function = nullptr;
201 options.mode = S_IRWXUGO;
202 options.mode_bits = 0;
203 options.created_directory_format = nullptr;
204 options.set_security_context = nullptr;
206 initialize_main (&argc, &argv);
207 set_program_name (argv[0]);
208 setlocale (LC_ALL, "");
209 bindtextdomain (PACKAGE, LOCALEDIR);
210 textdomain (PACKAGE);
212 atexit (close_stdout);
214 while ((optc = getopt_long (argc, argv, "pm:vZ", longopts, nullptr)) != -1)
216 switch (optc)
218 case 'p':
219 options.make_ancestor_function = make_ancestor;
220 break;
221 case 'm':
222 specified_mode = optarg;
223 break;
224 case 'v': /* --verbose */
225 options.created_directory_format = _("created directory %s");
226 break;
227 case 'Z':
228 if (is_smack_enabled ())
230 /* We don't yet support -Z to restore context with SMACK. */
231 scontext = optarg;
233 else if (is_selinux_enabled () > 0)
235 if (optarg)
236 scontext = optarg;
237 else
239 options.set_security_context = selabel_open (SELABEL_CTX_FILE,
240 nullptr, 0);
241 if (! options.set_security_context)
242 error (0, errno, _("warning: ignoring --context"));
245 else if (optarg)
247 error (0, 0,
248 _("warning: ignoring --context; "
249 "it requires an SELinux/SMACK-enabled kernel"));
251 break;
252 case_GETOPT_HELP_CHAR;
253 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
254 default:
255 usage (EXIT_FAILURE);
259 if (optind == argc)
261 error (0, 0, _("missing operand"));
262 usage (EXIT_FAILURE);
265 /* FIXME: This assumes mkdir() is done in the same process.
266 If that's not always the case we would need to call this
267 like we do when options.set_security_context. */
268 if (scontext)
270 int ret = 0;
271 if (is_smack_enabled ())
272 ret = smack_set_label_for_self (scontext);
273 else
274 ret = setfscreatecon (scontext);
276 if (ret < 0)
277 die (EXIT_FAILURE, errno,
278 _("failed to set default file creation context to %s"),
279 quote (scontext));
283 if (options.make_ancestor_function || specified_mode)
285 mode_t umask_value = umask (0);
286 options.umask_ancestor = umask_value & ~(S_IWUSR | S_IXUSR);
288 if (specified_mode)
290 struct mode_change *change = mode_compile (specified_mode);
291 if (!change)
292 die (EXIT_FAILURE, 0, _("invalid mode %s"),
293 quote (specified_mode));
294 options.mode = mode_adjust (S_IRWXUGO, true, umask_value, change,
295 &options.mode_bits);
296 options.umask_self = umask_value & ~options.mode;
297 free (change);
299 else
301 options.mode = S_IRWXUGO;
302 options.umask_self = umask_value;
305 umask (options.umask_self);
308 return savewd_process_files (argc - optind, argv + optind,
309 process_dir, &options);