chiark / gitweb /
fstab-generator: Allow mount.usr without mount.usrflags, honor rw/ro
[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 "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)
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         if (!arg_usr_options)
489                 opts = arg_root_rw > 0 ? "rw" : "ro";
490         else if (!mount_test_option(arg_usr_options, "ro") &&
491                  !mount_test_option(arg_usr_options, "rw"))
492                 opts = strappenda(arg_usr_options, ",", arg_root_rw > 0 ? "rw" : "ro");
493         else
494                 opts = arg_usr_options;
495
496         log_debug("Found entry what=%s where=/sysroot/usr type=%s", what, strna(arg_usr_fstype));
497         return add_mount(what,
498                          "/sysroot/usr",
499                          arg_usr_fstype,
500                          opts,
501                          1,
502                          false,
503                          false,
504                          false,
505                          SPECIAL_INITRD_ROOT_FS_TARGET,
506                          "/proc/cmdline");
507 }
508
509 static int parse_proc_cmdline_item(const char *key, const char *value) {
510         int r;
511
512         /* root=, usr=, usrfstype= and roofstype= may occur more than once, the last
513          * instance should take precedence.  In the case of multiple rootflags=
514          * or usrflags= the arguments should be concatenated */
515
516         if (STR_IN_SET(key, "fstab", "rd.fstab") && value) {
517
518                 r = parse_boolean(value);
519                 if (r < 0)
520                         log_warning("Failed to parse fstab switch %s. Ignoring.", value);
521                 else
522                         arg_fstab_enabled = r;
523
524         } else if (streq(key, "root") && value) {
525
526                 if (free_and_strdup(&arg_root_what, value) < 0)
527                         return log_oom();
528
529         } else if (streq(key, "rootfstype") && value) {
530
531                 if (free_and_strdup(&arg_root_fstype, value) < 0)
532                         return log_oom();
533
534         } else if (streq(key, "rootflags") && value) {
535                 char *o;
536
537                 o = arg_root_options ?
538                         strjoin(arg_root_options, ",", value, NULL) :
539                         strdup(value);
540                 if (!o)
541                         return log_oom();
542
543                 free(arg_root_options);
544                 arg_root_options = o;
545
546         } else if (streq(key, "mount.usr") && value) {
547
548                 if (free_and_strdup(&arg_usr_what, value) < 0)
549                         return log_oom();
550
551         } else if (streq(key, "mount.usrfstype") && value) {
552
553                 if (free_and_strdup(&arg_usr_fstype, value) < 0)
554                         return log_oom();
555
556         } else if (streq(key, "mount.usrflags") && value) {
557                 char *o;
558
559                 o = arg_usr_options ?
560                         strjoin(arg_usr_options, ",", value, NULL) :
561                         strdup(value);
562                 if (!o)
563                         return log_oom();
564
565                 free(arg_usr_options);
566                 arg_usr_options = o;
567
568         } else if (streq(key, "rw") && !value)
569                 arg_root_rw = true;
570         else if (streq(key, "ro") && !value)
571                 arg_root_rw = false;
572
573         return 0;
574 }
575
576 int main(int argc, char *argv[]) {
577         int r = 0;
578
579         if (argc > 1 && argc != 4) {
580                 log_error("This program takes three or no arguments.");
581                 return EXIT_FAILURE;
582         }
583
584         if (argc > 1)
585                 arg_dest = argv[1];
586
587         log_set_target(LOG_TARGET_SAFE);
588         log_parse_environment();
589         log_open();
590
591         umask(0022);
592
593         r = parse_proc_cmdline(parse_proc_cmdline_item);
594         if (r < 0)
595                 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
596
597         /* Always honour root= and usr= in the kernel command line if we are in an initrd */
598         if (in_initrd()) {
599                 r = add_root_mount();
600                 if (r == 0)
601                         r = add_usr_mount();
602         }
603
604         /* Honour /etc/fstab only when that's enabled */
605         if (arg_fstab_enabled) {
606                 int k;
607
608                 log_debug("Parsing /etc/fstab");
609
610                 /* Parse the local /etc/fstab, possibly from the initrd */
611                 k = parse_fstab(false);
612                 if (k < 0)
613                         r = k;
614
615                 /* If running in the initrd also parse the /etc/fstab from the host */
616                 if (in_initrd()) {
617                         log_debug("Parsing /sysroot/etc/fstab");
618
619                         k = parse_fstab(true);
620                         if (k < 0)
621                                 r = k;
622                 }
623         }
624
625         free(arg_root_what);
626         free(arg_root_fstype);
627         free(arg_root_options);
628
629         free(arg_usr_what);
630         free(arg_usr_fstype);
631         free(arg_usr_options);
632
633         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
634 }