From b65467b64758d05b42944a8428b2458c86e46719 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 23 Feb 2026 04:40:54 +0900 Subject: [PATCH 1/5] sd-device: shorten code a bit --- src/libsystemd/sd-device/device-private.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libsystemd/sd-device/device-private.c b/src/libsystemd/sd-device/device-private.c index 035e7290e0..3cd3e96d1c 100644 --- a/src/libsystemd/sd-device/device-private.c +++ b/src/libsystemd/sd-device/device-private.c @@ -705,8 +705,7 @@ void device_cleanup_tags(sd_device *device) { void device_cleanup_devlinks(sd_device *device) { assert(device); - set_free(device->devlinks); - device->devlinks = NULL; + device->devlinks = set_free(device->devlinks); device->property_devlinks_outdated = true; device->devlinks_generation++; } From 1c867dc71af12bb7afb1981f83fea14eb4b98993 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 23 Feb 2026 05:26:46 +0900 Subject: [PATCH 2/5] sd-device: move copy_all_tags() from udev No functional change, preparation for the next commit. --- src/libsystemd/sd-device/device-private.c | 17 +++++++++++++++++ src/libsystemd/sd-device/device-private.h | 1 + src/udev/udev-event.c | 19 +------------------ 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/libsystemd/sd-device/device-private.c b/src/libsystemd/sd-device/device-private.c index 3cd3e96d1c..828697e617 100644 --- a/src/libsystemd/sd-device/device-private.c +++ b/src/libsystemd/sd-device/device-private.c @@ -693,6 +693,23 @@ int device_clone_with_db(sd_device *device, sd_device **ret) { return 0; } +int device_copy_all_tags(sd_device *dest, sd_device *src) { + int r; + + assert(dest); + + if (!src) + return 0; + + FOREACH_DEVICE_TAG(src, tag) { + r = device_add_tag(dest, tag, /* both= */ false); + if (r < 0) + return r; + } + + return 0; +} + void device_cleanup_tags(sd_device *device) { assert(device); diff --git a/src/libsystemd/sd-device/device-private.h b/src/libsystemd/sd-device/device-private.h index 8f88b38c46..376a4a479c 100644 --- a/src/libsystemd/sd-device/device-private.h +++ b/src/libsystemd/sd-device/device-private.h @@ -45,6 +45,7 @@ int device_add_property(sd_device *device, const char *key, const char *value); int device_add_propertyf(sd_device *device, const char *key, const char *format, ...) _printf_(3, 4); int device_add_tag(sd_device *device, const char *tag, bool both); void device_remove_tag(sd_device *device, const char *tag); +int device_copy_all_tags(sd_device *dest, sd_device *src); void device_cleanup_tags(sd_device *device); void device_cleanup_devlinks(sd_device *device); diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c index ad7b9fc776..e09bdd0cfa 100644 --- a/src/udev/udev-event.c +++ b/src/udev/udev-event.c @@ -329,23 +329,6 @@ static int event_execute_rules_on_remove(UdevEvent *event, UdevRules *rules) { return r; } -static int copy_all_tags(sd_device *d, sd_device *s) { - int r; - - assert(d); - - if (!s) - return 0; - - FOREACH_DEVICE_TAG(s, tag) { - r = device_add_tag(d, tag, false); - if (r < 0) - return r; - } - - return 0; -} - static int update_clone(UdevEvent *event) { sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev_db_clone); int r; @@ -398,7 +381,7 @@ int udev_event_execute_rules(UdevEvent *event, UdevRules *rules) { if (r < 0) return log_device_debug_errno(dev, r, "Failed to clone sd_device object: %m"); - r = copy_all_tags(dev, event->dev_db_clone); + r = device_copy_all_tags(dev, event->dev_db_clone); if (r < 0) log_device_warning_errno(dev, r, "Failed to copy all tags from old database entry, ignoring: %m"); From fc03d200c7e91481680802fce46c5e4244aa92a0 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 23 Feb 2026 04:58:43 +0900 Subject: [PATCH 3/5] sd-device: do not clear sd_device.all_tags even on TAG="hoge" The current tag concept has been introduced by e77b146f825ef1bb63c297cc713962b94422d2c6 (v247) to make symlinks in /run/udev/tags/ are 'sticky'. However, when TAG= (rather than TAG+=) is specified, then the tags assigned in the previous events were also cleared. This fixes the issue and now symlinks in /run/udev/tags/ are really 'sticky'. Fortunately, TAG= is mostly unused. So, the issue should not affect and the fix should not change anything on almost all systems. --- src/libsystemd/sd-device/device-private.c | 14 ++++++++++++-- src/libsystemd/sd-device/device-private.h | 2 +- src/udev/udev-rules.c | 7 +++++-- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/libsystemd/sd-device/device-private.c b/src/libsystemd/sd-device/device-private.c index 828697e617..d640eeb250 100644 --- a/src/libsystemd/sd-device/device-private.c +++ b/src/libsystemd/sd-device/device-private.c @@ -710,13 +710,23 @@ int device_copy_all_tags(sd_device *dest, sd_device *src) { return 0; } -void device_cleanup_tags(sd_device *device) { +int device_cleanup_tags(sd_device *device, sd_device *original) { + int r; + assert(device); - device->all_tags = set_free(device->all_tags); + _cleanup_set_free_ Set *saved = TAKE_PTR(device->all_tags); device->current_tags = set_free(device->current_tags); device->property_tags_outdated = true; device->tags_generation++; + + r = device_copy_all_tags(device, original); + if (r < 0) { + set_free_and_replace(device->all_tags, saved); + return r; + } + + return 0; } void device_cleanup_devlinks(sd_device *device) { diff --git a/src/libsystemd/sd-device/device-private.h b/src/libsystemd/sd-device/device-private.h index 376a4a479c..90f4e103f5 100644 --- a/src/libsystemd/sd-device/device-private.h +++ b/src/libsystemd/sd-device/device-private.h @@ -46,7 +46,7 @@ int device_add_propertyf(sd_device *device, const char *key, const char *format, int device_add_tag(sd_device *device, const char *tag, bool both); void device_remove_tag(sd_device *device, const char *tag); int device_copy_all_tags(sd_device *dest, sd_device *src); -void device_cleanup_tags(sd_device *device); +int device_cleanup_tags(sd_device *device, sd_device *original); void device_cleanup_devlinks(sd_device *device); uint64_t device_get_properties_generation(sd_device *device); diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c index 06686e3715..f0e4fbccfd 100644 --- a/src/udev/udev-rules.c +++ b/src/udev/udev-rules.c @@ -2900,8 +2900,11 @@ static int udev_rule_apply_token_to_event( assert(IN_SET(token->op, OP_ASSIGN, OP_ADD)); - if (token->op == OP_ASSIGN) - device_cleanup_tags(dev); + if (token->op == OP_ASSIGN) { + r = device_cleanup_tags(dev, event->dev_db_clone); + if (r < 0) + log_event_warning_errno(event, token, r, "Failed to clear previously assigned tags, ignoring: %m"); + } r = device_add_tag(dev, buf, /* both= */ true); if (r == -ENOMEM) From e0a2369dd1ea3b932c6431c015ca275922beb4ac Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 23 Feb 2026 05:38:03 +0900 Subject: [PATCH 4/5] sd-device: do not try to remove previous tag indexes The removed code in device_tag_index() in fact does nothing, as sd_device.all_tags is never cleared. Moreover, not only the code is meaningless, but it is theoretically/logically wrong, as the symlinks in /run/udev/tags/ should be 'sticky', hence we should even not try to remove them. --- src/libsystemd/sd-device/device-private.c | 8 +------- src/libsystemd/sd-device/device-private.h | 2 +- src/udev/udev-error.c | 2 +- src/udev/udev-event.c | 4 ++-- 4 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/libsystemd/sd-device/device-private.c b/src/libsystemd/sd-device/device-private.c index d640eeb250..054c3545d0 100644 --- a/src/libsystemd/sd-device/device-private.c +++ b/src/libsystemd/sd-device/device-private.c @@ -769,15 +769,9 @@ static int device_tag(sd_device *device, const char *tag, bool add) { return 0; } -int device_tag_index(sd_device *device, sd_device *device_old, bool add) { +int device_tag_index(sd_device *device, bool add) { int r = 0; - if (add && device_old) - /* delete possible left-over tags */ - FOREACH_DEVICE_TAG(device_old, tag) - if (!sd_device_has_tag(device, tag)) - RET_GATHER(r, device_tag(device_old, tag, false)); - FOREACH_DEVICE_TAG(device, tag) RET_GATHER(r, device_tag(device, tag, add)); diff --git a/src/libsystemd/sd-device/device-private.h b/src/libsystemd/sd-device/device-private.h index 90f4e103f5..f2f349f2ac 100644 --- a/src/libsystemd/sd-device/device-private.h +++ b/src/libsystemd/sd-device/device-private.h @@ -59,7 +59,7 @@ int device_get_properties_strv(sd_device *device, char ***ret); int device_clone_with_db(sd_device *device, sd_device **ret); -int device_tag_index(sd_device *device, sd_device *device_old, bool add); +int device_tag_index(sd_device *device, bool add); bool device_should_have_db(sd_device *device); int device_has_db(sd_device *device); int device_update_db(sd_device *device); diff --git a/src/udev/udev-error.c b/src/udev/udev-error.c index 9f06ed4a1c..95739aaee6 100644 --- a/src/udev/udev-error.c +++ b/src/udev/udev-error.c @@ -63,7 +63,7 @@ int device_broadcast_on_error(sd_device *dev, sd_device_monitor *monitor) { /* delete state from disk */ (void) device_delete_db(dev); - (void) device_tag_index(dev, /* device_old= */ NULL, /* add= */ false); + (void) device_tag_index(dev, /* add= */ false); r = device_monitor_send(monitor, /* destination= */ NULL, dev); if (r < 0) { diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c index e09bdd0cfa..49c692944e 100644 --- a/src/udev/udev-event.c +++ b/src/udev/udev-event.c @@ -310,7 +310,7 @@ static int event_execute_rules_on_remove(UdevEvent *event, UdevRules *rules) { log_device_debug_errno(dev, r, "Failed to read database under /run/udev/data/: %m"); if (EVENT_MODE_DESTRUCTIVE(event)) { - r = device_tag_index(dev, NULL, false); + r = device_tag_index(dev, /* add= */ false); if (r < 0) log_device_debug_errno(dev, r, "Failed to remove corresponding tag files under /run/udev/tag/, ignoring: %m"); @@ -416,7 +416,7 @@ int udev_event_execute_rules(UdevEvent *event, UdevRules *rules) { if (EVENT_MODE_DESTRUCTIVE(event)) { /* (re)write database file */ - r = device_tag_index(dev, event->dev_db_clone, true); + r = device_tag_index(dev, /* add= */ true); if (r < 0) return log_device_debug_errno(dev, r, "Failed to update tags under /run/udev/tag/: %m"); } From aaadf3e907980a28910bf780b3c4852e73d6a78a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 23 Feb 2026 04:11:30 +0900 Subject: [PATCH 5/5] udev/dump: also dump current tags The "TAG" token in udev rules handles the current tags. Let's also show the current tags. --- src/udev/udev-dump.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/udev/udev-dump.c b/src/udev/udev-dump.c index e8d23377ff..57e008c73d 100644 --- a/src/udev/udev-dump.c +++ b/src/udev/udev-dump.c @@ -270,6 +270,20 @@ static int dump_event_json(UdevEvent *event, sd_json_format_flags_t flags, FILE return r; } + tags = strv_free(tags); + + FOREACH_DEVICE_CURRENT_TAG(dev, tag) { + r = strv_extend(&tags, tag); + if (r < 0) + return r; + } + + if (!strv_isempty(tags)) { + r = sd_json_variant_set_field_strv(&v, "currentTags", strv_sort(tags)); + if (r < 0) + return r; + } + char **properties; if (device_get_properties_strv(dev, &properties) >= 0 && !strv_isempty(properties)) { r = sd_json_variant_set_field_strv(&v, "properties", strv_sort(properties)); @@ -417,6 +431,12 @@ int dump_event(UdevEvent *event, sd_json_format_flags_t flags, FILE *f) { fprintf(f, " %s\n", tag); } + if (sd_device_get_current_tag_first(dev)) { + fprintf(f, "%sCurrent Tags:%s\n", ansi_highlight(), ansi_normal()); + FOREACH_DEVICE_CURRENT_TAG(dev, tag) + fprintf(f, " %s\n", tag); + } + char **properties; if (device_get_properties_strv(dev, &properties) >= 0 && !strv_isempty(properties)) { bool space = true;