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