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