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