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