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