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