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