udev: use device ID to find blockers

If two devices have the same devnum and subsystem (more specifically,
if the device is block or not), or have the same ifindex, then IDs of
these devices are equivalent.
Hence, the previous conditions are covered by comparing device IDs.

Of course, events with a same ID should be already blocked by the
devpath check. So, this should not change anything.

However, udevd saves many kinds of data under /run/udev named with
the device ID. If multiple workers processes events for the same device
ID, then the database may become corrupted.

Let's explicitly check the device IDs for safety and simplicity.
This commit is contained in:
Yu Watanabe 2022-04-15 12:00:56 +09:00
parent fa2ba7aea8
commit 29d0245832

View file

@ -131,6 +131,9 @@ typedef struct Event {
sd_device_action_t action;
uint64_t seqnum;
uint64_t blocker_seqnum;
const char *id;
const char *devpath;
const char *devpath_old;
usec_t retry_again_next_usec;
usec_t retry_again_timeout_usec;
@ -852,12 +855,9 @@ static int event_run(Event *event) {
}
static int event_is_blocked(Event *event) {
const char *subsystem, *devpath, *devpath_old = NULL;
dev_t devnum = makedev(0, 0);
Event *loop_event = NULL;
size_t devpath_len;
int r, ifindex = 0;
bool is_block;
int r;
/* lookup event for identical, parent, child device */
@ -903,34 +903,11 @@ static int event_is_blocked(Event *event) {
assert(loop_event->seqnum > event->blocker_seqnum &&
loop_event->seqnum < event->seqnum);
r = sd_device_get_subsystem(event->dev, &subsystem);
if (r < 0)
return r;
is_block = streq(subsystem, "block");
r = sd_device_get_devpath(event->dev, &devpath);
if (r < 0)
return r;
devpath_len = strlen(devpath);
r = sd_device_get_property_value(event->dev, "DEVPATH_OLD", &devpath_old);
if (r < 0 && r != -ENOENT)
return r;
r = sd_device_get_devnum(event->dev, &devnum);
if (r < 0 && r != -ENOENT)
return r;
r = sd_device_get_ifindex(event->dev, &ifindex);
if (r < 0 && r != -ENOENT)
return r;
devpath_len = strlen(event->devpath);
/* check if queue contains events we depend on */
LIST_FOREACH(event, e, loop_event) {
size_t loop_devpath_len, common;
const char *loop_devpath;
loop_event = e;
@ -938,42 +915,20 @@ static int event_is_blocked(Event *event) {
if (loop_event->seqnum >= event->seqnum)
goto no_blocker;
/* check major/minor */
if (major(devnum) != 0) {
const char *s;
dev_t d;
if (sd_device_get_subsystem(loop_event->dev, &s) < 0)
continue;
if (sd_device_get_devnum(loop_event->dev, &d) >= 0 &&
devnum == d && is_block == streq(s, "block"))
break;
}
/* check network device ifindex */
if (ifindex > 0) {
int i;
if (sd_device_get_ifindex(loop_event->dev, &i) >= 0 &&
ifindex == i)
break;
}
if (sd_device_get_devpath(loop_event->dev, &loop_devpath) < 0)
continue;
/* check our old name */
if (devpath_old && streq(devpath_old, loop_devpath))
if (streq_ptr(loop_event->id, event->id))
break;
loop_devpath_len = strlen(loop_devpath);
/* check our old name */
if (event->devpath_old && streq(event->devpath_old, loop_event->devpath))
break;
loop_devpath_len = strlen(loop_event->devpath);
/* compare devpath */
common = MIN(devpath_len, loop_devpath_len);
/* one devpath is contained in the other? */
if (!strneq(devpath, loop_devpath, common))
if (!strneq(event->devpath, loop_event->devpath, common))
continue;
/* identical device event found */
@ -981,11 +936,11 @@ static int event_is_blocked(Event *event) {
break;
/* parent device event found */
if (devpath[common] == '/')
if (event->devpath[common] == '/')
break;
/* child device event found */
if (loop_devpath[common] == '/')
if (loop_event->devpath[common] == '/')
break;
}
@ -1134,6 +1089,7 @@ static int event_queue_assume_block_device_unlocked(Manager *manager, sd_device
}
static int event_queue_insert(Manager *manager, sd_device *dev) {
const char *devpath, *devpath_old = NULL, *id = NULL;
sd_device_action_t action;
uint64_t seqnum;
Event *event;
@ -1154,6 +1110,18 @@ static int event_queue_insert(Manager *manager, sd_device *dev) {
if (r < 0)
return r;
r = sd_device_get_devpath(dev, &devpath);
if (r < 0)
return r;
r = sd_device_get_property_value(dev, "DEVPATH_OLD", &devpath_old);
if (r < 0 && r != -ENOENT)
return r;
r = device_get_device_id(dev, &id);
if (r < 0 && r != -ENOENT)
return r;
event = new(Event, 1);
if (!event)
return -ENOMEM;
@ -1163,6 +1131,9 @@ static int event_queue_insert(Manager *manager, sd_device *dev) {
.dev = sd_device_ref(dev),
.seqnum = seqnum,
.action = action,
.id = id,
.devpath = devpath,
.devpath_old = devpath_old,
.state = EVENT_QUEUED,
};