]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-device: do not read uevent file in device_clone_with_db()
authorYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 5 Jul 2023 02:44:00 +0000 (11:44 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 5 Jul 2023 14:45:57 +0000 (23:45 +0900)
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.

src/libsystemd/sd-device/device-private.c

index 9ded8c17a178f3563a2bff6d1c7706423e8ab96e..52cf9f2ab8c7b6299b73fcc4d26c0b23e6b44e9e 100644 (file)
@@ -620,51 +620,56 @@ int device_get_devlink_priority(sd_device *device, int *ret) {
         return 0;
 }
 
-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;
 
@@ -672,27 +677,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;
-}
-
 void device_cleanup_tags(sd_device *device) {
         assert(device);