chiark / gitweb /
98772fb56be0e7655e39ba9b10aa4acdd72e3691
[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 *what = NULL;
406         const char *opts;
407
408         if (isempty(arg_root_what)) {
409                 log_debug("Could not find a root= entry on the kernel commandline.");
410                 return 0;
411         }
412
413         what = fstab_node_to_udev_node(arg_root_what);
414         if (!path_is_absolute(what)) {
415                 log_debug("Skipping entry what=%s where=/sysroot type=%s", what, strna(arg_root_fstype));
416                 return 0;
417         }
418
419         if (!arg_root_options)
420                 opts = arg_root_rw > 0 ? "rw" : "ro";
421         else if (arg_root_rw >= 0 ||
422                  (!mount_test_option(arg_root_options, "ro") &&
423                   !mount_test_option(arg_root_options, "rw")))
424                 opts = strappenda3(arg_root_options, ",", arg_root_rw > 0 ? "rw" : "ro");
425         else
426                 opts = arg_root_options;
427
428         log_debug("Found entry what=%s where=/sysroot type=%s", what, strna(arg_root_fstype));
429         return add_mount(what,
430                          "/sysroot",
431                          arg_root_fstype,
432                          opts,
433                          1,
434                          false,
435                          false,
436                          false,
437                          SPECIAL_INITRD_ROOT_FS_TARGET,
438                          "/proc/cmdline");
439 }
440
441 static int parse_proc_cmdline_item(const char *key, const char *value) {
442         int r;
443
444         /* root= and roofstype= may occur more than once, the last
445          * instance should take precedence.  In the case of multiple
446          * rootflags= the arguments should be concatenated */
447
448         if (STR_IN_SET(key, "fstab", "rd.fstab") && value) {
449
450                 r = parse_boolean(value);
451                 if (r < 0)
452                         log_warning("Failed to parse fstab switch %s. Ignoring.", value);
453                 else
454                         arg_fstab_enabled = r;
455
456         } else if (streq(key, "root") && value) {
457
458                 free(arg_root_what);
459                 arg_root_what = strdup(value);
460                 if (!arg_root_what)
461                         return log_oom();
462
463         } else if (streq(key, "rootfstype") && value) {
464
465                 free(arg_root_fstype);
466                 arg_root_fstype = strdup(value);
467                 if (!arg_root_fstype)
468                         return log_oom();
469
470         } else if (streq(key, "rootflags") && value) {
471                 char *o;
472
473                 o = arg_root_options ?
474                         strjoin(arg_root_options, ",", value, NULL) :
475                         strdup(value);
476                 if (!o)
477                         return log_oom();
478
479                 free(arg_root_options);
480                 arg_root_options = o;
481
482         } else if (streq(key, "rw") && !value)
483                 arg_root_rw = true;
484         else if (streq(key, "ro") && !value)
485                 arg_root_rw = false;
486
487         return 0;
488 }
489
490 int main(int argc, char *argv[]) {
491         int r = 0;
492
493         if (argc > 1 && argc != 4) {
494                 log_error("This program takes three or no arguments.");
495                 return EXIT_FAILURE;
496         }
497
498         if (argc > 1)
499                 arg_dest = argv[1];
500
501         log_set_target(LOG_TARGET_SAFE);
502         log_parse_environment();
503         log_open();
504
505         umask(0022);
506
507         if (parse_proc_cmdline(parse_proc_cmdline_item) < 0)
508                 return EXIT_FAILURE;
509
510         /* Always honour root= in the kernel command line if we are in an initrd */
511         if (in_initrd())
512                 r = add_root_mount();
513
514         /* Honour /etc/fstab only when that's enabled */
515         if (arg_fstab_enabled) {
516                 int k;
517
518                 log_debug("Parsing /etc/fstab");
519
520                 /* Parse the local /etc/fstab, possibly from the initrd */
521                 k = parse_fstab(false);
522                 if (k < 0)
523                         r = k;
524
525                 /* If running in the initrd also parse the /etc/fstab from the host */
526                 if (in_initrd()) {
527                         log_debug("Parsing /sysroot/etc/fstab");
528
529                         k = parse_fstab(true);
530                         if (k < 0)
531                                 r = k;
532                 }
533         }
534
535         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
536 }