diff --git a/src/udev/udev-watch.c b/src/udev/udev-watch.c index c28c43b2af..1b8e2b8dbd 100644 --- a/src/udev/udev-watch.c +++ b/src/udev/udev-watch.c @@ -181,6 +181,9 @@ int udev_watch_begin(int inotify_fd, sd_device *dev) { assert(inotify_fd >= 0); assert(dev); + /* Ignore the request of watching the device node on remove event, as the device node specified by + * DEVNAME= has already been removed, and may already be assigned to another device. Consider the + * case e.g. a USB stick memory was unplugged and then another one is plugged. */ if (device_for_action(dev, SD_DEVICE_REMOVE)) return 0; diff --git a/src/udev/udev-worker.c b/src/udev/udev-worker.c index 5577f5fab8..59f56f653c 100644 --- a/src/udev/udev-worker.c +++ b/src/udev/udev-worker.c @@ -97,6 +97,12 @@ static int worker_lock_whole_disk(sd_device *dev, int *ret_fd) { * event handling; in the case udev acquired the lock, the external process can block until udev has * finished its event handling. */ + /* Do not try to lock device on remove event, as the device node specified by DEVNAME= has already + * been removed, and may already be assigned to another device. Consider the case e.g. a USB stick + * memory was unplugged and then another one is plugged. */ + if (device_for_action(dev, SD_DEVICE_REMOVE)) + goto nolock; + r = udev_get_whole_disk(dev, &dev_whole_disk, &val); if (r < 0) return r; @@ -200,26 +206,31 @@ static int worker_process_device(UdevWorker *worker, sd_device *dev) { if (r < 0) return r; + /* Process RUN=. */ udev_event_execute_run(udev_event); if (!worker->rtnl) /* in case rtnl was initialized */ worker->rtnl = sd_netlink_ref(udev_event->rtnl); + /* Enable watch if requested. */ if (udev_event->inotify_watch) { r = udev_watch_begin(worker->inotify_fd, dev); if (r < 0 && r != -ENOENT) /* The device may be already removed, ignore -ENOENT. */ log_device_warning_errno(dev, r, "Failed to add inotify watch, ignoring: %m"); } - /* Finalize database. */ - r = device_add_property(dev, "ID_PROCESSING", NULL); - if (r < 0) - return log_device_warning_errno(dev, r, "Failed to remove 'ID_PROCESSING' property: %m"); + /* Finalize database. But do not re-create database on remove, which has been already removed in + * event_execute_rules_on_remove(). */ + if (!device_for_action(dev, SD_DEVICE_REMOVE)) { + r = device_add_property(dev, "ID_PROCESSING", NULL); + if (r < 0) + return log_device_warning_errno(dev, r, "Failed to remove 'ID_PROCESSING' property: %m"); - r = device_update_db(dev); - if (r < 0) - return log_device_warning_errno(dev, r, "Failed to update database under /run/udev/data/: %m"); + r = device_update_db(dev); + if (r < 0) + return log_device_warning_errno(dev, r, "Failed to update database under /run/udev/data/: %m"); + } log_device_uevent(dev, "Device processed"); return 0; diff --git a/test/units/TEST-17-UDEV.database.sh b/test/units/TEST-17-UDEV.database.sh new file mode 100755 index 0000000000..2b66333cad --- /dev/null +++ b/test/units/TEST-17-UDEV.database.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -ex +set -o pipefail + +udevadm control --log-level=debug + +IFNAME=test-udev-aaa +ip link add "$IFNAME" type dummy +IFINDEX=$(ip -json link show "$IFNAME" | jq '.[].ifindex') +udevadm wait --timeout 10 "/sys/class/net/$IFNAME" +# Check if the database file is created. +[[ -e "/run/udev/data/n$IFINDEX" ]] + +ip link del "$IFNAME" +udevadm wait --timeout 10 --removed --settle "/sys/class/net/$IFNAME" +# CHeck if the database file is removed. +[[ ! -e "/run/udev/data/n$IFINDEX" ]] + +udevadm control --log-level=info + +exit 0