chiark / gitweb /
2ece12f79202d24bee85944c0fce862ff0dd57b3
[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 (isempty(arg_root_what)) {
401                 log_debug("Could not find a root= entry on the kernel command line.");
402                 return 0;
403         }
404
405         what = fstab_node_to_udev_node(arg_root_what);
406         if (!path_is_absolute(what)) {
407                 log_debug("Skipping entry what=%s where=/sysroot type=%s", what, strna(arg_root_fstype));
408                 return 0;
409         }
410
411         if (!arg_root_options)
412                 opts = arg_root_rw > 0 ? "rw" : "ro";
413         else if (arg_root_rw >= 0 ||
414                  !fstab_test_option(arg_root_options, "ro\0" "rw\0"))
415                 opts = strjoina(arg_root_options, ",", arg_root_rw > 0 ? "rw" : "ro");
416         else
417                 opts = arg_root_options;
418
419         log_debug("Found entry what=%s where=/sysroot type=%s", what, strna(arg_root_fstype));
420         return add_mount(what,
421                          "/sysroot",
422                          arg_root_fstype,
423                          opts,
424                          1,
425                          false,
426                          false,
427                          false,
428                          SPECIAL_INITRD_ROOT_FS_TARGET,
429                          "/proc/cmdline");
430 }
431
432 static int add_usr_mount(void) {
433         _cleanup_free_ char *what = NULL;
434         const char *opts;
435
436         if (!arg_usr_what && !arg_usr_fstype && !arg_usr_options)
437                 return 0;
438
439         if (arg_root_what && !arg_usr_what) {
440                 arg_usr_what = strdup(arg_root_what);
441
442                 if (!arg_usr_what)
443                         return log_oom();
444         }
445
446         if (arg_root_fstype && !arg_usr_fstype) {
447                 arg_usr_fstype = strdup(arg_root_fstype);
448
449                 if (!arg_usr_fstype)
450                         return log_oom();
451         }
452
453         if (arg_root_options && !arg_usr_options) {
454                 arg_usr_options = strdup(arg_root_options);
455
456                 if (!arg_usr_options)
457                         return log_oom();
458         }
459
460         if (!arg_usr_what)
461                 return 0;
462
463         what = fstab_node_to_udev_node(arg_usr_what);
464         if (!path_is_absolute(what)) {
465                 log_debug("Skipping entry what=%s where=/sysroot/usr type=%s", what, strna(arg_usr_fstype));
466                 return -1;
467         }
468
469         if (!arg_usr_options)
470                 opts = arg_root_rw > 0 ? "rw" : "ro";
471         else if (!fstab_test_option(arg_usr_options, "ro\0" "rw\0"))
472                 opts = strjoina(arg_usr_options, ",", arg_root_rw > 0 ? "rw" : "ro");
473         else
474                 opts = arg_usr_options;
475
476         log_debug("Found entry what=%s where=/sysroot/usr type=%s", what, strna(arg_usr_fstype));
477         return add_mount(what,
478                          "/sysroot/usr",
479                          arg_usr_fstype,
480                          opts,
481                          1,
482                          false,
483                          false,
484                          false,
485                          SPECIAL_INITRD_ROOT_FS_TARGET,
486                          "/proc/cmdline");
487 }
488
489 static int parse_proc_cmdline_item(const char *key, const char *value) {
490         int r;
491
492         /* root=, usr=, usrfstype= and roofstype= may occur more than once, the last
493          * instance should take precedence.  In the case of multiple rootflags=
494          * or usrflags= the arguments should be concatenated */
495
496         if (STR_IN_SET(key, "fstab", "rd.fstab") && value) {
497
498                 r = parse_boolean(value);
499                 if (r < 0)
500                         log_warning("Failed to parse fstab switch %s. Ignoring.", value);
501                 else
502                         arg_fstab_enabled = r;
503
504         } else if (streq(key, "root") && value) {
505
506                 if (free_and_strdup(&arg_root_what, value) < 0)
507                         return log_oom();
508
509         } else if (streq(key, "rootfstype") && value) {
510
511                 if (free_and_strdup(&arg_root_fstype, value) < 0)
512                         return log_oom();
513
514         } else if (streq(key, "rootflags") && value) {
515                 char *o;
516
517                 o = arg_root_options ?
518                         strjoin(arg_root_options, ",", value, NULL) :
519                         strdup(value);
520                 if (!o)
521                         return log_oom();
522
523                 free(arg_root_options);
524                 arg_root_options = o;
525
526         } else if (streq(key, "mount.usr") && value) {
527
528                 if (free_and_strdup(&arg_usr_what, value) < 0)
529                         return log_oom();
530
531         } else if (streq(key, "mount.usrfstype") && value) {
532
533                 if (free_and_strdup(&arg_usr_fstype, value) < 0)
534                         return log_oom();
535
536         } else if (streq(key, "mount.usrflags") && value) {
537                 char *o;
538
539                 o = arg_usr_options ?
540                         strjoin(arg_usr_options, ",", value, NULL) :
541                         strdup(value);
542                 if (!o)
543                         return log_oom();
544
545                 free(arg_usr_options);
546                 arg_usr_options = o;
547
548         } else if (streq(key, "rw") && !value)
549                 arg_root_rw = true;
550         else if (streq(key, "ro") && !value)
551                 arg_root_rw = false;
552
553         return 0;
554 }
555
556 int main(int argc, char *argv[]) {
557         int r = 0;
558
559         if (argc > 1 && argc != 4) {
560                 log_error("This program takes three or no arguments.");
561                 return EXIT_FAILURE;
562         }
563
564         if (argc > 1)
565                 arg_dest = argv[1];
566
567         log_set_target(LOG_TARGET_SAFE);
568         log_parse_environment();
569         log_open();
570
571         umask(0022);
572
573         r = parse_proc_cmdline(parse_proc_cmdline_item);
574         if (r < 0)
575                 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
576
577         /* Always honour root= and usr= in the kernel command line if we are in an initrd */
578         if (in_initrd()) {
579                 r = add_root_mount();
580                 if (r == 0)
581                         r = add_usr_mount();
582         }
583
584         /* Honour /etc/fstab only when that's enabled */
585         if (arg_fstab_enabled) {
586                 int k;
587
588                 log_debug("Parsing /etc/fstab");
589
590                 /* Parse the local /etc/fstab, possibly from the initrd */
591                 k = parse_fstab(false);
592                 if (k < 0)
593                         r = k;
594
595                 /* If running in the initrd also parse the /etc/fstab from the host */
596                 if (in_initrd()) {
597                         log_debug("Parsing /sysroot/etc/fstab");
598
599                         k = parse_fstab(true);
600                         if (k < 0)
601                                 r = k;
602                 }
603         }
604
605         free(arg_root_what);
606         free(arg_root_fstype);
607         free(arg_root_options);
608
609         free(arg_usr_what);
610         free(arg_usr_fstype);
611         free(arg_usr_options);
612
613         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
614 }