]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/fstab-generator/fstab-generator.c
0336888b0207e63aaa4e61448f5c8aee9074e50d
[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
37 static const char *arg_dest = "/tmp";
38 static bool arg_enabled = true;
39
40 static int mount_find_pri(struct mntent *me, int *ret) {
41 char *end, *pri;
42 unsigned long r;
43
44 assert(me);
45 assert(ret);
46
47 pri = hasmntopt(me, "pri");
48 if (!pri)
49 return 0;
50
51 pri += 4;
52
53 errno = 0;
54 r = strtoul(pri, &end, 10);
55 if (errno > 0)
56 return -errno;
57
58 if (end == pri || (*end != ',' && *end != 0))
59 return -EINVAL;
60
61 *ret = (int) r;
62 return 1;
63 }
64
65 static int add_swap(const char *what, struct mntent *me) {
66 _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL;
67 _cleanup_fclose_ FILE *f = NULL;
68 bool noauto;
69 int r, pri = -1;
70
71 assert(what);
72 assert(me);
73
74 r = mount_find_pri(me, &pri);
75 if (r < 0) {
76 log_error("Failed to parse priority");
77 return pri;
78 }
79
80 noauto = !!hasmntopt(me, "noauto");
81
82 name = unit_name_from_path(what, ".swap");
83 if (!name)
84 return log_oom();
85
86 unit = strjoin(arg_dest, "/", name, NULL);
87 if (!unit)
88 return log_oom();
89
90 f = fopen(unit, "wxe");
91 if (!f) {
92 if (errno == EEXIST)
93 log_error("Failed to create swap unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
94 else
95 log_error("Failed to create unit file %s: %m", unit);
96 return -errno;
97 }
98
99 fprintf(f,
100 "# Automatically generated by systemd-fstab-generator\n\n"
101 "[Unit]\n"
102 "SourcePath=/etc/fstab\n\n"
103 "[Swap]\n"
104 "What=%s\n",
105 what);
106
107 if (pri >= 0)
108 fprintf(f,
109 "Priority=%i\n",
110 pri);
111
112 fflush(f);
113 if (ferror(f)) {
114 log_error("Failed to write unit file %s: %m", unit);
115 return -errno;
116 }
117
118 if (!noauto) {
119 lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL);
120 if (!lnk)
121 return log_oom();
122
123 mkdir_parents_label(lnk, 0755);
124 if (symlink(unit, lnk) < 0) {
125 log_error("Failed to create symlink %s: %m", lnk);
126 return -errno;
127 }
128 }
129
130 return 0;
131 }
132
133 static bool mount_is_network(struct mntent *me) {
134 assert(me);
135
136 return
137 hasmntopt(me, "_netdev") ||
138 fstype_is_network(me->mnt_type);
139 }
140
141 static bool mount_in_initrd(struct mntent *me) {
142 assert(me);
143
144 return
145 hasmntopt(me, "x-initrd.mount") ||
146 streq(me->mnt_dir, "/usr");
147 }
148
149 static int add_fsck(FILE *f, const char *what, const char *where, const char *type, int passno) {
150 assert(f);
151
152 if (passno == 0)
153 return 0;
154
155 if (!is_device_path(what)) {
156 log_warning("Checking was requested for \"%s\", but it is not a device.", what);
157 return 0;
158 }
159
160 if (type && !streq(type, "auto")) {
161 int r;
162 const char *checker;
163
164 checker = strappenda("/sbin/fsck.", type);
165 r = access(checker, X_OK);
166 if (r < 0) {
167 log_warning("Checking was requested for %s, but %s cannot be used: %m", what, checker);
168
169 /* treat missing check as essentially OK */
170 return errno == ENOENT ? 0 : -errno;
171 }
172 }
173
174 if (streq(where, "/")) {
175 char *lnk;
176
177 lnk = strappenda(arg_dest, "/" SPECIAL_LOCAL_FS_TARGET ".wants/systemd-fsck-root.service");
178 mkdir_parents_label(lnk, 0755);
179 if (symlink(SYSTEM_DATA_UNIT_PATH "/systemd-fsck-root.service", lnk) < 0) {
180 log_error("Failed to create symlink %s: %m", lnk);
181 return -errno;
182 }
183 } else {
184 _cleanup_free_ char *fsck = NULL;
185
186 fsck = unit_name_from_path_instance("systemd-fsck", what, ".service");
187 if (!fsck)
188 return log_oom();
189
190 fprintf(f,
191 "RequiresOverridable=%s\n"
192 "After=%s\n",
193 fsck,
194 fsck);
195 }
196
197 return 0;
198 }
199
200 static int add_mount(
201 const char *what,
202 const char *where,
203 const char *type,
204 const char *opts,
205 int passno,
206 bool noauto,
207 bool nofail,
208 bool automount,
209 const char *post,
210 const char *source) {
211 _cleanup_free_ char
212 *name = NULL, *unit = NULL, *lnk = NULL,
213 *automount_name = NULL, *automount_unit = NULL;
214 _cleanup_fclose_ FILE *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 source);
257
258 if (post && !noauto && !nofail && !automount)
259 fprintf(f,
260 "Before=%s\n",
261 post);
262
263 r = add_fsck(f, what, where, type, passno);
264 if (r < 0)
265 return r;
266
267 fprintf(f,
268 "\n"
269 "[Mount]\n"
270 "What=%s\n"
271 "Where=%s\n"
272 "Type=%s\n",
273 what,
274 where,
275 type);
276
277 if (!isempty(opts) &&
278 !streq(opts, "defaults"))
279 fprintf(f,
280 "Options=%s\n",
281 opts);
282
283 fflush(f);
284 if (ferror(f)) {
285 log_error("Failed to write unit file %s: %m", unit);
286 return -errno;
287 }
288
289 if (!noauto) {
290 if (post) {
291 lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL);
292 if (!lnk)
293 return log_oom();
294
295 mkdir_parents_label(lnk, 0755);
296 if (symlink(unit, lnk) < 0) {
297 log_error("Failed to create symlink %s: %m", lnk);
298 return -errno;
299 }
300 }
301 }
302
303 if (automount && !path_equal(where, "/")) {
304 automount_name = unit_name_from_path(where, ".automount");
305 if (!automount_name)
306 return log_oom();
307
308 automount_unit = strjoin(arg_dest, "/", automount_name, NULL);
309 if (!automount_unit)
310 return log_oom();
311
312 fclose(f);
313 f = fopen(automount_unit, "wxe");
314 if (!f) {
315 log_error("Failed to create unit file %s: %m", automount_unit);
316 return -errno;
317 }
318
319 fprintf(f,
320 "# Automatically generated by systemd-fstab-generator\n\n"
321 "[Unit]\n"
322 "SourcePath=%s\n",
323 source);
324
325 if (post)
326 fprintf(f,
327 "Before= %s\n",
328 post);
329
330 fprintf(f,
331 "[Automount]\n"
332 "Where=%s\n",
333 where);
334
335 fflush(f);
336 if (ferror(f)) {
337 log_error("Failed to write unit file %s: %m", automount_unit);
338 return -errno;
339 }
340
341 free(lnk);
342 lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL);
343 if (!lnk)
344 return log_oom();
345
346 mkdir_parents_label(lnk, 0755);
347 if (symlink(automount_unit, lnk) < 0) {
348 log_error("Failed to create symlink %s: %m", lnk);
349 return -errno;
350 }
351 }
352
353 return 0;
354 }
355
356 static int parse_fstab(const char *prefix, bool initrd) {
357 char *fstab_path;
358 _cleanup_endmntent_ FILE *f;
359 int r = 0;
360 struct mntent *me;
361
362 fstab_path = strappenda(strempty(prefix), "/etc/fstab");
363 f = setmntent(fstab_path, "r");
364 if (!f) {
365 if (errno == ENOENT)
366 return 0;
367
368 log_error("Failed to open %s/etc/fstab: %m", strempty(prefix));
369 return -errno;
370 }
371
372 while ((me = getmntent(f))) {
373 _cleanup_free_ char *where = NULL, *what = NULL;
374 int k;
375
376 if (initrd && !mount_in_initrd(me))
377 continue;
378
379 what = fstab_node_to_udev_node(me->mnt_fsname);
380 where = strjoin(strempty(prefix), me->mnt_dir, NULL);
381 if (!what || !where)
382 return log_oom();
383
384 if (is_path(where))
385 path_kill_slashes(where);
386
387 log_debug("Found entry what=%s where=%s type=%s", what, where, me->mnt_type);
388
389 if (streq(me->mnt_type, "swap"))
390 k = add_swap(what, me);
391 else {
392 bool noauto, nofail, automount;
393 const char *post;
394
395 noauto = !!hasmntopt(me, "noauto");
396 nofail = !!hasmntopt(me, "nofail");
397 automount =
398 hasmntopt(me, "comment=systemd.automount") ||
399 hasmntopt(me, "x-systemd.automount");
400
401 if (initrd) {
402 post = SPECIAL_INITRD_FS_TARGET;
403 } else if (mount_in_initrd(me)) {
404 post = SPECIAL_INITRD_ROOT_FS_TARGET;
405 } else if (mount_is_network(me)) {
406 post = SPECIAL_REMOTE_FS_TARGET;
407 } else {
408 post = SPECIAL_LOCAL_FS_TARGET;
409 }
410
411 k = add_mount(what, where, me->mnt_type, me->mnt_opts,
412 me->mnt_passno, noauto, nofail, automount,
413 post, fstab_path);
414 }
415
416 if (k < 0)
417 r = k;
418 }
419
420 return r;
421 }
422
423 static int parse_new_root_from_proc_cmdline(void) {
424 _cleanup_free_ char *what = NULL, *type = NULL, *opts = NULL, *line = NULL;
425 bool noauto, nofail;
426 char *w, *state;
427 size_t l;
428 int r;
429
430 r = proc_cmdline(&line);
431 if (r < 0)
432 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
433 if (r <= 0)
434 return 0;
435
436 opts = strdup("ro");
437 type = strdup("auto");
438 if (!opts || !type)
439 return log_oom();
440
441 /* root= and roofstype= may occur more than once, the last instance should take precedence.
442 * In the case of multiple rootflags= the arguments should be concatenated */
443 FOREACH_WORD_QUOTED(w, l, line, state) {
444 _cleanup_free_ char *word;
445
446 word = strndup(w, l);
447 if (!word)
448 return log_oom();
449
450 else if (startswith(word, "root=")) {
451 free(what);
452 what = fstab_node_to_udev_node(word+5);
453 if (!what)
454 return log_oom();
455
456 } else if (startswith(word, "rootfstype=")) {
457 free(type);
458 type = strdup(word + 11);
459 if (!type)
460 return log_oom();
461
462 } else if (startswith(word, "rootflags=")) {
463 char *o;
464
465 o = strjoin(opts, ",", word + 10, NULL);
466 if (!o)
467 return log_oom();
468
469 free(opts);
470 opts = o;
471
472 } else if (streq(word, "ro") || streq(word, "rw")) {
473 char *o;
474
475 o = strjoin(opts, ",", word, NULL);
476 if (!o)
477 return log_oom();
478
479 free(opts);
480 opts = o;
481 }
482 }
483
484 noauto = !!strstr(opts, "noauto");
485 nofail = !!strstr(opts, "nofail");
486
487 if (!what) {
488 log_debug("Could not find a root= entry on the kernel commandline.");
489 return 0;
490 }
491
492 if (what[0] != '/') {
493 log_debug("Skipping entry what=%s where=/sysroot type=%s", what, type);
494 return 0;
495 }
496
497 log_debug("Found entry what=%s where=/sysroot type=%s", what, type);
498 r = add_mount(what, "/sysroot", type, opts, 1, noauto, nofail, false,
499 SPECIAL_INITRD_ROOT_FS_TARGET, "/proc/cmdline");
500
501 return (r < 0) ? r : 0;
502 }
503
504 static int parse_proc_cmdline(void) {
505 _cleanup_free_ char *line = NULL;
506 char *w, *state;
507 size_t l;
508 int r;
509
510 r = proc_cmdline(&line);
511 if (r < 0)
512 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
513 if (r <= 0)
514 return 0;
515
516 FOREACH_WORD_QUOTED(w, l, line, state) {
517 _cleanup_free_ char *word = NULL;
518
519 word = strndup(w, l);
520 if (!word)
521 return log_oom();
522
523 if (startswith(word, "fstab=")) {
524 r = parse_boolean(word + 6);
525 if (r < 0)
526 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
527 else
528 arg_enabled = r;
529
530 } else if (startswith(word, "rd.fstab=")) {
531
532 if (in_initrd()) {
533 r = parse_boolean(word + 9);
534 if (r < 0)
535 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 9);
536 else
537 arg_enabled = r;
538 }
539
540 } else if (startswith(word, "fstab.") ||
541 (in_initrd() && startswith(word, "rd.fstab."))) {
542
543 log_warning("Unknown kernel switch %s. Ignoring.", word);
544 }
545 }
546
547 return 0;
548 }
549
550 int main(int argc, char *argv[]) {
551 int r = 0, k, l = 0;
552
553 if (argc > 1 && argc != 4) {
554 log_error("This program takes three or no arguments.");
555 return EXIT_FAILURE;
556 }
557
558 if (argc > 1)
559 arg_dest = argv[1];
560
561 log_set_target(LOG_TARGET_SAFE);
562 log_parse_environment();
563 log_open();
564
565 umask(0022);
566
567 if (parse_proc_cmdline() < 0)
568 return EXIT_FAILURE;
569
570 if (in_initrd())
571 r = parse_new_root_from_proc_cmdline();
572
573 if (!arg_enabled)
574 return (r < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
575
576 k = parse_fstab(NULL, false);
577
578 if (in_initrd())
579 l = parse_fstab("/sysroot", true);
580
581 return (r < 0) || (k < 0) || (l < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
582 }