]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/fstab-generator/fstab-generator.c
fstab-generator: set "ro" as default for sysroot.mount
[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 int add_mount(const char *what, const char *where, const char *type, const char *opts,
195 int passno, bool wait, bool noauto, bool nofail, bool automount, bool isbind, bool isnetwork,
196 const char *source) {
197 char _cleanup_free_
198 *name = NULL, *unit = NULL, *lnk = NULL, *device = NULL,
199 *automount_name = NULL, *automount_unit = NULL;
200 FILE _cleanup_fclose_ *f = NULL;
201 int r;
202 const char *post, *pre;
203
204 assert(what);
205 assert(where);
206 assert(type);
207 assert(opts);
208 assert(source);
209
210 if (streq(type, "autofs"))
211 return 0;
212
213 if (!is_path(where)) {
214 log_warning("Mount point %s is not a valid path, ignoring.", where);
215 return 0;
216 }
217
218 if (mount_point_is_api(where) ||
219 mount_point_ignore(where))
220 return 0;
221
222 if (isnetwork) {
223 post = SPECIAL_REMOTE_FS_TARGET;
224 pre = SPECIAL_REMOTE_FS_PRE_TARGET;
225 } else {
226 post = SPECIAL_LOCAL_FS_TARGET;
227 pre = SPECIAL_LOCAL_FS_PRE_TARGET;
228 }
229
230 name = unit_name_from_path(where, ".mount");
231 if (!name)
232 return log_oom();
233
234 unit = strjoin(arg_dest, "/", name, NULL);
235 if (!unit)
236 return log_oom();
237
238 f = fopen(unit, "wxe");
239 if (!f) {
240 if (errno == EEXIST)
241 log_error("Failed to create mount unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
242 else
243 log_error("Failed to create unit file %s: %m", unit);
244 return -errno;
245 }
246
247 fprintf(f,
248 "# Automatically generated by systemd-fstab-generator\n\n"
249 "[Unit]\n"
250 "SourcePath=%s\n"
251 "DefaultDependencies=no\n",
252 source);
253
254 if (!path_equal(where, "/"))
255 fprintf(f,
256 "After=%s\n"
257 "Wants=%s\n"
258 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
259 "Before=" SPECIAL_UMOUNT_TARGET "\n",
260 pre,
261 pre);
262
263
264 if (!noauto && !nofail && !automount)
265 fprintf(f,
266 "Before=%s\n",
267 post);
268
269 fprintf(f,
270 "\n"
271 "[Mount]\n"
272 "What=%s\n"
273 "Where=%s\n"
274 "Type=%s\n"
275 "FsckPassNo=%i\n",
276 what,
277 where,
278 type,
279 passno);
280
281 if (!isempty(opts) &&
282 !streq(opts, "defaults"))
283 fprintf(f,
284 "Options=%s\n",
285 opts);
286
287 if (wait)
288 fprintf(f,
289 "TimeoutSec=0\n");
290
291 fflush(f);
292 if (ferror(f)) {
293 log_error("Failed to write unit file %s: %m", unit);
294 return -errno;
295 }
296
297 if (!noauto) {
298 lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL);
299 if (!lnk)
300 return log_oom();
301
302 mkdir_parents_label(lnk, 0755);
303 if (symlink(unit, lnk) < 0) {
304 log_error("Failed to create symlink %s: %m", lnk);
305 return -errno;
306 }
307
308 if (!isbind &&
309 !path_equal(where, "/")) {
310
311 r = device_name(what, &device);
312 if (r < 0)
313 return r;
314
315 if (r > 0) {
316 free(lnk);
317 lnk = strjoin(arg_dest, "/", device, ".wants/", name, NULL);
318 if (!lnk)
319 return log_oom();
320
321 mkdir_parents_label(lnk, 0755);
322 if (symlink(unit, lnk) < 0) {
323 log_error("Failed to create symlink %s: %m", lnk);
324 return -errno;
325 }
326 }
327 }
328 }
329
330 if (automount && !path_equal(where, "/")) {
331 automount_name = unit_name_from_path(where, ".automount");
332 if (!name)
333 return log_oom();
334
335 automount_unit = strjoin(arg_dest, "/", automount_name, NULL);
336 if (!automount_unit)
337 return log_oom();
338
339 fclose(f);
340 f = fopen(automount_unit, "wxe");
341 if (!f) {
342 log_error("Failed to create unit file %s: %m", automount_unit);
343 return -errno;
344 }
345
346 fprintf(f,
347 "# Automatically generated by systemd-fstab-generator\n\n"
348 "[Unit]\n"
349 "SourcePath=/etc/fstab\n"
350 "DefaultDependencies=no\n"
351 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
352 "Before=" SPECIAL_UMOUNT_TARGET " %s\n"
353 "\n"
354 "[Automount]\n"
355 "Where=%s\n",
356 post,
357 where);
358
359 fflush(f);
360 if (ferror(f)) {
361 log_error("Failed to write unit file %s: %m", automount_unit);
362 return -errno;
363 }
364
365 free(lnk);
366 lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL);
367 if (!lnk)
368 return log_oom();
369
370 mkdir_parents_label(lnk, 0755);
371 if (symlink(automount_unit, lnk) < 0) {
372 log_error("Failed to create symlink %s: %m", lnk);
373 return -errno;
374 }
375 }
376
377 return 0;
378 }
379
380 static int parse_fstab(void) {
381 FILE *f;
382 int r = 0;
383 struct mntent *me;
384
385 errno = 0;
386 f = setmntent("/etc/fstab", "r");
387 if (!f) {
388 if (errno == ENOENT)
389 return 0;
390
391 log_error("Failed to open /etc/fstab: %m");
392 return -errno;
393 }
394
395 while ((me = getmntent(f))) {
396 char _cleanup_free_ *where = NULL, *what = NULL;
397 int k;
398
399 what = fstab_node_to_udev_node(me->mnt_fsname);
400 where = strdup(me->mnt_dir);
401 if (!what || !where) {
402 r = log_oom();
403 goto finish;
404 }
405
406 if (is_path(where))
407 path_kill_slashes(where);
408
409 log_debug("Found entry what=%s where=%s type=%s", what, where, me->mnt_type);
410
411 if (streq(me->mnt_type, "swap"))
412 k = add_swap(what, me);
413 else {
414 bool noauto, nofail, automount, isbind, isnetwork;
415
416 noauto = !!hasmntopt(me, "noauto");
417 nofail = !!hasmntopt(me, "nofail");
418 automount =
419 hasmntopt(me, "comment=systemd.automount") ||
420 hasmntopt(me, "x-systemd.automount");
421 isbind = mount_is_bind(me);
422 isnetwork = mount_is_network(me);
423
424 k = add_mount(what, where, me->mnt_type, me->mnt_opts,
425 me->mnt_passno, false, noauto, nofail,
426 automount, isbind, isnetwork,
427 "/etc/fstab");
428 }
429
430 if (k < 0)
431 r = k;
432 }
433
434 finish:
435 endmntent(f);
436 return r;
437 }
438
439 static int parse_new_root_from_proc_cmdline(void) {
440 char *w, *state;
441 _cleanup_free_ char *what = NULL, *type = NULL, *opts = NULL, *line = NULL;
442 int r;
443 size_t l;
444 bool wait = false;
445
446 r = read_one_line_file("/proc/cmdline", &line);
447 if (r < 0) {
448 log_error("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
449 return 0;
450 }
451
452 opts = strdup("ro");
453 type = strdup("auto");
454 if (!opts || !type)
455 return log_oom();
456
457 /* root= and roofstype= may occur more than once, the last instance should take precedence.
458 * In the case of multiple rootflags= the arguments should be concatenated */
459 FOREACH_WORD_QUOTED(w, l, line, state) {
460 char *word, *tmp_word;
461
462 word = strndup(w, l);
463 if (!word)
464 return log_oom();
465
466 else if (startswith(word, "root=")) {
467 free(what);
468 what = fstab_node_to_udev_node(word+5);
469 if (!what)
470 return log_oom();
471
472 } else if (startswith(word, "rootfstype=")) {
473 free(type);
474 type = strdup(word + 11);
475 if (!type)
476 return log_oom();
477
478 } else if (startswith(word, "rootflags=")) {
479 tmp_word = opts;
480 opts = strjoin(opts, ",", word + 10, NULL);
481 free(tmp_word);
482 if (!opts)
483 return log_oom();
484
485 } else if (streq(word, "ro") || streq(word, "rw")) {
486 tmp_word = opts;
487 opts = strjoin(opts, ",", word, NULL);
488 free(tmp_word);
489 if (!opts)
490 return log_oom();
491
492 } else if (streq(word, "rootwait"))
493 wait = true;
494
495 free(word);
496 }
497
498 if (what) {
499
500 log_debug("Found entry what=%s where=/sysroot type=%s", what, type);
501 r = add_mount(what, "/sysroot", type, opts, 0, wait, false, false,
502 false, false, false, "/proc/cmdline");
503
504 if (r < 0)
505 return r;
506 } else
507 log_error("Could not find a root= entry on the kernel commandline.");
508
509 return 0;
510 }
511
512 static int parse_proc_cmdline(void) {
513 char _cleanup_free_ *line = NULL;
514 char *w, *state;
515 int r;
516 size_t l;
517
518 if (detect_container(NULL) > 0)
519 return 0;
520
521 r = read_one_line_file("/proc/cmdline", &line);
522 if (r < 0) {
523 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
524 return 0;
525 }
526
527 FOREACH_WORD_QUOTED(w, l, line, state) {
528 char _cleanup_free_ *word = NULL;
529
530 word = strndup(w, l);
531 if (!word)
532 return log_oom();
533
534 if (startswith(word, "fstab=")) {
535 r = parse_boolean(word + 6);
536 if (r < 0)
537 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
538 else
539 arg_enabled = r;
540
541 } else if (startswith(word, "rd.fstab=")) {
542
543 if (in_initrd()) {
544 r = parse_boolean(word + 6);
545 if (r < 0)
546 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
547 else
548 arg_enabled = r;
549 }
550
551 } else if (startswith(word, "fstab.") ||
552 (in_initrd() && startswith(word, "rd.fstab."))) {
553
554 log_warning("Unknown kernel switch %s. Ignoring.", word);
555 }
556 }
557
558 return 0;
559 }
560
561 int main(int argc, char *argv[]) {
562 int r, k = 0;
563
564 if (argc > 1 && argc != 4) {
565 log_error("This program takes three or no arguments.");
566 return EXIT_FAILURE;
567 }
568
569 if (argc > 1)
570 arg_dest = argv[1];
571
572 log_set_target(LOG_TARGET_SAFE);
573 log_parse_environment();
574 log_open();
575
576 umask(0022);
577
578 if (parse_proc_cmdline() < 0)
579 return EXIT_FAILURE;
580
581 if (in_initrd())
582 k = parse_new_root_from_proc_cmdline();
583
584 if (!arg_enabled)
585 return EXIT_SUCCESS;
586
587 r = parse_fstab();
588
589 return (r < 0) || (k < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
590 }