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