maint: update all copyright year number ranges
[coreutils.git] / src / mkdir.c
blob91ab51509670ad87a46148a4301b82077156d91e
1 /* mkdir -- make directories
2 Copyright (C) 1990-2018 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/selinux.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, NULL, 'm'},
45 {"parents", no_argument, NULL, 'p'},
46 {"verbose", no_argument, NULL, 'v'},
47 {GETOPT_HELP_OPTION_DECL},
48 {GETOPT_VERSION_OPTION_DECL},
49 {NULL, 0, NULL, 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 -v, --verbose print a message for each created directory\n\
70 "), stdout);
71 fputs (_("\
72 -Z set SELinux security context of each created directory\n\
73 to the default type\n\
74 --context[=CTX] like -Z, or if CTX is specified then set the SELinux\n\
75 or SMACK security context to CTX\n\
76 "), stdout);
77 fputs (HELP_OPTION_DESCRIPTION, stdout);
78 fputs (VERSION_OPTION_DESCRIPTION, stdout);
79 emit_ancillary_info (PROGRAM_NAME);
81 exit (status);
84 /* Options passed to subsidiary functions. */
85 struct mkdir_options
87 /* Function to make an ancestor, or NULL if ancestors should not be
88 made. */
89 int (*make_ancestor_function) (char const *, char const *, void *);
91 /* Umask value in effect. */
92 mode_t umask_value;
94 /* Mode for directory itself. */
95 mode_t mode;
97 /* File mode bits affected by MODE. */
98 mode_t mode_bits;
100 /* Set the SELinux File Context. */
101 bool set_security_context;
103 /* If not null, format to use when reporting newly made directories. */
104 char const *created_directory_format;
107 /* Report that directory DIR was made, if OPTIONS requests this. */
108 static void
109 announce_mkdir (char const *dir, void *options)
111 struct mkdir_options const *o = options;
112 if (o->created_directory_format)
113 prog_fprintf (stdout, o->created_directory_format, quoteaf (dir));
116 /* Make ancestor directory DIR, whose last component is COMPONENT,
117 with options OPTIONS. Assume the working directory is COMPONENT's
118 parent. Return 0 if successful and the resulting directory is
119 readable, 1 if successful but the resulting directory is not
120 readable, -1 (setting errno) otherwise. */
121 static int
122 make_ancestor (char const *dir, char const *component, void *options)
124 struct mkdir_options const *o = options;
126 if (o->set_security_context && defaultcon (component, S_IFDIR) < 0
127 && ! ignorable_ctx_err (errno))
128 error (0, errno, _("failed to set default creation context for %s"),
129 quoteaf (dir));
131 mode_t user_wx = S_IWUSR | S_IXUSR;
132 bool self_denying_umask = (o->umask_value & user_wx) != 0;
133 if (self_denying_umask)
134 umask (o->umask_value & ~user_wx);
135 int r = mkdir (component, S_IRWXUGO);
136 if (self_denying_umask)
138 int mkdir_errno = errno;
139 umask (o->umask_value);
140 errno = mkdir_errno;
142 if (r == 0)
144 r = (o->umask_value & S_IRUSR) != 0;
145 announce_mkdir (dir, options);
147 return r;
150 /* Process a command-line file name. */
151 static int
152 process_dir (char *dir, struct savewd *wd, void *options)
154 struct mkdir_options const *o = options;
156 /* If possible set context before DIR created. */
157 if (o->set_security_context)
159 if (! o->make_ancestor_function && defaultcon (dir, S_IFDIR) < 0
160 && ! ignorable_ctx_err (errno))
161 error (0, errno, _("failed to set default creation context for %s"),
162 quoteaf (dir));
165 int ret = (make_dir_parents (dir, wd, o->make_ancestor_function, options,
166 o->mode, announce_mkdir,
167 o->mode_bits, (uid_t) -1, (gid_t) -1, true)
168 ? EXIT_SUCCESS
169 : EXIT_FAILURE);
171 /* FIXME: Due to the current structure of make_dir_parents()
172 we don't have the facility to call defaultcon() before the
173 final component of DIR is created. So for now, create the
174 final component with the context from previous component
175 and here we set the context for the final component. */
176 if (ret == EXIT_SUCCESS && o->set_security_context
177 && o->make_ancestor_function)
179 if (! restorecon (last_component (dir), false, false)
180 && ! ignorable_ctx_err (errno))
181 error (0, errno, _("failed to restore context for %s"),
182 quoteaf (dir));
185 return ret;
189 main (int argc, char **argv)
191 const char *specified_mode = NULL;
192 int optc;
193 char const *scontext = NULL;
194 struct mkdir_options options;
196 options.make_ancestor_function = NULL;
197 options.mode = S_IRWXUGO;
198 options.mode_bits = 0;
199 options.created_directory_format = NULL;
200 options.set_security_context = false;
202 initialize_main (&argc, &argv);
203 set_program_name (argv[0]);
204 setlocale (LC_ALL, "");
205 bindtextdomain (PACKAGE, LOCALEDIR);
206 textdomain (PACKAGE);
208 atexit (close_stdout);
210 while ((optc = getopt_long (argc, argv, "pm:vZ", longopts, NULL)) != -1)
212 switch (optc)
214 case 'p':
215 options.make_ancestor_function = make_ancestor;
216 break;
217 case 'm':
218 specified_mode = optarg;
219 break;
220 case 'v': /* --verbose */
221 options.created_directory_format = _("created directory %s");
222 break;
223 case 'Z':
224 if (is_smack_enabled ())
226 /* We don't yet support -Z to restore context with SMACK. */
227 scontext = optarg;
229 else if (is_selinux_enabled () > 0)
231 if (optarg)
232 scontext = optarg;
233 else
234 options.set_security_context = true;
236 else if (optarg)
238 error (0, 0,
239 _("warning: ignoring --context; "
240 "it requires an SELinux/SMACK-enabled kernel"));
242 break;
243 case_GETOPT_HELP_CHAR;
244 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
245 default:
246 usage (EXIT_FAILURE);
250 if (optind == argc)
252 error (0, 0, _("missing operand"));
253 usage (EXIT_FAILURE);
256 /* FIXME: This assumes mkdir() is done in the same process.
257 If that's not always the case we would need to call this
258 like we do when options.set_security_context == true. */
259 if (scontext)
261 int ret = 0;
262 if (is_smack_enabled ())
263 ret = smack_set_label_for_self (scontext);
264 else
265 ret = setfscreatecon (se_const (scontext));
267 if (ret < 0)
268 die (EXIT_FAILURE, errno,
269 _("failed to set default file creation context to %s"),
270 quote (scontext));
274 if (options.make_ancestor_function || specified_mode)
276 mode_t umask_value = umask (0);
277 umask (umask_value);
278 options.umask_value = umask_value;
280 if (specified_mode)
282 struct mode_change *change = mode_compile (specified_mode);
283 if (!change)
284 die (EXIT_FAILURE, 0, _("invalid mode %s"),
285 quote (specified_mode));
286 options.mode = mode_adjust (S_IRWXUGO, true, umask_value, change,
287 &options.mode_bits);
288 free (change);
290 else
291 options.mode = S_IRWXUGO;
294 return savewd_process_files (argc - optind, argv + optind,
295 process_dir, &options);