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