]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/fstab-generator/fstab-generator.c
treewide: use log_*_errno whenever %m is in the format string
[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
5607d856
ZJS
77static int add_swap(
78 const char *what,
79 struct mntent *me,
80 bool noauto,
81 bool nofail) {
82
1dc2ced4 83 _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL;
7fd1b19b 84 _cleanup_fclose_ FILE *f = NULL;
6b1dc2bd
LP
85 int r, pri = -1;
86
87 assert(what);
88 assert(me);
89
689aede8
LP
90 if (detect_container(NULL) > 0) {
91 log_info("Running in a container, ignoring fstab swap entry for %s.", what);
92 return 0;
93 }
94
6b1dc2bd
LP
95 r = mount_find_pri(me, &pri);
96 if (r < 0) {
97 log_error("Failed to parse priority");
4f52d3fe
ZJS
98 return r;
99 }
100
6b1dc2bd 101 name = unit_name_from_path(what, ".swap");
d0aa9ce5
ZJS
102 if (!name)
103 return log_oom();
6b1dc2bd 104
b7def684 105 unit = strjoin(arg_dest, "/", name, NULL);
d0aa9ce5
ZJS
106 if (!unit)
107 return log_oom();
6b1dc2bd
LP
108
109 f = fopen(unit, "wxe");
110 if (!f) {
67ab5f76
TG
111 if (errno == EEXIST)
112 log_error("Failed to create swap unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
113 else
56f64d95 114 log_error_errno(errno, "Failed to create unit file %s: %m", unit);
d0aa9ce5 115 return -errno;
6b1dc2bd
LP
116 }
117
6b1dc2bd 118 fprintf(f,
64347fc2
TG
119 "# Automatically generated by systemd-fstab-generator\n\n"
120 "[Unit]\n"
c3834f9b
LP
121 "SourcePath=/etc/fstab\n"
122 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n\n"
6b1dc2bd
LP
123 "[Swap]\n"
124 "What=%s\n",
125 what);
126
47cb901e
LP
127 /* Note that we currently pass the priority field twice, once
128 * in Priority=, and once in Options= */
6b1dc2bd 129 if (pri >= 0)
4f52d3fe
ZJS
130 fprintf(f, "Priority=%i\n", pri);
131
47cb901e
LP
132 if (!isempty(me->mnt_opts) && !streq(me->mnt_opts, "defaults"))
133 fprintf(f, "Options=%s\n", me->mnt_opts);
6b1dc2bd 134
47cb901e 135 r = fflush_and_check(f);
23bbb0de
MS
136 if (r < 0)
137 return log_error_errno(r, "Failed to write unit file %s: %m", unit);
6b1dc2bd 138
b3208b66
ZJS
139 /* use what as where, to have a nicer error message */
140 r = generator_write_timeouts(arg_dest, what, what, me->mnt_opts, NULL);
141 if (r < 0)
142 return r;
143
4e82fe52 144 if (!noauto) {
5607d856
ZJS
145 lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET,
146 nofail ? ".wants/" : ".requires/", name, NULL);
4e82fe52
TG
147 if (!lnk)
148 return log_oom();
149
150 mkdir_parents_label(lnk, 0755);
151 if (symlink(unit, lnk) < 0) {
56f64d95 152 log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
4e82fe52
TG
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
56f64d95 232 log_error_errno(errno, "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)) {
56f64d95 272 log_error_errno(errno, "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) {
56f64d95 283 log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
5ecdcf41 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) {
56f64d95 300 log_error_errno(errno, "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)) {
56f64d95 323 log_error_errno(errno, "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) {
56f64d95 334 log_error_errno(errno, "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
56f64d95 354 log_error_errno(errno, "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;
5607d856 360 bool noauto, nofail;
6b1dc2bd
LP
361 int k;
362
3d22d1ab
TG
363 if (initrd && !mount_in_initrd(me))
364 continue;
365
6b1dc2bd 366 what = fstab_node_to_udev_node(me->mnt_fsname);
e48fdd84
LP
367 if (!what)
368 return log_oom();
369
689aede8
LP
370 if (detect_container(NULL) > 0 && is_device_path(what)) {
371 log_info("Running in a container, ignoring fstab device entry for %s.", what);
372 continue;
373 }
374
e48fdd84
LP
375 where = initrd ? strappend("/sysroot/", me->mnt_dir) : strdup(me->mnt_dir);
376 if (!where)
5862d652 377 return log_oom();
6b1dc2bd 378
ec6ceb18 379 if (is_path(where))
6b1dc2bd
LP
380 path_kill_slashes(where);
381
5607d856
ZJS
382 noauto = !!hasmntopt(me, "noauto");
383 nofail = !!hasmntopt(me, "nofail");
384 log_debug("Found entry what=%s where=%s type=%s nofail=%s noauto=%s",
385 what, where, me->mnt_type,
386 yes_no(noauto), yes_no(nofail));
6b1dc2bd
LP
387
388 if (streq(me->mnt_type, "swap"))
5607d856 389 k = add_swap(what, me, noauto, nofail);
5e398e54 390 else {
5607d856 391 bool automount;
80c3b720 392 const char *post;
5e398e54 393
5e398e54
TG
394 automount =
395 hasmntopt(me, "comment=systemd.automount") ||
396 hasmntopt(me, "x-systemd.automount");
700e07ff 397
e48fdd84 398 if (initrd)
700e07ff 399 post = SPECIAL_INITRD_FS_TARGET;
e48fdd84 400 else if (mount_in_initrd(me))
9e5f0f92 401 post = SPECIAL_INITRD_ROOT_FS_TARGET;
e48fdd84 402 else if (mount_is_network(me))
0c17fbce 403 post = SPECIAL_REMOTE_FS_TARGET;
e48fdd84 404 else
0c17fbce 405 post = SPECIAL_LOCAL_FS_TARGET;
5e398e54 406
6db615c1
LP
407 k = add_mount(what,
408 where,
409 me->mnt_type,
410 me->mnt_opts,
411 me->mnt_passno,
412 noauto,
413 nofail,
414 automount,
415 post,
416 fstab_path);
5e398e54 417 }
6b1dc2bd 418
6b1dc2bd
LP
419 if (k < 0)
420 r = k;
421 }
422
6b1dc2bd
LP
423 return r;
424}
425
6db615c1 426static int add_root_mount(void) {
75a59316
ZJS
427 _cleanup_free_ char *what = NULL;
428 const char *opts;
5e398e54 429
6db615c1 430 if (isempty(arg_root_what)) {
3f85ef0f 431 log_debug("Could not find a root= entry on the kernel command line.");
135b5212
HH
432 return 0;
433 }
5e398e54 434
6db615c1
LP
435 what = fstab_node_to_udev_node(arg_root_what);
436 if (!path_is_absolute(what)) {
437 log_debug("Skipping entry what=%s where=/sysroot type=%s", what, strna(arg_root_fstype));
135b5212
HH
438 return 0;
439 }
5e398e54 440
6db615c1 441 if (!arg_root_options)
75a59316
ZJS
442 opts = arg_root_rw > 0 ? "rw" : "ro";
443 else if (arg_root_rw >= 0 ||
444 (!mount_test_option(arg_root_options, "ro") &&
445 !mount_test_option(arg_root_options, "rw")))
8085f163 446 opts = strappenda(arg_root_options, ",", arg_root_rw > 0 ? "rw" : "ro");
75a59316
ZJS
447 else
448 opts = arg_root_options;
5e398e54 449
6db615c1
LP
450 log_debug("Found entry what=%s where=/sysroot type=%s", what, strna(arg_root_fstype));
451 return add_mount(what,
452 "/sysroot",
453 arg_root_fstype,
75a59316 454 opts,
6db615c1 455 1,
5ecdcf41
LP
456 false,
457 false,
6db615c1
LP
458 false,
459 SPECIAL_INITRD_ROOT_FS_TARGET,
460 "/proc/cmdline");
5e398e54
TG
461}
462
9f103625
TH
463static int add_usr_mount(void) {
464 _cleanup_free_ char *what = NULL;
465 const char *opts;
466
467 if (!arg_usr_what && !arg_usr_fstype && !arg_usr_options)
468 return 0;
469
470 if (arg_root_what && !arg_usr_what) {
471 arg_usr_what = strdup(arg_root_what);
472
473 if (!arg_usr_what)
474 return log_oom();
475 }
476
477 if (arg_root_fstype && !arg_usr_fstype) {
478 arg_usr_fstype = strdup(arg_root_fstype);
479
480 if (!arg_usr_fstype)
481 return log_oom();
482 }
483
484 if (arg_root_options && !arg_usr_options) {
485 arg_usr_options = strdup(arg_root_options);
486
487 if (!arg_usr_options)
488 return log_oom();
489 }
490
491 if (!arg_usr_what || !arg_usr_options)
492 return 0;
493
494 what = fstab_node_to_udev_node(arg_usr_what);
495 if (!path_is_absolute(what)) {
496 log_debug("Skipping entry what=%s where=/sysroot/usr type=%s", what, strna(arg_usr_fstype));
497 return -1;
498 }
499
500 opts = arg_usr_options;
501
502 log_debug("Found entry what=%s where=/sysroot/usr type=%s", what, strna(arg_usr_fstype));
503 return add_mount(what,
504 "/sysroot/usr",
505 arg_usr_fstype,
506 opts,
507 1,
508 false,
509 false,
510 false,
511 SPECIAL_INITRD_ROOT_FS_TARGET,
512 "/proc/cmdline");
513}
514
059cb385 515static int parse_proc_cmdline_item(const char *key, const char *value) {
74df0fca 516 int r;
94734142 517
9f103625
TH
518 /* root=, usr=, usrfstype= and roofstype= may occur more than once, the last
519 * instance should take precedence. In the case of multiple rootflags=
520 * or usrflags= the arguments should be concatenated */
6db615c1 521
059cb385 522 if (STR_IN_SET(key, "fstab", "rd.fstab") && value) {
e48fdd84 523
059cb385 524 r = parse_boolean(value);
141a79f4 525 if (r < 0)
059cb385 526 log_warning("Failed to parse fstab switch %s. Ignoring.", value);
141a79f4 527 else
e48fdd84 528 arg_fstab_enabled = r;
94734142 529
6db615c1
LP
530 } else if (streq(key, "root") && value) {
531
f88dc3ed 532 if (free_and_strdup(&arg_root_what, value) < 0)
6db615c1
LP
533 return log_oom();
534
535 } else if (streq(key, "rootfstype") && value) {
536
f88dc3ed 537 if (free_and_strdup(&arg_root_fstype, value) < 0)
6db615c1
LP
538 return log_oom();
539
540 } else if (streq(key, "rootflags") && value) {
541 char *o;
542
543 o = arg_root_options ?
544 strjoin(arg_root_options, ",", value, NULL) :
545 strdup(value);
546 if (!o)
547 return log_oom();
548
549 free(arg_root_options);
550 arg_root_options = o;
551
9f103625
TH
552 } else if (streq(key, "mount.usr") && value) {
553
554 if (free_and_strdup(&arg_usr_what, value) < 0)
555 return log_oom();
556
557 } else if (streq(key, "mount.usrfstype") && value) {
558
559 if (free_and_strdup(&arg_usr_fstype, value) < 0)
560 return log_oom();
561
562 } else if (streq(key, "mount.usrflags") && value) {
563 char *o;
564
565 o = arg_usr_options ?
566 strjoin(arg_usr_options, ",", value, NULL) :
567 strdup(value);
568 if (!o)
569 return log_oom();
570
571 free(arg_usr_options);
572 arg_usr_options = o;
573
6db615c1
LP
574 } else if (streq(key, "rw") && !value)
575 arg_root_rw = true;
576 else if (streq(key, "ro") && !value)
577 arg_root_rw = false;
94734142 578
d0aa9ce5 579 return 0;
94734142
LP
580}
581
6b1dc2bd 582int main(int argc, char *argv[]) {
e48fdd84 583 int r = 0;
6b1dc2bd 584
07719a21
LP
585 if (argc > 1 && argc != 4) {
586 log_error("This program takes three or no arguments.");
6b1dc2bd
LP
587 return EXIT_FAILURE;
588 }
589
590 if (argc > 1)
591 arg_dest = argv[1];
592
a6903061 593 log_set_target(LOG_TARGET_SAFE);
6b1dc2bd
LP
594 log_parse_environment();
595 log_open();
596
6b1dc2bd
LP
597 umask(0022);
598
b5884878
LP
599 r = parse_proc_cmdline(parse_proc_cmdline_item);
600 if (r < 0)
da927ba9 601 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
94734142 602
9f103625
TH
603 /* Always honour root= and usr= in the kernel command line if we are in an initrd */
604 if (in_initrd()) {
6db615c1 605 r = add_root_mount();
9f103625
TH
606 if (r == 0)
607 r = add_usr_mount();
608 }
5e398e54 609
e48fdd84
LP
610 /* Honour /etc/fstab only when that's enabled */
611 if (arg_fstab_enabled) {
612 int k;
ac4785b0 613
75a59316
ZJS
614 log_debug("Parsing /etc/fstab");
615
e48fdd84
LP
616 /* Parse the local /etc/fstab, possibly from the initrd */
617 k = parse_fstab(false);
618 if (k < 0)
619 r = k;
ac4785b0 620
e48fdd84
LP
621 /* If running in the initrd also parse the /etc/fstab from the host */
622 if (in_initrd()) {
75a59316
ZJS
623 log_debug("Parsing /sysroot/etc/fstab");
624
e48fdd84
LP
625 k = parse_fstab(true);
626 if (k < 0)
627 r = k;
628 }
629 }
6b1dc2bd 630
126cc760
ZJS
631 free(arg_root_what);
632
e48fdd84 633 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
6b1dc2bd 634}