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