]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/fstab-generator/fstab-generator.c
honor SELinux labels, when creating and writing config files
[thirdparty/systemd.git] / src / fstab-generator / fstab-generator.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2012 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <stdio.h>
23 #include <mntent.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <unistd.h>
27
28 #include "log.h"
29 #include "util.h"
30 #include "unit-name.h"
31 #include "path-util.h"
32 #include "mount-setup.h"
33 #include "special.h"
34 #include "mkdir.h"
35 #include "virt.h"
36 #include "fileio.h"
37
38 static const char *arg_dest = "/tmp";
39 static bool arg_enabled = true;
40
41 static int device_name(const char *path, char **unit) {
42 char *p;
43
44 assert(path);
45
46 if (!is_device_path(path))
47 return 0;
48
49 p = unit_name_from_path(path, ".device");
50 if (!p)
51 return log_oom();
52
53 *unit = p;
54 return 1;
55 }
56
57 static int mount_find_pri(struct mntent *me, int *ret) {
58 char *end, *pri;
59 unsigned long r;
60
61 assert(me);
62 assert(ret);
63
64 pri = hasmntopt(me, "pri");
65 if (!pri)
66 return 0;
67
68 pri += 4;
69
70 errno = 0;
71 r = strtoul(pri, &end, 10);
72 if (errno != 0)
73 return -errno;
74
75 if (end == pri || (*end != ',' && *end != 0))
76 return -EINVAL;
77
78 *ret = (int) r;
79 return 1;
80 }
81
82 static int add_swap(const char *what, struct mntent *me) {
83 char _cleanup_free_ *name = NULL, *unit = NULL, *lnk = NULL, *device = NULL;
84 FILE _cleanup_fclose_ *f = NULL;
85 bool noauto, nofail;
86 int r, pri = -1;
87
88 assert(what);
89 assert(me);
90
91 r = mount_find_pri(me, &pri);
92 if (r < 0) {
93 log_error("Failed to parse priority");
94 return pri;
95 }
96
97 noauto = !!hasmntopt(me, "noauto");
98 nofail = !!hasmntopt(me, "nofail");
99
100 name = unit_name_from_path(what, ".swap");
101 if (!name)
102 return log_oom();
103
104 unit = strjoin(arg_dest, "/", name, NULL);
105 if (!unit)
106 return log_oom();
107
108 f = fopen(unit, "wxe");
109 if (!f) {
110 if (errno == EEXIST)
111 log_error("Failed to create swap unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
112 else
113 log_error("Failed to create unit file %s: %m", unit);
114 return -errno;
115 }
116
117 fputs("# Automatically generated by systemd-fstab-generator\n\n"
118 "[Unit]\n"
119 "SourcePath=/etc/fstab\n"
120 "DefaultDependencies=no\n"
121 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
122 "Before=" SPECIAL_UMOUNT_TARGET "\n", f);
123
124 if (!noauto && !nofail)
125 fputs("Before=" SPECIAL_SWAP_TARGET "\n", f);
126
127 fprintf(f,
128 "\n"
129 "[Swap]\n"
130 "What=%s\n",
131 what);
132
133 if (pri >= 0)
134 fprintf(f,
135 "Priority=%i\n",
136 pri);
137
138 fflush(f);
139 if (ferror(f)) {
140 log_error("Failed to write unit file %s: %m", unit);
141 return -errno;
142 }
143
144 if (!noauto) {
145 lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL);
146 if (!lnk)
147 return log_oom();
148
149 mkdir_parents_label(lnk, 0755);
150 if (symlink(unit, lnk) < 0) {
151 log_error("Failed to create symlink %s: %m", lnk);
152 return -errno;
153 }
154
155 r = device_name(what, &device);
156 if (r < 0)
157 return r;
158
159 if (r > 0) {
160 free(lnk);
161 lnk = strjoin(arg_dest, "/", device, ".wants/", name, NULL);
162 if (!lnk)
163 return log_oom();
164
165 mkdir_parents_label(lnk, 0755);
166 if (symlink(unit, lnk) < 0) {
167 log_error("Failed to create symlink %s: %m", lnk);
168 return -errno;
169 }
170 }
171 }
172
173 return 0;
174 }
175
176 static bool mount_is_bind(struct mntent *me) {
177 assert(me);
178
179 return
180 hasmntopt(me, "bind") ||
181 streq(me->mnt_type, "bind");
182 }
183
184 static bool mount_is_network(struct mntent *me) {
185 assert(me);
186
187 return
188 hasmntopt(me, "_netdev") ||
189 fstype_is_network(me->mnt_type);
190 }
191
192 static int add_mount(const char *what, const char *where, const char *type, const char *opts,
193 int passno, bool wait, bool noauto, bool nofail, bool automount, bool isbind, bool isnetwork,
194 const char *source) {
195 char _cleanup_free_
196 *name = NULL, *unit = NULL, *lnk = NULL, *device = NULL,
197 *automount_name = NULL, *automount_unit = NULL;
198 FILE _cleanup_fclose_ *f = NULL;
199 int r;
200 const char *post, *pre;
201
202 assert(what);
203 assert(where);
204 assert(type);
205 assert(opts);
206 assert(source);
207
208 if (streq(type, "autofs"))
209 return 0;
210
211 if (!is_path(where)) {
212 log_warning("Mount point %s is not a valid path, ignoring.", where);
213 return 0;
214 }
215
216 if (mount_point_is_api(where) ||
217 mount_point_ignore(where))
218 return 0;
219
220 if (isnetwork) {
221 post = SPECIAL_REMOTE_FS_TARGET;
222 pre = SPECIAL_REMOTE_FS_PRE_TARGET;
223 } else {
224 post = SPECIAL_LOCAL_FS_TARGET;
225 pre = SPECIAL_LOCAL_FS_PRE_TARGET;
226 }
227
228 name = unit_name_from_path(where, ".mount");
229 if (!name)
230 return log_oom();
231
232 unit = strjoin(arg_dest, "/", name, NULL);
233 if (!unit)
234 return log_oom();
235
236 f = fopen(unit, "wxe");
237 if (!f) {
238 if (errno == EEXIST)
239 log_error("Failed to create mount unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
240 else
241 log_error("Failed to create unit file %s: %m", unit);
242 return -errno;
243 }
244
245 fprintf(f,
246 "# Automatically generated by systemd-fstab-generator\n\n"
247 "[Unit]\n"
248 "SourcePath=%s\n"
249 "DefaultDependencies=no\n",
250 source);
251
252 if (!path_equal(where, "/"))
253 fprintf(f,
254 "After=%s\n"
255 "Wants=%s\n"
256 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
257 "Before=" SPECIAL_UMOUNT_TARGET "\n",
258 pre,
259 pre);
260
261
262 if (!noauto && !nofail && !automount)
263 fprintf(f,
264 "Before=%s\n",
265 post);
266
267 fprintf(f,
268 "\n"
269 "[Mount]\n"
270 "What=%s\n"
271 "Where=%s\n"
272 "Type=%s\n"
273 "FsckPassNo=%i\n",
274 what,
275 where,
276 type,
277 passno);
278
279 if (!isempty(opts) &&
280 !streq(opts, "defaults"))
281 fprintf(f,
282 "Options=%s\n",
283 opts);
284
285 if (wait)
286 fprintf(f,
287 "TimeoutSec=0\n");
288
289 fflush(f);
290 if (ferror(f)) {
291 log_error("Failed to write unit file %s: %m", unit);
292 return -errno;
293 }
294
295 if (!noauto) {
296 lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL);
297 if (!lnk)
298 return log_oom();
299
300 mkdir_parents_label(lnk, 0755);
301 if (symlink(unit, lnk) < 0) {
302 log_error("Failed to create symlink %s: %m", lnk);
303 return -errno;
304 }
305
306 if (!isbind &&
307 !path_equal(where, "/")) {
308
309 r = device_name(what, &device);
310 if (r < 0)
311 return r;
312
313 if (r > 0) {
314 free(lnk);
315 lnk = strjoin(arg_dest, "/", device, ".wants/", name, NULL);
316 if (!lnk)
317 return log_oom();
318
319 mkdir_parents_label(lnk, 0755);
320 if (symlink(unit, lnk) < 0) {
321 log_error("Failed to create symlink %s: %m", lnk);
322 return -errno;
323 }
324 }
325 }
326 }
327
328 if (automount && !path_equal(where, "/")) {
329 automount_name = unit_name_from_path(where, ".automount");
330 if (!name)
331 return log_oom();
332
333 automount_unit = strjoin(arg_dest, "/", automount_name, NULL);
334 if (!automount_unit)
335 return log_oom();
336
337 fclose(f);
338 f = fopen(automount_unit, "wxe");
339 if (!f) {
340 log_error("Failed to create unit file %s: %m", automount_unit);
341 return -errno;
342 }
343
344 fprintf(f,
345 "# Automatically generated by systemd-fstab-generator\n\n"
346 "[Unit]\n"
347 "SourcePath=/etc/fstab\n"
348 "DefaultDependencies=no\n"
349 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
350 "Before=" SPECIAL_UMOUNT_TARGET " %s\n"
351 "\n"
352 "[Automount]\n"
353 "Where=%s\n",
354 post,
355 where);
356
357 fflush(f);
358 if (ferror(f)) {
359 log_error("Failed to write unit file %s: %m", automount_unit);
360 return -errno;
361 }
362
363 free(lnk);
364 lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL);
365 if (!lnk)
366 return log_oom();
367
368 mkdir_parents_label(lnk, 0755);
369 if (symlink(automount_unit, lnk) < 0) {
370 log_error("Failed to create symlink %s: %m", lnk);
371 return -errno;
372 }
373 }
374
375 return 0;
376 }
377
378 static int parse_fstab(void) {
379 FILE *f;
380 int r = 0;
381 struct mntent *me;
382
383 errno = 0;
384 f = setmntent("/etc/fstab", "r");
385 if (!f) {
386 if (errno == ENOENT)
387 return 0;
388
389 log_error("Failed to open /etc/fstab: %m");
390 return -errno;
391 }
392
393 while ((me = getmntent(f))) {
394 char _cleanup_free_ *where = NULL, *what = NULL;
395 int k;
396
397 what = fstab_node_to_udev_node(me->mnt_fsname);
398 where = strdup(me->mnt_dir);
399 if (!what || !where) {
400 r = log_oom();
401 goto finish;
402 }
403
404 if (is_path(where))
405 path_kill_slashes(where);
406
407 log_debug("Found entry what=%s where=%s type=%s", what, where, me->mnt_type);
408
409 if (streq(me->mnt_type, "swap"))
410 k = add_swap(what, me);
411 else {
412 bool noauto, nofail, automount, isbind, isnetwork;
413
414 noauto = !!hasmntopt(me, "noauto");
415 nofail = !!hasmntopt(me, "nofail");
416 automount =
417 hasmntopt(me, "comment=systemd.automount") ||
418 hasmntopt(me, "x-systemd.automount");
419 isbind = mount_is_bind(me);
420 isnetwork = mount_is_network(me);
421
422 k = add_mount(what, where, me->mnt_type, me->mnt_opts,
423 me->mnt_passno, false, noauto, nofail,
424 automount, isbind, isnetwork,
425 "/etc/fstab");
426 }
427
428 if (k < 0)
429 r = k;
430 }
431
432 finish:
433 endmntent(f);
434 return r;
435 }
436
437 static int parse_new_root_from_proc_cmdline(void) {
438 char *w, *state;
439 _cleanup_free_ char *what = NULL, *type = NULL, *opts = NULL, *line = NULL;
440 int r;
441 size_t l;
442 bool wait = false;
443
444 r = read_one_line_file("/proc/cmdline", &line);
445 if (r < 0) {
446 log_error("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
447 return 0;
448 }
449
450 opts = strdup("defaults");
451 type = strdup("auto");
452 if (!opts || !type)
453 return log_oom();
454
455 /* root= and roofstype= may occur more than once, the last instance should take precedence.
456 * In the case of multiple rootflags= the arguments should be concatenated */
457 FOREACH_WORD_QUOTED(w, l, line, state) {
458 char *word, *tmp_word;
459
460 word = strndup(w, l);
461 if (!word)
462 return log_oom();
463
464 else if (startswith(word, "root=")) {
465 free(what);
466 what = fstab_node_to_udev_node(word+5);
467 if (!what)
468 return log_oom();
469
470 } else if (startswith(word, "rootfstype=")) {
471 free(type);
472 type = strdup(word + 11);
473 if (!type)
474 return log_oom();
475
476 } else if (startswith(word, "rootflags=")) {
477 tmp_word = opts;
478 opts = strjoin(opts, ",", word + 10, NULL);
479 free(tmp_word);
480 if (!opts)
481 return log_oom();
482
483 } else if (streq(word, "ro") || streq(word, "rw")) {
484 tmp_word = opts;
485 opts = strjoin(opts, ",", word, NULL);
486 free(tmp_word);
487 if (!opts)
488 return log_oom();
489
490 } else if (streq(word, "rootwait"))
491 wait = true;
492
493 free(word);
494 }
495
496 if (what) {
497
498 log_debug("Found entry what=%s where=/new_root type=%s", what, type);
499 r = add_mount(what, "/new_root", type, opts, 0, wait, false, false,
500 false, false, false, "/proc/cmdline");
501
502 if (r < 0)
503 return r;
504 } else
505 log_error("Could not find a root= entry on the kernel commandline.");
506
507 return 0;
508 }
509
510 static int parse_proc_cmdline(void) {
511 char _cleanup_free_ *line = NULL;
512 char *w, *state;
513 int r;
514 size_t l;
515
516 if (detect_container(NULL) > 0)
517 return 0;
518
519 r = read_one_line_file("/proc/cmdline", &line);
520 if (r < 0) {
521 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
522 return 0;
523 }
524
525 FOREACH_WORD_QUOTED(w, l, line, state) {
526 char _cleanup_free_ *word = NULL;
527
528 word = strndup(w, l);
529 if (!word)
530 return log_oom();
531
532 if (startswith(word, "fstab=")) {
533 r = parse_boolean(word + 6);
534 if (r < 0)
535 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
536 else
537 arg_enabled = r;
538
539 } else if (startswith(word, "rd.fstab=")) {
540
541 if (in_initrd()) {
542 r = parse_boolean(word + 6);
543 if (r < 0)
544 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
545 else
546 arg_enabled = r;
547 }
548
549 } else if (startswith(word, "fstab.") ||
550 (in_initrd() && startswith(word, "rd.fstab."))) {
551
552 log_warning("Unknown kernel switch %s. Ignoring.", word);
553 }
554 }
555
556 return 0;
557 }
558
559 int main(int argc, char *argv[]) {
560 int r, k = 0;
561
562 if (argc > 1 && argc != 4) {
563 log_error("This program takes three or no arguments.");
564 return EXIT_FAILURE;
565 }
566
567 if (argc > 1)
568 arg_dest = argv[1];
569
570 log_set_target(LOG_TARGET_SAFE);
571 log_parse_environment();
572 log_open();
573
574 umask(0022);
575
576 if (parse_proc_cmdline() < 0)
577 return EXIT_FAILURE;
578
579 if (in_initrd())
580 k = parse_new_root_from_proc_cmdline();
581
582 if (!arg_enabled)
583 return EXIT_SUCCESS;
584
585 r = parse_fstab();
586
587 return (r < 0) || (k < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
588 }