Avoid possible race on NFS shares in which may that the network devices disappears before the associated NFS share becomes unmounted (bug #861489). To do this make sure that sys-subsystem-net-devices-.device used for the NFS share is added as "After=" dependency to the .mount. --- Makefile.am | 2 src/core/mount-iface.c | 173 +++++++++++++++++++++++++++++++++++++++++++++++++ src/core/mount-iface.h | 25 +++++++ src/core/mount.c | 35 +++++++++ src/shared/util.c | 1 5 files changed, 234 insertions(+), 2 deletions(-) Index: systemd-218/Makefile.am =================================================================== --- systemd-218.orig/Makefile.am +++ systemd-218/Makefile.am @@ -1134,6 +1134,8 @@ libsystemd_core_la_SOURCES = \ src/core/machine-id-setup.h \ src/core/mount-setup.c \ src/core/mount-setup.h \ + src/core/mount-iface.c \ + src/core/mount-iface.h \ src/core/kmod-setup.c \ src/core/kmod-setup.h \ src/core/loopback-setup.h \ Index: systemd-218/src/core/mount-iface.c =================================================================== --- /dev/null +++ systemd-218/src/core/mount-iface.c @@ -0,0 +1,173 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2014 Werner Fink + + 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 . +***/ + +/* + * Find the name of the network interface to which a IP address belongs to. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "def.h" +#include "mount-iface.h" + +static struct ifaddrs *ifa_list; + +_pure_ static unsigned int mask2prefix(const void* ipv6) +{ + unsigned int nippels = 0; + unsigned int i; + + assert(ipv6); + + for (i = 0; i < sizeof(struct in6_addr); i++) { + uint8_t byte = ((const uint8_t*)ipv6)[i]; + if (byte == 0xFF) { + nippels += sizeof(uint8_t); + continue; + } + while (byte & 0x80) { + nippels++; + byte <<= 1; + } + break; + } + + return nippels; +} + +static void netmask(unsigned int prefix, const void* in6, void* out6) +{ + unsigned int nippels; + unsigned int i; + + assert(in6); + assert(out6); + + for (i = 0; i < sizeof(struct in6_addr); i++) { + nippels = (prefix < sizeof(uint8_t)) ? prefix : sizeof(uint8_t); + ((uint8_t*)out6)[i] = ((const uint8_t*)in6)[i] & (0xFF00>>nippels); + prefix -= nippels; + } +} + +char *host2iface(const char *ip) +{ + const struct ifaddrs *ifa; + uint32_t ip4 = 0; + char *ret = NULL; + struct search { + union { + struct in_addr addr; + struct in6_addr addr6; + }; + int family; + } host; + int r; + + if (!ifa_list && (getifaddrs(&ifa_list) < 0)) { + log_oom(); + goto err; + } + + if (strchr(ip, ':')) { + r = inet_pton(AF_INET6, ip, &host.addr6); + host.family = AF_INET6; + } else { + r = inet_pton(AF_INET, ip, &host.addr); + host.family = AF_INET; + } + + if (r < 0) { + log_error("Failed to convert IP address %s from text to binary: %m", ip); + goto err; + } + + for (ifa = ifa_list; ifa != NULL; ifa = ifa->ifa_next) { + + if (!ifa->ifa_addr) + continue; + if (ifa->ifa_flags & IFF_POINTOPOINT) + continue; + if (!ifa->ifa_addr) + continue; + if (!ifa->ifa_netmask) + continue; + + if (ifa->ifa_addr->sa_family == AF_INET) { + uint32_t addr, dest, mask; + + if (host.family != AF_INET) + continue; + if (!ifa->ifa_broadaddr) + continue; + + if (!ip4) + ip4 = (uint32_t)ntohl(host.addr.s_addr); + + addr = (uint32_t)ntohl(((struct sockaddr_in*)ifa->ifa_addr)->sin_addr.s_addr); + if ((addr & 0xFF000000) == 0x7F000000) /* IPV4 loopback */ + continue; + + mask = (uint32_t)ntohl(((struct sockaddr_in*)ifa->ifa_netmask)->sin_addr.s_addr); + dest = (uint32_t)ntohl(((struct sockaddr_in*)ifa->ifa_broadaddr)->sin_addr.s_addr); + if ((ip4 & mask) != (dest & mask)) + continue; + + ret = ifa->ifa_name; + break; + } else if (ifa->ifa_addr->sa_family == AF_INET6) { + struct in6_addr *addr, *mask, dest, ip6; + unsigned int prefix; + + if (host.family != AF_INET6) + continue; + + addr = &((struct sockaddr_in6*)ifa->ifa_addr)->sin6_addr; + mask = &((struct sockaddr_in6*)ifa->ifa_netmask)->sin6_addr; + prefix = mask2prefix(mask); + + netmask(prefix, addr, &dest); + netmask(prefix, &host.addr6, &ip6); + + if (memcmp(&dest, &ip6, sizeof(struct in6_addr)) != 0) + continue; + + ret = ifa->ifa_name; + break; + } + } +err: + return ret; +} + +void freeroutes(void) +{ + if (ifa_list) + freeifaddrs(ifa_list); + ifa_list = NULL; +} Index: systemd-218/src/core/mount-iface.h =================================================================== --- /dev/null +++ systemd-218/src/core/mount-iface.h @@ -0,0 +1,25 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Werner Fink + + 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 . +***/ + +char *host2iface(const char *ip); +void freeroutes(void); Index: systemd-218/src/core/mount.c =================================================================== --- systemd-218.orig/src/core/mount.c +++ systemd-218/src/core/mount.c @@ -38,6 +38,7 @@ #include "mkdir.h" #include "path-util.h" #include "mount-setup.h" +#include "mount-iface.h" #include "unit-name.h" #include "dbus-mount.h" #include "special.h" @@ -1365,8 +1366,9 @@ static int mount_add_one( _cleanup_free_ char *e = NULL, *w = NULL, *o = NULL, *f = NULL; bool load_extras = false; MountParameters *p; - bool delete, changed = false; + bool delete, changed = false, isnetwork; Unit *u; + char *c; int r; assert(m); @@ -1391,6 +1393,8 @@ static int mount_add_one( if (!e) return -ENOMEM; + isnetwork = fstype_is_network(fstype); + u = manager_get_unit(m, e); if (!u) { delete = true; @@ -1419,7 +1423,7 @@ static int mount_add_one( if (m->running_as == SYSTEMD_SYSTEM) { const char* target; - target = mount_needs_network(options, fstype) ? SPECIAL_REMOTE_FS_TARGET : SPECIAL_LOCAL_FS_TARGET; + target = isnetwork ? SPECIAL_REMOTE_FS_TARGET : SPECIAL_LOCAL_FS_TARGET; r = unit_add_dependency_by_name(u, UNIT_BEFORE, target, NULL, true); if (r < 0) goto fail; @@ -1505,6 +1509,32 @@ static int mount_add_one( goto fail; } + if (isnetwork && (c = strrchr(p->what, ':')) && *(c+1) == '/') { + _cleanup_free_ char *opt = strdup(p->options); + char *addr; + + if (opt && (addr = strstr(opt, ",addr="))) { + char *colon, *iface; + + addr += 6; + if ((colon = strchr(addr, ','))) + *colon = '\0'; + + iface = host2iface(addr); + if (iface) { + _cleanup_free_ char* target = NULL; + if (asprintf(&target, "sys-subsystem-net-devices-%s.device", iface) < 0) + log_oom(); + else { + r = unit_add_dependency_by_name(u, UNIT_AFTER, target, NULL, true); + if (r < 0) + log_unit_error(u->id, "Failed to add dependency on %s, ignoring: %s", + target, strerror(-r)); + } + } + } + } + if (changed) unit_add_to_dbus_queue(u); @@ -1560,6 +1590,7 @@ static int mount_load_proc_self_mountinf if (r == 0 && k < 0) r = k; } + freeroutes(); /* Just in case of using the routing table with host2iface() */ return r; } Index: systemd-218/src/shared/util.c =================================================================== --- systemd-218.orig/src/shared/util.c +++ systemd-218/src/shared/util.c @@ -1667,6 +1667,7 @@ bool fstype_is_network(const char *fstyp "ncp\0" "nfs\0" "nfs4\0" + "afs\0" "gfs\0" "gfs2\0" "glusterfs\0";