chiark / gitweb /
fstab-generator: rename x-initrd-rootfs.mount to x-initrd.rootfs
[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 "virt.h"
36 #include "fileio.h"
37
38 static const char *arg_dest = "/tmp";
39 static bool arg_enabled = true;
40
41 static int device_name(const char *path, char **unit) {
42         char *p;
43
44         assert(path);
45
46         if (!is_device_path(path))
47                 return 0;
48
49         p = unit_name_from_path(path, ".device");
50         if (!p)
51                 return log_oom();
52
53         *unit = p;
54         return 1;
55 }
56
57 static int mount_find_pri(struct mntent *me, int *ret) {
58         char *end, *pri;
59         unsigned long r;
60
61         assert(me);
62         assert(ret);
63
64         pri = hasmntopt(me, "pri");
65         if (!pri)
66                 return 0;
67
68         pri += 4;
69
70         errno = 0;
71         r = strtoul(pri, &end, 10);
72         if (errno != 0)
73                 return -errno;
74
75         if (end == pri || (*end != ',' && *end != 0))
76                 return -EINVAL;
77
78         *ret = (int) r;
79         return 1;
80 }
81
82 static int add_swap(const char *what, struct mntent *me) {
83         char _cleanup_free_ *name = NULL, *unit = NULL, *lnk = NULL, *device = NULL;
84         FILE _cleanup_fclose_ *f = NULL;
85         bool noauto, nofail;
86         int r, pri = -1;
87
88         assert(what);
89         assert(me);
90
91         r = mount_find_pri(me, &pri);
92         if (r < 0) {
93                 log_error("Failed to parse priority");
94                 return pri;
95         }
96
97         noauto = !!hasmntopt(me, "noauto");
98         nofail = !!hasmntopt(me, "nofail");
99
100         name = unit_name_from_path(what, ".swap");
101         if (!name)
102                 return log_oom();
103
104         unit = strjoin(arg_dest, "/", name, NULL);
105         if (!unit)
106                 return log_oom();
107
108         f = fopen(unit, "wxe");
109         if (!f) {
110                 if (errno == EEXIST)
111                         log_error("Failed to create swap unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
112                 else
113                         log_error("Failed to create unit file %s: %m", unit);
114                 return -errno;
115         }
116
117         fputs("# Automatically generated by systemd-fstab-generator\n\n"
118               "[Unit]\n"
119               "SourcePath=/etc/fstab\n"
120               "DefaultDependencies=no\n"
121               "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
122               "Before=" SPECIAL_UMOUNT_TARGET "\n", f);
123
124         if (!noauto && !nofail)
125                 fputs("Before=" SPECIAL_SWAP_TARGET "\n", f);
126
127         fprintf(f,
128                 "\n"
129                 "[Swap]\n"
130                 "What=%s\n",
131                 what);
132
133         if (pri >= 0)
134                 fprintf(f,
135                         "Priority=%i\n",
136                         pri);
137
138         fflush(f);
139         if (ferror(f)) {
140                 log_error("Failed to write unit file %s: %m", unit);
141                 return -errno;
142         }
143
144         if (!noauto) {
145                 lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL);
146                 if (!lnk)
147                         return log_oom();
148
149                 mkdir_parents_label(lnk, 0755);
150                 if (symlink(unit, lnk) < 0) {
151                         log_error("Failed to create symlink %s: %m", lnk);
152                         return -errno;
153                 }
154
155                 r = device_name(what, &device);
156                 if (r < 0)
157                         return r;
158
159                 if (r > 0) {
160                         free(lnk);
161                         lnk = strjoin(arg_dest, "/", device, ".wants/", name, NULL);
162                         if (!lnk)
163                                 return log_oom();
164
165                         mkdir_parents_label(lnk, 0755);
166                         if (symlink(unit, lnk) < 0) {
167                                 log_error("Failed to create symlink %s: %m", lnk);
168                                 return -errno;
169                         }
170                 }
171         }
172
173         return 0;
174 }
175
176 static bool mount_is_bind(struct mntent *me) {
177         assert(me);
178
179         return
180                 hasmntopt(me, "bind") ||
181                 streq(me->mnt_type, "bind") ||
182                 hasmntopt(me, "rbind") ||
183                 streq(me->mnt_type, "rbind");
184 }
185
186 static bool mount_is_network(struct mntent *me) {
187         assert(me);
188
189         return
190                 hasmntopt(me, "_netdev") ||
191                 fstype_is_network(me->mnt_type);
192 }
193
194 static bool mount_in_initrd(struct mntent *me) {
195         assert(me);
196
197         return
198                 hasmntopt(me, "x-initrd.mount") ||
199                 streq(me->mnt_dir, "/usr");
200 }
201
202 static bool mount_is_rootfs(struct mntent *me) {
203         assert(me);
204
205         return hasmntopt(me, "x-initrd.rootfs");
206 }
207
208 static int add_mount(const char *what, const char *where, const char *type, const char *opts,
209                      int passno, bool noauto, bool nofail, bool automount, bool isbind,
210                      const char *pre, const char *post, const char *source) {
211         char _cleanup_free_
212                 *name = NULL, *unit = NULL, *lnk = NULL, *device = NULL,
213                 *automount_name = NULL, *automount_unit = NULL;
214         FILE _cleanup_fclose_ *f = NULL;
215         int r;
216
217         assert(what);
218         assert(where);
219         assert(type);
220         assert(opts);
221         assert(source);
222
223         if (streq(type, "autofs"))
224                 return 0;
225
226         if (!is_path(where)) {
227                 log_warning("Mount point %s is not a valid path, ignoring.", where);
228                 return 0;
229         }
230
231         if (mount_point_is_api(where) ||
232             mount_point_ignore(where))
233                 return 0;
234
235         name = unit_name_from_path(where, ".mount");
236         if (!name)
237                 return log_oom();
238
239         unit = strjoin(arg_dest, "/", name, NULL);
240         if (!unit)
241                 return log_oom();
242
243         f = fopen(unit, "wxe");
244         if (!f) {
245                 if (errno == EEXIST)
246                         log_error("Failed to create mount unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
247                 else
248                         log_error("Failed to create unit file %s: %m", unit);
249                 return -errno;
250         }
251
252         fprintf(f,
253               "# Automatically generated by systemd-fstab-generator\n\n"
254               "[Unit]\n"
255               "SourcePath=%s\n"
256               "DefaultDependencies=no\n",
257               source);
258
259         if (!path_equal(where, "/")) {
260                 if (pre)
261                         fprintf(f,
262                                 "After=%s\n"
263                                 "Wants=%s\n",
264                                 pre,
265                                 pre);
266                 fprintf(f,
267                         "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
268                         "Before=" SPECIAL_UMOUNT_TARGET "\n");
269         }
270
271         if (post && !noauto && !nofail && !automount)
272                 fprintf(f,
273                         "Before=%s\n",
274                         post);
275
276         fprintf(f,
277                 "\n"
278                 "[Mount]\n"
279                 "What=%s\n"
280                 "Where=%s\n"
281                 "Type=%s\n"
282                 "FsckPassNo=%i\n",
283                 what,
284                 where,
285                 type,
286                 passno);
287
288         if (!isempty(opts) &&
289             !streq(opts, "defaults"))
290                 fprintf(f,
291                         "Options=%s\n",
292                         opts);
293
294         fflush(f);
295         if (ferror(f)) {
296                 log_error("Failed to write unit file %s: %m", unit);
297                 return -errno;
298         }
299
300         if (!noauto) {
301                 if (post) {
302                         lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL);
303                         if (!lnk)
304                                 return log_oom();
305
306                         mkdir_parents_label(lnk, 0755);
307                         if (symlink(unit, lnk) < 0) {
308                                 log_error("Failed to create symlink %s: %m", lnk);
309                                 return -errno;
310                         }
311                 }
312
313                 if (!isbind &&
314                     !path_equal(where, "/")) {
315
316                         r = device_name(what, &device);
317                         if (r < 0)
318                                 return r;
319
320                         if (r > 0) {
321                                 free(lnk);
322                                 lnk = strjoin(arg_dest, "/", device, ".wants/", name, NULL);
323                                 if (!lnk)
324                                         return log_oom();
325
326                                 mkdir_parents_label(lnk, 0755);
327                                 if (symlink(unit, lnk) < 0) {
328                                         log_error("Failed to create symlink %s: %m", lnk);
329                                         return -errno;
330                                 }
331                         }
332                 }
333         }
334
335         if (automount && !path_equal(where, "/")) {
336                 automount_name = unit_name_from_path(where, ".automount");
337                 if (!name)
338                         return log_oom();
339
340                 automount_unit = strjoin(arg_dest, "/", automount_name, NULL);
341                 if (!automount_unit)
342                         return log_oom();
343
344                 fclose(f);
345                 f = fopen(automount_unit, "wxe");
346                 if (!f) {
347                         log_error("Failed to create unit file %s: %m", automount_unit);
348                         return -errno;
349                 }
350
351                 fprintf(f,
352                         "# Automatically generated by systemd-fstab-generator\n\n"
353                         "[Unit]\n"
354                         "SourcePath=%s\n"
355                         "DefaultDependencies=no\n"
356                         "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
357                         "Before=" SPECIAL_UMOUNT_TARGET "\n",
358                         source);
359
360                 if (post)
361                         fprintf(f,
362                                 "Before= %s\n",
363                                 post);
364
365                 fprintf(f,
366                         "[Automount]\n"
367                         "Where=%s\n",
368                         where);
369
370                 fflush(f);
371                 if (ferror(f)) {
372                         log_error("Failed to write unit file %s: %m", automount_unit);
373                         return -errno;
374                 }
375
376                 free(lnk);
377                 lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL);
378                 if (!lnk)
379                         return log_oom();
380
381                 mkdir_parents_label(lnk, 0755);
382                 if (symlink(automount_unit, lnk) < 0) {
383                         log_error("Failed to create symlink %s: %m", lnk);
384                         return -errno;
385                 }
386         }
387
388         return 0;
389 }
390
391 static int parse_fstab(const char *prefix, bool initrd) {
392         FILE *f;
393         _cleanup_free_ char *fstab_path = NULL;
394         int r = 0;
395         struct mntent *me;
396
397         errno = 0;
398         fstab_path = strjoin(prefix, "/etc/fstab", NULL);
399         f = setmntent(fstab_path, "r");
400         if (!f) {
401                 if (errno == ENOENT)
402                         return 0;
403
404                 log_error("Failed to open %s/etc/fstab: %m", prefix);
405                 return -errno;
406         }
407
408         while ((me = getmntent(f))) {
409                 char _cleanup_free_ *where = NULL, *what = NULL;
410                 int k;
411
412                 if (initrd && !mount_in_initrd(me))
413                         continue;
414
415                 what = fstab_node_to_udev_node(me->mnt_fsname);
416                 where = strjoin(prefix, me->mnt_dir, NULL);
417                 if (!what || !where) {
418                         r = log_oom();
419                         goto finish;
420                 }
421
422                 if (is_path(where))
423                         path_kill_slashes(where);
424
425                 log_debug("Found entry what=%s where=%s type=%s", what, where, me->mnt_type);
426
427                 if (streq(me->mnt_type, "swap"))
428                         k = add_swap(what, me);
429                 else {
430                         bool noauto, nofail, automount, isbind;
431                         const char *pre, *post;
432
433                         noauto = !!hasmntopt(me, "noauto");
434                         nofail = !!hasmntopt(me, "nofail");
435                         automount =
436                                   hasmntopt(me, "comment=systemd.automount") ||
437                                   hasmntopt(me, "x-systemd.automount");
438                         isbind = mount_is_bind(me);
439
440                         if (initrd) {
441                                 post = SPECIAL_INITRD_FS_TARGET;
442                                 pre = NULL;
443                         } else if (mount_is_rootfs(me)) {
444                                 post = SPECIAL_INITRD_ROOT_FS_TARGET;
445                                 pre = NULL;
446                         } else if (mount_is_network(me)) {
447                                 post = SPECIAL_REMOTE_FS_TARGET;
448                                 pre = SPECIAL_REMOTE_FS_PRE_TARGET;
449                         } else {
450                                 post = SPECIAL_LOCAL_FS_TARGET;
451                                 pre = SPECIAL_LOCAL_FS_PRE_TARGET;
452                         }
453
454                         k = add_mount(what, where, me->mnt_type, me->mnt_opts,
455                                      me->mnt_passno, noauto, nofail, automount,
456                                      isbind, pre, post, fstab_path);
457                 }
458
459                 if (k < 0)
460                         r = k;
461         }
462
463 finish:
464         endmntent(f);
465         return r;
466 }
467
468 static int parse_new_root_from_proc_cmdline(void) {
469         char *w, *state;
470         _cleanup_free_ char *what = NULL, *type = NULL, *opts = NULL, *line = NULL;
471         int r;
472         size_t l;
473
474         r = read_one_line_file("/proc/cmdline", &line);
475         if (r < 0) {
476                 log_error("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
477                 return 0;
478         }
479
480         opts = strdup("ro");
481         type = strdup("auto");
482         if (!opts || !type)
483                 return log_oom();
484
485         /* root= and roofstype= may occur more than once, the last instance should take precedence.
486          * In the case of multiple rootflags= the arguments should be concatenated */
487         FOREACH_WORD_QUOTED(w, l, line, state) {
488                 char *word, *tmp_word;
489
490                 word = strndup(w, l);
491                 if (!word)
492                         return log_oom();
493
494                 else if (startswith(word, "root=")) {
495                         free(what);
496                         what = fstab_node_to_udev_node(word+5);
497                         if (!what)
498                                 return log_oom();
499
500                 } else if (startswith(word, "rootfstype=")) {
501                         free(type);
502                         type = strdup(word + 11);
503                         if (!type)
504                                 return log_oom();
505
506                 } else if (startswith(word, "rootflags=")) {
507                         tmp_word = opts;
508                         opts = strjoin(opts, ",", word + 10, NULL);
509                         free(tmp_word);
510                         if (!opts)
511                                 return log_oom();
512
513                 } else if (streq(word, "ro") || streq(word, "rw")) {
514                         tmp_word = opts;
515                         opts = strjoin(opts, ",", word, NULL);
516                         free(tmp_word);
517                         if (!opts)
518                                 return log_oom();
519
520                 }
521
522                 free(word);
523         }
524
525         if (!what) {
526                 log_error("Could not find a root= entry on the kernel commandline.");
527                 return 0;
528         }
529
530         if (what[0] != '/') {
531                 log_debug("Skipping entry what=%s where=/sysroot type=%s", what, type);
532                 return 0;
533         }
534
535         log_debug("Found entry what=%s where=/sysroot type=%s", what, type);
536         r = add_mount(what, "/sysroot", type, opts, 0, false, false, false,
537                       false, NULL, SPECIAL_INITRD_ROOT_FS_TARGET, "/proc/cmdline");
538
539         return (r < 0) ? r : 0;
540 }
541
542 static int parse_proc_cmdline(void) {
543         char _cleanup_free_ *line = NULL;
544         char *w, *state;
545         int r;
546         size_t l;
547
548         if (detect_container(NULL) > 0)
549                 return 0;
550
551         r = read_one_line_file("/proc/cmdline", &line);
552         if (r < 0) {
553                 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
554                 return 0;
555         }
556
557         FOREACH_WORD_QUOTED(w, l, line, state) {
558                 char _cleanup_free_ *word = NULL;
559
560                 word = strndup(w, l);
561                 if (!word)
562                         return log_oom();
563
564                 if (startswith(word, "fstab=")) {
565                         r = parse_boolean(word + 6);
566                         if (r < 0)
567                                 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
568                         else
569                                 arg_enabled = r;
570
571                 } else if (startswith(word, "rd.fstab=")) {
572
573                         if (in_initrd()) {
574                                 r = parse_boolean(word + 6);
575                                 if (r < 0)
576                                         log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
577                                 else
578                                         arg_enabled = r;
579                         }
580
581                 } else if (startswith(word, "fstab.") ||
582                            (in_initrd() && startswith(word, "rd.fstab."))) {
583
584                         log_warning("Unknown kernel switch %s. Ignoring.", word);
585                 }
586         }
587
588         return 0;
589 }
590
591 int main(int argc, char *argv[]) {
592         int r = 0, k, l = 0;
593
594         if (argc > 1 && argc != 4) {
595                 log_error("This program takes three or no arguments.");
596                 return EXIT_FAILURE;
597         }
598
599         if (argc > 1)
600                 arg_dest = argv[1];
601
602         log_set_target(LOG_TARGET_SAFE);
603         log_parse_environment();
604         log_open();
605
606         umask(0022);
607
608         if (parse_proc_cmdline() < 0)
609                 return EXIT_FAILURE;
610
611         if (in_initrd())
612                 r = parse_new_root_from_proc_cmdline();
613
614         if (!arg_enabled)
615                 return (r < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
616
617         k = parse_fstab("", false);
618
619         if (in_initrd())
620                 l = parse_fstab("/sysroot", true);
621
622         return (r < 0) || (k < 0) || (l < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
623 }