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