1
0
mirror of https://github.com/systemd/systemd synced 2026-04-25 16:34:50 +02:00

Compare commits

..

No commits in common. "8dc3c2f197370e2a63dce6ab03d48eba85c4ffdf" and "bab34bc12e8f3802516497cee2b419b197e728ab" have entirely different histories.

16 changed files with 151 additions and 217 deletions

View File

@ -21,7 +21,7 @@ UBUNTU_RELEASE="$(lsb_release -cs)"
create_container() {
# Create autopkgtest LXC image; this sometimes fails with "Unable to fetch
# GPG key from keyserver", so retry a few times with different keyservers.
for keyserver in "keys.openpgp.org" "" "keyserver.ubuntu.com" "keys.gnupg.net"; do
for keyserver in "" "keys.gnupg.net" "keys.openpgp.org" "keyserver.ubuntu.com"; do
for retry in {1..5}; do
sudo lxc-create -n "$CONTAINER" -t download -- -d "$DISTRO" -r "$RELEASE" -a "$ARCH" ${keyserver:+--keyserver "$keyserver"} && break 2
sleep $((retry*retry))
@ -36,16 +36,8 @@ create_container() {
# enable source repositories so that apt-get build-dep works
sudo lxc-attach -n "$CONTAINER" -- sh -ex <<EOF
sed 's/^deb/deb-src/' /etc/apt/sources.list >> /etc/apt/sources.list.d/sources.list
# We might attach the console too soon
while ! systemctl --quiet --wait is-system-running; do sleep 1; done
# Manpages database trigger takes a lot of time and is not useful in a CI
echo 'man-db man-db/auto-update boolean false' | debconf-set-selections
# Speed up dpkg, image is thrown away after the test
mkdir -p /etc/dpkg/dpkg.cfg.d/
echo 'force-unsafe-io' > /etc/dpkg/dpkg.cfg.d/unsafe_io
# For some reason, it is necessary to run this manually or the interface won't be configured
# Note that we avoid networkd, as some of the tests will break it later on
dhclient
# wait until online
while [ -z "\$(ip route list 0/0)" ]; do sleep 1; done
apt-get -q --allow-releaseinfo-change update
apt-get -y dist-upgrade
apt-get install -y eatmydata
@ -96,10 +88,6 @@ EOF
rm -rf debian/patches
# disable autopkgtests which are not for upstream
sed -i '/# NOUPSTREAM/ q' debian/tests/control
# TODO: rebooting via autopkgtest-reboot seems to be broken, disable these tests for now
sed -i -n '1,/Tests: boot-and-services/p;/Tests: udev/,$p' debian/tests/control
sed -i '/Tests: boot-and-services/d' debian/tests/control
sed -i '/Tests: boot-smoke/,$d' debian/tests/control
# enable more unit tests
sed -i '/^CONFFLAGS =/ s/=/= --werror -Dtests=unsafe -Dsplit-usr=true -Dslow-tests=true -Dfuzz-tests=true -Dman=true /' debian/rules
# no orig tarball

View File

@ -7,7 +7,7 @@ name: Debian autopkgtest (LXC)
agent:
machine:
type: e1-standard-2
os_image: ubuntu2004
os_image: ubuntu1804
# Cancel any running or queued job for the same ref
auto_cancel:

56
README
View File

@ -30,21 +30,14 @@ LICENSE:
LGPL-2.1-or-later for all code, exceptions noted in LICENSES/README.md
REQUIREMENTS:
Linux kernel ≥ 3.15
≥ 4.5 for pids controller in cgroup v2
≥ 4.6 for cgroup namespaces
≥ 4.9 for RENAME_NOREPLACE support in vfat
≥ 4.10 for cgroup-bpf egress and ingress hooks
≥ 4.15 for cgroup-bpf device hook and cpu controller in cgroup v2
≥ 4.17 for cgroup-bpf socket address hooks
≥ 5.3 for bounded loops in BPF program
≥ 5.4 for signed Verity images
≥ 5.7 for BPF links and the BPF LSM hook
Kernel versions below 4.15 have significant gaps in functionality and
are not recommended for use with this version of systemd. Taint flag
'old-kernel' will be set. Systemd will most likely still function, but
upstream support and testing are limited.
Linux kernel >= 3.15
Linux kernel >= 4.2 for unified cgroup hierarchy support
Linux kernel >= 4.10 for cgroup-bpf egress and ingress hooks
Linux kernel >= 4.15 for cgroup-bpf device hook
Linux kernel >= 4.17 for cgroup-bpf socket address hooks
Linux kernel >= 5.3 for bounded-loops in BPF program
Linux kernel >= 5.4 for signed Verity images support
Linux kernel >= 5.7 for BPF links and the BPF LSM hook
Kernel Config Options:
CONFIG_DEVTMPFS
@ -337,41 +330,24 @@ SYSV INIT.D SCRIPTS:
Please see src/systemctl/systemd-sysv-install.SKELETON for how this
needs to look like, and provide an implementation at the marked places.
WARNINGS and TAINT FLAGS:
WARNINGS:
systemd will warn during early boot if /usr is not already mounted at
this point (that means: either located on the same file system as / or
already mounted in the initrd). While in systemd itself very little
will break if /usr is on a separate late-mounted partition, many of its
dependencies very likely will break sooner or later in one form or
will break if /usr is on a separate, late-mounted partition, many of
its dependencies very likely will break sooner or later in one form or
another. For example, udev rules tend to refer to binaries in /usr,
binaries that link to libraries in /usr or binaries that refer to data
files in /usr. Since these breakages are not always directly visible,
systemd will warn about this. Such setups are not really supported by
the basic set of Linux OS components. Taint flag 'split-usr' will be
set when this condition is detected.
systemd will warn about this, since this kind of file system setup is
not really supported anymore by the basic set of Linux OS components.
systemd requires that the /run mount point exists. systemd also
requires that /var/run is a symlink to /run.
For more information on this issue consult
https://www.freedesktop.org/wiki/Software/systemd/separate-usr-is-broken
systemd requires that the /run mount point exists. systemd also
requires that /var/run is a symlink to /run. Taint flag 'var-run-bad'
will be set when this condition is detected.
Systemd will also warn when the cgroup support is unavailable in the
kernel (taint flag 'cgroups-missing'), the system is using the old
cgroup hierarchy (taint flag 'cgroupsv1'), the hardware clock is
running in non-UTC mode (taint flag 'local-hwclock'), the kernel
overflow UID or GID are not 65534 (taint flags 'overflowuid-not-65534'
and 'overflowgid-not-65534'), the UID or GID range assigned to the
running systemd instance covers less than 0…65534 (taint flags
'short-uid-range' and 'short-gid-range').
Taint conditions are logged during boot, but may also be checked at any
time with:
busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager Tainted
VALGRIND:
To run systemd under valgrind, compile with meson option
-Dvalgrind=true and have valgrind development headers installed
(i.e. valgrind-devel or equivalent). Otherwise, false positives will be

View File

@ -403,18 +403,6 @@
</listitem>
</varlistentry>
<varlistentry>
<term><constant>v251</constant></term>
<listitem><para>Since version <constant>v247</constant> we no longer set
<varname>ID_NET_NAME_SLOT</varname> if we detect that a PCI device associated with a slot is a PCI
bridge as that would create naming conflict when there are more child devices on that bridge. Now,
this is relaxed and we will use slot information to generate the name based on it but only if
the PCI device has multiple functions. This is safe because distinct function number is a part of
the device name for multifunction devices.</para>
</listitem>
</varlistentry>
</variablelist>
<para>Note that <constant>latest</constant> may be used to denote the latest scheme known (to this

View File

@ -74,4 +74,4 @@
/* Path where systemd-oomd listens for varlink connections from user managers to report changes in ManagedOOM settings. */
#define VARLINK_ADDR_PATH_MANAGED_OOM_USER "/run/systemd/oom/io.system.ManagedOOM"
#define KERNEL_BASELINE_VERSION "4.15"
#define KERNEL_BASELINE_VERSION "3.15"

View File

@ -4384,7 +4384,7 @@ char *manager_taint_string(Manager *m) {
buf = new(char, sizeof("split-usr:"
"cgroups-missing:"
"cgroupsv1:"
"cgrousv1:"
"local-hwclock:"
"var-run-bad:"
"overflowuid-not-65534:"

View File

@ -23,7 +23,6 @@ static const NamingScheme naming_schemes[] = {
{ "v247", NAMING_V247 },
{ "v249", NAMING_V249 },
{ "v250", NAMING_V250 },
{ "v251", NAMING_V251 },
/* … add more schemes here, as the logic to name devices is updated … */
EXTRA_NET_NAMING_MAP

View File

@ -22,21 +22,20 @@
* OS versions, but not fully stabilize them. */
typedef enum NamingSchemeFlags {
/* First, the individual features */
NAMING_SR_IOV_V = 1 << 0, /* Use "v" suffix for SR-IOV, see 609948c7043a */
NAMING_NPAR_ARI = 1 << 1, /* Use NPAR "ARI", see 6bc04997b6ea */
NAMING_INFINIBAND = 1 << 2, /* Use "ib" prefix for infiniband, see 938d30aa98df */
NAMING_ZERO_ACPI_INDEX = 1 << 3, /* Use zero acpi_index field, see d81186ef4f6a */
NAMING_ALLOW_RERENAMES = 1 << 4, /* Allow re-renaming of devices, see #9006 */
NAMING_STABLE_VIRTUAL_MACS = 1 << 5, /* Use device name to generate MAC, see 6d3646406560 */
NAMING_NETDEVSIM = 1 << 6, /* Generate names for netdevsim devices, see eaa9d507d855 */
NAMING_LABEL_NOPREFIX = 1 << 7, /* Don't prepend ID_NET_LABEL_ONBOARD with interface type prefix */
NAMING_NSPAWN_LONG_HASH = 1 << 8, /* Shorten nspawn interfaces by including 24bit hash, instead of simple truncation */
NAMING_BRIDGE_NO_SLOT = 1 << 9, /* Don't use PCI hotplug slot information if the corresponding device is a PCI bridge */
NAMING_SLOT_FUNCTION_ID = 1 << 10, /* Use function_id if present to identify PCI hotplug slots */
NAMING_16BIT_INDEX = 1 << 11, /* Allow full 16-bit for the onboard index */
NAMING_REPLACE_STRICTLY = 1 << 12, /* Use udev_replace_ifname() for NAME= rule */
NAMING_XEN_VIF = 1 << 13, /* Generate names for Xen netfront devices */
NAMING_BRIDGE_MULTIFUNCTION_SLOT = 1 << 14, /* Use PCI hotplug slot information associated with bridge, but only if PCI device is multifunction */
NAMING_SR_IOV_V = 1 << 0, /* Use "v" suffix for SR-IOV, see 609948c7043a */
NAMING_NPAR_ARI = 1 << 1, /* Use NPAR "ARI", see 6bc04997b6ea */
NAMING_INFINIBAND = 1 << 2, /* Use "ib" prefix for infiniband, see 938d30aa98df */
NAMING_ZERO_ACPI_INDEX = 1 << 3, /* Use zero acpi_index field, see d81186ef4f6a */
NAMING_ALLOW_RERENAMES = 1 << 4, /* Allow re-renaming of devices, see #9006 */
NAMING_STABLE_VIRTUAL_MACS = 1 << 5, /* Use device name to generate MAC, see 6d3646406560 */
NAMING_NETDEVSIM = 1 << 6, /* Generate names for netdevsim devices, see eaa9d507d855 */
NAMING_LABEL_NOPREFIX = 1 << 7, /* Don't prepend ID_NET_LABEL_ONBOARD with interface type prefix */
NAMING_NSPAWN_LONG_HASH = 1 << 8, /* Shorten nspawn interfaces by including 24bit hash, instead of simple truncation */
NAMING_BRIDGE_NO_SLOT = 1 << 9, /* Don't use PCI hotplug slot information if the corresponding device is a PCI bridge */
NAMING_SLOT_FUNCTION_ID = 1 << 10, /* Use function_id if present to identify PCI hotplug slots */
NAMING_16BIT_INDEX = 1 << 11, /* Allow full 16-bit for the onboard index */
NAMING_REPLACE_STRICTLY = 1 << 12, /* Use udev_replace_ifname() for NAME= rule */
NAMING_XEN_VIF = 1 << 13, /* GEnerate names for Xen netfront devices */
/* And now the masks that combine the features above */
NAMING_V238 = 0,
@ -48,7 +47,6 @@ typedef enum NamingSchemeFlags {
NAMING_V247 = NAMING_V245 | NAMING_BRIDGE_NO_SLOT,
NAMING_V249 = NAMING_V247 | NAMING_SLOT_FUNCTION_ID | NAMING_16BIT_INDEX | NAMING_REPLACE_STRICTLY,
NAMING_V250 = NAMING_V249 | NAMING_XEN_VIF,
NAMING_V251 = NAMING_V250 | NAMING_BRIDGE_MULTIFUNCTION_SLOT,
EXTRA_NET_NAMING_SCHEMES

View File

@ -451,15 +451,8 @@ static int dev_pci_slot(sd_device *dev, const LinkInfo *info, NetNames *names) {
* devices that will try to claim the same index and that would create name
* collision. */
if (naming_scheme_has(NAMING_BRIDGE_NO_SLOT) && is_pci_bridge(hotplug_slot_dev)) {
if (naming_scheme_has(NAMING_BRIDGE_MULTIFUNCTION_SLOT) && !is_pci_multifunction(names->pcidev)) {
log_device_debug(dev, "Not using slot information because the PCI device associated with the hotplug slot is a bridge and the PCI device has single function.");
return 0;
}
if (!naming_scheme_has(NAMING_BRIDGE_MULTIFUNCTION_SLOT)) {
log_device_debug(dev, "Not using slot information because the PCI device is a bridge.");
return 0;
}
log_device_debug(dev, "Not using slot information because the PCI device is a bridge.");
return 0;
}
break;

View File

@ -966,6 +966,9 @@ static int update_devnode(UdevEvent *event) {
if (r < 0 && r != -ENOENT)
return log_device_error_errno(dev, r, "Failed to get devnode mode: %m");
}
if (event->mode == MODE_INVALID && gid_is_valid(event->gid) && event->gid > 0)
/* If group is set, but mode is not set, "upgrade" mode for the group. */
event->mode = 0660;
bool apply_mac = device_for_action(dev, SD_DEVICE_ADD);

View File

@ -13,7 +13,6 @@
#include "device-private.h"
#include "device-util.h"
#include "dirent-util.h"
#include "escape.h"
#include "fd-util.h"
#include "format-util.h"
#include "fs-util.h"
@ -591,30 +590,51 @@ int udev_node_remove(sd_device *dev) {
return 0;
}
static int udev_node_apply_permissions_impl(
sd_device *dev, /* can be NULL, only used for logging. */
int node_fd,
const char *devnode,
int udev_node_apply_permissions(
sd_device *dev,
bool apply_mac,
mode_t mode,
uid_t uid,
gid_t gid,
OrderedHashmap *seclabel_list) {
const char *devnode, *subsystem;
bool apply_mode, apply_uid, apply_gid;
_cleanup_close_ int node_fd = -1;
struct stat stats;
dev_t devnum;
int r;
assert(node_fd >= 0);
assert(devnode);
assert(dev);
r = sd_device_get_devname(dev, &devnode);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to get devname: %m");
r = sd_device_get_subsystem(dev, &subsystem);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to get subsystem: %m");
r = sd_device_get_devnum(dev, &devnum);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to get devnum: %m");
if (streq(subsystem, "block"))
mode |= S_IFBLK;
else
mode |= S_IFCHR;
node_fd = sd_device_open(dev, O_PATH|O_CLOEXEC);
if (node_fd < 0) {
if (ERRNO_IS_DEVICE_ABSENT(node_fd)) {
log_device_debug_errno(dev, node_fd, "Device node %s is missing, skipping handling.", devnode);
return 0; /* This is necessarily racey, so ignore missing the device */
}
return log_device_debug_errno(dev, node_fd, "Cannot open node %s: %m", devnode);
}
if (fstat(node_fd, &stats) < 0)
return log_device_debug_errno(dev, errno, "cannot stat() node %s: %m", devnode);
/* If group is set, but mode is not set, "upgrade" mode for the group. */
if (mode == MODE_INVALID && gid_is_valid(gid) && gid > 0)
mode = 0660;
apply_mode = mode != MODE_INVALID && (stats.st_mode & 0777) != (mode & 0777);
apply_uid = uid_is_valid(uid) && stats.st_uid != uid;
apply_gid = gid_is_valid(gid) && stats.st_gid != gid;
@ -688,95 +708,3 @@ static int udev_node_apply_permissions_impl(
return 0;
}
int udev_node_apply_permissions(
sd_device *dev,
bool apply_mac,
mode_t mode,
uid_t uid,
gid_t gid,
OrderedHashmap *seclabel_list) {
const char *devnode;
_cleanup_close_ int node_fd = -1;
int r;
assert(dev);
r = sd_device_get_devname(dev, &devnode);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to get devname: %m");
node_fd = sd_device_open(dev, O_PATH|O_CLOEXEC);
if (node_fd < 0) {
if (ERRNO_IS_DEVICE_ABSENT(node_fd)) {
log_device_debug_errno(dev, node_fd, "Device node %s is missing, skipping handling.", devnode);
return 0; /* This is necessarily racey, so ignore missing the device */
}
return log_device_debug_errno(dev, node_fd, "Cannot open node %s: %m", devnode);
}
return udev_node_apply_permissions_impl(dev, node_fd, devnode, apply_mac, mode, uid, gid, seclabel_list);
}
int static_node_apply_permissions(
const char *name,
mode_t mode,
uid_t uid,
gid_t gid,
char **tags) {
_cleanup_free_ char *unescaped_filename = NULL;
_cleanup_close_ int node_fd = -1;
const char *devnode;
struct stat stats;
int r;
assert(name);
if (uid == UID_INVALID && gid == GID_INVALID && mode == MODE_INVALID && !tags)
return 0;
devnode = strjoina("/dev/", name);
node_fd = open(devnode, O_PATH|O_CLOEXEC);
if (node_fd < 0) {
if (errno != ENOENT)
return log_error_errno(errno, "Failed to open %s: %m", devnode);
return 0;
}
if (fstat(node_fd, &stats) < 0)
return log_error_errno(errno, "Failed to stat %s: %m", devnode);
if (!S_ISBLK(stats.st_mode) && !S_ISCHR(stats.st_mode)) {
log_warning("%s is neither block nor character device, ignoring.", devnode);
return 0;
}
if (!strv_isempty(tags)) {
unescaped_filename = xescape(name, "/.");
if (!unescaped_filename)
return log_oom();
}
/* export the tags to a directory as symlinks, allowing otherwise dead nodes to be tagged */
STRV_FOREACH(t, tags) {
_cleanup_free_ char *p = NULL;
p = path_join("/run/udev/static_node-tags/", *t, unescaped_filename);
if (!p)
return log_oom();
r = mkdir_parents(p, 0755);
if (r < 0)
return log_error_errno(r, "Failed to create parent directory for %s: %m", p);
r = symlink(devnode, p);
if (r < 0 && errno != EEXIST)
return log_error_errno(errno, "Failed to create symlink %s -> %s: %m", p, devnode);
}
return udev_node_apply_permissions_impl(NULL, node_fd, devnode, false, mode, uid, gid, NULL);
}

View File

@ -15,13 +15,6 @@ int udev_node_apply_permissions(
uid_t uid,
gid_t gid,
OrderedHashmap *seclabel_list);
int static_node_apply_permissions(
const char *name,
mode_t mode,
uid_t uid,
gid_t gid,
char **tags);
int udev_node_remove(sd_device *dev);
int udev_node_update(sd_device *dev, sd_device *dev_old);

View File

@ -9,6 +9,7 @@
#include "device-private.h"
#include "device-util.h"
#include "dirent-util.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
@ -29,7 +30,6 @@
#include "udev-builtin.h"
#include "udev-event.h"
#include "udev-netlink.h"
#include "udev-node.h"
#include "udev-rules.h"
#include "udev-util.h"
#include "user-util.h"
@ -2536,6 +2536,72 @@ int udev_rules_apply_to_event(
return 0;
}
static int apply_static_dev_perms(const char *devnode, uid_t uid, gid_t gid, mode_t mode, char **tags) {
char device_node[UDEV_PATH_SIZE], tags_dir[UDEV_PATH_SIZE], tag_symlink[UDEV_PATH_SIZE];
_cleanup_free_ char *unescaped_filename = NULL;
struct stat stats;
int r;
assert(devnode);
if (uid == UID_INVALID && gid == GID_INVALID && mode == MODE_INVALID && !tags)
return 0;
strscpyl(device_node, sizeof(device_node), "/dev/", devnode, NULL);
if (stat(device_node, &stats) < 0) {
if (errno != ENOENT)
return log_error_errno(errno, "Failed to stat %s: %m", device_node);
return 0;
}
if (!S_ISBLK(stats.st_mode) && !S_ISCHR(stats.st_mode)) {
log_warning("%s is neither block nor character device, ignoring.", device_node);
return 0;
}
if (!strv_isempty(tags)) {
unescaped_filename = xescape(devnode, "/.");
if (!unescaped_filename)
return log_oom();
}
/* export the tags to a directory as symlinks, allowing otherwise dead nodes to be tagged */
STRV_FOREACH(t, tags) {
strscpyl(tags_dir, sizeof(tags_dir), "/run/udev/static_node-tags/", *t, "/", NULL);
r = mkdir_p(tags_dir, 0755);
if (r < 0)
return log_error_errno(r, "Failed to create %s: %m", tags_dir);
strscpyl(tag_symlink, sizeof(tag_symlink), tags_dir, unescaped_filename, NULL);
r = symlink(device_node, tag_symlink);
if (r < 0 && errno != EEXIST)
return log_error_errno(errno, "Failed to create symlink %s -> %s: %m",
tag_symlink, device_node);
}
/* don't touch the permissions if only the tags were set */
if (uid == UID_INVALID && gid == GID_INVALID && mode == MODE_INVALID)
return 0;
if (mode == MODE_INVALID)
mode = gid_is_valid(gid) ? 0660 : 0600;
if (!uid_is_valid(uid))
uid = 0;
if (!gid_is_valid(gid))
gid = 0;
r = chmod_and_chown(device_node, mode, uid, gid);
if (r == -ENOENT)
return 0;
if (r < 0)
return log_error_errno(r, "Failed to chown '%s' %u %u: %m", device_node, uid, gid);
else
log_debug("chown '%s' %u:%u with mode %#o", device_node, uid, gid, mode);
(void) utimensat(AT_FDCWD, device_node, NULL, 0);
return 0;
}
static int udev_rule_line_apply_static_dev_perms(UdevRuleLine *rule_line) {
_cleanup_strv_free_ char **tags = NULL;
uid_t uid = UID_INVALID;
@ -2560,7 +2626,7 @@ static int udev_rule_line_apply_static_dev_perms(UdevRuleLine *rule_line) {
if (r < 0)
return log_oom();
} else if (token->type == TK_A_OPTIONS_STATIC_NODE) {
r = static_node_apply_permissions(token->value, mode, uid, gid, tags);
r = apply_static_dev_perms(token->value, uid, gid, mode, tags);
if (r < 0)
return r;
}

View File

@ -144,7 +144,6 @@ static int find_devno(
const char *device,
bool backing) {
_cleanup_close_ int fd = -1;
dev_t devt, whole_devt;
struct stat st;
int r;
@ -154,11 +153,7 @@ static int find_devno(
assert(*devnos || *n_devnos == 0);
assert(device);
fd = open(device, O_CLOEXEC|O_PATH);
if (fd < 0)
return log_error_errno(errno, "Failed to open '%s': %m", device);
if (fstat(fd, &st) < 0)
if (stat(device, &st) < 0)
return log_error_errno(errno, "Failed to stat '%s': %m", device);
if (S_ISBLK(st.st_mode))
@ -171,13 +166,20 @@ static int find_devno(
devt = st.st_dev;
else {
_cleanup_close_ int regfd = -1;
struct stat st2;
/* If major(st.st_dev) is zero, this might mean we are backed by btrfs, which needs special
* handing, to get the backing device node. */
regfd = fd_reopen(fd, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
regfd = open(device, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
if (regfd < 0)
return log_error_errno(regfd, "Failed to open '%s': %m", device);
return log_error_errno(errno, "Failed to open '%s': %m", device);
/* Extra safety: let's check we are still looking at the same file */
if (fstat(regfd, &st2) < 0)
return log_error_errno(errno, "Failed to stat '%s': %m", device);
if (!stat_inode_same(&st, &st2))
return log_error_errno(SYNTHETIC_ERRNO(ENXIO), "File '%s' was replaced while we were looking at it.", device);
r = btrfs_get_block_device_fd(regfd, &devt);
if (r == -ENOTTY)

View File

@ -1711,7 +1711,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
'25-ip6tnl-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
'25-veth.netdev', '25-ip6tnl-slaac.network', '25-ipv6-prefix.network',
'25-ip6tnl-tunnel-local-slaac.netdev', '25-ip6tnl-tunnel-local-slaac.network',
'25-ip6tnl-tunnel-external.netdev', '26-netdev-link-local-addressing-yes.network')
'25-ip6tnl-external.netdev', '26-netdev-link-local-addressing-yes.network')
start_networkd()
self.wait_online(['ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable',
'ip6tnl-slaac:degraded', 'ip6tnl-external:degraded',