udev-node: chase devlink on remove event

Previously, readlink_malloc() was used to find the target device.
However, devlink is a relative symbolic link, hence the path obtained by
readlink_malloc() never matched with the one by sd_device_get_devname().
Also path_startswith(target, "/dev/") was always negative.

Let's use chase_and_stat() to obtain the absolute path to the target
device that the devlink currently points to.

Fixes the following spurious logs on remove event:

(udev-worker)[2338]: loop1p1: Processing device (SEQNUM=2231, ACTION=remove)
(udev-worker)[2338]: Notify message sent to '@4814750286313953891': "INOTIFY_WATCH_REMOVE=1"
(udev-worker)[2338]: loop1p1: Symbolic link '/dev/udevtest/x2' points to '../loop1p1' which is outside of '/dev/', updating it.
(udev-worker)[2338]: loop1p1: No reference left for '/dev/udevtest/x2', removing
(udev-worker)[2338]: loop1p1: Symbolic link '/dev/disk/by-diskseq/9-part1' points to '../../loop1p1' which is outside of '/dev/', updating it.
(udev-worker)[2338]: loop1p1: No reference left for '/dev/disk/by-diskseq/9-part1', removing
(udev-worker)[2338]: loop1p1: Symbolic link '/dev/disk/by-label/udevtest-b-fs1' points to '../../loop1p1' which is outside of '/dev/', updating it.
(udev-worker)[2338]: loop1p1: No reference left for '/dev/disk/by-label/udevtest-b-fs1', removing
(udev-worker)[2338]: loop1p1: Symbolic link '/dev/disk/by-uuid/221d135f-7bf7-45b8-abeb-d9b3afb2c215' points to '../../loop1p1' which is outside of '/dev/', updating it.
(udev-worker)[2338]: loop1p1: No reference left for '/dev/disk/by-uuid/221d135f-7bf7-45b8-abeb-d9b3afb2c215', removing
(udev-worker)[2338]: loop1p1: Symbolic link '/dev/disk/by-partlabel/udevtest-b-part1' points to '../../loop1p1' which is outside of '/dev/', updating it.
(udev-worker)[2338]: loop1p1: No reference left for '/dev/disk/by-partlabel/udevtest-b-part1', removing
(udev-worker)[2338]: loop1p1: Symbolic link '/dev/udevtest/b1' points to '../loop1p1' which is outside of '/dev/', updating it.
(udev-worker)[2338]: loop1p1: No reference left for '/dev/udevtest/b1', removing
(udev-worker)[2338]: loop1p1: Symbolic link '/dev/udevtest/x1' points to '../loop1p1' which is outside of '/dev/', updating it.
(udev-worker)[2338]: loop1p1: No reference left for '/dev/udevtest/x1', removing
(udev-worker)[2338]: loop1p1: Symbolic link '/dev/disk/by-partuuid/5ef627b6-95f1-a942-8d7a-bd4ac53485ac' points to '../../loop1p1' which is outside of '/dev/', updating it.
(udev-worker)[2338]: loop1p1: No reference left for '/dev/disk/by-partuuid/5ef627b6-95f1-a942-8d7a-bd4ac53485ac', removing
(udev-worker)[2338]: loop1p1: Symbolic link '/dev/udevtest/b3' points to '../loop1p1' which is outside of '/dev/', updating it.
(udev-worker)[2338]: loop1p1: No reference left for '/dev/udevtest/b3', removing
(udev-worker)[2338]: loop1p1: Symbolic link '/dev/disk/by-loop-inode/0:36-13-part1' points to '../../loop1p1' which is outside of '/dev/', updating it.
(udev-worker)[2338]: loop1p1: No reference left for '/dev/disk/by-loop-inode/0:36-13-part1', removing
(udev-worker)[2338]: loop1p1: Symbolic link '/dev/disk/by-loop-ref/\x2ftmp\x2fudevtest-b.img-part1' points to '../../loop1p1' which is outside of '/dev/', updating it.
(udev-worker)[2338]: loop1p1: No reference left for '/dev/disk/by-loop-ref/\x2ftmp\x2fudevtest-b.img-part1', removing
(udev-worker)[2338]: loop1p1: Device processed (SEQNUM=2231, ACTION=remove)

(cherry picked from commit 03a65741d77d61929b98977d1bfdf2b470e14b55)
This commit is contained in:
Yu Watanabe
2026-10-16 17:24:51 +01:00
committed by Luca Boccassi
parent 2c4e3b9a6b
commit 96be8707ef
+7 -16
View File
@@ -7,6 +7,7 @@
#include "sd-id128.h"
#include "alloc-util.h"
#include "chase.h"
#include "device-private.h"
#include "device-util.h"
#include "devnum-util.h"
@@ -423,22 +424,21 @@ static int link_update(sd_device *dev, const char *slink, bool add) {
return log_device_debug_errno(dev, r, "Failed to update stack directory for '%s': %m", slink);
if (!add) {
_cleanup_free_ char *target = NULL;
/* Especially on 'remove' event, the device ID obtained by node_get_current() may not be
* reliable, as the device node and/or device number may be reused. Hence, let's read the
* devlink here and if it points to our device node, then we need to update the devlink. If
* it points to another device node, then it is already owned by another device, hence we
* should not touch it and keep it as is. */
r = readlink_malloc(slink, &target);
_cleanup_free_ char *target = NULL;
struct stat st;
r = chase_and_stat(slink, /* root= */ NULL, /* chase_flags= */ 0, &target, &st);
if (r < 0) {
if (r != -ENOENT)
log_device_debug_errno(dev, r, "Failed to read symbolic link '%s', ignoring: %m", slink);
log_device_debug_errno(dev, r, "Failed to chase and stat '%s', ignoring: %m", slink);
/* The devlink does not exist. Let's find the most suitable owner, and create the
* devlink. This is typically not necessary and does nothing, but for safety in the
* case that the devlink is removed manually. */
/* The devlink or its target device does not exist. Let's find the most suitable
* owner, and create or update the devlink. */
return link_search_and_update(dev, slink, dirfd, add);
}
@@ -458,15 +458,6 @@ static int link_update(sd_device *dev, const char *slink, bool add) {
return link_search_and_update(dev, slink, dirfd, add);
}
struct stat st;
if (lstat(target, &st) < 0) {
if (errno != ENOENT)
log_device_debug_errno(dev, errno, "Failed to stat '%s', ignoring: %m", target);
/* The current target device is also already removed? Let's update. */
return link_search_and_update(dev, slink, dirfd, add);
}
if (!IN_SET(st.st_mode & S_IFMT, S_IFBLK, S_IFCHR)) {
log_device_debug(dev, "Symbolic link '%s' points to '%s' which is not a device node, updating it.", slink, target);
return link_search_and_update(dev, slink, dirfd, add);