summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/libsystemd/sd-device/device-internal.h44
-rw-r--r--src/libsystemd/sd-device/device-private.c200
-rw-r--r--src/libsystemd/sd-device/device-private.h5
-rw-r--r--src/libsystemd/sd-device/sd-device.c50
-rw-r--r--src/rfkill/rfkill.c86
-rw-r--r--src/shared/dissect-image.c224
-rw-r--r--src/shared/udev-util.c78
-rw-r--r--src/shared/udev-util.h4
-rw-r--r--src/udev/udev-event.c2
9 files changed, 286 insertions, 407 deletions
diff --git a/src/libsystemd/sd-device/device-internal.h b/src/libsystemd/sd-device/device-internal.h
index 39e984cf7c..3ffca35cbe 100644
--- a/src/libsystemd/sd-device/device-internal.h
+++ b/src/libsystemd/sd-device/device-internal.h
@@ -5,12 +5,14 @@
#include "hashmap.h"
#include "set.h"
+#include "time-util.h"
struct sd_device {
unsigned n_ref;
+ int watch_handle;
+
sd_device *parent;
- bool parent_set; /* no need to try to reload parent */
OrderedHashmap *properties;
Iterator properties_iterator;
@@ -24,60 +26,59 @@ struct sd_device {
Set *sysattrs; /* names of sysattrs */
Iterator sysattrs_iterator;
- bool sysattrs_read; /* don't try to re-read sysattrs once read */
Set *tags;
Iterator tags_iterator;
uint64_t tags_generation; /* changes whenever the tags are changed */
uint64_t tags_iterator_generation; /* generation when iteration was started */
- bool property_tags_outdated; /* need to update TAGS= property */
Set *devlinks;
Iterator devlinks_iterator;
uint64_t devlinks_generation; /* changes whenever the devlinks are changed */
uint64_t devlinks_iterator_generation; /* generation when iteration was started */
- bool property_devlinks_outdated; /* need to update DEVLINKS= property */
int devlink_priority;
+ int ifindex;
+ char *devtype;
+ char *devname;
+ dev_t devnum;
+
char **properties_strv; /* the properties hashmap as a strv */
uint8_t *properties_nulstr; /* the same as a nulstr */
size_t properties_nulstr_len;
- bool properties_buf_outdated; /* need to reread hashmap */
-
- int watch_handle;
char *syspath;
const char *devpath;
const char *sysnum;
char *sysname;
- bool sysname_set; /* don't reread sysname */
-
- char *devtype;
- int ifindex;
- char *devname;
- dev_t devnum;
char *subsystem;
- bool subsystem_set; /* don't reread subsystem */
char *driver_subsystem; /* only set for the 'drivers' subsystem */
- bool driver_subsystem_set; /* don't reread subsystem */
char *driver;
- bool driver_set; /* don't reread driver */
char *id_filename;
- bool is_initialized;
uint64_t usec_initialized;
mode_t devmode;
uid_t devuid;
gid_t devgid;
- bool uevent_loaded; /* don't reread uevent */
+ bool parent_set:1; /* no need to try to reload parent */
+ bool sysattrs_read:1; /* don't try to re-read sysattrs once read */
+ bool property_tags_outdated:1; /* need to update TAGS= property */
+ bool property_devlinks_outdated:1; /* need to update DEVLINKS= property */
+ bool properties_buf_outdated:1; /* need to reread hashmap */
+ bool sysname_set:1; /* don't reread sysname */
+ bool subsystem_set:1; /* don't reread subsystem */
+ bool driver_subsystem_set:1; /* don't reread subsystem */
+ bool driver_set:1; /* don't reread driver */
+ bool uevent_loaded:1; /* don't reread uevent */
bool db_loaded; /* don't reread db */
- bool sealed; /* don't read more information from uevent/db */
- bool db_persist; /* don't clean up the db when switching from initrd to real root */
+ bool is_initialized:1;
+ bool sealed:1; /* don't read more information from uevent/db */
+ bool db_persist:1; /* don't clean up the db when switching from initrd to real root */
};
typedef enum DeviceAction {
@@ -97,7 +98,6 @@ int device_new_aux(sd_device **ret);
int device_add_property_aux(sd_device *device, const char *key, const char *value, bool db);
int device_add_property_internal(sd_device *device, const char *key, const char *value);
int device_read_uevent_file(sd_device *device);
-int device_read_db_aux(sd_device *device, bool force);
int device_set_syspath(sd_device *device, const char *_syspath, bool verify);
int device_set_ifindex(sd_device *device, const char *ifindex);
@@ -107,7 +107,7 @@ int device_set_devtype(sd_device *device, const char *_devtype);
int device_set_devnum(sd_device *device, const char *major, const char *minor);
int device_set_subsystem(sd_device *device, const char *_subsystem);
int device_set_driver(sd_device *device, const char *_driver);
-int device_set_usec_initialized(sd_device *device, const char *initialized);
+int device_set_usec_initialized(sd_device *device, usec_t when);
DeviceAction device_action_from_string(const char *s) _pure_;
const char *device_action_to_string(DeviceAction a) _const_;
diff --git a/src/libsystemd/sd-device/device-private.c b/src/libsystemd/sd-device/device-private.c
index 95a581e66e..36beb3e7df 100644
--- a/src/libsystemd/sd-device/device-private.c
+++ b/src/libsystemd/sd-device/device-private.c
@@ -47,81 +47,6 @@ int device_add_property(sd_device *device, const char *key, const char *value) {
return 0;
}
-static int device_add_property_internal_from_string(sd_device *device, const char *str) {
- _cleanup_free_ char *key = NULL;
- char *value;
-
- assert(device);
- assert(str);
-
- key = strdup(str);
- if (!key)
- return -ENOMEM;
-
- value = strchr(key, '=');
- if (!value)
- return -EINVAL;
-
- *value = '\0';
-
- if (isempty(++value))
- value = NULL;
-
- return device_add_property_internal(device, key, value);
-}
-
-static int handle_db_line(sd_device *device, char key, const char *value) {
- char *path;
- int r;
-
- assert(device);
- assert(value);
-
- switch (key) {
- case 'S':
- path = strjoina("/dev/", value);
- r = device_add_devlink(device, path);
- if (r < 0)
- return r;
-
- break;
- case 'L':
- r = safe_atoi(value, &device->devlink_priority);
- if (r < 0)
- return r;
-
- break;
- case 'E':
- r = device_add_property_internal_from_string(device, value);
- if (r < 0)
- return r;
-
- break;
- case 'G':
- r = device_add_tag(device, value);
- if (r < 0)
- return r;
-
- break;
- case 'W':
- r = safe_atoi(value, &device->watch_handle);
- if (r < 0)
- return r;
-
- break;
- case 'I':
- r = device_set_usec_initialized(device, value);
- if (r < 0)
- return r;
-
- break;
- default:
- log_device_debug(device, "sd-device: Unknown key '%c' in device db, ignoring", key);
- }
-
- return 0;
-}
-
void device_set_devlink_priority(sd_device *device, int priority) {
assert(device);
@@ -135,119 +60,16 @@ void device_set_is_initialized(sd_device *device) {
}
int device_ensure_usec_initialized(sd_device *device, sd_device *device_old) {
- char num[DECIMAL_STR_MAX(usec_t)];
- usec_t usec_initialized;
- int r;
+ usec_t when;
assert(device);
if (device_old && device_old->usec_initialized > 0)
- usec_initialized = device_old->usec_initialized;
+ when = device_old->usec_initialized;
else
- usec_initialized = now(CLOCK_MONOTONIC);
-
- r = snprintf(num, sizeof(num), USEC_FMT, usec_initialized);
- if (r < 0)
- return -errno;
-
- r = device_set_usec_initialized(device, num);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-static int device_read_db(sd_device *device) {
- _cleanup_free_ char *db = NULL;
- char *path;
- const char *id, *value;
- char key;
- size_t db_len;
- unsigned i;
- int r;
-
- enum {
- PRE_KEY,
- KEY,
- PRE_VALUE,
- VALUE,
- INVALID_LINE,
- } state = PRE_KEY;
-
- assert(device);
-
- if (device->db_loaded || device->sealed)
- return 0;
-
- r = device_get_id_filename(device, &id);
- if (r < 0)
- return r;
-
- path = strjoina("/run/udev/data/", id);
-
- r = read_full_file(path, &db, &db_len);
- if (r < 0) {
- if (r == -ENOENT)
- return 0;
- else
- return log_device_debug_errno(device, r, "sd-device: Failed to read db '%s': %m", path);
- }
-
- /* devices with a database entry are initialized */
- device_set_is_initialized(device);
-
- for (i = 0; i < db_len; i++) {
- switch (state) {
- case PRE_KEY:
- if (!strchr(NEWLINE, db[i])) {
- key = db[i];
+ when = now(CLOCK_MONOTONIC);
- state = KEY;
- }
-
- break;
- case KEY:
- if (db[i] != ':') {
- log_device_debug(device, "sd-device: Invalid db entry with key '%c', ignoring", key);
-
- state = INVALID_LINE;
- } else {
- db[i] = '\0';
-
- state = PRE_VALUE;
- }
-
- break;
- case PRE_VALUE:
- value = &db[i];
-
- state = VALUE;
-
- break;
- case INVALID_LINE:
- if (strchr(NEWLINE, db[i]))
- state = PRE_KEY;
-
- break;
- case VALUE:
- if (strchr(NEWLINE, db[i])) {
- db[i] = '\0';
- r = handle_db_line(device, key, value);
- if (r < 0)
- log_device_debug_errno(device, r, "sd-device: Failed to handle db entry '%c:%s', ignoring: %m", key, value);
-
- state = PRE_KEY;
- }
-
- break;
- default:
- assert_not_reached("Invalid state when parsing db");
- }
- }
-
- device->db_loaded = true;
-
- return 0;
+ return device_set_usec_initialized(device, when);
}
uint64_t device_get_properties_generation(sd_device *device) {
@@ -391,7 +213,13 @@ static int device_amend(sd_device *device, const char *key, const char *value) {
if (r < 0)
return log_device_debug_errno(device, r, "sd-device: Failed to set devname to '%s': %m", value);
} else if (streq(key, "USEC_INITIALIZED")) {
- r = device_set_usec_initialized(device, value);
+ usec_t t;
+
+ r = safe_atou64(value, &t);
+ if (r < 0)
+ return log_device_debug_errno(device, r, "sd-device: Failed to parse timestamp '%s': %m", value);
+
+ r = device_set_usec_initialized(device, t);
if (r < 0)
return log_device_debug_errno(device, r, "sd-device: Failed to set usec-initialized to '%s': %m", value);
} else if (streq(key, "DRIVER")) {
@@ -1115,9 +943,3 @@ int device_delete_db(sd_device *device) {
return 0;
}
-
-int device_read_db_force(sd_device *device) {
- assert(device);
-
- return device_read_db_aux(device, true);
-}
diff --git a/src/libsystemd/sd-device/device-private.h b/src/libsystemd/sd-device/device-private.h
index 7e4a78e067..56558b38c6 100644
--- a/src/libsystemd/sd-device/device-private.h
+++ b/src/libsystemd/sd-device/device-private.h
@@ -49,4 +49,7 @@ int device_new_from_synthetic_event(sd_device **new_device, const char *syspath,
int device_tag_index(sd_device *dev, sd_device *dev_old, bool add);
int device_update_db(sd_device *device);
int device_delete_db(sd_device *device);
-int device_read_db_force(sd_device *device);
+int device_read_db_internal(sd_device *device, bool force);
+static inline int device_read_db(sd_device *device) {
+ return device_read_db_internal(device, false);
+}
diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c
index d5583488f2..db58615df5 100644
--- a/src/libsystemd/sd-device/sd-device.c
+++ b/src/libsystemd/sd-device/sd-device.c
@@ -22,6 +22,7 @@
#include "set.h"
#include "socket-util.h"
#include "stat-util.h"
+#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
#include "strxcpyx.h"
@@ -498,8 +499,6 @@ int device_read_uevent_file(sd_device *device) {
if (device->uevent_loaded || device->sealed)
return 0;
- device->uevent_loaded = true;
-
r = sd_device_get_syspath(device, &syspath);
if (r < 0)
return r;
@@ -507,15 +506,19 @@ int device_read_uevent_file(sd_device *device) {
path = strjoina(syspath, "/uevent");
r = read_full_file(path, &uevent, &uevent_len);
- if (r == -EACCES)
+ if (r == -EACCES) {
/* empty uevent files may be write-only */
+ device->uevent_loaded = true;
return 0;
- else if (r == -ENOENT)
+ }
+ if (r == -ENOENT)
/* some devices may not have uevent files, see set_syspath() */
return 0;
- else if (r < 0)
+ if (r < 0)
return log_device_debug_errno(device, r, "sd-device: Failed to read uevent file '%s': %m", path);
+ device->uevent_loaded = true;
+
for (i = 0; i < uevent_len; i++)
switch (state) {
case PRE_KEY:
@@ -1147,23 +1150,19 @@ static int device_add_property_internal_from_string(sd_device *device, const cha
return device_add_property_internal(device, key, value);
}
-int device_set_usec_initialized(sd_device *device, const char *initialized) {
- uint64_t usec_initialized;
+int device_set_usec_initialized(sd_device *device, usec_t when) {
+ char s[DECIMAL_STR_MAX(usec_t)];
int r;
assert(device);
- assert(initialized);
- r = safe_atou64(initialized, &usec_initialized);
- if (r < 0)
- return r;
+ xsprintf(s, USEC_FMT, when);
- r = device_add_property_internal(device, "USEC_INITIALIZED", initialized);
+ r = device_add_property_internal(device, "USEC_INITIALIZED", s);
if (r < 0)
return r;
- device->usec_initialized = usec_initialized;
-
+ device->usec_initialized = when;
return 0;
}
@@ -1194,12 +1193,19 @@ static int handle_db_line(sd_device *device, char key, const char *value) {
return r;
break;
- case 'I':
- r = device_set_usec_initialized(device, value);
+ case 'I': {
+ usec_t t;
+
+ r = safe_atou64(value, &t);
+ if (r < 0)
+ return r;
+
+ r = device_set_usec_initialized(device, t);
if (r < 0)
return r;
break;
+ }
case 'L':
r = safe_atoi(value, &device->devlink_priority);
if (r < 0)
@@ -1281,7 +1287,7 @@ int device_get_id_filename(sd_device *device, const char **ret) {
return 0;
}
-int device_read_db_aux(sd_device *device, bool force) {
+int device_read_db_internal(sd_device *device, bool force) {
_cleanup_free_ char *db = NULL;
char *path;
const char *id, *value;
@@ -1298,11 +1304,11 @@ int device_read_db_aux(sd_device *device, bool force) {
INVALID_LINE,
} state = PRE_KEY;
+ assert(device);
+
if (device->db_loaded || (!force && device->sealed))
return 0;
- device->db_loaded = true;
-
r = device_get_id_filename(device, &id);
if (r < 0)
return r;
@@ -1320,6 +1326,8 @@ int device_read_db_aux(sd_device *device, bool force) {
/* devices with a database entry are initialized */
device->is_initialized = true;
+ device->db_loaded = true;
+
for (i = 0; i < db_len; i++) {
switch (state) {
case PRE_KEY:
@@ -1372,10 +1380,6 @@ int device_read_db_aux(sd_device *device, bool force) {
return 0;
}
-static int device_read_db(sd_device *device) {
- return device_read_db_aux(device, false);
-}
-
_public_ int sd_device_get_is_initialized(sd_device *device) {
int r;
diff --git a/src/rfkill/rfkill.c b/src/rfkill/rfkill.c
index 1cc887df12..ac21dc064c 100644
--- a/src/rfkill/rfkill.c
+++ b/src/rfkill/rfkill.c
@@ -18,6 +18,7 @@
#include "proc-cmdline.h"
#include "string-table.h"
#include "string-util.h"
+#include "udev-util.h"
#include "util.h"
#include "list.h"
@@ -88,89 +89,6 @@ static int find_device(
return 0;
}
-struct DeviceMonitorData {
- const char *sysname;
- sd_device *device;
-};
-
-static int device_monitor_handler(sd_device_monitor *monitor, sd_device *device, void *userdata) {
- struct DeviceMonitorData *data = userdata;
- const char *sysname;
-
- assert(device);
- assert(data);
- assert(data->sysname);
-
- if (sd_device_get_sysname(device, &sysname) >= 0 && streq(sysname, data->sysname)) {
- data->device = sd_device_ref(device);
- return sd_event_exit(sd_device_monitor_get_event(monitor), 0);
- }
-
- return 0;
-}
-
-static int wait_for_initialized(
- sd_device *device,
- sd_device **ret) {
-
- _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL;
- _cleanup_(sd_event_unrefp) sd_event *event = NULL;
- _cleanup_(sd_device_unrefp) sd_device *d = NULL;
- struct DeviceMonitorData data = {};
- int r;
-
- assert(device);
- assert(ret);
-
- if (sd_device_get_is_initialized(device) > 0) {
- *ret = sd_device_ref(device);
- return 0;
- }
-
- assert_se(sd_device_get_sysname(device, &data.sysname) >= 0);
-
- /* Wait until the device is initialized, so that we can get
- * access to the ID_PATH property */
-
- r = sd_event_default(&event);
- if (r < 0)
- return log_error_errno(r, "Failed to get default event: %m");
-
- r = sd_device_monitor_new(&monitor);
- if (r < 0)
- return log_error_errno(r, "Failed to acquire monitor: %m");
-
- r = sd_device_monitor_filter_add_match_subsystem_devtype(monitor, "rfkill", NULL);
- if (r < 0)
- return log_error_errno(r, "Failed to add rfkill device match to monitor: %m");
-
- r = sd_device_monitor_attach_event(monitor, event);
- if (r < 0)
- return log_error_errno(r, "Failed to attach event to device monitor: %m");
-
- r = sd_device_monitor_start(monitor, device_monitor_handler, &data);
- if (r < 0)
- return log_error_errno(r, "Failed to start device monitor: %m");
-
- /* Check again, maybe things changed */
- r = sd_device_new_from_subsystem_sysname(&d, "rfkill", data.sysname);
- if (r < 0)
- return log_full_errno(IN_SET(r, -ENOENT, -ENXIO, -ENODEV) ? LOG_DEBUG : LOG_ERR, r,
- "Failed to open device '%s': %m", data.sysname);
-
- if (sd_device_get_is_initialized(d) > 0) {
- *ret = TAKE_PTR(d);
- return 0;
- }
-
- r = sd_event_loop(event);
- if (r < 0)
- return log_error_errno(r, "Event loop failed: %m");
-
- *ret = TAKE_PTR(data.device);
- return 0;
-}
-
static int determine_state_file(
const struct rfkill_event *event,
char **ret) {
@@ -187,7 +105,7 @@ static int determine_state_file(
if (r < 0)
return r;
- r = wait_for_initialized(d, &device);
+ r = device_wait_for_initialization(d, "rfkill", &device);
if (r < 0)
return r;
diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c
index 6ea8e4df8d..4c2e41c8b8 100644
--- a/src/shared/dissect-image.c
+++ b/src/shared/dissect-image.c
@@ -40,6 +40,7 @@
#include "string-util.h"
#include "strv.h"
#include "tmpfile-util.h"
+#include "udev-util.h"
#include "user-util.h"
#include "xattr-util.h"
@@ -116,6 +117,133 @@ static bool device_is_block(sd_device *d) {
return streq(ss, "block");
}
+
+static int enumerator_for_parent(sd_device *d, sd_device_enumerator **ret) {
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ int r;
+
+ r = sd_device_enumerator_new(&e);
+ if (r < 0)
+ return r;
+
+ r = sd_device_enumerator_allow_uninitialized(e);
+ if (r < 0)
+ return r;
+
+ r = sd_device_enumerator_add_match_parent(e, d);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(e);
+ return 0;
+}
+
+/* how many times to wait for the device nodes to appear */
+#define N_DEVICE_NODE_LIST_ATTEMPTS 10
+
+static int wait_for_partitions_to_appear(
+ int fd,
+ sd_device *d,
+ unsigned num_partitions,
+ sd_device_enumerator **ret_enumerator) {
+
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ sd_device *q;
+ unsigned n;
+ int r;
+
+ r = enumerator_for_parent(d, &e);
+ if (r < 0)
+ return r;
+
+ /* Count the partitions enumerated by the kernel */
+ n = 0;
+ FOREACH_DEVICE(e, q) {
+ if (sd_device_get_devnum(q, NULL) < 0)
+ continue;
+ if (!device_is_block(q))
+ continue;
+ if (device_is_mmc_special_partition(q))
+ continue;
+
+ r = device_wait_for_initialization(q, "block", NULL);
+ if (r < 0)
+ return r;
+
+ n++;
+ }
+
+ if (n == num_partitions + 1) {
+ *ret_enumerator = TAKE_PTR(e);
+ return 0; /* success! */
+ }
+ if (n > num_partitions + 1)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO),
+ "blkid and kernel partition lists do not match.");
+
+ /* The kernel has probed fewer partitions than blkid? Maybe the kernel prober is still running or it
+ * got EBUSY because udev already opened the device. Let's reprobe the device, which is a synchronous
+ * call that waits until probing is complete. */
+
+ for (unsigned j = 0; ; j++) {
+ if (j++ > 20)
+ return -EBUSY;
+
+ if (ioctl(fd, BLKRRPART, 0) >= 0)
+ break;
+ r = -errno;
+ if (r == -EINVAL) {
+ struct loop_info64 info;
+
+ /* If we are running on a loop device that has partition scanning off, return
+ * an explicit recognizable error about this, so that callers can generate a
+ * proper message explaining the situation. */
+
+ if (ioctl(fd, LOOP_GET_STATUS64, &info) >= 0 && (info.lo_flags & LO_FLAGS_PARTSCAN) == 0) {
+ log_debug("Device is a loop device and partition scanning is off!");
+ return -EPROTONOSUPPORT;
+ }
+ }
+ if (r != -EBUSY)
+ return r;
+
+ /* If something else has the device open, such as an udev rule, the ioctl will return
+ * EBUSY. Since there's no way to wait until it isn't busy anymore, let's just wait a bit,
+ * and try again.
+ *
+ * This is really something they should fix in the kernel! */
+ (void) usleep(50 * USEC_PER_MSEC);
+
+ }
+
+ return -EAGAIN; /* no success yet, try again */
+}
+
+static int loop_wait_for_partitions_to_appear(
+ int fd,
+ sd_device *d,
+ unsigned num_partitions,
+ sd_device_enumerator **ret_enumerator) {
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
+ int r;
+
+ log_debug("Waiting for device (parent + %d partitions) to appear...", num_partitions);
+
+ r = device_wait_for_initialization(d, "block", &device);
+ if (r < 0)
+ return r;
+
+ for (unsigned i = 0; i < N_DEVICE_NODE_LIST_ATTEMPTS; i++) {
+ r = wait_for_partitions_to_appear(fd, device, num_partitions, ret_enumerator);
+ if (r != -EAGAIN)
+ return r;
+ }
+
+ return log_debug_errno(SYNTHETIC_ERRNO(ENXIO),
+ "Kernel partitions dit not appear within %d attempts",
+ N_DEVICE_NODE_LIST_ATTEMPTS);
+}
+
#endif
int dissect_image(
@@ -204,6 +332,10 @@ int dissect_image(
if (!m)
return -ENOMEM;
+ r = sd_device_new_from_devnum(&d, 'b', st.st_rdev);
+ if (r < 0)
+ return r;
+
if (!(flags & DISSECT_IMAGE_GPT_ONLY) &&
(flags & DISSECT_IMAGE_REQUIRE_ROOT)) {
const char *usage = NULL;
@@ -237,6 +369,10 @@ int dissect_image(
m->encrypted = streq_ptr(fstype, "crypto_LUKS");
+ r = loop_wait_for_partitions_to_appear(fd, d, 0, &e);
+ if (r < 0)
+ return r;
+
*ret = TAKE_PTR(m);
return 0;
@@ -258,96 +394,10 @@ int dissect_image(
if (!pl)
return -errno ?: -ENOMEM;
- r = sd_device_new_from_devnum(&d, 'b', st.st_rdev);
+ r = loop_wait_for_partitions_to_appear(fd, d, blkid_partlist_numof_partitions(pl), &e);
if (r < 0)
return r;
- for (i = 0;; i++) {
- int n, z;
-
- if (i >= 10) {
- log_debug("Kernel partitions never appeared.");
- return -ENXIO;
- }
-
- r = sd_device_enumerator_new(&e);
- if (r < 0)
- return r;
-
- r = sd_device_enumerator_allow_uninitialized(e);
- if (r < 0)
- return r;
-
- r = sd_device_enumerator_add_match_parent(e, d);
- if (r < 0)
- return r;
-
- /* Count the partitions enumerated by the kernel */
- n = 0;
- FOREACH_DEVICE(e, q) {
- if (sd_device_get_devnum(q, NULL) < 0)
- continue;
-
- if (!device_is_block(q))
- continue;
-
- if (device_is_mmc_special_partition(q))
- continue;
- n++;
- }
-
- /* Count the partitions enumerated by blkid */
- z = blkid_partlist_numof_partitions(pl);
- if (n == z + 1)
- break;
- if (n > z + 1) {
- log_debug("blkid and kernel partition list do not match.");
- return -EIO;
- }
- if (n < z + 1) {
- unsigned j = 0;
-
- /* The kernel has probed fewer partitions than blkid? Maybe the kernel prober is still running
- * or it got EBUSY because udev already opened the device. Let's reprobe the device, which is a
- * synchronous call that waits until probing is complete. */
-
- for (;;) {
- if (j++ > 20)
- return -EBUSY;
-
- if (ioctl(fd, BLKRRPART, 0) < 0) {
- r = -errno;
-
- if (r == -EINVAL) {
- struct loop_info64 info;
-
- /* If we are running on a loop device that has partition scanning off,
- * return an explicit recognizable error about this, so that callers
- * can generate a proper message explaining the situation. */
-
- if (ioctl(fd, LOOP_GET_STATUS64, &info) >= 0 && (info.lo_flags & LO_FLAGS_PARTSCAN) == 0) {
- log_debug("Device is loop device and partition scanning is off!");
- return -EPROTONOSUPPORT;
- }
- }
- if (r != -EBUSY)
- return r;
- } else
- break;
-
- /* If something else has the device open, such as an udev rule, the ioctl will return
- * EBUSY. Since there's no way to wait until it isn't busy anymore, let's just wait a
- * bit, and try again.
- *
- * This is really something they should fix in the kernel! */
-
- (void) usleep(50 * USEC_PER_MSEC);
- }
- }
-
- e = sd_device_enumerator_unref(e);
- }
-
FOREACH_DEVICE(e, q) {
unsigned long long pflags;
blkid_partition pp;
diff --git a/src/shared/udev-util.c b/src/shared/udev-util.c
index fdeaf8f613..4200032b3b 100644
--- a/src/shared/udev-util.c
+++ b/src/shared/udev-util.c
@@ -91,3 +91,81 @@ int udev_parse_config_full(
return 0;
}
+
+struct DeviceMonitorData {
+ const char *sysname;
+ sd_device *device;
+};
+
+static int device_monitor_handler(sd_device_monitor *monitor, sd_device *device, void *userdata) {
+ struct DeviceMonitorData *data = userdata;
+ const char *sysname;
+
+ assert(device);
+ assert(data);
+ assert(data->sysname);
+ assert(!data->device);
+
+ if (sd_device_get_sysname(device, &sysname) >= 0 && streq(sysname, data->sysname)) {
+ data->device = sd_device_ref(device);
+ return sd_event_exit(sd_device_monitor_get_event(monitor), 0);
+ }
+
+ return 0;
+}
+
+int device_wait_for_initialization(sd_device *device, const char *subsystem, sd_device **ret) {
+ _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+ struct DeviceMonitorData data = {};
+ int r;
+
+ assert(device);
+ assert(subsystem);
+
+ if (sd_device_get_is_initialized(device) > 0) {
+ if (ret)
+ *ret = sd_device_ref(device);
+ return 0;
+ }
+
+ assert_se(sd_device_get_sysname(device, &data.sysname) >= 0);
+
+ /* Wait until the device is initialized, so that we can get access to the ID_PATH property */
+
+ r = sd_event_default(&event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get default event: %m");
+
+ r = sd_device_monitor_new(&monitor);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire monitor: %m");
+
+ r = sd_device_monitor_filter_add_match_subsystem_devtype(monitor, subsystem, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add %s subsystem match to monitor: %m", subsystem);
+
+ r = sd_device_monitor_attach_event(monitor, event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to attach event to device monitor: %m");
+
+ r = sd_device_monitor_start(monitor, device_monitor_handler, &data);
+ if (r < 0)
+ return log_error_errno(r, "Failed to start device monitor: %m");
+
+ /* Check again, maybe things changed. Udev will re-read the db if the device wasn't initialized
+ * yet. */
+ if (sd_device_get_is_initialized(device) > 0) {
+ if (ret)
+ *ret = sd_device_ref(device);
+ return 0;
+ }
+
+ r = sd_event_loop(event);
+ if (r < 0)
+ return log_error_errno(r, "Event loop failed: %m");
+
+ if (ret)
+ *ret = TAKE_PTR(data.device);
+ return 0;
+}
diff --git a/src/shared/udev-util.h b/src/shared/udev-util.h
index be08fe1ff8..932c4a9cd5 100644
--- a/src/shared/udev-util.h
+++ b/src/shared/udev-util.h
@@ -1,6 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
+#include "sd-device.h"
+
#include "time-util.h"
typedef enum ResolveNameTiming {
@@ -23,3 +25,5 @@ int udev_parse_config_full(
static inline int udev_parse_config(void) {
return udev_parse_config_full(NULL, NULL, NULL, NULL);
}
+
+int device_wait_for_initialization(sd_device *device, const char *subsystem, sd_device **ret);
diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c
index bc0a32f39b..e28d6a5d08 100644
--- a/src/udev/udev-event.c
+++ b/src/udev/udev-event.c
@@ -785,7 +785,7 @@ static void event_execute_rules_on_remove(
sd_device *dev = event->dev;
int r;
- r = device_read_db_force(dev);
+ r = device_read_db_internal(dev, true);
if (r < 0)
log_device_debug_errno(dev, r, "Failed to read database under /run/udev/data/: %m");