]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/fstab-generator/fstab-generator.c
Merge pull request #1190 from poettering/rework-virt
[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 "fstab-util.h"
33 #include "mount-setup.h"
34 #include "special.h"
35 #include "mkdir.h"
36 #include "generator.h"
37 #include "strv.h"
38 #include "virt.h"
39
40 static const char *arg_dest = "/tmp";
41 static bool arg_fstab_enabled = true;
42 static char *arg_root_what = NULL;
43 static char *arg_root_fstype = NULL;
44 static char *arg_root_options = NULL;
45 static int arg_root_rw = -1;
46 static char *arg_usr_what = NULL;
47 static char *arg_usr_fstype = NULL;
48 static char *arg_usr_options = NULL;
49
50 static int add_swap(
51 const char *what,
52 struct mntent *me,
53 bool noauto,
54 bool nofail) {
55
56 _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL;
57 _cleanup_fclose_ FILE *f = NULL;
58 int r;
59
60 assert(what);
61 assert(me);
62
63 if (access("/proc/swaps", F_OK) < 0) {
64 log_info("Swap not supported, ignoring fstab swap entry for %s.", what);
65 return 0;
66 }
67
68 if (detect_container() > 0) {
69 log_info("Running in a container, ignoring fstab swap entry for %s.", what);
70 return 0;
71 }
72
73 r = unit_name_from_path(what, ".swap", &name);
74 if (r < 0)
75 return log_error_errno(r, "Failed to generate unit name: %m");
76
77 unit = strjoin(arg_dest, "/", name, NULL);
78 if (!unit)
79 return log_oom();
80
81 f = fopen(unit, "wxe");
82 if (!f) {
83 if (errno == EEXIST)
84 log_error("Failed to create swap unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
85 else
86 log_error_errno(errno, "Failed to create unit file %s: %m", unit);
87 return -errno;
88 }
89
90 fprintf(f,
91 "# Automatically generated by systemd-fstab-generator\n\n"
92 "[Unit]\n"
93 "SourcePath=/etc/fstab\n"
94 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n\n"
95 "[Swap]\n"
96 "What=%s\n",
97 what);
98
99 if (!isempty(me->mnt_opts) && !streq(me->mnt_opts, "defaults"))
100 fprintf(f, "Options=%s\n", me->mnt_opts);
101
102 r = fflush_and_check(f);
103 if (r < 0)
104 return log_error_errno(r, "Failed to write unit file %s: %m", unit);
105
106 /* use what as where, to have a nicer error message */
107 r = generator_write_timeouts(arg_dest, what, what, me->mnt_opts, NULL);
108 if (r < 0)
109 return r;
110
111 if (!noauto) {
112 lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET,
113 nofail ? ".wants/" : ".requires/", name, NULL);
114 if (!lnk)
115 return log_oom();
116
117 mkdir_parents_label(lnk, 0755);
118 if (symlink(unit, lnk) < 0)
119 return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
120 }
121
122 return 0;
123 }
124
125 static bool mount_is_network(struct mntent *me) {
126 assert(me);
127
128 return fstab_test_option(me->mnt_opts, "_netdev\0") ||
129 fstype_is_network(me->mnt_type);
130 }
131
132 static bool mount_in_initrd(struct mntent *me) {
133 assert(me);
134
135 return fstab_test_option(me->mnt_opts, "x-initrd.mount\0") ||
136 streq(me->mnt_dir, "/usr");
137 }
138
139 static int write_idle_timeout(FILE *f, const char *where, const char *opts) {
140 _cleanup_free_ char *timeout = NULL;
141 char timespan[FORMAT_TIMESPAN_MAX];
142 usec_t u;
143 int r;
144
145 r = fstab_filter_options(opts, "x-systemd.idle-timeout\0", NULL, &timeout, NULL);
146 if (r < 0)
147 return log_warning_errno(r, "Failed to parse options: %m");
148 if (r == 0)
149 return 0;
150
151 r = parse_sec(timeout, &u);
152 if (r < 0) {
153 log_warning("Failed to parse timeout for %s, ignoring: %s", where, timeout);
154 return 0;
155 }
156
157 fprintf(f, "TimeoutIdleSec=%s\n", format_timespan(timespan, sizeof(timespan), u, 0));
158
159 return 0;
160 }
161
162 static int write_requires_after(FILE *f, const char *opts) {
163 _cleanup_strv_free_ char **names = NULL, **units = NULL;
164 _cleanup_free_ char *res = NULL;
165 char **s;
166 int r;
167
168 assert(f);
169 assert(opts);
170
171 r = fstab_extract_values(opts, "x-systemd.requires", &names);
172 if (r < 0)
173 return log_warning_errno(r, "Failed to parse options: %m");
174 if (r == 0)
175 return 0;
176
177 STRV_FOREACH(s, names) {
178 char *x;
179
180 r = unit_name_mangle_with_suffix(*s, UNIT_NAME_NOGLOB, ".mount", &x);
181 if (r < 0)
182 return log_error_errno(r, "Failed to generate unit name: %m");
183 r = strv_consume(&units, x);
184 if (r < 0)
185 return log_oom();
186 }
187
188 if (units) {
189 res = strv_join(units, " ");
190 if (!res)
191 return log_oom();
192 fprintf(f, "After=%1$s\nRequires=%1$s\n", res);
193 }
194
195 return 0;
196 }
197
198 static int write_requires_mounts_for(FILE *f, const char *opts) {
199 _cleanup_strv_free_ char **paths = NULL;
200 _cleanup_free_ char *res = NULL;
201 int r;
202
203 assert(f);
204 assert(opts);
205
206 r = fstab_extract_values(opts, "x-systemd.requires-mounts-for", &paths);
207 if (r < 0)
208 return log_warning_errno(r, "Failed to parse options: %m");
209 if (r == 0)
210 return 0;
211
212 res = strv_join(paths, " ");
213 if (!res)
214 return log_oom();
215
216 fprintf(f, "RequiresMountsFor=%s\n", res);
217
218 return 0;
219 }
220
221 static int add_mount(
222 const char *what,
223 const char *where,
224 const char *fstype,
225 const char *opts,
226 int passno,
227 bool noauto,
228 bool nofail,
229 bool automount,
230 const char *post,
231 const char *source) {
232
233 _cleanup_free_ char
234 *name = NULL, *unit = NULL, *lnk = NULL,
235 *automount_name = NULL, *automount_unit = NULL,
236 *filtered = NULL;
237 _cleanup_fclose_ FILE *f = NULL;
238 int r;
239
240 assert(what);
241 assert(where);
242 assert(opts);
243 assert(source);
244
245 if (streq_ptr(fstype, "autofs"))
246 return 0;
247
248 if (!is_path(where)) {
249 log_warning("Mount point %s is not a valid path, ignoring.", where);
250 return 0;
251 }
252
253 if (mount_point_is_api(where) ||
254 mount_point_ignore(where))
255 return 0;
256
257 if (path_equal(where, "/")) {
258 if (noauto)
259 log_warning("Ignoring \"noauto\" for root device");
260 if (nofail)
261 log_warning("Ignoring \"nofail\" for root device");
262 if (automount)
263 log_warning("Ignoring automount option for root device");
264
265 noauto = nofail = automount = false;
266 }
267
268 r = unit_name_from_path(where, ".mount", &name);
269 if (r < 0)
270 return log_error_errno(r, "Failed to generate unit name: %m");
271
272 unit = strjoin(arg_dest, "/", name, NULL);
273 if (!unit)
274 return log_oom();
275
276 f = fopen(unit, "wxe");
277 if (!f) {
278 if (errno == EEXIST)
279 log_error("Failed to create mount unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
280 else
281 log_error_errno(errno, "Failed to create unit file %s: %m", unit);
282 return -errno;
283 }
284
285 fprintf(f,
286 "# Automatically generated by systemd-fstab-generator\n\n"
287 "[Unit]\n"
288 "SourcePath=%s\n"
289 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
290 source);
291
292 if (post && !noauto && !nofail && !automount)
293 fprintf(f, "Before=%s\n", post);
294
295 if (!automount && opts) {
296 r = write_requires_after(f, opts);
297 if (r < 0)
298 return r;
299 r = write_requires_mounts_for(f, opts);
300 if (r < 0)
301 return r;
302 }
303
304 if (passno != 0) {
305 r = generator_write_fsck_deps(f, arg_dest, what, where, fstype);
306 if (r < 0)
307 return r;
308 }
309
310 fprintf(f,
311 "\n"
312 "[Mount]\n"
313 "What=%s\n"
314 "Where=%s\n",
315 what,
316 where);
317
318 if (!isempty(fstype) && !streq(fstype, "auto"))
319 fprintf(f, "Type=%s\n", fstype);
320
321 r = generator_write_timeouts(arg_dest, what, where, opts, &filtered);
322 if (r < 0)
323 return r;
324
325 if (!isempty(filtered) && !streq(filtered, "defaults"))
326 fprintf(f, "Options=%s\n", filtered);
327
328 r = fflush_and_check(f);
329 if (r < 0)
330 return log_error_errno(r, "Failed to write unit file %s: %m", unit);
331
332 if (!noauto && post) {
333 lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL);
334 if (!lnk)
335 return log_oom();
336
337 mkdir_parents_label(lnk, 0755);
338 if (symlink(unit, lnk) < 0)
339 return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
340 }
341
342 if (automount) {
343 r = unit_name_from_path(where, ".automount", &automount_name);
344 if (r < 0)
345 return log_error_errno(r, "Failed to generate unit name: %m");
346
347 automount_unit = strjoin(arg_dest, "/", automount_name, NULL);
348 if (!automount_unit)
349 return log_oom();
350
351 fclose(f);
352 f = fopen(automount_unit, "wxe");
353 if (!f)
354 return log_error_errno(errno, "Failed to create unit file %s: %m", automount_unit);
355
356 fprintf(f,
357 "# Automatically generated by systemd-fstab-generator\n\n"
358 "[Unit]\n"
359 "SourcePath=%s\n"
360 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
361 source);
362
363 if (post)
364 fprintf(f,
365 "Before=%s\n",
366 post);
367
368 if (opts) {
369 r = write_requires_after(f, opts);
370 if (r < 0)
371 return r;
372 r = write_requires_mounts_for(f, opts);
373 if (r < 0)
374 return r;
375 }
376
377 fprintf(f,
378 "[Automount]\n"
379 "Where=%s\n",
380 where);
381
382 r = write_idle_timeout(f, where, opts);
383 if (r < 0)
384 return r;
385
386 r = fflush_and_check(f);
387 if (r < 0)
388 return log_error_errno(r, "Failed to write unit file %s: %m", automount_unit);
389
390 free(lnk);
391 lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL);
392 if (!lnk)
393 return log_oom();
394
395 mkdir_parents_label(lnk, 0755);
396 if (symlink(automount_unit, lnk) < 0)
397 return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
398 }
399
400 return 0;
401 }
402
403 static int parse_fstab(bool initrd) {
404 _cleanup_endmntent_ FILE *f = NULL;
405 const char *fstab_path;
406 struct mntent *me;
407 int r = 0;
408
409 fstab_path = initrd ? "/sysroot/etc/fstab" : "/etc/fstab";
410 f = setmntent(fstab_path, "re");
411 if (!f) {
412 if (errno == ENOENT)
413 return 0;
414
415 log_error_errno(errno, "Failed to open %s: %m", fstab_path);
416 return -errno;
417 }
418
419 while ((me = getmntent(f))) {
420 _cleanup_free_ char *where = NULL, *what = NULL;
421 bool noauto, nofail;
422 int k;
423
424 if (initrd && !mount_in_initrd(me))
425 continue;
426
427 what = fstab_node_to_udev_node(me->mnt_fsname);
428 if (!what)
429 return log_oom();
430
431 if (is_device_path(what) && path_is_read_only_fs("sys") > 0) {
432 log_info("Running in a container, ignoring fstab device entry for %s.", what);
433 continue;
434 }
435
436 where = initrd ? strappend("/sysroot/", me->mnt_dir) : strdup(me->mnt_dir);
437 if (!where)
438 return log_oom();
439
440 if (is_path(where))
441 path_kill_slashes(where);
442
443 noauto = fstab_test_yes_no_option(me->mnt_opts, "noauto\0" "auto\0");
444 nofail = fstab_test_yes_no_option(me->mnt_opts, "nofail\0" "fail\0");
445 log_debug("Found entry what=%s where=%s type=%s nofail=%s noauto=%s",
446 what, where, me->mnt_type,
447 yes_no(noauto), yes_no(nofail));
448
449 if (streq(me->mnt_type, "swap"))
450 k = add_swap(what, me, noauto, nofail);
451 else {
452 bool automount;
453 const char *post;
454
455 automount = fstab_test_option(me->mnt_opts,
456 "comment=systemd.automount\0"
457 "x-systemd.automount\0");
458 if (initrd)
459 post = SPECIAL_INITRD_FS_TARGET;
460 else if (mount_in_initrd(me))
461 post = SPECIAL_INITRD_ROOT_FS_TARGET;
462 else if (mount_is_network(me))
463 post = SPECIAL_REMOTE_FS_TARGET;
464 else
465 post = SPECIAL_LOCAL_FS_TARGET;
466
467 k = add_mount(what,
468 where,
469 me->mnt_type,
470 me->mnt_opts,
471 me->mnt_passno,
472 noauto,
473 nofail,
474 automount,
475 post,
476 fstab_path);
477 }
478
479 if (k < 0)
480 r = k;
481 }
482
483 return r;
484 }
485
486 static int add_sysroot_mount(void) {
487 _cleanup_free_ char *what = NULL;
488 const char *opts;
489
490 if (isempty(arg_root_what)) {
491 log_debug("Could not find a root= entry on the kernel command line.");
492 return 0;
493 }
494
495 what = fstab_node_to_udev_node(arg_root_what);
496 if (!what)
497 return log_oom();
498
499 if (!arg_root_options)
500 opts = arg_root_rw > 0 ? "rw" : "ro";
501 else if (arg_root_rw >= 0 ||
502 !fstab_test_option(arg_root_options, "ro\0" "rw\0"))
503 opts = strjoina(arg_root_options, ",", arg_root_rw > 0 ? "rw" : "ro");
504 else
505 opts = arg_root_options;
506
507 log_debug("Found entry what=%s where=/sysroot type=%s", what, strna(arg_root_fstype));
508 return add_mount(what,
509 "/sysroot",
510 arg_root_fstype,
511 opts,
512 is_device_path(what) ? 1 : 0,
513 false,
514 false,
515 false,
516 SPECIAL_INITRD_ROOT_FS_TARGET,
517 "/proc/cmdline");
518 }
519
520 static int add_sysroot_usr_mount(void) {
521 _cleanup_free_ char *what = NULL;
522 const char *opts;
523
524 if (!arg_usr_what && !arg_usr_fstype && !arg_usr_options)
525 return 0;
526
527 if (arg_root_what && !arg_usr_what) {
528 arg_usr_what = strdup(arg_root_what);
529
530 if (!arg_usr_what)
531 return log_oom();
532 }
533
534 if (arg_root_fstype && !arg_usr_fstype) {
535 arg_usr_fstype = strdup(arg_root_fstype);
536
537 if (!arg_usr_fstype)
538 return log_oom();
539 }
540
541 if (arg_root_options && !arg_usr_options) {
542 arg_usr_options = strdup(arg_root_options);
543
544 if (!arg_usr_options)
545 return log_oom();
546 }
547
548 if (!arg_usr_what)
549 return 0;
550
551 what = fstab_node_to_udev_node(arg_usr_what);
552 if (!path_is_absolute(what)) {
553 log_debug("Skipping entry what=%s where=/sysroot/usr type=%s", what, strna(arg_usr_fstype));
554 return -1;
555 }
556
557 if (!arg_usr_options)
558 opts = arg_root_rw > 0 ? "rw" : "ro";
559 else if (!fstab_test_option(arg_usr_options, "ro\0" "rw\0"))
560 opts = strjoina(arg_usr_options, ",", arg_root_rw > 0 ? "rw" : "ro");
561 else
562 opts = arg_usr_options;
563
564 log_debug("Found entry what=%s where=/sysroot/usr type=%s", what, strna(arg_usr_fstype));
565 return add_mount(what,
566 "/sysroot/usr",
567 arg_usr_fstype,
568 opts,
569 1,
570 false,
571 false,
572 false,
573 SPECIAL_INITRD_ROOT_FS_TARGET,
574 "/proc/cmdline");
575 }
576
577 static int parse_proc_cmdline_item(const char *key, const char *value) {
578 int r;
579
580 /* root=, usr=, usrfstype= and roofstype= may occur more than once, the last
581 * instance should take precedence. In the case of multiple rootflags=
582 * or usrflags= the arguments should be concatenated */
583
584 if (STR_IN_SET(key, "fstab", "rd.fstab") && value) {
585
586 r = parse_boolean(value);
587 if (r < 0)
588 log_warning("Failed to parse fstab switch %s. Ignoring.", value);
589 else
590 arg_fstab_enabled = r;
591
592 } else if (streq(key, "root") && value) {
593
594 if (free_and_strdup(&arg_root_what, value) < 0)
595 return log_oom();
596
597 } else if (streq(key, "rootfstype") && value) {
598
599 if (free_and_strdup(&arg_root_fstype, value) < 0)
600 return log_oom();
601
602 } else if (streq(key, "rootflags") && value) {
603 char *o;
604
605 o = arg_root_options ?
606 strjoin(arg_root_options, ",", value, NULL) :
607 strdup(value);
608 if (!o)
609 return log_oom();
610
611 free(arg_root_options);
612 arg_root_options = o;
613
614 } else if (streq(key, "mount.usr") && value) {
615
616 if (free_and_strdup(&arg_usr_what, value) < 0)
617 return log_oom();
618
619 } else if (streq(key, "mount.usrfstype") && value) {
620
621 if (free_and_strdup(&arg_usr_fstype, value) < 0)
622 return log_oom();
623
624 } else if (streq(key, "mount.usrflags") && value) {
625 char *o;
626
627 o = arg_usr_options ?
628 strjoin(arg_usr_options, ",", value, NULL) :
629 strdup(value);
630 if (!o)
631 return log_oom();
632
633 free(arg_usr_options);
634 arg_usr_options = o;
635
636 } else if (streq(key, "rw") && !value)
637 arg_root_rw = true;
638 else if (streq(key, "ro") && !value)
639 arg_root_rw = false;
640
641 return 0;
642 }
643
644 int main(int argc, char *argv[]) {
645 int r = 0;
646
647 if (argc > 1 && argc != 4) {
648 log_error("This program takes three or no arguments.");
649 return EXIT_FAILURE;
650 }
651
652 if (argc > 1)
653 arg_dest = argv[1];
654
655 log_set_target(LOG_TARGET_SAFE);
656 log_parse_environment();
657 log_open();
658
659 umask(0022);
660
661 r = parse_proc_cmdline(parse_proc_cmdline_item);
662 if (r < 0)
663 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
664
665 /* Always honour root= and usr= in the kernel command line if we are in an initrd */
666 if (in_initrd()) {
667 r = add_sysroot_mount();
668 if (r == 0)
669 r = add_sysroot_usr_mount();
670 }
671
672 /* Honour /etc/fstab only when that's enabled */
673 if (arg_fstab_enabled) {
674 int k;
675
676 log_debug("Parsing /etc/fstab");
677
678 /* Parse the local /etc/fstab, possibly from the initrd */
679 k = parse_fstab(false);
680 if (k < 0)
681 r = k;
682
683 /* If running in the initrd also parse the /etc/fstab from the host */
684 if (in_initrd()) {
685 log_debug("Parsing /sysroot/etc/fstab");
686
687 k = parse_fstab(true);
688 if (k < 0)
689 r = k;
690 }
691 }
692
693 free(arg_root_what);
694 free(arg_root_fstype);
695 free(arg_root_options);
696
697 free(arg_usr_what);
698 free(arg_usr_fstype);
699 free(arg_usr_options);
700
701 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
702 }