]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/fstab-generator/fstab-generator.c
fstab-generator: add missing OOM check
[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 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 _cleanup_free_ char *fstab_path = NULL;
393 FILE *f;
394 int r = 0;
395 struct mntent *me;
396
397 fstab_path = strjoin(strempty(prefix), "/etc/fstab", NULL);
398 if (!fstab_path)
399 return log_oom();
400
401 f = setmntent(fstab_path, "r");
402 if (!f) {
403 if (errno == ENOENT)
404 return 0;
405
406 log_error("Failed to open %s/etc/fstab: %m", prefix);
407 return -errno;
408 }
409
410 while ((me = getmntent(f))) {
411 char _cleanup_free_ *where = NULL, *what = NULL;
412 int k;
413
414 if (initrd && !mount_in_initrd(me))
415 continue;
416
417 what = fstab_node_to_udev_node(me->mnt_fsname);
418 where = strjoin(prefix, me->mnt_dir, NULL);
419 if (!what || !where) {
420 r = log_oom();
421 goto finish;
422 }
423
424 if (is_path(where))
425 path_kill_slashes(where);
426
427 log_debug("Found entry what=%s where=%s type=%s", what, where, me->mnt_type);
428
429 if (streq(me->mnt_type, "swap"))
430 k = add_swap(what, me);
431 else {
432 bool noauto, nofail, automount, isbind;
433 const char *pre, *post;
434
435 noauto = !!hasmntopt(me, "noauto");
436 nofail = !!hasmntopt(me, "nofail");
437 automount =
438 hasmntopt(me, "comment=systemd.automount") ||
439 hasmntopt(me, "x-systemd.automount");
440 isbind = mount_is_bind(me);
441
442 if (initrd) {
443 post = SPECIAL_INITRD_FS_TARGET;
444 pre = NULL;
445 } else if (mount_is_rootfs(me)) {
446 post = SPECIAL_INITRD_ROOT_FS_TARGET;
447 pre = NULL;
448 } else if (mount_is_network(me)) {
449 post = SPECIAL_REMOTE_FS_TARGET;
450 pre = SPECIAL_REMOTE_FS_PRE_TARGET;
451 } else {
452 post = SPECIAL_LOCAL_FS_TARGET;
453 pre = SPECIAL_LOCAL_FS_PRE_TARGET;
454 }
455
456 k = add_mount(what, where, me->mnt_type, me->mnt_opts,
457 me->mnt_passno, noauto, nofail, automount,
458 isbind, pre, post, fstab_path);
459 }
460
461 if (k < 0)
462 r = k;
463 }
464
465 finish:
466 endmntent(f);
467 return r;
468 }
469
470 static int parse_new_root_from_proc_cmdline(void) {
471 char *w, *state;
472 _cleanup_free_ char *what = NULL, *type = NULL, *opts = NULL, *line = NULL;
473 int r;
474 size_t l;
475
476 r = read_one_line_file("/proc/cmdline", &line);
477 if (r < 0) {
478 log_error("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
479 return 0;
480 }
481
482 opts = strdup("ro");
483 type = strdup("auto");
484 if (!opts || !type)
485 return log_oom();
486
487 /* root= and roofstype= may occur more than once, the last instance should take precedence.
488 * In the case of multiple rootflags= the arguments should be concatenated */
489 FOREACH_WORD_QUOTED(w, l, line, state) {
490 char *word, *tmp_word;
491
492 word = strndup(w, l);
493 if (!word)
494 return log_oom();
495
496 else if (startswith(word, "root=")) {
497 free(what);
498 what = fstab_node_to_udev_node(word+5);
499 if (!what)
500 return log_oom();
501
502 } else if (startswith(word, "rootfstype=")) {
503 free(type);
504 type = strdup(word + 11);
505 if (!type)
506 return log_oom();
507
508 } else if (startswith(word, "rootflags=")) {
509 tmp_word = opts;
510 opts = strjoin(opts, ",", word + 10, NULL);
511 free(tmp_word);
512 if (!opts)
513 return log_oom();
514
515 } else if (streq(word, "ro") || streq(word, "rw")) {
516 tmp_word = opts;
517 opts = strjoin(opts, ",", word, NULL);
518 free(tmp_word);
519 if (!opts)
520 return log_oom();
521
522 }
523
524 free(word);
525 }
526
527 if (!what) {
528 log_error("Could not find a root= entry on the kernel commandline.");
529 return 0;
530 }
531
532 if (what[0] != '/') {
533 log_debug("Skipping entry what=%s where=/sysroot type=%s", what, type);
534 return 0;
535 }
536
537 log_debug("Found entry what=%s where=/sysroot type=%s", what, type);
538 r = add_mount(what, "/sysroot", type, opts, 0, false, false, false,
539 false, NULL, SPECIAL_INITRD_ROOT_FS_TARGET, "/proc/cmdline");
540
541 return (r < 0) ? r : 0;
542 }
543
544 static int parse_proc_cmdline(void) {
545 char _cleanup_free_ *line = NULL;
546 char *w, *state;
547 int r;
548 size_t l;
549
550 if (detect_container(NULL) > 0)
551 return 0;
552
553 r = read_one_line_file("/proc/cmdline", &line);
554 if (r < 0) {
555 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
556 return 0;
557 }
558
559 FOREACH_WORD_QUOTED(w, l, line, state) {
560 char _cleanup_free_ *word = NULL;
561
562 word = strndup(w, l);
563 if (!word)
564 return log_oom();
565
566 if (startswith(word, "fstab=")) {
567 r = parse_boolean(word + 6);
568 if (r < 0)
569 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
570 else
571 arg_enabled = r;
572
573 } else if (startswith(word, "rd.fstab=")) {
574
575 if (in_initrd()) {
576 r = parse_boolean(word + 6);
577 if (r < 0)
578 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
579 else
580 arg_enabled = r;
581 }
582
583 } else if (startswith(word, "fstab.") ||
584 (in_initrd() && startswith(word, "rd.fstab."))) {
585
586 log_warning("Unknown kernel switch %s. Ignoring.", word);
587 }
588 }
589
590 return 0;
591 }
592
593 int main(int argc, char *argv[]) {
594 int r = 0, k, l = 0;
595
596 if (argc > 1 && argc != 4) {
597 log_error("This program takes three or no arguments.");
598 return EXIT_FAILURE;
599 }
600
601 if (argc > 1)
602 arg_dest = argv[1];
603
604 log_set_target(LOG_TARGET_SAFE);
605 log_parse_environment();
606 log_open();
607
608 umask(0022);
609
610 if (parse_proc_cmdline() < 0)
611 return EXIT_FAILURE;
612
613 if (in_initrd())
614 r = parse_new_root_from_proc_cmdline();
615
616 if (!arg_enabled)
617 return (r < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
618
619 k = parse_fstab(NULL, false);
620
621 if (in_initrd())
622 l = parse_fstab("/sysroot", true);
623
624 return (r < 0) || (k < 0) || (l < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
625 }