Systemd/src/fstab-generator/fstab-generator.c
Tom Gundersen 67ab5f761f fstab-generator: make error more helpful in case of duplicates in fstab
Traditional sysvinit systems would not complain about duplicates in
fstab. Rather it (through monut -a) would mount one fs on top of another,
in effect the last entry taking precedent.

In systemd, the first entry takes precedent, all subsequent ones are
ignored and an error is printed.

The change of behavior and the source of this error message was causing
some confusion, so give a hint what migt be wrong.
2012-11-27 01:57:57 +01:00

559 lines
16 KiB
C

/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2012 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdio.h>
#include <mntent.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include "log.h"
#include "util.h"
#include "unit-name.h"
#include "path-util.h"
#include "mount-setup.h"
#include "special.h"
#include "mkdir.h"
#include "virt.h"
static const char *arg_dest = "/tmp";
static bool arg_enabled = true;
static int device_name(const char *path, char **unit) {
char *p;
assert(path);
if (!is_device_path(path))
return 0;
p = unit_name_from_path(path, ".device");
if (!p)
return log_oom();
*unit = p;
return 1;
}
static int mount_find_pri(struct mntent *me, int *ret) {
char *end, *pri;
unsigned long r;
assert(me);
assert(ret);
pri = hasmntopt(me, "pri");
if (!pri)
return 0;
pri += 4;
errno = 0;
r = strtoul(pri, &end, 10);
if (errno != 0)
return -errno;
if (end == pri || (*end != ',' && *end != 0))
return -EINVAL;
*ret = (int) r;
return 1;
}
static int add_swap(const char *what, struct mntent *me) {
char *name = NULL, *unit = NULL, *lnk = NULL, *device = NULL;
FILE *f = NULL;
bool noauto, nofail;
int r, pri = -1;
assert(what);
assert(me);
r = mount_find_pri(me, &pri);
if (r < 0) {
log_error("Failed to parse priority");
return pri;
}
noauto = !!hasmntopt(me, "noauto");
nofail = !!hasmntopt(me, "nofail");
name = unit_name_from_path(what, ".swap");
if (!name) {
r = log_oom();
goto finish;
}
unit = strjoin(arg_dest, "/", name, NULL);
if (!unit) {
r = log_oom();
goto finish;
}
f = fopen(unit, "wxe");
if (!f) {
r = -errno;
if (errno == EEXIST)
log_error("Failed to create swap unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
else
log_error("Failed to create unit file %s: %m", unit);
goto finish;
}
fputs("# Automatically generated by systemd-fstab-generator\n\n"
"[Unit]\n"
"SourcePath=/etc/fstab\n"
"DefaultDependencies=no\n"
"Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
"Before=" SPECIAL_UMOUNT_TARGET "\n", f);
if (!noauto && !nofail)
fputs("Before=" SPECIAL_SWAP_TARGET "\n", f);
fprintf(f,
"\n"
"[Swap]\n"
"What=%s\n",
what);
if (pri >= 0)
fprintf(f,
"Priority=%i\n",
pri);
fflush(f);
if (ferror(f)) {
log_error("Failed to write unit file %s: %m", unit);
r = -errno;
goto finish;
}
if (!noauto) {
lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL);
if (!lnk) {
r = log_oom();
goto finish;
}
mkdir_parents_label(lnk, 0755);
if (symlink(unit, lnk) < 0) {
log_error("Failed to create symlink %s: %m", lnk);
r = -errno;
goto finish;
}
r = device_name(what, &device);
if (r < 0)
goto finish;
if (r > 0) {
free(lnk);
lnk = strjoin(arg_dest, "/", device, ".wants/", name, NULL);
if (!lnk) {
r = log_oom();
goto finish;
}
mkdir_parents_label(lnk, 0755);
if (symlink(unit, lnk) < 0) {
log_error("Failed to create symlink %s: %m", lnk);
r = -errno;
goto finish;
}
}
}
r = 0;
finish:
if (f)
fclose(f);
free(unit);
free(lnk);
free(name);
free(device);
return r;
}
static bool mount_is_network(struct mntent *me) {
assert(me);
return
hasmntopt(me, "_netdev") ||
fstype_is_network(me->mnt_type);
}
static int add_mount(const char *what, const char *where, struct mntent *me) {
char *name = NULL, *unit = NULL, *lnk = NULL, *device = NULL, *automount_name = NULL, *automount_unit = NULL;
FILE *f = NULL;
bool noauto, nofail, automount, isbind, isnetwork;
int r;
const char *post, *pre;
assert(what);
assert(where);
assert(me);
if (streq(me->mnt_type, "autofs"))
return 0;
if (!is_path(where)) {
log_warning("Mount point %s is not a valid path, ignoring.", where);
return 0;
}
if (mount_point_is_api(where) ||
mount_point_ignore(where))
return 0;
isnetwork = mount_is_network(me);
isbind = !!hasmntopt(me, "bind");
noauto = !!hasmntopt(me, "noauto");
nofail = !!hasmntopt(me, "nofail");
automount =
hasmntopt(me, "comment=systemd.automount") ||
hasmntopt(me, "x-systemd.automount");
if (isnetwork) {
post = SPECIAL_REMOTE_FS_TARGET;
pre = SPECIAL_REMOTE_FS_PRE_TARGET;
} else {
post = SPECIAL_LOCAL_FS_TARGET;
pre = SPECIAL_LOCAL_FS_PRE_TARGET;
}
name = unit_name_from_path(where, ".mount");
if (!name) {
r = log_oom();
goto finish;
}
unit = strjoin(arg_dest, "/", name, NULL);
if (!unit) {
r = log_oom();
goto finish;
}
f = fopen(unit, "wxe");
if (!f) {
r = -errno;
if (errno == EEXIST)
log_error("Failed to create mount unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
else
log_error("Failed to create unit file %s: %m", unit);
goto finish;
}
fputs("# Automatically generated by systemd-fstab-generator\n\n"
"[Unit]\n"
"SourcePath=/etc/fstab\n"
"DefaultDependencies=no\n", f);
if (!path_equal(where, "/"))
fprintf(f,
"After=%s\n"
"Wants=%s\n"
"Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
"Before=" SPECIAL_UMOUNT_TARGET "\n",
pre,
pre);
if (!noauto && !nofail && !automount)
fprintf(f,
"Before=%s\n",
post);
fprintf(f,
"\n"
"[Mount]\n"
"What=%s\n"
"Where=%s\n"
"Type=%s\n"
"FsckPassNo=%i\n",
what,
where,
me->mnt_type,
me->mnt_passno);
if (!isempty(me->mnt_opts) &&
!streq(me->mnt_opts, "defaults"))
fprintf(f,
"Options=%s\n",
me->mnt_opts);
fflush(f);
if (ferror(f)) {
log_error("Failed to write unit file %s: %m", unit);
r = -errno;
goto finish;
}
if (!noauto) {
lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL);
if (!lnk) {
r = log_oom();
goto finish;
}
mkdir_parents_label(lnk, 0755);
if (symlink(unit, lnk) < 0) {
log_error("Failed to create symlink %s: %m", lnk);
r = -errno;
goto finish;
}
if (!isbind &&
!path_equal(where, "/")) {
r = device_name(what, &device);
if (r < 0)
goto finish;
if (r > 0) {
free(lnk);
lnk = strjoin(arg_dest, "/", device, ".wants/", name, NULL);
if (!lnk) {
r = log_oom();
goto finish;
}
mkdir_parents_label(lnk, 0755);
if (symlink(unit, lnk) < 0) {
log_error("Failed to create symlink %s: %m", lnk);
r = -errno;
goto finish;
}
}
}
}
if (automount && !path_equal(where, "/")) {
automount_name = unit_name_from_path(where, ".automount");
if (!name) {
r = log_oom();
goto finish;
}
automount_unit = strjoin(arg_dest, "/", automount_name, NULL);
if (!automount_unit) {
r = log_oom();
goto finish;
}
fclose(f);
f = fopen(automount_unit, "wxe");
if (!f) {
r = -errno;
log_error("Failed to create unit file %s: %m", automount_unit);
goto finish;
}
fprintf(f,
"# Automatically generated by systemd-fstab-generator\n\n"
"[Unit]\n"
"SourcePath=/etc/fstab\n"
"DefaultDependencies=no\n"
"Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
"Before=" SPECIAL_UMOUNT_TARGET " %s\n"
"\n"
"[Automount]\n"
"Where=%s\n",
post,
where);
fflush(f);
if (ferror(f)) {
log_error("Failed to write unit file %s: %m", automount_unit);
r = -errno;
goto finish;
}
free(lnk);
lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL);
if (!lnk) {
r = log_oom();
goto finish;
}
mkdir_parents_label(lnk, 0755);
if (symlink(automount_unit, lnk) < 0) {
log_error("Failed to create symlink %s: %m", lnk);
r = -errno;
goto finish;
}
}
r = 0;
finish:
if (f)
fclose(f);
free(unit);
free(lnk);
free(name);
free(device);
free(automount_name);
free(automount_unit);
return r;
}
static int parse_fstab(void) {
FILE *f;
int r = 0;
struct mntent *me;
errno = 0;
f = setmntent("/etc/fstab", "r");
if (!f) {
if (errno == ENOENT)
return 0;
log_error("Failed to open /etc/fstab: %m");
return -errno;
}
while ((me = getmntent(f))) {
char *where, *what;
int k;
what = fstab_node_to_udev_node(me->mnt_fsname);
if (!what) {
r = log_oom();
goto finish;
}
where = strdup(me->mnt_dir);
if (!where) {
r = log_oom();
free(what);
goto finish;
}
if (is_path(where))
path_kill_slashes(where);
log_debug("Found entry what=%s where=%s type=%s", what, where, me->mnt_type);
if (streq(me->mnt_type, "swap"))
k = add_swap(what, me);
else
k = add_mount(what, where, me);
free(what);
free(where);
if (k < 0)
r = k;
}
finish:
endmntent(f);
return r;
}
static int parse_proc_cmdline(void) {
char *line, *w, *state;
int r;
size_t l;
if (detect_container(NULL) > 0)
return 0;
r = read_one_line_file("/proc/cmdline", &line);
if (r < 0) {
log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
return 0;
}
FOREACH_WORD_QUOTED(w, l, line, state) {
char *word;
word = strndup(w, l);
if (!word) {
r = log_oom();
goto finish;
}
if (startswith(word, "fstab=")) {
r = parse_boolean(word + 6);
if (r < 0)
log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
else
arg_enabled = r;
} else if (startswith(word, "rd.fstab=")) {
if (in_initrd()) {
r = parse_boolean(word + 6);
if (r < 0)
log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
else
arg_enabled = r;
}
} else if (startswith(word, "fstab.") ||
(in_initrd() && startswith(word, "rd.fstab."))) {
log_warning("Unknown kernel switch %s. Ignoring.", word);
}
free(word);
}
r = 0;
finish:
free(line);
return r;
}
int main(int argc, char *argv[]) {
int r;
if (argc > 1 && argc != 4) {
log_error("This program takes three or no arguments.");
return EXIT_FAILURE;
}
if (argc > 1)
arg_dest = argv[1];
log_set_target(LOG_TARGET_SAFE);
log_parse_environment();
log_open();
umask(0022);
if (parse_proc_cmdline() < 0)
return EXIT_FAILURE;
if (!arg_enabled)
return EXIT_SUCCESS;
r = parse_fstab();
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}