From 35e49f2856dc7e80cfc6c9af3dca4e3aad9b8cb5 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Wed, 5 Jul 2023 11:44:00 +0900
Subject: [PATCH] sd-device: do not read uevent file in device_clone_with_db()

Follow-up for 381f6d4ba5551898e7ff19189485072b94879281.

When the function is called, the device may be already removed, and
another device has the same syspath. Such situation can occur when a
partition removed and another is created. In that case, the sysfs paths
of the removed and newly created partitions can be same, but their
devnums are different, and thus the database files corresponding to the
devices are also different.

Fixes #27981.

Conflict:adapt context
Reference:https://github.com/systemd/systemd/commit/35e49f2856dc7e80cfc6c9af3dca4e3aad9b8cb5

---
 src/libsystemd/sd-device/device-private.c | 78 +++++++++--------------
 1 file changed, 31 insertions(+), 47 deletions(-)

diff --git a/src/libsystemd/sd-device/device-private.c b/src/libsystemd/sd-device/device-private.c
index 61f174a..d1600f6 100644
--- a/src/libsystemd/sd-device/device-private.c
+++ b/src/libsystemd/sd-device/device-private.c
@@ -747,51 +747,56 @@ int device_rename(sd_device *device, const char *name) {
         return device_add_property_internal(device, "INTERFACE", name);
 }
 
-static int device_shallow_clone(sd_device *device, sd_device **ret) {
+int device_clone_with_db(sd_device *device, sd_device **ret) {
         _cleanup_(sd_device_unrefp) sd_device *dest = NULL;
-        const char *val = NULL;
+        const char *key, *val;
         int r;
 
         assert(device);
         assert(ret);
 
+        /* The device may be already removed. Let's copy minimal set of information that was obtained through
+         * netlink socket. */
+
         r = device_new_aux(&dest);
         if (r < 0)
                 return r;
 
+        /* Seal device to prevent reading the uevent file, as the device may have been already removed. */
+        dest->sealed = true;
+
+        /* Copy syspath, then also devname, sysname or sysnum can be obtained. */
         r = device_set_syspath(dest, device->syspath, false);
         if (r < 0)
                 return r;
 
-        (void) sd_device_get_subsystem(device, &val);
-        r = device_set_subsystem(dest, val);
-        if (r < 0)
-                return r;
-        if (streq_ptr(val, "drivers")) {
-                r = free_and_strdup(&dest->driver_subsystem, device->driver_subsystem);
+        /* Copy other information stored in database. Here, do not use FOREACH_DEVICE_PROPERTY() and
+         * sd_device_get_property_value(), as they calls device_properties_prepare() ->
+         * device_read_uevent_file(), but as commented in the above, the device may be already removed and
+         * reading uevent file may fail. */
+        ORDERED_HASHMAP_FOREACH_KEY(val, key, device->properties) {
+                if (streq(key, "MINOR"))
+                        continue;
+
+                if (streq(key, "MAJOR")) {
+                        const char *minor = NULL;
+
+                        minor = ordered_hashmap_get(device->properties, "MINOR");
+                        r = device_set_devnum(dest, val, minor);
+                } else
+                        r = device_amend(dest, key, val);
                 if (r < 0)
                         return r;
-        }
-
-        /* The device may be already removed. Let's copy minimal set of information to make
-         * device_get_device_id() work without uevent file. */
 
-        if (sd_device_get_property_value(device, "IFINDEX", &val) >= 0) {
-                r = device_set_ifindex(dest, val);
-                if (r < 0)
-                        return r;
-        }
-
-        if (sd_device_get_property_value(device, "MAJOR", &val) >= 0) {
-                const char *minor = NULL;
-
-                (void) sd_device_get_property_value(device, "MINOR", &minor);
-                r = device_set_devnum(dest, val, minor);
-                if (r < 0)
-                        return r;
+                if (streq(key, "SUBSYSTEM") && streq(val, "drivers")) {
+                        r = free_and_strdup(&dest->driver_subsystem, device->driver_subsystem);
+                        if (r < 0)
+                                return r;
+                }
         }
 
-        r = device_read_uevent_file(dest);
+        /* Finally, read the udev database. */
+        r = device_read_db_internal(dest, /* force = */ true);
         if (r < 0)
                 return r;
 
@@ -799,27 +804,6 @@ static int device_shallow_clone(sd_device *device, sd_device **ret) {
         return 0;
 }
 
-int device_clone_with_db(sd_device *device, sd_device **ret) {
-        _cleanup_(sd_device_unrefp) sd_device *dest = NULL;
-        int r;
-
-        assert(device);
-        assert(ret);
-
-        r = device_shallow_clone(device, &dest);
-        if (r < 0)
-                return r;
-
-        r = device_read_db(dest);
-        if (r < 0)
-                return r;
-
-        dest->sealed = true;
-
-        *ret = TAKE_PTR(dest);
-        return 0;
-}
-
 int device_new_from_synthetic_event(sd_device **new_device, const char *syspath, const char *action) {
         _cleanup_(sd_device_unrefp) sd_device *ret = NULL;
         int r;
-- 
2.39.1

