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