selinux: adjust utils to run restorecon with -Z
[coreutils.git] / src / mkdir.c
blob25b1da5e77f24dde1e6f009a1186d1866bc05fbf
1 /* mkdir -- make directories
2 Copyright (C) 1990-2013 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 <http://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 "error.h"
27 #include "mkdir-p.h"
28 #include "modechange.h"
29 #include "prog-fprintf.h"
30 #include "quote.h"
31 #include "savewd.h"
32 #include "selinux.h"
33 #include "smack.h"
35 /* The official name of this program (e.g., no 'g' prefix). */
36 #define PROGRAM_NAME "mkdir"
38 #define AUTHORS proper_name ("David MacKenzie")
40 static struct option const longopts[] =
42 {GETOPT_SELINUX_CONTEXT_OPTION_DECL},
43 {"mode", required_argument, NULL, 'm'},
44 {"parents", no_argument, NULL, 'p'},
45 {"verbose", no_argument, NULL, 'v'},
46 {GETOPT_HELP_OPTION_DECL},
47 {GETOPT_VERSION_OPTION_DECL},
48 {NULL, 0, NULL, 0}
51 void
52 usage (int status)
54 if (status != EXIT_SUCCESS)
55 emit_try_help ();
56 else
58 printf (_("Usage: %s [OPTION]... DIRECTORY...\n"), program_name);
59 fputs (_("\
60 Create the DIRECTORY(ies), if they do not already exist.\n\
61 "), stdout);
63 emit_mandatory_arg_note ();
65 fputs (_("\
66 -m, --mode=MODE set file mode (as in chmod), not a=rwx - umask\n\
67 -p, --parents no error if existing, make parent directories as needed\n\
68 -v, --verbose print a message for each created directory\n\
69 -Z, --context[=CTX] set the SELinux security context of each created\n\
70 directory to default type or to CTX if specified\n\
71 "), stdout);
72 fputs (HELP_OPTION_DESCRIPTION, stdout);
73 fputs (VERSION_OPTION_DESCRIPTION, stdout);
74 emit_ancillary_info ();
76 exit (status);
79 /* Options passed to subsidiary functions. */
80 struct mkdir_options
82 /* Function to make an ancestor, or NULL if ancestors should not be
83 made. */
84 int (*make_ancestor_function) (char const *, char const *, void *);
86 /* Umask value in effect. */
87 mode_t umask_value;
89 /* Mode for directory itself. */
90 mode_t mode;
92 /* File mode bits affected by MODE. */
93 mode_t mode_bits;
95 /* Set the SELinux File Context. */
96 bool set_security_context;
98 /* If not null, format to use when reporting newly made directories. */
99 char const *created_directory_format;
102 /* Report that directory DIR was made, if OPTIONS requests this. */
103 static void
104 announce_mkdir (char const *dir, void *options)
106 struct mkdir_options const *o = options;
107 if (o->created_directory_format)
108 prog_fprintf (stdout, o->created_directory_format, quote (dir));
111 /* Make ancestor directory DIR, whose last component is COMPONENT,
112 with options OPTIONS. Assume the working directory is COMPONENT's
113 parent. Return 0 if successful and the resulting directory is
114 readable, 1 if successful but the resulting directory is not
115 readable, -1 (setting errno) otherwise. */
116 static int
117 make_ancestor (char const *dir, char const *component, void *options)
119 struct mkdir_options const *o = options;
121 if (o->set_security_context && defaultcon (dir, S_IFDIR) < 0)
122 error (0, errno, _("failed to set default creation context for %s"),
123 quote (dir));
125 mode_t user_wx = S_IWUSR | S_IXUSR;
126 bool self_denying_umask = (o->umask_value & user_wx) != 0;
127 if (self_denying_umask)
128 umask (o->umask_value & ~user_wx);
129 int r = mkdir (component, S_IRWXUGO);
130 if (self_denying_umask)
132 int mkdir_errno = errno;
133 umask (o->umask_value);
134 errno = mkdir_errno;
136 if (r == 0)
138 r = (o->umask_value & S_IRUSR) != 0;
139 announce_mkdir (dir, options);
141 return r;
144 /* Process a command-line file name. */
145 static int
146 process_dir (char *dir, struct savewd *wd, void *options)
148 struct mkdir_options const *o = options;
149 bool set_defaultcon = false;
151 /* If possible set context before DIR created. */
152 if (o->set_security_context)
154 if (! o->make_ancestor_function)
155 set_defaultcon = true;
156 else
158 char *pdir = dir_name (dir);
159 struct stat st;
160 if (STREQ (pdir, ".")
161 || (stat (pdir, &st) == 0 && S_ISDIR (st.st_mode)))
162 set_defaultcon = true;
163 free (pdir);
165 if (set_defaultcon && defaultcon (dir, S_IFDIR) < 0)
166 error (0, errno, _("failed to set default creation context for %s"),
167 quote (dir));
170 int ret = (make_dir_parents (dir, wd, o->make_ancestor_function, options,
171 o->mode, announce_mkdir,
172 o->mode_bits, (uid_t) -1, (gid_t) -1, true)
173 ? EXIT_SUCCESS
174 : EXIT_FAILURE);
176 /* FIXME: Due to the current structure of make_dir_parents()
177 we don't have the facility to call defaultcon() before the
178 final component of DIR is created. So for now, create the
179 final component with the context from previous component
180 and here we set the context for the final component. */
181 if (ret == EXIT_SUCCESS && o->set_security_context && ! set_defaultcon)
183 if (restorecon (last_component (dir), false, false) < 0)
184 error (0, errno, _("failed to set restore context for %s"),
185 quote (dir));
188 return ret;
192 main (int argc, char **argv)
194 const char *specified_mode = NULL;
195 int optc;
196 security_context_t scontext = NULL;
197 struct mkdir_options options;
199 options.make_ancestor_function = NULL;
200 options.mode = S_IRWXUGO;
201 options.mode_bits = 0;
202 options.created_directory_format = NULL;
203 options.set_security_context = false;
205 initialize_main (&argc, &argv);
206 set_program_name (argv[0]);
207 setlocale (LC_ALL, "");
208 bindtextdomain (PACKAGE, LOCALEDIR);
209 textdomain (PACKAGE);
211 atexit (close_stdout);
213 while ((optc = getopt_long (argc, argv, "pm:vZ", longopts, NULL)) != -1)
215 switch (optc)
217 case 'p':
218 options.make_ancestor_function = make_ancestor;
219 break;
220 case 'm':
221 specified_mode = optarg;
222 break;
223 case 'v': /* --verbose */
224 options.created_directory_format = _("created directory %s");
225 break;
226 case 'Z':
227 if (is_smack_enabled ())
229 /* We don't yet support -Z to restore context with SMACK. */
230 scontext = optarg;
232 else if (is_selinux_enabled () > 0)
234 if (optarg)
235 scontext = optarg;
236 else
237 options.set_security_context = true;
239 else if (optarg)
241 error (0, 0,
242 _("warning: ignoring --context; "
243 "it requires an SELinux/SMACK-enabled kernel"));
245 break;
246 case_GETOPT_HELP_CHAR;
247 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
248 default:
249 usage (EXIT_FAILURE);
253 if (optind == argc)
255 error (0, 0, _("missing operand"));
256 usage (EXIT_FAILURE);
259 /* FIXME: This assumes mkdir() is done in the same process.
260 If that's not always the case we would need to call this
261 like we do when options.set_security_context == true. */
262 if (scontext)
264 int ret = 0;
265 if (is_smack_enabled ())
266 ret = smack_set_label_for_self (scontext);
267 else
268 ret = setfscreatecon (scontext);
270 if (ret < 0)
271 error (EXIT_FAILURE, errno,
272 _("failed to set default file creation context to %s"),
273 quote (scontext));
277 if (options.make_ancestor_function || specified_mode)
279 mode_t umask_value = umask (0);
280 umask (umask_value);
281 options.umask_value = umask_value;
283 if (specified_mode)
285 struct mode_change *change = mode_compile (specified_mode);
286 if (!change)
287 error (EXIT_FAILURE, 0, _("invalid mode %s"),
288 quote (specified_mode));
289 options.mode = mode_adjust (S_IRWXUGO, true, umask_value, change,
290 &options.mode_bits);
291 free (change);
293 else
294 options.mode = S_IRWXUGO;
297 exit (savewd_process_files (argc - optind, argv + optind,
298 process_dir, &options));