]> git.ipfire.org Git - thirdparty/systemd.git/blob - 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
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 "fileio.h"
36 #include "generator.h"
37 #include "strv.h"
38 #include "virt.h"
39
40 static const char *arg_dest = "/tmp";
41 static bool arg_fstab_enabled = true;
42 static char *arg_root_what = NULL;
43 static char *arg_root_fstype = NULL;
44 static char *arg_root_options = NULL;
45 static int arg_root_rw = -1;
46 static char *arg_usr_what = NULL;
47 static char *arg_usr_fstype = NULL;
48 static char *arg_usr_options = NULL;
49
50 static int mount_find_pri(struct mntent *me, int *ret) {
51 char *end, *opt;
52 unsigned long r;
53
54 assert(me);
55 assert(ret);
56
57 opt = hasmntopt(me, "pri");
58 if (!opt)
59 return 0;
60
61 opt += strlen("pri");
62 if (*opt != '=')
63 return -EINVAL;
64
65 errno = 0;
66 r = strtoul(opt + 1, &end, 10);
67 if (errno > 0)
68 return -errno;
69
70 if (end == opt + 1 || (*end != ',' && *end != 0))
71 return -EINVAL;
72
73 *ret = (int) r;
74 return 1;
75 }
76
77 static int add_swap(
78 const char *what,
79 struct mntent *me,
80 bool noauto,
81 bool nofail) {
82
83 _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL;
84 _cleanup_fclose_ FILE *f = NULL;
85 int r, pri = -1;
86
87 assert(what);
88 assert(me);
89
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
95 r = mount_find_pri(me, &pri);
96 if (r < 0) {
97 log_error("Failed to parse priority");
98 return r;
99 }
100
101 name = unit_name_from_path(what, ".swap");
102 if (!name)
103 return log_oom();
104
105 unit = strjoin(arg_dest, "/", name, NULL);
106 if (!unit)
107 return log_oom();
108
109 f = fopen(unit, "wxe");
110 if (!f) {
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_errno(errno, "Failed to create unit file %s: %m", unit);
115 return -errno;
116 }
117
118 fprintf(f,
119 "# Automatically generated by systemd-fstab-generator\n\n"
120 "[Unit]\n"
121 "SourcePath=/etc/fstab\n"
122 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n\n"
123 "[Swap]\n"
124 "What=%s\n",
125 what);
126
127 /* Note that we currently pass the priority field twice, once
128 * in Priority=, and once in Options= */
129 if (pri >= 0)
130 fprintf(f, "Priority=%i\n", pri);
131
132 if (!isempty(me->mnt_opts) && !streq(me->mnt_opts, "defaults"))
133 fprintf(f, "Options=%s\n", me->mnt_opts);
134
135 r = fflush_and_check(f);
136 if (r < 0)
137 return log_error_errno(r, "Failed to write unit file %s: %m", unit);
138
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
144 if (!noauto) {
145 lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET,
146 nofail ? ".wants/" : ".requires/", name, NULL);
147 if (!lnk)
148 return log_oom();
149
150 mkdir_parents_label(lnk, 0755);
151 if (symlink(unit, lnk) < 0) {
152 log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
153 return -errno;
154 }
155 }
156
157 return 0;
158 }
159
160 static 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
168 static 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
176 static int add_mount(
177 const char *what,
178 const char *where,
179 const char *fstype,
180 const char *opts,
181 int passno,
182 bool noauto,
183 bool nofail,
184 bool automount,
185 const char *post,
186 const char *source) {
187
188 _cleanup_free_ char
189 *name = NULL, *unit = NULL, *lnk = NULL,
190 *automount_name = NULL, *automount_unit = NULL,
191 *filtered = NULL;
192 _cleanup_fclose_ FILE *f = NULL;
193 int r;
194
195 assert(what);
196 assert(where);
197 assert(opts);
198 assert(source);
199
200 if (streq_ptr(fstype, "autofs"))
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
212 if (path_equal(where, "/")) {
213 /* The root disk is not an option */
214 automount = false;
215 noauto = false;
216 nofail = false;
217 }
218
219 name = unit_name_from_path(where, ".mount");
220 if (!name)
221 return log_oom();
222
223 unit = strjoin(arg_dest, "/", name, NULL);
224 if (!unit)
225 return log_oom();
226
227 f = fopen(unit, "wxe");
228 if (!f) {
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
232 log_error_errno(errno, "Failed to create unit file %s: %m", unit);
233 return -errno;
234 }
235
236 fprintf(f,
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);
242
243 if (post && !noauto && !nofail && !automount)
244 fprintf(f, "Before=%s\n", post);
245
246 if (passno != 0) {
247 r = generator_write_fsck_deps(f, arg_dest, what, where, fstype);
248 if (r < 0)
249 return r;
250 }
251
252 fprintf(f,
253 "\n"
254 "[Mount]\n"
255 "What=%s\n"
256 "Where=%s\n",
257 what,
258 where);
259
260 if (!isempty(fstype) && !streq(fstype, "auto"))
261 fprintf(f, "Type=%s\n", fstype);
262
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);
269
270 fflush(f);
271 if (ferror(f)) {
272 log_error_errno(errno, "Failed to write unit file %s: %m", unit);
273 return -errno;
274 }
275
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) {
283 log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
284 return -errno;
285 }
286 }
287
288 if (automount) {
289 automount_name = unit_name_from_path(where, ".automount");
290 if (!automount_name)
291 return log_oom();
292
293 automount_unit = strjoin(arg_dest, "/", automount_name, NULL);
294 if (!automount_unit)
295 return log_oom();
296
297 fclose(f);
298 f = fopen(automount_unit, "wxe");
299 if (!f) {
300 log_error_errno(errno, "Failed to create unit file %s: %m", automount_unit);
301 return -errno;
302 }
303
304 fprintf(f,
305 "# Automatically generated by systemd-fstab-generator\n\n"
306 "[Unit]\n"
307 "SourcePath=%s\n"
308 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
309 source);
310
311 if (post)
312 fprintf(f,
313 "Before=%s\n",
314 post);
315
316 fprintf(f,
317 "[Automount]\n"
318 "Where=%s\n",
319 where);
320
321 fflush(f);
322 if (ferror(f)) {
323 log_error_errno(errno, "Failed to write unit file %s: %m", automount_unit);
324 return -errno;
325 }
326
327 free(lnk);
328 lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL);
329 if (!lnk)
330 return log_oom();
331
332 mkdir_parents_label(lnk, 0755);
333 if (symlink(automount_unit, lnk) < 0) {
334 log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
335 return -errno;
336 }
337 }
338
339 return 0;
340 }
341
342 static int parse_fstab(bool initrd) {
343 _cleanup_endmntent_ FILE *f = NULL;
344 const char *fstab_path;
345 struct mntent *me;
346 int r = 0;
347
348 fstab_path = initrd ? "/sysroot/etc/fstab" : "/etc/fstab";
349 f = setmntent(fstab_path, "re");
350 if (!f) {
351 if (errno == ENOENT)
352 return 0;
353
354 log_error_errno(errno, "Failed to open %s: %m", fstab_path);
355 return -errno;
356 }
357
358 while ((me = getmntent(f))) {
359 _cleanup_free_ char *where = NULL, *what = NULL;
360 bool noauto, nofail;
361 int k;
362
363 if (initrd && !mount_in_initrd(me))
364 continue;
365
366 what = fstab_node_to_udev_node(me->mnt_fsname);
367 if (!what)
368 return log_oom();
369
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
375 where = initrd ? strappend("/sysroot/", me->mnt_dir) : strdup(me->mnt_dir);
376 if (!where)
377 return log_oom();
378
379 if (is_path(where))
380 path_kill_slashes(where);
381
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));
387
388 if (streq(me->mnt_type, "swap"))
389 k = add_swap(what, me, noauto, nofail);
390 else {
391 bool automount;
392 const char *post;
393
394 automount =
395 hasmntopt(me, "comment=systemd.automount") ||
396 hasmntopt(me, "x-systemd.automount");
397
398 if (initrd)
399 post = SPECIAL_INITRD_FS_TARGET;
400 else if (mount_in_initrd(me))
401 post = SPECIAL_INITRD_ROOT_FS_TARGET;
402 else if (mount_is_network(me))
403 post = SPECIAL_REMOTE_FS_TARGET;
404 else
405 post = SPECIAL_LOCAL_FS_TARGET;
406
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);
417 }
418
419 if (k < 0)
420 r = k;
421 }
422
423 return r;
424 }
425
426 static int add_root_mount(void) {
427 _cleanup_free_ char *what = NULL;
428 const char *opts;
429
430 if (isempty(arg_root_what)) {
431 log_debug("Could not find a root= entry on the kernel command line.");
432 return 0;
433 }
434
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));
438 return 0;
439 }
440
441 if (!arg_root_options)
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")))
446 opts = strappenda(arg_root_options, ",", arg_root_rw > 0 ? "rw" : "ro");
447 else
448 opts = arg_root_options;
449
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,
454 opts,
455 1,
456 false,
457 false,
458 false,
459 SPECIAL_INITRD_ROOT_FS_TARGET,
460 "/proc/cmdline");
461 }
462
463 static 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
515 static int parse_proc_cmdline_item(const char *key, const char *value) {
516 int r;
517
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 */
521
522 if (STR_IN_SET(key, "fstab", "rd.fstab") && value) {
523
524 r = parse_boolean(value);
525 if (r < 0)
526 log_warning("Failed to parse fstab switch %s. Ignoring.", value);
527 else
528 arg_fstab_enabled = r;
529
530 } else if (streq(key, "root") && value) {
531
532 if (free_and_strdup(&arg_root_what, value) < 0)
533 return log_oom();
534
535 } else if (streq(key, "rootfstype") && value) {
536
537 if (free_and_strdup(&arg_root_fstype, value) < 0)
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
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
574 } else if (streq(key, "rw") && !value)
575 arg_root_rw = true;
576 else if (streq(key, "ro") && !value)
577 arg_root_rw = false;
578
579 return 0;
580 }
581
582 int main(int argc, char *argv[]) {
583 int r = 0;
584
585 if (argc > 1 && argc != 4) {
586 log_error("This program takes three or no arguments.");
587 return EXIT_FAILURE;
588 }
589
590 if (argc > 1)
591 arg_dest = argv[1];
592
593 log_set_target(LOG_TARGET_SAFE);
594 log_parse_environment();
595 log_open();
596
597 umask(0022);
598
599 r = parse_proc_cmdline(parse_proc_cmdline_item);
600 if (r < 0)
601 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
602
603 /* Always honour root= and usr= in the kernel command line if we are in an initrd */
604 if (in_initrd()) {
605 r = add_root_mount();
606 if (r == 0)
607 r = add_usr_mount();
608 }
609
610 /* Honour /etc/fstab only when that's enabled */
611 if (arg_fstab_enabled) {
612 int k;
613
614 log_debug("Parsing /etc/fstab");
615
616 /* Parse the local /etc/fstab, possibly from the initrd */
617 k = parse_fstab(false);
618 if (k < 0)
619 r = k;
620
621 /* If running in the initrd also parse the /etc/fstab from the host */
622 if (in_initrd()) {
623 log_debug("Parsing /sysroot/etc/fstab");
624
625 k = parse_fstab(true);
626 if (k < 0)
627 r = k;
628 }
629 }
630
631 free(arg_root_what);
632
633 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
634 }