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