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