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