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