]> git.proxmox.com Git - systemd.git/blob - src/fstab-generator/fstab-generator.c
9efccb983de752d14f1b310829dce33341524941
[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 _cleanup_free_ char *fstab_path = NULL;
305 FILE *f;
306 int r = 0;
307 struct mntent *me;
308
309 fstab_path = strjoin(strempty(prefix), "/etc/fstab", NULL);
310 if (!fstab_path)
311 return log_oom();
312
313 f = setmntent(fstab_path, "r");
314 if (!f) {
315 if (errno == ENOENT)
316 return 0;
317
318 log_error("Failed to open %s/etc/fstab: %m", strempty(prefix));
319 return -errno;
320 }
321
322 while ((me = getmntent(f))) {
323 _cleanup_free_ char *where = NULL, *what = NULL;
324 int k;
325
326 if (initrd && !mount_in_initrd(me))
327 continue;
328
329 what = fstab_node_to_udev_node(me->mnt_fsname);
330 where = strjoin(strempty(prefix), me->mnt_dir, NULL);
331 if (!what || !where) {
332 r = log_oom();
333 goto finish;
334 }
335
336 if (is_path(where))
337 path_kill_slashes(where);
338
339 log_debug("Found entry what=%s where=%s type=%s", what, where, me->mnt_type);
340
341 if (streq(me->mnt_type, "swap"))
342 k = add_swap(what, me);
343 else {
344 bool noauto, nofail, automount;
345 const char *post;
346
347 noauto = !!hasmntopt(me, "noauto");
348 nofail = !!hasmntopt(me, "nofail");
349 automount =
350 hasmntopt(me, "comment=systemd.automount") ||
351 hasmntopt(me, "x-systemd.automount");
352
353 if (initrd) {
354 post = SPECIAL_INITRD_FS_TARGET;
355 } else if (mount_in_initrd(me)) {
356 post = SPECIAL_INITRD_ROOT_FS_TARGET;
357 } else if (mount_is_network(me)) {
358 post = SPECIAL_REMOTE_FS_TARGET;
359 } else {
360 post = SPECIAL_LOCAL_FS_TARGET;
361 }
362
363 k = add_mount(what, where, me->mnt_type, me->mnt_opts,
364 me->mnt_passno, noauto, nofail, automount,
365 post, fstab_path);
366 }
367
368 if (k < 0)
369 r = k;
370 }
371
372 finish:
373 endmntent(f);
374 return r;
375 }
376
377 static int parse_new_root_from_proc_cmdline(void) {
378 _cleanup_free_ char *what = NULL, *type = NULL, *opts = NULL, *line = NULL;
379 char *w, *state;
380 int r;
381 size_t l;
382 bool noauto, nofail;
383
384 r = read_one_line_file("/proc/cmdline", &line);
385 if (r < 0) {
386 log_error("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
387 return 0;
388 }
389
390 opts = strdup("ro");
391 type = strdup("auto");
392 if (!opts || !type)
393 return log_oom();
394
395 /* root= and roofstype= may occur more than once, the last instance should take precedence.
396 * In the case of multiple rootflags= the arguments should be concatenated */
397 FOREACH_WORD_QUOTED(w, l, line, state) {
398 _cleanup_free_ char *word;
399
400 word = strndup(w, l);
401 if (!word)
402 return log_oom();
403
404 else if (startswith(word, "root=")) {
405 free(what);
406 what = fstab_node_to_udev_node(word+5);
407 if (!what)
408 return log_oom();
409
410 } else if (startswith(word, "rootfstype=")) {
411 free(type);
412 type = strdup(word + 11);
413 if (!type)
414 return log_oom();
415
416 } else if (startswith(word, "rootflags=")) {
417 char *o;
418
419 o = strjoin(opts, ",", word + 10, NULL);
420 if (!o)
421 return log_oom();
422
423 free(opts);
424 opts = o;
425
426 } else if (streq(word, "ro") || streq(word, "rw")) {
427 char *o;
428
429 o = strjoin(opts, ",", word, NULL);
430 if (!o)
431 return log_oom();
432
433 free(opts);
434 opts = o;
435 }
436 }
437
438 noauto = !!strstr(opts, "noauto");
439 nofail = !!strstr(opts, "nofail");
440
441 if (!what) {
442 log_debug("Could not find a root= entry on the kernel commandline.");
443 return 0;
444 }
445
446 if (what[0] != '/') {
447 log_debug("Skipping entry what=%s where=/sysroot type=%s", what, type);
448 return 0;
449 }
450
451 log_debug("Found entry what=%s where=/sysroot type=%s", what, type);
452 r = add_mount(what, "/sysroot", type, opts, 0, noauto, nofail, false,
453 SPECIAL_INITRD_ROOT_FS_TARGET, "/proc/cmdline");
454
455 return (r < 0) ? r : 0;
456 }
457
458 static int parse_proc_cmdline(void) {
459 _cleanup_free_ char *line = NULL;
460 char *w, *state;
461 int r;
462 size_t l;
463
464 if (detect_container(NULL) > 0)
465 return 0;
466
467 r = read_one_line_file("/proc/cmdline", &line);
468 if (r < 0) {
469 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
470 return 0;
471 }
472
473 FOREACH_WORD_QUOTED(w, l, line, state) {
474 _cleanup_free_ char *word = NULL;
475
476 word = strndup(w, l);
477 if (!word)
478 return log_oom();
479
480 if (startswith(word, "fstab=")) {
481 r = parse_boolean(word + 6);
482 if (r < 0)
483 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
484 else
485 arg_enabled = r;
486
487 } else if (startswith(word, "rd.fstab=")) {
488
489 if (in_initrd()) {
490 r = parse_boolean(word + 9);
491 if (r < 0)
492 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 9);
493 else
494 arg_enabled = r;
495 }
496
497 } else if (startswith(word, "fstab.") ||
498 (in_initrd() && startswith(word, "rd.fstab."))) {
499
500 log_warning("Unknown kernel switch %s. Ignoring.", word);
501 }
502 }
503
504 return 0;
505 }
506
507 int main(int argc, char *argv[]) {
508 int r = 0, k, l = 0;
509
510 if (argc > 1 && argc != 4) {
511 log_error("This program takes three or no arguments.");
512 return EXIT_FAILURE;
513 }
514
515 if (argc > 1)
516 arg_dest = argv[1];
517
518 log_set_target(LOG_TARGET_SAFE);
519 log_parse_environment();
520 log_open();
521
522 umask(0022);
523
524 if (parse_proc_cmdline() < 0)
525 return EXIT_FAILURE;
526
527 if (in_initrd())
528 r = parse_new_root_from_proc_cmdline();
529
530 if (!arg_enabled)
531 return (r < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
532
533 k = parse_fstab(NULL, false);
534
535 if (in_initrd())
536 l = parse_fstab("/sysroot", true);
537
538 return (r < 0) || (k < 0) || (l < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
539 }