]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/fstab-generator/fstab-generator.c
fstab-generator: drop x-initrd.rootfs mount option
[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 hasmntopt(me, "rbind") ||
183 streq(me->mnt_type, "rbind");
184 }
185
186 static bool mount_is_network(struct mntent *me) {
187 assert(me);
188
189 return
190 hasmntopt(me, "_netdev") ||
191 fstype_is_network(me->mnt_type);
192 }
193
194 static bool mount_in_initrd(struct mntent *me) {
195 assert(me);
196
197 return
198 hasmntopt(me, "x-initrd.mount") ||
199 streq(me->mnt_dir, "/usr");
200 }
201
202 static int add_mount(
203 const char *what,
204 const char *where,
205 const char *type,
206 const char *opts,
207 int passno,
208 bool noauto,
209 bool nofail,
210 bool automount,
211 bool isbind,
212 const char *pre,
213 const char *post,
214 const char *setup,
215 const char *source) {
216 char _cleanup_free_
217 *name = NULL, *unit = NULL, *lnk = NULL, *device = NULL,
218 *automount_name = NULL, *automount_unit = NULL;
219 FILE _cleanup_fclose_ *f = NULL;
220 int r;
221
222 assert(what);
223 assert(where);
224 assert(type);
225 assert(opts);
226 assert(source);
227
228 if (streq(type, "autofs"))
229 return 0;
230
231 if (!is_path(where)) {
232 log_warning("Mount point %s is not a valid path, ignoring.", where);
233 return 0;
234 }
235
236 if (mount_point_is_api(where) ||
237 mount_point_ignore(where))
238 return 0;
239
240 name = unit_name_from_path(where, ".mount");
241 if (!name)
242 return log_oom();
243
244 unit = strjoin(arg_dest, "/", name, NULL);
245 if (!unit)
246 return log_oom();
247
248 f = fopen(unit, "wxe");
249 if (!f) {
250 if (errno == EEXIST)
251 log_error("Failed to create mount unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
252 else
253 log_error("Failed to create unit file %s: %m", unit);
254 return -errno;
255 }
256
257 fprintf(f,
258 "# Automatically generated by systemd-fstab-generator\n\n"
259 "[Unit]\n"
260 "SourcePath=%s\n"
261 "DefaultDependencies=no\n",
262 source);
263
264 if (!path_equal(where, "/")) {
265 if (pre)
266 fprintf(f,
267 "After=%s\n",
268 pre);
269
270 if (setup)
271 fprintf(f,
272 "Wants=%s\n",
273 setup);
274
275 fprintf(f,
276 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
277 "Before=" SPECIAL_UMOUNT_TARGET "\n");
278 }
279
280 if (post && !noauto && !nofail && !automount)
281 fprintf(f,
282 "Before=%s\n",
283 post);
284
285 fprintf(f,
286 "\n"
287 "[Mount]\n"
288 "What=%s\n"
289 "Where=%s\n"
290 "Type=%s\n"
291 "FsckPassNo=%i\n",
292 what,
293 where,
294 type,
295 passno);
296
297 if (!isempty(opts) &&
298 !streq(opts, "defaults"))
299 fprintf(f,
300 "Options=%s\n",
301 opts);
302
303 fflush(f);
304 if (ferror(f)) {
305 log_error("Failed to write unit file %s: %m", unit);
306 return -errno;
307 }
308
309 if (!noauto) {
310 if (post) {
311 lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL);
312 if (!lnk)
313 return log_oom();
314
315 mkdir_parents_label(lnk, 0755);
316 if (symlink(unit, lnk) < 0) {
317 log_error("Failed to create symlink %s: %m", lnk);
318 return -errno;
319 }
320 }
321
322 if (!isbind &&
323 !path_equal(where, "/")) {
324
325 r = device_name(what, &device);
326 if (r < 0)
327 return r;
328
329 if (r > 0) {
330 free(lnk);
331 lnk = strjoin(arg_dest, "/", device, ".wants/", name, NULL);
332 if (!lnk)
333 return log_oom();
334
335 mkdir_parents_label(lnk, 0755);
336 if (symlink(unit, lnk) < 0) {
337 log_error("Failed to create symlink %s: %m", lnk);
338 return -errno;
339 }
340 }
341 }
342 }
343
344 if (automount && !path_equal(where, "/")) {
345 automount_name = unit_name_from_path(where, ".automount");
346 if (!name)
347 return log_oom();
348
349 automount_unit = strjoin(arg_dest, "/", automount_name, NULL);
350 if (!automount_unit)
351 return log_oom();
352
353 fclose(f);
354 f = fopen(automount_unit, "wxe");
355 if (!f) {
356 log_error("Failed to create unit file %s: %m", automount_unit);
357 return -errno;
358 }
359
360 fprintf(f,
361 "# Automatically generated by systemd-fstab-generator\n\n"
362 "[Unit]\n"
363 "SourcePath=%s\n"
364 "DefaultDependencies=no\n"
365 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
366 "Before=" SPECIAL_UMOUNT_TARGET "\n",
367 source);
368
369 if (post)
370 fprintf(f,
371 "Before= %s\n",
372 post);
373
374 fprintf(f,
375 "[Automount]\n"
376 "Where=%s\n",
377 where);
378
379 fflush(f);
380 if (ferror(f)) {
381 log_error("Failed to write unit file %s: %m", automount_unit);
382 return -errno;
383 }
384
385 free(lnk);
386 lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL);
387 if (!lnk)
388 return log_oom();
389
390 mkdir_parents_label(lnk, 0755);
391 if (symlink(automount_unit, lnk) < 0) {
392 log_error("Failed to create symlink %s: %m", lnk);
393 return -errno;
394 }
395 }
396
397 return 0;
398 }
399
400 static int parse_fstab(const char *prefix, bool initrd) {
401 _cleanup_free_ char *fstab_path = NULL;
402 FILE *f;
403 int r = 0;
404 struct mntent *me;
405
406 fstab_path = strjoin(strempty(prefix), "/etc/fstab", NULL);
407 if (!fstab_path)
408 return log_oom();
409
410 f = setmntent(fstab_path, "r");
411 if (!f) {
412 if (errno == ENOENT)
413 return 0;
414
415 log_error("Failed to open %s/etc/fstab: %m", strempty(prefix));
416 return -errno;
417 }
418
419 while ((me = getmntent(f))) {
420 char _cleanup_free_ *where = NULL, *what = NULL;
421 int k;
422
423 if (initrd && !mount_in_initrd(me))
424 continue;
425
426 what = fstab_node_to_udev_node(me->mnt_fsname);
427 where = strjoin(strempty(prefix), me->mnt_dir, NULL);
428 if (!what || !where) {
429 r = log_oom();
430 goto finish;
431 }
432
433 if (is_path(where))
434 path_kill_slashes(where);
435
436 log_debug("Found entry what=%s where=%s type=%s", what, where, me->mnt_type);
437
438 if (streq(me->mnt_type, "swap"))
439 k = add_swap(what, me);
440 else {
441 bool noauto, nofail, automount, isbind;
442 const char *pre, *post, *setup;
443
444 noauto = !!hasmntopt(me, "noauto");
445 nofail = !!hasmntopt(me, "nofail");
446 automount =
447 hasmntopt(me, "comment=systemd.automount") ||
448 hasmntopt(me, "x-systemd.automount");
449 isbind = mount_is_bind(me);
450
451 if (initrd) {
452 post = SPECIAL_INITRD_FS_TARGET;
453 pre = NULL;
454 setup = NULL;
455 } else if (mount_in_initrd(me)) {
456 post = SPECIAL_INITRD_ROOT_FS_TARGET;
457 pre = NULL;
458 setup = NULL;
459 } else if (mount_is_network(me)) {
460 post = SPECIAL_REMOTE_FS_TARGET;
461 pre = SPECIAL_REMOTE_FS_PRE_TARGET;
462 setup = SPECIAL_REMOTE_FS_SETUP_TARGET;
463 } else {
464 post = SPECIAL_LOCAL_FS_TARGET;
465 pre = SPECIAL_LOCAL_FS_PRE_TARGET;
466 setup = NULL;
467 }
468
469 k = add_mount(what, where, me->mnt_type, me->mnt_opts,
470 me->mnt_passno, noauto, nofail, automount,
471 isbind, pre, post, setup, fstab_path);
472 }
473
474 if (k < 0)
475 r = k;
476 }
477
478 finish:
479 endmntent(f);
480 return r;
481 }
482
483 static int parse_new_root_from_proc_cmdline(void) {
484 _cleanup_free_ char *what = NULL, *type = NULL, *opts = NULL, *line = NULL;
485 char *w, *state;
486 int r;
487 size_t l;
488
489 r = read_one_line_file("/proc/cmdline", &line);
490 if (r < 0) {
491 log_error("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
492 return 0;
493 }
494
495 opts = strdup("ro");
496 type = strdup("auto");
497 if (!opts || !type)
498 return log_oom();
499
500 /* root= and roofstype= may occur more than once, the last instance should take precedence.
501 * In the case of multiple rootflags= the arguments should be concatenated */
502 FOREACH_WORD_QUOTED(w, l, line, state) {
503 _cleanup_free_ char *word;
504
505 word = strndup(w, l);
506 if (!word)
507 return log_oom();
508
509 else if (startswith(word, "root=")) {
510 free(what);
511 what = fstab_node_to_udev_node(word+5);
512 if (!what)
513 return log_oom();
514
515 } else if (startswith(word, "rootfstype=")) {
516 free(type);
517 type = strdup(word + 11);
518 if (!type)
519 return log_oom();
520
521 } else if (startswith(word, "rootflags=")) {
522 char *o;
523
524 o = strjoin(opts, ",", word + 10, NULL);
525 if (!o)
526 return log_oom();
527
528 free(opts);
529 opts = o;
530
531 } else if (streq(word, "ro") || streq(word, "rw")) {
532 char *o;
533
534 o = strjoin(opts, ",", word, NULL);
535 if (!o)
536 return log_oom();
537
538 free(opts);
539 opts = o;
540 }
541 }
542
543 if (!what) {
544 log_debug("Could not find a root= entry on the kernel commandline.");
545 return 0;
546 }
547
548 if (what[0] != '/') {
549 log_debug("Skipping entry what=%s where=/sysroot type=%s", what, type);
550 return 0;
551 }
552
553 log_debug("Found entry what=%s where=/sysroot type=%s", what, type);
554 r = add_mount(what, "/sysroot", type, opts, 0, false, false, false,
555 false, NULL, SPECIAL_INITRD_ROOT_FS_TARGET, NULL, "/proc/cmdline");
556
557 return (r < 0) ? r : 0;
558 }
559
560 static int parse_proc_cmdline(void) {
561 char _cleanup_free_ *line = NULL;
562 char *w, *state;
563 int r;
564 size_t l;
565
566 if (detect_container(NULL) > 0)
567 return 0;
568
569 r = read_one_line_file("/proc/cmdline", &line);
570 if (r < 0) {
571 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
572 return 0;
573 }
574
575 FOREACH_WORD_QUOTED(w, l, line, state) {
576 char _cleanup_free_ *word = NULL;
577
578 word = strndup(w, l);
579 if (!word)
580 return log_oom();
581
582 if (startswith(word, "fstab=")) {
583 r = parse_boolean(word + 6);
584 if (r < 0)
585 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
586 else
587 arg_enabled = r;
588
589 } else if (startswith(word, "rd.fstab=")) {
590
591 if (in_initrd()) {
592 r = parse_boolean(word + 6);
593 if (r < 0)
594 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
595 else
596 arg_enabled = r;
597 }
598
599 } else if (startswith(word, "fstab.") ||
600 (in_initrd() && startswith(word, "rd.fstab."))) {
601
602 log_warning("Unknown kernel switch %s. Ignoring.", word);
603 }
604 }
605
606 return 0;
607 }
608
609 int main(int argc, char *argv[]) {
610 int r = 0, k, l = 0;
611
612 if (argc > 1 && argc != 4) {
613 log_error("This program takes three or no arguments.");
614 return EXIT_FAILURE;
615 }
616
617 if (argc > 1)
618 arg_dest = argv[1];
619
620 log_set_target(LOG_TARGET_SAFE);
621 log_parse_environment();
622 log_open();
623
624 umask(0022);
625
626 if (parse_proc_cmdline() < 0)
627 return EXIT_FAILURE;
628
629 if (in_initrd())
630 r = parse_new_root_from_proc_cmdline();
631
632 if (!arg_enabled)
633 return (r < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
634
635 k = parse_fstab(NULL, false);
636
637 if (in_initrd())
638 l = parse_fstab("/sysroot", true);
639
640 return (r < 0) || (k < 0) || (l < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
641 }