]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/fstab-generator/fstab-generator.c
fstab-generator: place initrd /sysroot mounts in initrd-fs.target
[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 "mount-setup.h"
33 #include "special.h"
34 #include "mkdir.h"
35 #include "virt.h"
36 #include "fileio.h"
37
38 static const char *arg_dest = "/tmp";
39 static bool arg_enabled = true;
40
41 static int device_name(const char *path, char **unit) {
42 char *p;
43
44 assert(path);
45
46 if (!is_device_path(path))
47 return 0;
48
49 p = unit_name_from_path(path, ".device");
50 if (!p)
51 return log_oom();
52
53 *unit = p;
54 return 1;
55 }
56
57 static int mount_find_pri(struct mntent *me, int *ret) {
58 char *end, *pri;
59 unsigned long r;
60
61 assert(me);
62 assert(ret);
63
64 pri = hasmntopt(me, "pri");
65 if (!pri)
66 return 0;
67
68 pri += 4;
69
70 errno = 0;
71 r = strtoul(pri, &end, 10);
72 if (errno != 0)
73 return -errno;
74
75 if (end == pri || (*end != ',' && *end != 0))
76 return -EINVAL;
77
78 *ret = (int) r;
79 return 1;
80 }
81
82 static int add_swap(const char *what, struct mntent *me) {
83 char _cleanup_free_ *name = NULL, *unit = NULL, *lnk = NULL, *device = NULL;
84 FILE _cleanup_fclose_ *f = NULL;
85 bool noauto, nofail;
86 int r, pri = -1;
87
88 assert(what);
89 assert(me);
90
91 r = mount_find_pri(me, &pri);
92 if (r < 0) {
93 log_error("Failed to parse priority");
94 return pri;
95 }
96
97 noauto = !!hasmntopt(me, "noauto");
98 nofail = !!hasmntopt(me, "nofail");
99
100 name = unit_name_from_path(what, ".swap");
101 if (!name)
102 return log_oom();
103
104 unit = strjoin(arg_dest, "/", name, NULL);
105 if (!unit)
106 return log_oom();
107
108 f = fopen(unit, "wxe");
109 if (!f) {
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);
114 return -errno;
115 }
116
117 fputs("# Automatically generated by systemd-fstab-generator\n\n"
118 "[Unit]\n"
119 "SourcePath=/etc/fstab\n"
120 "DefaultDependencies=no\n"
121 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
122 "Before=" SPECIAL_UMOUNT_TARGET "\n", f);
123
124 if (!noauto && !nofail)
125 fputs("Before=" SPECIAL_SWAP_TARGET "\n", f);
126
127 fprintf(f,
128 "\n"
129 "[Swap]\n"
130 "What=%s\n",
131 what);
132
133 if (pri >= 0)
134 fprintf(f,
135 "Priority=%i\n",
136 pri);
137
138 fflush(f);
139 if (ferror(f)) {
140 log_error("Failed to write unit file %s: %m", unit);
141 return -errno;
142 }
143
144 if (!noauto) {
145 lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL);
146 if (!lnk)
147 return log_oom();
148
149 mkdir_parents_label(lnk, 0755);
150 if (symlink(unit, lnk) < 0) {
151 log_error("Failed to create symlink %s: %m", lnk);
152 return -errno;
153 }
154
155 r = device_name(what, &device);
156 if (r < 0)
157 return r;
158
159 if (r > 0) {
160 free(lnk);
161 lnk = strjoin(arg_dest, "/", device, ".wants/", name, NULL);
162 if (!lnk)
163 return log_oom();
164
165 mkdir_parents_label(lnk, 0755);
166 if (symlink(unit, lnk) < 0) {
167 log_error("Failed to create symlink %s: %m", lnk);
168 return -errno;
169 }
170 }
171 }
172
173 return 0;
174 }
175
176 static bool mount_is_bind(struct mntent *me) {
177 assert(me);
178
179 return
180 hasmntopt(me, "bind") ||
181 streq(me->mnt_type, "bind") ||
182 hasmntopt(me, "rbind") ||
183 streq(me->mnt_type, "rbind");
184 }
185
186 static bool mount_is_network(struct mntent *me) {
187 assert(me);
188
189 return
190 hasmntopt(me, "_netdev") ||
191 fstype_is_network(me->mnt_type);
192 }
193
194 static bool mount_in_initrd(struct mntent *me) {
195 assert(me);
196
197 return
198 hasmntopt(me, "x-initrd.mount") ||
199 streq(me->mnt_dir, "/usr");
200 }
201
202 static int add_mount(const char *what, const char *where, const char *type, const char *opts,
203 int passno, bool noauto, bool nofail, bool automount, bool isbind,
204 bool remote_fs_target, bool initrd_fs_target, const char *source) {
205 char _cleanup_free_
206 *name = NULL, *unit = NULL, *lnk = NULL, *device = NULL,
207 *automount_name = NULL, *automount_unit = NULL;
208 FILE _cleanup_fclose_ *f = NULL;
209 int r;
210 const char *post, *pre;
211
212 assert(what);
213 assert(where);
214 assert(type);
215 assert(opts);
216 assert(source);
217
218 if (streq(type, "autofs"))
219 return 0;
220
221 if (!is_path(where)) {
222 log_warning("Mount point %s is not a valid path, ignoring.", where);
223 return 0;
224 }
225
226 if (mount_point_is_api(where) ||
227 mount_point_ignore(where))
228 return 0;
229
230 if (remote_fs_target) {
231 post = SPECIAL_REMOTE_FS_TARGET;
232 pre = SPECIAL_REMOTE_FS_PRE_TARGET;
233 } else if (initrd_fs_target) {
234 post = SPECIAL_INITRD_FS_TARGET;
235 pre = SPECIAL_INITRD_FS_PRE_TARGET;
236 } else {
237 post = SPECIAL_LOCAL_FS_TARGET;
238 pre = SPECIAL_LOCAL_FS_PRE_TARGET;
239 }
240
241 name = unit_name_from_path(where, ".mount");
242 if (!name)
243 return log_oom();
244
245 unit = strjoin(arg_dest, "/", name, NULL);
246 if (!unit)
247 return log_oom();
248
249 f = fopen(unit, "wxe");
250 if (!f) {
251 if (errno == EEXIST)
252 log_error("Failed to create mount unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
253 else
254 log_error("Failed to create unit file %s: %m", unit);
255 return -errno;
256 }
257
258 fprintf(f,
259 "# Automatically generated by systemd-fstab-generator\n\n"
260 "[Unit]\n"
261 "SourcePath=%s\n"
262 "DefaultDependencies=no\n",
263 source);
264
265 if (!path_equal(where, "/"))
266 fprintf(f,
267 "After=%s\n"
268 "Wants=%s\n"
269 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
270 "Before=" SPECIAL_UMOUNT_TARGET "\n",
271 pre,
272 pre);
273
274
275 if (!noauto && !nofail && !automount)
276 fprintf(f,
277 "Before=%s\n",
278 post);
279
280 fprintf(f,
281 "\n"
282 "[Mount]\n"
283 "What=%s\n"
284 "Where=%s\n"
285 "Type=%s\n"
286 "FsckPassNo=%i\n",
287 what,
288 where,
289 type,
290 passno);
291
292 if (!isempty(opts) &&
293 !streq(opts, "defaults"))
294 fprintf(f,
295 "Options=%s\n",
296 opts);
297
298 fflush(f);
299 if (ferror(f)) {
300 log_error("Failed to write unit file %s: %m", unit);
301 return -errno;
302 }
303
304 if (!noauto) {
305 lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL);
306 if (!lnk)
307 return log_oom();
308
309 mkdir_parents_label(lnk, 0755);
310 if (symlink(unit, lnk) < 0) {
311 log_error("Failed to create symlink %s: %m", lnk);
312 return -errno;
313 }
314
315 if (!isbind &&
316 !path_equal(where, "/")) {
317
318 r = device_name(what, &device);
319 if (r < 0)
320 return r;
321
322 if (r > 0) {
323 free(lnk);
324 lnk = strjoin(arg_dest, "/", device, ".wants/", name, NULL);
325 if (!lnk)
326 return log_oom();
327
328 mkdir_parents_label(lnk, 0755);
329 if (symlink(unit, lnk) < 0) {
330 log_error("Failed to create symlink %s: %m", lnk);
331 return -errno;
332 }
333 }
334 }
335 }
336
337 if (automount && !path_equal(where, "/")) {
338 automount_name = unit_name_from_path(where, ".automount");
339 if (!name)
340 return log_oom();
341
342 automount_unit = strjoin(arg_dest, "/", automount_name, NULL);
343 if (!automount_unit)
344 return log_oom();
345
346 fclose(f);
347 f = fopen(automount_unit, "wxe");
348 if (!f) {
349 log_error("Failed to create unit file %s: %m", automount_unit);
350 return -errno;
351 }
352
353 fprintf(f,
354 "# Automatically generated by systemd-fstab-generator\n\n"
355 "[Unit]\n"
356 "SourcePath=%s\n"
357 "DefaultDependencies=no\n"
358 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
359 "Before=" SPECIAL_UMOUNT_TARGET " %s\n"
360 "\n"
361 "[Automount]\n"
362 "Where=%s\n",
363 source,
364 post,
365 where);
366
367 fflush(f);
368 if (ferror(f)) {
369 log_error("Failed to write unit file %s: %m", automount_unit);
370 return -errno;
371 }
372
373 free(lnk);
374 lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL);
375 if (!lnk)
376 return log_oom();
377
378 mkdir_parents_label(lnk, 0755);
379 if (symlink(automount_unit, lnk) < 0) {
380 log_error("Failed to create symlink %s: %m", lnk);
381 return -errno;
382 }
383 }
384
385 return 0;
386 }
387
388 static int parse_fstab(const char *prefix, bool initrd) {
389 FILE *f;
390 _cleanup_free_ char *fstab_path = NULL;
391 int r = 0;
392 struct mntent *me;
393
394 errno = 0;
395 fstab_path = strjoin(prefix, "/etc/fstab", NULL);
396 f = setmntent(fstab_path, "r");
397 if (!f) {
398 if (errno == ENOENT)
399 return 0;
400
401 log_error("Failed to open %s/etc/fstab: %m", prefix);
402 return -errno;
403 }
404
405 while ((me = getmntent(f))) {
406 char _cleanup_free_ *where = NULL, *what = NULL;
407 int k;
408
409 if (initrd && !mount_in_initrd(me))
410 continue;
411
412 what = fstab_node_to_udev_node(me->mnt_fsname);
413
414 where = strjoin(prefix, me->mnt_dir, NULL);
415 if (!what || !where) {
416 r = log_oom();
417 goto finish;
418 }
419
420 if (is_path(where))
421 path_kill_slashes(where);
422
423 if (initrd) {
424 char _cleanup_free_ *mu = NULL, *name = NULL;
425 /* Skip generation, if unit already exists */
426 name = unit_name_from_path(where, ".mount");
427 if (!name)
428 return log_oom();
429 mu = strjoin(arg_dest, "/", name, NULL);
430 if (!mu)
431 return log_oom();
432
433 k = access(mu, R_OK);
434 if (k == 0)
435 continue;
436 }
437
438 log_debug("Found entry what=%s where=%s type=%s", what, where, me->mnt_type);
439
440 if (streq(me->mnt_type, "swap"))
441 k = add_swap(what, me);
442 else {
443 bool noauto, nofail, automount, isbind, isnetwork;
444
445 noauto = !!hasmntopt(me, "noauto");
446 nofail = !!hasmntopt(me, "nofail");
447 automount =
448 hasmntopt(me, "comment=systemd.automount") ||
449 hasmntopt(me, "x-systemd.automount");
450 isbind = mount_is_bind(me);
451 isnetwork = mount_is_network(me);
452
453 k = add_mount(what, where, me->mnt_type, me->mnt_opts,
454 me->mnt_passno, noauto, nofail, automount,
455 isbind, isnetwork, initrd, fstab_path);
456 }
457
458 if (k < 0)
459 r = k;
460 }
461
462 finish:
463 endmntent(f);
464 return r;
465 }
466
467 static int parse_new_root_from_proc_cmdline(void) {
468 char *w, *state;
469 _cleanup_free_ char *what = NULL, *type = NULL, *opts = NULL, *line = NULL, *mu = NULL;
470 int r;
471 size_t l;
472
473 /* Skip generation, if sysroot.mount already exists */
474 mu = strjoin(arg_dest, "/", "sysroot.mount", NULL);
475 if (!mu)
476 return log_oom();
477
478 r = access(mu, R_OK);
479 if (r == 0)
480 return 0;
481
482 r = read_one_line_file("/proc/cmdline", &line);
483 if (r < 0) {
484 log_error("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
485 return 0;
486 }
487
488 opts = strdup("ro");
489 type = strdup("auto");
490 if (!opts || !type)
491 return log_oom();
492
493 /* root= and roofstype= may occur more than once, the last instance should take precedence.
494 * In the case of multiple rootflags= the arguments should be concatenated */
495 FOREACH_WORD_QUOTED(w, l, line, state) {
496 char *word, *tmp_word;
497
498 word = strndup(w, l);
499 if (!word)
500 return log_oom();
501
502 else if (startswith(word, "root=")) {
503 free(what);
504 what = fstab_node_to_udev_node(word+5);
505 if (!what)
506 return log_oom();
507
508 } else if (startswith(word, "rootfstype=")) {
509 free(type);
510 type = strdup(word + 11);
511 if (!type)
512 return log_oom();
513
514 } else if (startswith(word, "rootflags=")) {
515 tmp_word = opts;
516 opts = strjoin(opts, ",", word + 10, NULL);
517 free(tmp_word);
518 if (!opts)
519 return log_oom();
520
521 } else if (streq(word, "ro") || streq(word, "rw")) {
522 tmp_word = opts;
523 opts = strjoin(opts, ",", word, NULL);
524 free(tmp_word);
525 if (!opts)
526 return log_oom();
527
528 }
529
530 free(word);
531 }
532
533 if (what) {
534
535 log_debug("Found entry what=%s where=/sysroot type=%s", what, type);
536 r = add_mount(what, "/sysroot", type, opts, 0, false, false, false,
537 false, false, true, "/proc/cmdline");
538
539 if (r < 0)
540 return r;
541 } else
542 log_error("Could not find a root= entry on the kernel commandline.");
543
544 return 0;
545 }
546
547 static int parse_proc_cmdline(void) {
548 char _cleanup_free_ *line = NULL;
549 char *w, *state;
550 int r;
551 size_t l;
552
553 if (detect_container(NULL) > 0)
554 return 0;
555
556 r = read_one_line_file("/proc/cmdline", &line);
557 if (r < 0) {
558 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
559 return 0;
560 }
561
562 FOREACH_WORD_QUOTED(w, l, line, state) {
563 char _cleanup_free_ *word = NULL;
564
565 word = strndup(w, l);
566 if (!word)
567 return log_oom();
568
569 if (startswith(word, "fstab=")) {
570 r = parse_boolean(word + 6);
571 if (r < 0)
572 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
573 else
574 arg_enabled = r;
575
576 } else if (startswith(word, "rd.fstab=")) {
577
578 if (in_initrd()) {
579 r = parse_boolean(word + 6);
580 if (r < 0)
581 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
582 else
583 arg_enabled = r;
584 }
585
586 } else if (startswith(word, "fstab.") ||
587 (in_initrd() && startswith(word, "rd.fstab."))) {
588
589 log_warning("Unknown kernel switch %s. Ignoring.", word);
590 }
591 }
592
593 return 0;
594 }
595
596 int main(int argc, char *argv[]) {
597 int r = 0, k = 0, l = 0;
598
599 if (argc > 1 && argc != 4) {
600 log_error("This program takes three or no arguments.");
601 return EXIT_FAILURE;
602 }
603
604 if (argc > 1)
605 arg_dest = argv[1];
606
607 log_set_target(LOG_TARGET_SAFE);
608 log_parse_environment();
609 log_open();
610
611 umask(0022);
612
613 if (parse_proc_cmdline() < 0)
614 return EXIT_FAILURE;
615
616 if (arg_enabled)
617 r = parse_fstab("", false);
618
619 if (in_initrd()) {
620 if (arg_enabled)
621 k = parse_fstab("/sysroot", true);
622 l = parse_new_root_from_proc_cmdline();
623 }
624
625 return (r < 0) || (k < 0) || (l < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
626 }