From 1fdcf3ee40946ddb0ab0f60f56390faca9d11eda Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 23 Feb 2026 04:32:05 +0900 Subject: [PATCH 1/3] sd-device: drop support of udev database version 0 The udev database versioning has been introduced in v247, which is released on 2020-11-26. Let's drop the support of old udev database. --- src/libsystemd/sd-device/device-internal.h | 3 ++- src/libsystemd/sd-device/device-private.c | 2 +- src/libsystemd/sd-device/sd-device.c | 22 +--------------------- 3 files changed, 4 insertions(+), 23 deletions(-) diff --git a/src/libsystemd/sd-device/device-internal.h b/src/libsystemd/sd-device/device-internal.h index 16a5792350..f207a1618f 100644 --- a/src/libsystemd/sd-device/device-internal.h +++ b/src/libsystemd/sd-device/device-internal.h @@ -6,6 +6,7 @@ #include "sd-forward.h" #include "iterator.h" +#define OLDEST_UDEV_DATABASE_VERSION 1 #define LATEST_UDEV_DATABASE_VERSION 1 struct sd_device { @@ -51,7 +52,7 @@ struct sd_device { /* The database version indicates the supported features by the udev database. * This is saved and parsed in V field. * - * 0: None of the following features are supported (systemd version <= 246). + * 0: None of the following features are supported (systemd version <= 246), unsupported since v261. * 1: The current tags (Q) and the database version (V) features are implemented (>= 247). */ unsigned database_version; diff --git a/src/libsystemd/sd-device/device-private.c b/src/libsystemd/sd-device/device-private.c index 054c3545d0..48dce77689 100644 --- a/src/libsystemd/sd-device/device-private.c +++ b/src/libsystemd/sd-device/device-private.c @@ -912,7 +912,7 @@ int device_update_db(sd_device *device) { fprintf(f, "Q:%s\n", ct); /* Current tag */ /* Always write the latest database version here, instead of the value stored in - * device->database_version, as which may be 0. */ + * device->database_version. */ fputs("V:" STRINGIFY(LATEST_UDEV_DATABASE_VERSION) "\n", f); } diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c index ef67c649d1..ae44d0c86f 100644 --- a/src/libsystemd/sd-device/sd-device.c +++ b/src/libsystemd/sd-device/sd-device.c @@ -47,6 +47,7 @@ int device_new_aux(sd_device **ret) { .devuid = UID_INVALID, .devgid = GID_INVALID, .action = _SD_DEVICE_ACTION_INVALID, + .database_version = OLDEST_UDEV_DATABASE_VERSION, }; *ret = device; @@ -1966,26 +1967,11 @@ _public_ const char* sd_device_get_tag_next(sd_device *device) { return v; } -static bool device_database_supports_current_tags(sd_device *device) { - assert(device); - - (void) device_read_db(device); - - /* The current tags (saved in Q field) feature is implemented in database version 1. - * If the database version is 0, then the tags (NOT current tags, saved in G field) are not - * sticky. Thus, we can safely bypass the operations for the current tags (Q) to tags (G). */ - - return device->database_version >= 1; -} - _public_ const char* sd_device_get_current_tag_first(sd_device *device) { void *v; assert_return(device, NULL); - if (!device_database_supports_current_tags(device)) - return sd_device_get_tag_first(device); - (void) device_read_db(device); device->current_tags_iterator_generation = device->tags_generation; @@ -2000,9 +1986,6 @@ _public_ const char* sd_device_get_current_tag_next(sd_device *device) { assert_return(device, NULL); - if (!device_database_supports_current_tags(device)) - return sd_device_get_tag_next(device); - (void) device_read_db(device); if (device->current_tags_iterator_generation != device->tags_generation) @@ -2273,9 +2256,6 @@ _public_ int sd_device_has_current_tag(sd_device *device, const char *tag) { assert_return(device, -EINVAL); assert_return(tag, -EINVAL); - if (!device_database_supports_current_tags(device)) - return sd_device_has_tag(device, tag); - (void) device_read_db(device); return set_contains(device->current_tags, tag); From dfe0e81de68c13219f1c8a7ac9390c21204fb496 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 23 Feb 2026 01:28:41 +0900 Subject: [PATCH 2/3] udev/watch: use mapping from device ID -> watch on restart The mapping from device ID to watch handle has been introduced by e7f781e473f5119bf9246208a6de9f6b76a39c5d (v249, released on 2021-07-07). Let's drop the runtime upgradability of udevd from an ancient version. --- src/udev/udev-watch.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/udev/udev-watch.c b/src/udev/udev-watch.c index 08bc3492bb..0636c325e6 100644 --- a/src/udev/udev-watch.c +++ b/src/udev/udev-watch.c @@ -337,21 +337,20 @@ static int udev_watch_restore(Manager *manager) { return log_warning_errno(errno, "Failed to open old watches directory '%s': %m", old); FOREACH_DIRENT(de, dir, break) { - - /* For backward compatibility, read symlink from watch handle to device ID. This is necessary - * when udevd is restarted after upgrading from v248 or older. The new format (ID -> wd) was - * introduced by e7f781e473f5119bf9246208a6de9f6b76a39c5d (v249). */ - - int wd; - if (safe_atoi(de->d_name, &wd) < 0) - continue; /* This should be ID -> wd symlink. Skipping. */ + if (in_charset(de->d_name, DIGITS)) + continue; /* This should be wd -> ID symlink. Skipping. */ _cleanup_(sd_device_unrefp) sd_device *dev = NULL; - r = device_new_from_watch_handle_at(&dev, dirfd(dir), wd); + r = sd_device_new_from_device_id(&dev, de->d_name); if (r < 0) { + _cleanup_free_ char *wd_str = NULL; + + if (ERRNO_IS_NEG_DEVICE_ABSENT(r) || DEBUG_LOGGING) + (void) readlinkat_malloc(dirfd(dir), de->d_name, &wd_str); + log_full_errno(ERRNO_IS_NEG_DEVICE_ABSENT(r) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to create sd_device object from saved watch handle '%i', ignoring: %m", - wd); + "Failed to create sd_device object from device ID '%s' for watch handle '%s', ignoring: %m", + de->d_name, strna(wd_str)); continue; } From 3b6402446412ace7d99723398b0adac59230c881 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 23 Feb 2026 01:51:26 +0900 Subject: [PATCH 3/3] udev/node: drop support of old file format in /run/udev/links/ The new file format in /run/udev/links/ has been introduced in 377a83f0d80376456d9be203796f66f543a8b943 (v250, released on 2021-12-23). Let's drop the old format support, to simplify the logic. --- src/udev/udev-node.c | 80 ++++++++++++-------------------------------- 1 file changed, 22 insertions(+), 58 deletions(-) diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c index d30ffd9a56..4d1ecf5d11 100644 --- a/src/udev/udev-node.c +++ b/src/udev/udev-node.c @@ -79,84 +79,48 @@ static int node_create_symlink(sd_device *dev, const char *devnode, const char * } static int stack_directory_read_one(int dirfd, const char *id, char **devnode, int *priority) { - _cleanup_free_ char *buf = NULL; - int tmp_prio, r; + int r; assert(dirfd >= 0); assert(id); assert(priority); - /* This reads priority and device node from the symlink under /run/udev/links (or udev database). + /* This reads priority and device node from the symlink under /run/udev/links/ directory. * If 'devnode' is NULL, obtained priority is always set to '*priority'. If 'devnode' is non-NULL, - * this updates '*devnode' and '*priority'. */ + * this updates '*devnode' and '*priority' if the obtained one has a higher priority. */ - /* First, let's try to read the entry with the new format, which should replace the old format pretty - * quickly. */ + _cleanup_free_ char *buf = NULL; r = readlinkat_malloc(dirfd, id, &buf); - if (r >= 0) { - char *colon; + if (r < 0) + return r == -ENOENT ? -ENODEV : r; - /* With the new format, the devnode and priority can be obtained from symlink itself. */ + char *colon = strchr(buf, ':'); + if (!colon || colon == buf) + return -EINVAL; - colon = strchr(buf, ':'); - if (!colon || colon == buf) - return -EINVAL; + *colon = '\0'; - *colon = '\0'; + /* Of course, this check is racy, but it is not necessary to be perfect. Even if the device + * node will be removed after this check, we will receive 'remove' uevent, and the invalid + * symlink will be removed during processing the event. The check is just for shortening the + * timespan that the symlink points to a non-existing device node. */ + if (access(colon + 1, F_OK) < 0) + return -ENODEV; - /* Of course, this check is racy, but it is not necessary to be perfect. Even if the device - * node will be removed after this check, we will receive 'remove' uevent, and the invalid - * symlink will be removed during processing the event. The check is just for shortening the - * timespan that the symlink points to a non-existing device node. */ - if (access(colon + 1, F_OK) < 0) - return -ENODEV; - - r = safe_atoi(buf, &tmp_prio); - if (r < 0) - return r; - - if (!devnode) - goto finalize; + int tmp_prio; + r = safe_atoi(buf, &tmp_prio); + if (r < 0) + return r; + if (devnode) { if (*devnode && tmp_prio <= *priority) return 0; /* Unchanged */ r = free_and_strdup(devnode, colon + 1); if (r < 0) return r; + } - } else if (r == -EINVAL) { /* Not a symlink ? try the old format */ - _cleanup_(sd_device_unrefp) sd_device *dev = NULL; - const char *val; - - /* Old format. The devnode and priority must be obtained from uevent and udev database. */ - - r = sd_device_new_from_device_id(&dev, id); - if (r < 0) - return r; - - r = device_get_devlink_priority(dev, &tmp_prio); - if (r < 0) - return r; - - if (!devnode) - goto finalize; - - if (*devnode && tmp_prio <= *priority) - return 0; /* Unchanged */ - - r = sd_device_get_devname(dev, &val); - if (r < 0) - return r; - - r = free_and_strdup(devnode, val); - if (r < 0) - return r; - - } else - return r == -ENOENT ? -ENODEV : r; - -finalize: *priority = tmp_prio; return 1; /* Updated */ }