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