]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/fstab-generator/fstab-generator.c
af7276daa7c82212bfedab875b8924641f122542
[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 "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 int add_mount(const char *what, const char *where, const char *type, const char *opts,
195 int passno, bool wait, bool noauto, bool nofail, bool automount, bool isbind, bool isnetwork,
196 const char *source) {
197 char _cleanup_free_
198 *name = NULL, *unit = NULL, *lnk = NULL, *device = NULL,
199 *automount_name = NULL, *automount_unit = NULL;
200 FILE _cleanup_fclose_ *f = NULL;
201 int r;
202 const char *post, *pre;
203
204 assert(what);
205 assert(where);
206 assert(type);
207 assert(opts);
208 assert(source);
209
210 if (streq(type, "autofs"))
211 return 0;
212
213 if (!is_path(where)) {
214 log_warning("Mount point %s is not a valid path, ignoring.", where);
215 return 0;
216 }
217
218 if (mount_point_is_api(where) ||
219 mount_point_ignore(where))
220 return 0;
221
222 if (isnetwork) {
223 post = SPECIAL_REMOTE_FS_TARGET;
224 pre = SPECIAL_REMOTE_FS_PRE_TARGET;
225 } else {
226 post = SPECIAL_LOCAL_FS_TARGET;
227 pre = SPECIAL_LOCAL_FS_PRE_TARGET;
228 }
229
230 name = unit_name_from_path(where, ".mount");
231 if (!name)
232 return log_oom();
233
234 unit = strjoin(arg_dest, "/", name, NULL);
235 if (!unit)
236 return log_oom();
237
238 f = fopen(unit, "wxe");
239 if (!f) {
240 if (errno == EEXIST)
241 log_error("Failed to create mount unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
242 else
243 log_error("Failed to create unit file %s: %m", unit);
244 return -errno;
245 }
246
247 fprintf(f,
248 "# Automatically generated by systemd-fstab-generator\n\n"
249 "[Unit]\n"
250 "SourcePath=%s\n"
251 "DefaultDependencies=no\n",
252 source);
253
254 if (!path_equal(where, "/"))
255 fprintf(f,
256 "After=%s\n"
257 "Wants=%s\n"
258 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
259 "Before=" SPECIAL_UMOUNT_TARGET "\n",
260 pre,
261 pre);
262
263
264 if (!noauto && !nofail && !automount)
265 fprintf(f,
266 "Before=%s\n",
267 post);
268
269 fprintf(f,
270 "\n"
271 "[Mount]\n"
272 "What=%s\n"
273 "Where=%s\n"
274 "Type=%s\n"
275 "FsckPassNo=%i\n",
276 what,
277 where,
278 type,
279 passno);
280
281 if (!isempty(opts) &&
282 !streq(opts, "defaults"))
283 fprintf(f,
284 "Options=%s\n",
285 opts);
286
287 if (wait)
288 fprintf(f,
289 "TimeoutSec=0\n");
290
291 fflush(f);
292 if (ferror(f)) {
293 log_error("Failed to write unit file %s: %m", unit);
294 return -errno;
295 }
296
297 if (!noauto) {
298 lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL);
299 if (!lnk)
300 return log_oom();
301
302 mkdir_parents_label(lnk, 0755);
303 if (symlink(unit, lnk) < 0) {
304 log_error("Failed to create symlink %s: %m", lnk);
305 return -errno;
306 }
307
308 if (!isbind &&
309 !path_equal(where, "/")) {
310
311 r = device_name(what, &device);
312 if (r < 0)
313 return r;
314
315 if (r > 0) {
316 free(lnk);
317 lnk = strjoin(arg_dest, "/", device, ".wants/", name, NULL);
318 if (!lnk)
319 return log_oom();
320
321 mkdir_parents_label(lnk, 0755);
322 if (symlink(unit, lnk) < 0) {
323 log_error("Failed to create symlink %s: %m", lnk);
324 return -errno;
325 }
326 }
327 }
328 }
329
330 if (automount && !path_equal(where, "/")) {
331 automount_name = unit_name_from_path(where, ".automount");
332 if (!name)
333 return log_oom();
334
335 automount_unit = strjoin(arg_dest, "/", automount_name, NULL);
336 if (!automount_unit)
337 return log_oom();
338
339 fclose(f);
340 f = fopen(automount_unit, "wxe");
341 if (!f) {
342 log_error("Failed to create unit file %s: %m", automount_unit);
343 return -errno;
344 }
345
346 fprintf(f,
347 "# Automatically generated by systemd-fstab-generator\n\n"
348 "[Unit]\n"
349 "SourcePath=/etc/fstab\n"
350 "DefaultDependencies=no\n"
351 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
352 "Before=" SPECIAL_UMOUNT_TARGET " %s\n"
353 "\n"
354 "[Automount]\n"
355 "Where=%s\n",
356 post,
357 where);
358
359 fflush(f);
360 if (ferror(f)) {
361 log_error("Failed to write unit file %s: %m", automount_unit);
362 return -errno;
363 }
364
365 free(lnk);
366 lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL);
367 if (!lnk)
368 return log_oom();
369
370 mkdir_parents_label(lnk, 0755);
371 if (symlink(automount_unit, lnk) < 0) {
372 log_error("Failed to create symlink %s: %m", lnk);
373 return -errno;
374 }
375 }
376
377 return 0;
378 }
379
380 static int parse_fstab(void) {
381 FILE *f;
382 int r = 0;
383 struct mntent *me;
384
385 errno = 0;
386 f = setmntent("/etc/fstab", "r");
387 if (!f) {
388 if (errno == ENOENT)
389 return 0;
390
391 log_error("Failed to open /etc/fstab: %m");
392 return -errno;
393 }
394
395 while ((me = getmntent(f))) {
396 char _cleanup_free_ *where = NULL, *what = NULL;
397 int k;
398
399 what = fstab_node_to_udev_node(me->mnt_fsname);
400 where = strdup(me->mnt_dir);
401 if (!what || !where) {
402 r = log_oom();
403 goto finish;
404 }
405
406 if (is_path(where))
407 path_kill_slashes(where);
408
409 log_debug("Found entry what=%s where=%s type=%s", what, where, me->mnt_type);
410
411 if (streq(me->mnt_type, "swap"))
412 k = add_swap(what, me);
413 else {
414 bool noauto, nofail, automount, isbind, isnetwork;
415
416 noauto = !!hasmntopt(me, "noauto");
417 nofail = !!hasmntopt(me, "nofail");
418 automount =
419 hasmntopt(me, "comment=systemd.automount") ||
420 hasmntopt(me, "x-systemd.automount");
421 isbind = mount_is_bind(me);
422 isnetwork = mount_is_network(me);
423
424 k = add_mount(what, where, me->mnt_type, me->mnt_opts,
425 me->mnt_passno, false, noauto, nofail,
426 automount, isbind, isnetwork,
427 "/etc/fstab");
428 }
429
430 if (k < 0)
431 r = k;
432 }
433
434 finish:
435 endmntent(f);
436 return r;
437 }
438
439 static int parse_new_root_from_proc_cmdline(void) {
440 char *w, *state;
441 _cleanup_free_ char *what = NULL, *type = NULL, *opts = NULL, *line = NULL;
442 int r;
443 size_t l;
444 bool wait = false;
445
446 r = read_one_line_file("/proc/cmdline", &line);
447 if (r < 0) {
448 log_error("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
449 return 0;
450 }
451
452 type = strdup("auto");
453 if (!type)
454 return log_oom();
455
456 /* root= and roofstype= may occur more than once, the last instance should take precedence.
457 * In the case of multiple rootflags= the arguments should be concatenated */
458 FOREACH_WORD_QUOTED(w, l, line, state) {
459 char *word, *tmp_word;
460
461 word = strndup(w, l);
462 if (!word)
463 return log_oom();
464
465 else if (startswith(word, "root=")) {
466 free(what);
467 what = fstab_node_to_udev_node(word+5);
468 if (!what)
469 return log_oom();
470
471 } else if (startswith(word, "rootfstype=")) {
472 free(type);
473 type = strdup(word + 11);
474 if (!type)
475 return log_oom();
476
477 } else if (startswith(word, "rootflags=")) {
478 tmp_word = opts;
479 opts = strjoin(opts, ",", word + 10, NULL);
480 free(tmp_word);
481 if (!opts)
482 return log_oom();
483
484 } else if (streq(word, "ro") || streq(word, "rw")) {
485 tmp_word = opts;
486 opts = strjoin(opts, ",", word, NULL);
487 free(tmp_word);
488 if (!opts)
489 return log_oom();
490
491 } else if (streq(word, "rootwait"))
492 wait = true;
493
494 free(word);
495 }
496
497 if (what) {
498
499 log_debug("Found entry what=%s where=/sysroot type=%s", what, type);
500 r = add_mount(what, "/sysroot", type, opts, 0, wait, false, false,
501 false, false, false, "/proc/cmdline");
502
503 if (r < 0)
504 return r;
505 } else
506 log_error("Could not find a root= entry on the kernel commandline.");
507
508 return 0;
509 }
510
511 static int parse_proc_cmdline(void) {
512 char _cleanup_free_ *line = NULL;
513 char *w, *state;
514 int r;
515 size_t l;
516
517 if (detect_container(NULL) > 0)
518 return 0;
519
520 r = read_one_line_file("/proc/cmdline", &line);
521 if (r < 0) {
522 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
523 return 0;
524 }
525
526 FOREACH_WORD_QUOTED(w, l, line, state) {
527 char _cleanup_free_ *word = NULL;
528
529 word = strndup(w, l);
530 if (!word)
531 return log_oom();
532
533 if (startswith(word, "fstab=")) {
534 r = parse_boolean(word + 6);
535 if (r < 0)
536 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
537 else
538 arg_enabled = r;
539
540 } else if (startswith(word, "rd.fstab=")) {
541
542 if (in_initrd()) {
543 r = parse_boolean(word + 6);
544 if (r < 0)
545 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
546 else
547 arg_enabled = r;
548 }
549
550 } else if (startswith(word, "fstab.") ||
551 (in_initrd() && startswith(word, "rd.fstab."))) {
552
553 log_warning("Unknown kernel switch %s. Ignoring.", word);
554 }
555 }
556
557 return 0;
558 }
559
560 int main(int argc, char *argv[]) {
561 int r, k = 0;
562
563 if (argc > 1 && argc != 4) {
564 log_error("This program takes three or no arguments.");
565 return EXIT_FAILURE;
566 }
567
568 if (argc > 1)
569 arg_dest = argv[1];
570
571 log_set_target(LOG_TARGET_SAFE);
572 log_parse_environment();
573 log_open();
574
575 umask(0022);
576
577 if (parse_proc_cmdline() < 0)
578 return EXIT_FAILURE;
579
580 if (in_initrd())
581 k = parse_new_root_from_proc_cmdline();
582
583 if (!arg_enabled)
584 return EXIT_SUCCESS;
585
586 r = parse_fstab();
587
588 return (r < 0) || (k < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
589 }