]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/fstab-generator/fstab-generator.c
fsck: modernization
[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"
6b1dc2bd
LP
36
37static const char *arg_dest = "/tmp";
94734142 38static bool arg_enabled = true;
6b1dc2bd 39
6b1dc2bd
LP
40static int mount_find_pri(struct mntent *me, int *ret) {
41 char *end, *pri;
42 unsigned long r;
43
44 assert(me);
45 assert(ret);
46
47 pri = hasmntopt(me, "pri");
48 if (!pri)
49 return 0;
50
51 pri += 4;
52
53 errno = 0;
54 r = strtoul(pri, &end, 10);
8333c77e 55 if (errno > 0)
6b1dc2bd
LP
56 return -errno;
57
58 if (end == pri || (*end != ',' && *end != 0))
59 return -EINVAL;
60
61 *ret = (int) r;
62 return 1;
63}
64
65static int add_swap(const char *what, struct mntent *me) {
1dc2ced4 66 _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL;
7fd1b19b 67 _cleanup_fclose_ FILE *f = NULL;
4e82fe52 68 bool noauto;
6b1dc2bd
LP
69 int r, pri = -1;
70
71 assert(what);
72 assert(me);
73
74 r = mount_find_pri(me, &pri);
75 if (r < 0) {
76 log_error("Failed to parse priority");
77 return pri;
78 }
79
4e82fe52
TG
80 noauto = !!hasmntopt(me, "noauto");
81
6b1dc2bd 82 name = unit_name_from_path(what, ".swap");
d0aa9ce5
ZJS
83 if (!name)
84 return log_oom();
6b1dc2bd 85
b7def684 86 unit = strjoin(arg_dest, "/", name, NULL);
d0aa9ce5
ZJS
87 if (!unit)
88 return log_oom();
6b1dc2bd
LP
89
90 f = fopen(unit, "wxe");
91 if (!f) {
67ab5f76
TG
92 if (errno == EEXIST)
93 log_error("Failed to create swap unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
94 else
95 log_error("Failed to create unit file %s: %m", unit);
d0aa9ce5 96 return -errno;
6b1dc2bd
LP
97 }
98
6b1dc2bd 99 fprintf(f,
64347fc2
TG
100 "# Automatically generated by systemd-fstab-generator\n\n"
101 "[Unit]\n"
4e82fe52 102 "SourcePath=/etc/fstab\n\n"
6b1dc2bd
LP
103 "[Swap]\n"
104 "What=%s\n",
105 what);
106
107 if (pri >= 0)
108 fprintf(f,
109 "Priority=%i\n",
110 pri);
111
112 fflush(f);
113 if (ferror(f)) {
40b8acd0 114 log_error("Failed to write unit file %s: %m", unit);
d0aa9ce5 115 return -errno;
6b1dc2bd
LP
116 }
117
4e82fe52
TG
118 if (!noauto) {
119 lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL);
120 if (!lnk)
121 return log_oom();
122
123 mkdir_parents_label(lnk, 0755);
124 if (symlink(unit, lnk) < 0) {
125 log_error("Failed to create symlink %s: %m", lnk);
126 return -errno;
127 }
128 }
129
d0aa9ce5 130 return 0;
6b1dc2bd
LP
131}
132
6b1dc2bd
LP
133static bool mount_is_network(struct mntent *me) {
134 assert(me);
135
136 return
137 hasmntopt(me, "_netdev") ||
138 fstype_is_network(me->mnt_type);
139}
140
3d22d1ab
TG
141static bool mount_in_initrd(struct mntent *me) {
142 assert(me);
143
144 return
145 hasmntopt(me, "x-initrd.mount") ||
146 streq(me->mnt_dir, "/usr");
147}
148
e8d2f6cd
LP
149static int add_mount(
150 const char *what,
151 const char *where,
152 const char *type,
153 const char *opts,
154 int passno,
155 bool noauto,
156 bool nofail,
157 bool automount,
e8d2f6cd 158 const char *post,
e8d2f6cd 159 const char *source) {
7fd1b19b 160 _cleanup_free_ char
1dc2ced4 161 *name = NULL, *unit = NULL, *lnk = NULL,
d0aa9ce5 162 *automount_name = NULL, *automount_unit = NULL;
7fd1b19b 163 _cleanup_fclose_ FILE *f = NULL;
6b1dc2bd
LP
164
165 assert(what);
166 assert(where);
5e398e54
TG
167 assert(type);
168 assert(opts);
169 assert(source);
6b1dc2bd 170
5e398e54 171 if (streq(type, "autofs"))
6b1dc2bd
LP
172 return 0;
173
174 if (!is_path(where)) {
175 log_warning("Mount point %s is not a valid path, ignoring.", where);
176 return 0;
177 }
178
179 if (mount_point_is_api(where) ||
180 mount_point_ignore(where))
181 return 0;
182
6b1dc2bd 183 name = unit_name_from_path(where, ".mount");
d0aa9ce5
ZJS
184 if (!name)
185 return log_oom();
6b1dc2bd 186
b7def684 187 unit = strjoin(arg_dest, "/", name, NULL);
d0aa9ce5
ZJS
188 if (!unit)
189 return log_oom();
6b1dc2bd
LP
190
191 f = fopen(unit, "wxe");
192 if (!f) {
67ab5f76
TG
193 if (errno == EEXIST)
194 log_error("Failed to create mount unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
195 else
196 log_error("Failed to create unit file %s: %m", unit);
d0aa9ce5 197 return -errno;
6b1dc2bd
LP
198 }
199
5e398e54
TG
200 fprintf(f,
201 "# Automatically generated by systemd-fstab-generator\n\n"
6b1dc2bd 202 "[Unit]\n"
80c3b720 203 "SourcePath=%s\n",
5e398e54 204 source);
6b1dc2bd 205
700e07ff 206 if (post && !noauto && !nofail && !automount)
6b1dc2bd
LP
207 fprintf(f,
208 "Before=%s\n",
209 post);
210
64e70e4b 211 if (passno > 0) {
a7623afb
TG
212 if (streq(where, "/")) {
213 lnk = strjoin(arg_dest, "/", SPECIAL_LOCAL_FS_TARGET, ".wants/", "systemd-fsck-root.service", NULL);
214 if (!lnk)
215 return log_oom();
64e70e4b 216
a7623afb
TG
217 mkdir_parents_label(lnk, 0755);
218 if (symlink("systemd-fsck-root.service", lnk) < 0) {
219 log_error("Failed to create symlink %s: %m", lnk);
220 return -errno;
221 }
222 } else {
223 _cleanup_free_ char *fsck = NULL;
64e70e4b 224
a7623afb
TG
225 fsck = unit_name_from_path_instance("systemd-fsck", what, ".service");
226 if (!fsck)
227 return log_oom();
228
229 fprintf(f,
230 "Requires=%s\n"
231 "After=%s\n",
232 fsck,
233 fsck);
234 }
64e70e4b
TB
235 }
236
237
6b1dc2bd
LP
238 fprintf(f,
239 "\n"
240 "[Mount]\n"
241 "What=%s\n"
242 "Where=%s\n"
64e70e4b 243 "Type=%s\n",
6b1dc2bd
LP
244 what,
245 where,
64e70e4b 246 type);
6b1dc2bd 247
5e398e54
TG
248 if (!isempty(opts) &&
249 !streq(opts, "defaults"))
6b1dc2bd
LP
250 fprintf(f,
251 "Options=%s\n",
5e398e54
TG
252 opts);
253
6b1dc2bd
LP
254 fflush(f);
255 if (ferror(f)) {
40b8acd0 256 log_error("Failed to write unit file %s: %m", unit);
d0aa9ce5 257 return -errno;
6b1dc2bd
LP
258 }
259
260 if (!noauto) {
700e07ff 261 if (post) {
a7623afb 262 free(lnk);
700e07ff
HH
263 lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL);
264 if (!lnk)
265 return log_oom();
6b1dc2bd 266
700e07ff
HH
267 mkdir_parents_label(lnk, 0755);
268 if (symlink(unit, lnk) < 0) {
269 log_error("Failed to create symlink %s: %m", lnk);
270 return -errno;
271 }
6b1dc2bd 272 }
6b1dc2bd
LP
273 }
274
275 if (automount && !path_equal(where, "/")) {
276 automount_name = unit_name_from_path(where, ".automount");
01264ad1 277 if (!automount_name)
d0aa9ce5 278 return log_oom();
6b1dc2bd 279
b7def684 280 automount_unit = strjoin(arg_dest, "/", automount_name, NULL);
d0aa9ce5
ZJS
281 if (!automount_unit)
282 return log_oom();
6b1dc2bd
LP
283
284 fclose(f);
285 f = fopen(automount_unit, "wxe");
286 if (!f) {
40b8acd0 287 log_error("Failed to create unit file %s: %m", automount_unit);
d0aa9ce5 288 return -errno;
6b1dc2bd
LP
289 }
290
291 fprintf(f,
292 "# Automatically generated by systemd-fstab-generator\n\n"
293 "[Unit]\n"
80c3b720 294 "SourcePath=%s\n",
700e07ff
HH
295 source);
296
297 if (post)
298 fprintf(f,
299 "Before= %s\n",
300 post);
301
302 fprintf(f,
6b1dc2bd
LP
303 "[Automount]\n"
304 "Where=%s\n",
6b1dc2bd
LP
305 where);
306
307 fflush(f);
308 if (ferror(f)) {
40b8acd0 309 log_error("Failed to write unit file %s: %m", automount_unit);
d0aa9ce5 310 return -errno;
6b1dc2bd
LP
311 }
312
313 free(lnk);
b7def684 314 lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL);
d0aa9ce5
ZJS
315 if (!lnk)
316 return log_oom();
6b1dc2bd 317
d2e54fae 318 mkdir_parents_label(lnk, 0755);
6b1dc2bd 319 if (symlink(automount_unit, lnk) < 0) {
40b8acd0 320 log_error("Failed to create symlink %s: %m", lnk);
d0aa9ce5 321 return -errno;
6b1dc2bd
LP
322 }
323 }
324
d0aa9ce5 325 return 0;
6b1dc2bd
LP
326}
327
3d22d1ab 328static int parse_fstab(const char *prefix, bool initrd) {
5862d652
ZJS
329 char *fstab_path;
330 _cleanup_endmntent_ FILE *f;
6b1dc2bd
LP
331 int r = 0;
332 struct mntent *me;
333
5862d652 334 fstab_path = strappenda(strempty(prefix), "/etc/fstab");
3d22d1ab 335 f = setmntent(fstab_path, "r");
6b1dc2bd
LP
336 if (!f) {
337 if (errno == ENOENT)
338 return 0;
339
1b036593 340 log_error("Failed to open %s/etc/fstab: %m", strempty(prefix));
6b1dc2bd
LP
341 return -errno;
342 }
343
344 while ((me = getmntent(f))) {
7fd1b19b 345 _cleanup_free_ char *where = NULL, *what = NULL;
6b1dc2bd
LP
346 int k;
347
3d22d1ab
TG
348 if (initrd && !mount_in_initrd(me))
349 continue;
350
6b1dc2bd 351 what = fstab_node_to_udev_node(me->mnt_fsname);
1b036593 352 where = strjoin(strempty(prefix), me->mnt_dir, NULL);
5862d652
ZJS
353 if (!what || !where)
354 return log_oom();
6b1dc2bd 355
ec6ceb18 356 if (is_path(where))
6b1dc2bd
LP
357 path_kill_slashes(where);
358
359 log_debug("Found entry what=%s where=%s type=%s", what, where, me->mnt_type);
360
361 if (streq(me->mnt_type, "swap"))
362 k = add_swap(what, me);
5e398e54 363 else {
5073f89f 364 bool noauto, nofail, automount;
80c3b720 365 const char *post;
5e398e54
TG
366
367 noauto = !!hasmntopt(me, "noauto");
368 nofail = !!hasmntopt(me, "nofail");
369 automount =
370 hasmntopt(me, "comment=systemd.automount") ||
371 hasmntopt(me, "x-systemd.automount");
700e07ff
HH
372
373 if (initrd) {
374 post = SPECIAL_INITRD_FS_TARGET;
a87586a1 375 } else if (mount_in_initrd(me)) {
9e5f0f92 376 post = SPECIAL_INITRD_ROOT_FS_TARGET;
700e07ff 377 } else if (mount_is_network(me)) {
0c17fbce 378 post = SPECIAL_REMOTE_FS_TARGET;
700e07ff 379 } else {
0c17fbce 380 post = SPECIAL_LOCAL_FS_TARGET;
700e07ff 381 }
5e398e54
TG
382
383 k = add_mount(what, where, me->mnt_type, me->mnt_opts,
e8d2f6cd 384 me->mnt_passno, noauto, nofail, automount,
5073f89f 385 post, fstab_path);
5e398e54 386 }
6b1dc2bd 387
6b1dc2bd
LP
388 if (k < 0)
389 r = k;
390 }
391
6b1dc2bd
LP
392 return r;
393}
394
5e398e54 395static int parse_new_root_from_proc_cmdline(void) {
ac4785b0 396 _cleanup_free_ char *what = NULL, *type = NULL, *opts = NULL, *line = NULL;
74df0fca 397 bool noauto, nofail;
80c39ad2 398 char *w, *state;
5e398e54 399 size_t l;
74df0fca 400 int r;
5e398e54 401
74df0fca
LP
402 r = proc_cmdline(&line);
403 if (r < 0)
404 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
405 if (r <= 0)
5e398e54 406 return 0;
5e398e54 407
945d1442 408 opts = strdup("ro");
5e398e54 409 type = strdup("auto");
945d1442 410 if (!opts || !type)
5e398e54
TG
411 return log_oom();
412
b929bf04
TA
413 /* root= and roofstype= may occur more than once, the last instance should take precedence.
414 * In the case of multiple rootflags= the arguments should be concatenated */
5e398e54 415 FOREACH_WORD_QUOTED(w, l, line, state) {
80c39ad2 416 _cleanup_free_ char *word;
5e398e54
TG
417
418 word = strndup(w, l);
419 if (!word)
420 return log_oom();
421
422 else if (startswith(word, "root=")) {
423 free(what);
424 what = fstab_node_to_udev_node(word+5);
425 if (!what)
426 return log_oom();
427
428 } else if (startswith(word, "rootfstype=")) {
429 free(type);
430 type = strdup(word + 11);
431 if (!type)
432 return log_oom();
433
434 } else if (startswith(word, "rootflags=")) {
80c39ad2
LP
435 char *o;
436
437 o = strjoin(opts, ",", word + 10, NULL);
438 if (!o)
5e398e54
TG
439 return log_oom();
440
80c39ad2
LP
441 free(opts);
442 opts = o;
443
5e398e54 444 } else if (streq(word, "ro") || streq(word, "rw")) {
80c39ad2
LP
445 char *o;
446
447 o = strjoin(opts, ",", word, NULL);
448 if (!o)
5e398e54
TG
449 return log_oom();
450
80c39ad2
LP
451 free(opts);
452 opts = o;
533740e1 453 }
5e398e54
TG
454 }
455
21d1a678
WC
456 noauto = !!strstr(opts, "noauto");
457 nofail = !!strstr(opts, "nofail");
458
135b5212 459 if (!what) {
4e5ed9b6 460 log_debug("Could not find a root= entry on the kernel commandline.");
135b5212
HH
461 return 0;
462 }
5e398e54 463
135b5212
HH
464 if (what[0] != '/') {
465 log_debug("Skipping entry what=%s where=/sysroot type=%s", what, type);
466 return 0;
467 }
5e398e54 468
135b5212 469 log_debug("Found entry what=%s where=/sysroot type=%s", what, type);
7f5806d7 470 r = add_mount(what, "/sysroot", type, opts, 1, noauto, nofail, false,
5073f89f 471 SPECIAL_INITRD_ROOT_FS_TARGET, "/proc/cmdline");
5e398e54 472
135b5212 473 return (r < 0) ? r : 0;
5e398e54
TG
474}
475
94734142 476static int parse_proc_cmdline(void) {
7fd1b19b 477 _cleanup_free_ char *line = NULL;
d0aa9ce5 478 char *w, *state;
94734142 479 size_t l;
74df0fca 480 int r;
94734142 481
74df0fca
LP
482 r = proc_cmdline(&line);
483 if (r < 0)
94734142 484 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
74df0fca 485 if (r <= 0)
94734142 486 return 0;
94734142
LP
487
488 FOREACH_WORD_QUOTED(w, l, line, state) {
7fd1b19b 489 _cleanup_free_ char *word = NULL;
94734142
LP
490
491 word = strndup(w, l);
d0aa9ce5
ZJS
492 if (!word)
493 return log_oom();
94734142
LP
494
495 if (startswith(word, "fstab=")) {
496 r = parse_boolean(word + 6);
497 if (r < 0)
498 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
499 else
500 arg_enabled = r;
501
502 } else if (startswith(word, "rd.fstab=")) {
503
504 if (in_initrd()) {
ef53700c 505 r = parse_boolean(word + 9);
94734142 506 if (r < 0)
ef53700c 507 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 9);
94734142
LP
508 else
509 arg_enabled = r;
510 }
511
512 } else if (startswith(word, "fstab.") ||
513 (in_initrd() && startswith(word, "rd.fstab."))) {
514
515 log_warning("Unknown kernel switch %s. Ignoring.", word);
516 }
94734142
LP
517 }
518
d0aa9ce5 519 return 0;
94734142
LP
520}
521
6b1dc2bd 522int main(int argc, char *argv[]) {
ac4785b0 523 int r = 0, k, l = 0;
6b1dc2bd 524
07719a21
LP
525 if (argc > 1 && argc != 4) {
526 log_error("This program takes three or no arguments.");
6b1dc2bd
LP
527 return EXIT_FAILURE;
528 }
529
530 if (argc > 1)
531 arg_dest = argv[1];
532
a6903061 533 log_set_target(LOG_TARGET_SAFE);
6b1dc2bd
LP
534 log_parse_environment();
535 log_open();
536
6b1dc2bd
LP
537 umask(0022);
538
94734142
LP
539 if (parse_proc_cmdline() < 0)
540 return EXIT_FAILURE;
541
ac4785b0
HH
542 if (in_initrd())
543 r = parse_new_root_from_proc_cmdline();
5e398e54 544
ac4785b0
HH
545 if (!arg_enabled)
546 return (r < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
547
df3e57f5 548 k = parse_fstab(NULL, false);
ac4785b0
HH
549
550 if (in_initrd())
551 l = parse_fstab("/sysroot", true);
6b1dc2bd 552
3d22d1ab 553 return (r < 0) || (k < 0) || (l < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
6b1dc2bd 554}