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