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