]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/fstab-generator/fstab-generator.c
swap: replace Discard= setting by a more generic Options= setting
[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"
a5c32cff 35#include "fileio.h"
e48fdd84 36#include "generator.h"
059cb385 37#include "strv.h"
689aede8 38#include "virt.h"
6b1dc2bd
LP
39
40static const char *arg_dest = "/tmp";
e48fdd84 41static bool arg_fstab_enabled = true;
6db615c1
LP
42static char *arg_root_what = NULL;
43static char *arg_root_fstype = NULL;
44static char *arg_root_options = NULL;
45static int arg_root_rw = -1;
9f103625
TH
46static char *arg_usr_what = NULL;
47static char *arg_usr_fstype = NULL;
48static char *arg_usr_options = NULL;
6b1dc2bd 49
6b1dc2bd 50static int mount_find_pri(struct mntent *me, int *ret) {
4f52d3fe 51 char *end, *opt;
6b1dc2bd
LP
52 unsigned long r;
53
54 assert(me);
55 assert(ret);
56
4f52d3fe
ZJS
57 opt = hasmntopt(me, "pri");
58 if (!opt)
6b1dc2bd
LP
59 return 0;
60
4f52d3fe 61 opt += strlen("pri");
4f52d3fe
ZJS
62 if (*opt != '=')
63 return -EINVAL;
6b1dc2bd
LP
64
65 errno = 0;
4f52d3fe 66 r = strtoul(opt + 1, &end, 10);
8333c77e 67 if (errno > 0)
6b1dc2bd
LP
68 return -errno;
69
4f52d3fe 70 if (end == opt + 1 || (*end != ',' && *end != 0))
6b1dc2bd
LP
71 return -EINVAL;
72
73 *ret = (int) r;
74 return 1;
75}
76
77static int add_swap(const char *what, struct mntent *me) {
1dc2ced4 78 _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL;
7fd1b19b 79 _cleanup_fclose_ FILE *f = NULL;
86b23b07 80
4e82fe52 81 bool noauto;
6b1dc2bd
LP
82 int r, pri = -1;
83
84 assert(what);
85 assert(me);
86
689aede8
LP
87 if (detect_container(NULL) > 0) {
88 log_info("Running in a container, ignoring fstab swap entry for %s.", what);
89 return 0;
90 }
91
6b1dc2bd
LP
92 r = mount_find_pri(me, &pri);
93 if (r < 0) {
94 log_error("Failed to parse priority");
4f52d3fe
ZJS
95 return r;
96 }
97
4e82fe52
TG
98 noauto = !!hasmntopt(me, "noauto");
99
6b1dc2bd 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
6b1dc2bd 117 fprintf(f,
64347fc2
TG
118 "# Automatically generated by systemd-fstab-generator\n\n"
119 "[Unit]\n"
c3834f9b
LP
120 "SourcePath=/etc/fstab\n"
121 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n\n"
6b1dc2bd
LP
122 "[Swap]\n"
123 "What=%s\n",
124 what);
125
47cb901e
LP
126 /* Note that we currently pass the priority field twice, once
127 * in Priority=, and once in Options= */
6b1dc2bd 128 if (pri >= 0)
4f52d3fe
ZJS
129 fprintf(f, "Priority=%i\n", pri);
130
47cb901e
LP
131 if (!isempty(me->mnt_opts) && !streq(me->mnt_opts, "defaults"))
132 fprintf(f, "Options=%s\n", me->mnt_opts);
6b1dc2bd 133
47cb901e
LP
134 r = fflush_and_check(f);
135 if (r < 0) {
136 log_error("Failed to write unit file %s: %s", unit, strerror(-r));
137 return r;
6b1dc2bd
LP
138 }
139
b3208b66
ZJS
140 /* use what as where, to have a nicer error message */
141 r = generator_write_timeouts(arg_dest, what, what, me->mnt_opts, NULL);
142 if (r < 0)
143 return r;
144
4e82fe52
TG
145 if (!noauto) {
146 lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL);
147 if (!lnk)
148 return log_oom();
149
150 mkdir_parents_label(lnk, 0755);
151 if (symlink(unit, lnk) < 0) {
152 log_error("Failed to create symlink %s: %m", lnk);
153 return -errno;
154 }
155 }
156
d0aa9ce5 157 return 0;
6b1dc2bd
LP
158}
159
6b1dc2bd
LP
160static bool mount_is_network(struct mntent *me) {
161 assert(me);
162
163 return
164 hasmntopt(me, "_netdev") ||
165 fstype_is_network(me->mnt_type);
166}
167
3d22d1ab
TG
168static bool mount_in_initrd(struct mntent *me) {
169 assert(me);
170
171 return
172 hasmntopt(me, "x-initrd.mount") ||
173 streq(me->mnt_dir, "/usr");
174}
175
e8d2f6cd
LP
176static int add_mount(
177 const char *what,
178 const char *where,
6db615c1 179 const char *fstype,
e8d2f6cd
LP
180 const char *opts,
181 int passno,
182 bool noauto,
183 bool nofail,
184 bool automount,
e8d2f6cd 185 const char *post,
e8d2f6cd 186 const char *source) {
e48fdd84 187
7fd1b19b 188 _cleanup_free_ char
1dc2ced4 189 *name = NULL, *unit = NULL, *lnk = NULL,
29686440
ZJS
190 *automount_name = NULL, *automount_unit = NULL,
191 *filtered = NULL;
7fd1b19b 192 _cleanup_fclose_ FILE *f = NULL;
94192cda 193 int r;
6b1dc2bd
LP
194
195 assert(what);
196 assert(where);
5e398e54
TG
197 assert(opts);
198 assert(source);
6b1dc2bd 199
6db615c1 200 if (streq_ptr(fstype, "autofs"))
6b1dc2bd
LP
201 return 0;
202
203 if (!is_path(where)) {
204 log_warning("Mount point %s is not a valid path, ignoring.", where);
205 return 0;
206 }
207
208 if (mount_point_is_api(where) ||
209 mount_point_ignore(where))
210 return 0;
211
e48fdd84 212 if (path_equal(where, "/")) {
5ecdcf41 213 /* The root disk is not an option */
e48fdd84
LP
214 automount = false;
215 noauto = false;
216 nofail = false;
217 }
218
6b1dc2bd 219 name = unit_name_from_path(where, ".mount");
d0aa9ce5
ZJS
220 if (!name)
221 return log_oom();
6b1dc2bd 222
b7def684 223 unit = strjoin(arg_dest, "/", name, NULL);
d0aa9ce5
ZJS
224 if (!unit)
225 return log_oom();
6b1dc2bd
LP
226
227 f = fopen(unit, "wxe");
228 if (!f) {
67ab5f76
TG
229 if (errno == EEXIST)
230 log_error("Failed to create mount unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
231 else
232 log_error("Failed to create unit file %s: %m", unit);
d0aa9ce5 233 return -errno;
6b1dc2bd
LP
234 }
235
5e398e54 236 fprintf(f,
c3834f9b
LP
237 "# Automatically generated by systemd-fstab-generator\n\n"
238 "[Unit]\n"
239 "SourcePath=%s\n"
240 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
241 source);
6b1dc2bd 242
700e07ff 243 if (post && !noauto && !nofail && !automount)
5ecdcf41 244 fprintf(f, "Before=%s\n", post);
6b1dc2bd 245
e48fdd84 246 if (passno != 0) {
6db615c1 247 r = generator_write_fsck_deps(f, arg_dest, what, where, fstype);
e48fdd84
LP
248 if (r < 0)
249 return r;
250 }
64e70e4b 251
6b1dc2bd
LP
252 fprintf(f,
253 "\n"
254 "[Mount]\n"
255 "What=%s\n"
6db615c1 256 "Where=%s\n",
6b1dc2bd 257 what,
6db615c1
LP
258 where);
259
260 if (!isempty(fstype) && !streq(fstype, "auto"))
261 fprintf(f, "Type=%s\n", fstype);
6b1dc2bd 262
29686440
ZJS
263 r = generator_write_timeouts(arg_dest, what, where, opts, &filtered);
264 if (r < 0)
265 return r;
266
267 if (!isempty(filtered) && !streq(filtered, "defaults"))
268 fprintf(f, "Options=%s\n", filtered);
5e398e54 269
6b1dc2bd
LP
270 fflush(f);
271 if (ferror(f)) {
40b8acd0 272 log_error("Failed to write unit file %s: %m", unit);
d0aa9ce5 273 return -errno;
6b1dc2bd
LP
274 }
275
5ecdcf41
LP
276 if (!noauto && post) {
277 lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL);
278 if (!lnk)
279 return log_oom();
280
281 mkdir_parents_label(lnk, 0755);
282 if (symlink(unit, lnk) < 0) {
283 log_error("Failed to create symlink %s: %m", lnk);
284 return -errno;
6b1dc2bd 285 }
6b1dc2bd
LP
286 }
287
e48fdd84 288 if (automount) {
6b1dc2bd 289 automount_name = unit_name_from_path(where, ".automount");
01264ad1 290 if (!automount_name)
d0aa9ce5 291 return log_oom();
6b1dc2bd 292
b7def684 293 automount_unit = strjoin(arg_dest, "/", automount_name, NULL);
d0aa9ce5
ZJS
294 if (!automount_unit)
295 return log_oom();
6b1dc2bd
LP
296
297 fclose(f);
298 f = fopen(automount_unit, "wxe");
299 if (!f) {
40b8acd0 300 log_error("Failed to create unit file %s: %m", automount_unit);
d0aa9ce5 301 return -errno;
6b1dc2bd
LP
302 }
303
304 fprintf(f,
305 "# Automatically generated by systemd-fstab-generator\n\n"
306 "[Unit]\n"
c3834f9b
LP
307 "SourcePath=%s\n"
308 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
700e07ff
HH
309 source);
310
311 if (post)
312 fprintf(f,
e48fdd84 313 "Before=%s\n",
700e07ff
HH
314 post);
315
316 fprintf(f,
6b1dc2bd
LP
317 "[Automount]\n"
318 "Where=%s\n",
6b1dc2bd
LP
319 where);
320
321 fflush(f);
322 if (ferror(f)) {
40b8acd0 323 log_error("Failed to write unit file %s: %m", automount_unit);
d0aa9ce5 324 return -errno;
6b1dc2bd
LP
325 }
326
327 free(lnk);
b7def684 328 lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL);
d0aa9ce5
ZJS
329 if (!lnk)
330 return log_oom();
6b1dc2bd 331
d2e54fae 332 mkdir_parents_label(lnk, 0755);
6b1dc2bd 333 if (symlink(automount_unit, lnk) < 0) {
40b8acd0 334 log_error("Failed to create symlink %s: %m", lnk);
d0aa9ce5 335 return -errno;
6b1dc2bd
LP
336 }
337 }
338
d0aa9ce5 339 return 0;
6b1dc2bd
LP
340}
341
e48fdd84 342static int parse_fstab(bool initrd) {
6db615c1 343 _cleanup_endmntent_ FILE *f = NULL;
e48fdd84 344 const char *fstab_path;
6b1dc2bd 345 struct mntent *me;
e48fdd84 346 int r = 0;
6b1dc2bd 347
e48fdd84
LP
348 fstab_path = initrd ? "/sysroot/etc/fstab" : "/etc/fstab";
349 f = setmntent(fstab_path, "re");
6b1dc2bd
LP
350 if (!f) {
351 if (errno == ENOENT)
352 return 0;
353
e48fdd84 354 log_error("Failed to open %s: %m", fstab_path);
6b1dc2bd
LP
355 return -errno;
356 }
357
358 while ((me = getmntent(f))) {
7fd1b19b 359 _cleanup_free_ char *where = NULL, *what = NULL;
6b1dc2bd
LP
360 int k;
361
3d22d1ab
TG
362 if (initrd && !mount_in_initrd(me))
363 continue;
364
6b1dc2bd 365 what = fstab_node_to_udev_node(me->mnt_fsname);
e48fdd84
LP
366 if (!what)
367 return log_oom();
368
689aede8
LP
369 if (detect_container(NULL) > 0 && is_device_path(what)) {
370 log_info("Running in a container, ignoring fstab device entry for %s.", what);
371 continue;
372 }
373
e48fdd84
LP
374 where = initrd ? strappend("/sysroot/", me->mnt_dir) : strdup(me->mnt_dir);
375 if (!where)
5862d652 376 return log_oom();
6b1dc2bd 377
ec6ceb18 378 if (is_path(where))
6b1dc2bd
LP
379 path_kill_slashes(where);
380
381 log_debug("Found entry what=%s where=%s type=%s", what, where, me->mnt_type);
382
383 if (streq(me->mnt_type, "swap"))
384 k = add_swap(what, me);
5e398e54 385 else {
5073f89f 386 bool noauto, nofail, automount;
80c3b720 387 const char *post;
5e398e54
TG
388
389 noauto = !!hasmntopt(me, "noauto");
390 nofail = !!hasmntopt(me, "nofail");
391 automount =
392 hasmntopt(me, "comment=systemd.automount") ||
393 hasmntopt(me, "x-systemd.automount");
700e07ff 394
e48fdd84 395 if (initrd)
700e07ff 396 post = SPECIAL_INITRD_FS_TARGET;
e48fdd84 397 else if (mount_in_initrd(me))
9e5f0f92 398 post = SPECIAL_INITRD_ROOT_FS_TARGET;
e48fdd84 399 else if (mount_is_network(me))
0c17fbce 400 post = SPECIAL_REMOTE_FS_TARGET;
e48fdd84 401 else
0c17fbce 402 post = SPECIAL_LOCAL_FS_TARGET;
5e398e54 403
6db615c1
LP
404 k = add_mount(what,
405 where,
406 me->mnt_type,
407 me->mnt_opts,
408 me->mnt_passno,
409 noauto,
410 nofail,
411 automount,
412 post,
413 fstab_path);
5e398e54 414 }
6b1dc2bd 415
6b1dc2bd
LP
416 if (k < 0)
417 r = k;
418 }
419
6b1dc2bd
LP
420 return r;
421}
422
6db615c1 423static int add_root_mount(void) {
75a59316
ZJS
424 _cleanup_free_ char *what = NULL;
425 const char *opts;
5e398e54 426
6db615c1 427 if (isempty(arg_root_what)) {
4e5ed9b6 428 log_debug("Could not find a root= entry on the kernel commandline.");
135b5212
HH
429 return 0;
430 }
5e398e54 431
6db615c1
LP
432 what = fstab_node_to_udev_node(arg_root_what);
433 if (!path_is_absolute(what)) {
434 log_debug("Skipping entry what=%s where=/sysroot type=%s", what, strna(arg_root_fstype));
135b5212
HH
435 return 0;
436 }
5e398e54 437
6db615c1 438 if (!arg_root_options)
75a59316
ZJS
439 opts = arg_root_rw > 0 ? "rw" : "ro";
440 else if (arg_root_rw >= 0 ||
441 (!mount_test_option(arg_root_options, "ro") &&
442 !mount_test_option(arg_root_options, "rw")))
8085f163 443 opts = strappenda(arg_root_options, ",", arg_root_rw > 0 ? "rw" : "ro");
75a59316
ZJS
444 else
445 opts = arg_root_options;
5e398e54 446
6db615c1
LP
447 log_debug("Found entry what=%s where=/sysroot type=%s", what, strna(arg_root_fstype));
448 return add_mount(what,
449 "/sysroot",
450 arg_root_fstype,
75a59316 451 opts,
6db615c1 452 1,
5ecdcf41
LP
453 false,
454 false,
6db615c1
LP
455 false,
456 SPECIAL_INITRD_ROOT_FS_TARGET,
457 "/proc/cmdline");
5e398e54
TG
458}
459
9f103625
TH
460static int add_usr_mount(void) {
461 _cleanup_free_ char *what = NULL;
462 const char *opts;
463
464 if (!arg_usr_what && !arg_usr_fstype && !arg_usr_options)
465 return 0;
466
467 if (arg_root_what && !arg_usr_what) {
468 arg_usr_what = strdup(arg_root_what);
469
470 if (!arg_usr_what)
471 return log_oom();
472 }
473
474 if (arg_root_fstype && !arg_usr_fstype) {
475 arg_usr_fstype = strdup(arg_root_fstype);
476
477 if (!arg_usr_fstype)
478 return log_oom();
479 }
480
481 if (arg_root_options && !arg_usr_options) {
482 arg_usr_options = strdup(arg_root_options);
483
484 if (!arg_usr_options)
485 return log_oom();
486 }
487
488 if (!arg_usr_what || !arg_usr_options)
489 return 0;
490
491 what = fstab_node_to_udev_node(arg_usr_what);
492 if (!path_is_absolute(what)) {
493 log_debug("Skipping entry what=%s where=/sysroot/usr type=%s", what, strna(arg_usr_fstype));
494 return -1;
495 }
496
497 opts = arg_usr_options;
498
499 log_debug("Found entry what=%s where=/sysroot/usr type=%s", what, strna(arg_usr_fstype));
500 return add_mount(what,
501 "/sysroot/usr",
502 arg_usr_fstype,
503 opts,
504 1,
505 false,
506 false,
507 false,
508 SPECIAL_INITRD_ROOT_FS_TARGET,
509 "/proc/cmdline");
510}
511
059cb385 512static int parse_proc_cmdline_item(const char *key, const char *value) {
74df0fca 513 int r;
94734142 514
9f103625
TH
515 /* root=, usr=, usrfstype= and roofstype= may occur more than once, the last
516 * instance should take precedence. In the case of multiple rootflags=
517 * or usrflags= the arguments should be concatenated */
6db615c1 518
059cb385 519 if (STR_IN_SET(key, "fstab", "rd.fstab") && value) {
e48fdd84 520
059cb385 521 r = parse_boolean(value);
141a79f4 522 if (r < 0)
059cb385 523 log_warning("Failed to parse fstab switch %s. Ignoring.", value);
141a79f4 524 else
e48fdd84 525 arg_fstab_enabled = r;
94734142 526
6db615c1
LP
527 } else if (streq(key, "root") && value) {
528
f88dc3ed 529 if (free_and_strdup(&arg_root_what, value) < 0)
6db615c1
LP
530 return log_oom();
531
532 } else if (streq(key, "rootfstype") && value) {
533
f88dc3ed 534 if (free_and_strdup(&arg_root_fstype, value) < 0)
6db615c1
LP
535 return log_oom();
536
537 } else if (streq(key, "rootflags") && value) {
538 char *o;
539
540 o = arg_root_options ?
541 strjoin(arg_root_options, ",", value, NULL) :
542 strdup(value);
543 if (!o)
544 return log_oom();
545
546 free(arg_root_options);
547 arg_root_options = o;
548
9f103625
TH
549 } else if (streq(key, "mount.usr") && value) {
550
551 if (free_and_strdup(&arg_usr_what, value) < 0)
552 return log_oom();
553
554 } else if (streq(key, "mount.usrfstype") && value) {
555
556 if (free_and_strdup(&arg_usr_fstype, value) < 0)
557 return log_oom();
558
559 } else if (streq(key, "mount.usrflags") && value) {
560 char *o;
561
562 o = arg_usr_options ?
563 strjoin(arg_usr_options, ",", value, NULL) :
564 strdup(value);
565 if (!o)
566 return log_oom();
567
568 free(arg_usr_options);
569 arg_usr_options = o;
570
6db615c1
LP
571 } else if (streq(key, "rw") && !value)
572 arg_root_rw = true;
573 else if (streq(key, "ro") && !value)
574 arg_root_rw = false;
94734142 575
d0aa9ce5 576 return 0;
94734142
LP
577}
578
6b1dc2bd 579int main(int argc, char *argv[]) {
e48fdd84 580 int r = 0;
6b1dc2bd 581
07719a21
LP
582 if (argc > 1 && argc != 4) {
583 log_error("This program takes three or no arguments.");
6b1dc2bd
LP
584 return EXIT_FAILURE;
585 }
586
587 if (argc > 1)
588 arg_dest = argv[1];
589
a6903061 590 log_set_target(LOG_TARGET_SAFE);
6b1dc2bd
LP
591 log_parse_environment();
592 log_open();
593
6b1dc2bd
LP
594 umask(0022);
595
059cb385 596 if (parse_proc_cmdline(parse_proc_cmdline_item) < 0)
94734142
LP
597 return EXIT_FAILURE;
598
9f103625
TH
599 /* Always honour root= and usr= in the kernel command line if we are in an initrd */
600 if (in_initrd()) {
6db615c1 601 r = add_root_mount();
9f103625
TH
602 if (r == 0)
603 r = add_usr_mount();
604 }
5e398e54 605
e48fdd84
LP
606 /* Honour /etc/fstab only when that's enabled */
607 if (arg_fstab_enabled) {
608 int k;
ac4785b0 609
75a59316
ZJS
610 log_debug("Parsing /etc/fstab");
611
e48fdd84
LP
612 /* Parse the local /etc/fstab, possibly from the initrd */
613 k = parse_fstab(false);
614 if (k < 0)
615 r = k;
ac4785b0 616
e48fdd84
LP
617 /* If running in the initrd also parse the /etc/fstab from the host */
618 if (in_initrd()) {
75a59316
ZJS
619 log_debug("Parsing /sysroot/etc/fstab");
620
e48fdd84
LP
621 k = parse_fstab(true);
622 if (k < 0)
623 r = k;
624 }
625 }
6b1dc2bd 626
126cc760
ZJS
627 free(arg_root_what);
628
e48fdd84 629 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
6b1dc2bd 630}