shuf: tiny simplification
[coreutils.git] / src / mkdir.c
blob6861a02c91b2e41df383d08abddd3a5069368542
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> */
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 "mkdir-p.h"
27 #include "modechange.h"
28 #include "prog-fprintf.h"
29 #include "quote.h"
30 #include "savewd.h"
31 #include "selinux.h"
32 #include "smack.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}
50 void
51 usage (int status)
53 if (status != EXIT_SUCCESS)
54 emit_try_help ();
55 else
57 printf (_("Usage: %s [OPTION]... DIRECTORY...\n"), program_name);
58 fputs (_("\
59 Create the DIRECTORY(ies), if they do not already exist.\n\
60 "), stdout);
62 emit_mandatory_arg_note ();
64 fputs (_("\
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\
69 "), stdout);
70 fputs (_("\
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\
75 "), stdout);
76 fputs (HELP_OPTION_DESCRIPTION, stdout);
77 fputs (VERSION_OPTION_DESCRIPTION, stdout);
78 emit_ancillary_info (PROGRAM_NAME);
80 exit (status);
83 /* Options passed to subsidiary functions. */
84 struct mkdir_options
86 /* Function to make an ancestor, or nullptr if ancestors should not be
87 made. */
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. */
94 mode_t umask_self;
96 /* Mode for directory itself. */
97 mode_t mode;
99 /* File mode bits affected by MODE. */
100 mode_t mode_bits;
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. */
110 static void
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. */
123 static int
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"),
132 quoteaf (dir));
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);
141 errno = mkdir_errno;
143 if (r == 0)
145 r = (o->umask_ancestor & S_IRUSR) != 0;
146 announce_mkdir (dir, options);
148 return r;
151 /* Process a command-line file name. */
152 static int
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"),
164 quoteaf (dir));
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)
170 ? EXIT_SUCCESS
171 : EXIT_FAILURE);
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"),
184 quoteaf (dir));
187 return ret;
191 main (int argc, char **argv)
193 char const *specified_mode = nullptr;
194 int optc;
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)
214 switch (optc)
216 case 'p':
217 options.make_ancestor_function = make_ancestor;
218 break;
219 case 'm':
220 specified_mode = optarg;
221 break;
222 case 'v': /* --verbose */
223 options.created_directory_format = _("created directory %s");
224 break;
225 case 'Z':
226 if (is_smack_enabled ())
228 /* We don't yet support -Z to restore context with SMACK. */
229 scontext = optarg;
231 else if (is_selinux_enabled () > 0)
233 if (optarg)
234 scontext = optarg;
235 else
237 options.set_security_context = selabel_open (SELABEL_CTX_FILE,
238 nullptr, 0);
239 if (! options.set_security_context)
240 error (0, errno, _("warning: ignoring --context"));
243 else if (optarg)
245 error (0, 0,
246 _("warning: ignoring --context; "
247 "it requires an SELinux/SMACK-enabled kernel"));
249 break;
250 case_GETOPT_HELP_CHAR;
251 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
252 default:
253 usage (EXIT_FAILURE);
257 if (optind == argc)
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. */
266 if (scontext)
268 int ret = 0;
269 if (is_smack_enabled ())
270 ret = smack_set_label_for_self (scontext);
271 else
272 ret = setfscreatecon (scontext);
274 if (ret < 0)
275 error (EXIT_FAILURE, errno,
276 _("failed to set default file creation context to %s"),
277 quote (scontext));
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);
286 if (specified_mode)
288 struct mode_change *change = mode_compile (specified_mode);
289 if (!change)
290 error (EXIT_FAILURE, 0, _("invalid mode %s"),
291 quote (specified_mode));
292 options.mode = mode_adjust (S_IRWXUGO, true, umask_value, change,
293 &options.mode_bits);
294 options.umask_self = umask_value & ~options.mode;
295 free (change);
297 else
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);