From 01ea403b88bc5bca70aa4be9c5666c87541221df Mon Sep 17 00:00:00 2001 From: Roman Tarasov Date: Tue, 25 Aug 2026 15:21:21 +0300 Subject: [PATCH] Prevent detach and cache invalidation reuse races --- include/sm_image_cache.h | 9 +-- include/sm_mount_device.h | 4 +- src/sm_image.c | 127 +++++++++++++++++++++++--------------- src/sm_image_cache.c | 117 ++++++++++++++++++++++++++--------- src/sm_mount_device.c | 84 +++++++++++-------------- 5 files changed, 210 insertions(+), 131 deletions(-) diff --git a/include/sm_image_cache.h b/include/sm_image_cache.h index 99ff057..a9a03d3 100644 --- a/include/sm_image_cache.h +++ b/include/sm_image_cache.h @@ -43,16 +43,17 @@ bool mark_image_cache_detach_requested(const char *path, uint64_t generation, int unit_id, attach_backend_t backend); // Claim one detach attempt and return a stable snapshot of its record. -bool claim_image_cache_detach(const char *path, int unit_id, - attach_backend_t backend, +bool claim_image_cache_detach(const char *path, uint64_t generation, + int unit_id, attach_backend_t backend, image_cache_entry_t *entry_out); // Publish the result of a claimed detach attempt and release its claim. bool finish_image_cache_detach_attempt( const char *path, uint64_t generation, int unit_id, attach_backend_t backend, const attached_unit_detach_state_t *detach_state); -// Invalidate the same generation of a cached image mount entry. -bool invalidate_image_cache_entry(int index, uint64_t generation); +// Invalidate only when the slot still owns the complete expected attachment. +bool invalidate_image_cache_entry(int index, + const image_cache_entry_t *expected); // Resolve a device mapping from the in-memory mount cache. bool resolve_device_from_mount_cache(const char *mount_point, attach_backend_t *backend_out, diff --git a/include/sm_mount_device.h b/include/sm_mount_device.h index 9558f26..bef5cac 100644 --- a/include/sm_mount_device.h +++ b/include/sm_mount_device.h @@ -19,8 +19,8 @@ bool resolve_device_from_mount(const char *mount_point, bool is_active_image_mount_point(const char *path); // Wait until /dev/lvd2 is no longer mounted during startup. bool wait_for_lvd_release(void); -// Detach a previously attached MD or LVD unit. A non-NULL state makes an -// accepted asynchronous detach idempotent across later cleanup attempts. +// Detach a previously attached MD or LVD unit. State and node identity are +// required so an accepted asynchronous detach remains bound to one device. bool detach_attached_unit(attach_backend_t backend, int unit_id, attached_unit_detach_state_t *state); // Wait for an accepted detach to remove its node, or detect unit reuse. diff --git a/src/sm_image.c b/src/sm_image.c index 376920b..617a2dc 100644 --- a/src/sm_image.c +++ b/src/sm_image.c @@ -22,6 +22,43 @@ static bool unmount_completed(unmount_result_t result) { result.directory_removed; } +static bool invalidate_matching_image_cache_entry( + int index, const image_cache_entry_t *expected) { + image_cache_entry_t current; + if (!expected || !get_image_cache_entry(index, ¤t) || + current.generation != expected->generation || + strcmp(current.path, expected->path) != 0 || + strcmp(current.mount_point, expected->mount_point) != 0 || + current.unit_id != expected->unit_id || + current.backend != expected->backend || + (expected->detach_state.node_identity_valid && + (!current.detach_state.node_identity_valid || + current.detach_state.node_device != expected->detach_state.node_device || + current.detach_state.node_inode != expected->detach_state.node_inode || + current.detach_state.node_rdev != expected->detach_state.node_rdev))) { + errno = ESTALE; + return false; + } + return invalidate_image_cache_entry(index, ¤t); +} + +static void rollback_unpublished_attached_unit(attach_backend_t backend, + int unit_id, + const char *devname) { + attached_unit_detach_state_t detach_state = {0}; + bool detached = detach_attached_unit(backend, unit_id, &detach_state); + if (!detached && !detach_state.node_identity_valid && + wait_for_dev_node_state(devname, true)) { + detached = detach_attached_unit(backend, unit_id, &detach_state); + } + if (!detached && detach_state.requested) + detached = wait_for_attached_unit_release(backend, unit_id, &detach_state); + if (!detached) { + log_debug(" [IMG][%s] unpublished unit rollback pending: unit=%d error=%s", + attach_backend_name(backend), unit_id, strerror(errno)); + } +} + static bool image_fs_type_is_pfs(image_fs_type_t fs_type) { return fs_type == IMAGE_FS_PFS || fs_type == IMAGE_FS_PFSC_CONTAINER; } @@ -798,9 +835,9 @@ static attach_backend_t select_image_backend(const runtime_config_t *cfg, } static bool attach_image_device( - const char *file_path, const char *mount_point, image_fs_type_t fs_type, - bool mount_read_only, off_t file_size, attach_backend_t attach_backend, - int *unit_id_out, char *devname_out, size_t devname_size, + const char *file_path, image_fs_type_t fs_type, bool mount_read_only, + off_t file_size, attach_backend_t attach_backend, int *unit_id_out, + char *devname_out, size_t devname_size, const image_mount_profile_t *profile, uint16_t lvd_image_type, uint64_t generation) { bool attached = false; @@ -827,14 +864,9 @@ static bool attach_image_device( attach_backend)) { log_debug(" [IMG][%s] failed to publish attached unit: unit=%d source=%s", attach_backend_name(attach_backend), *unit_id_out, file_path); - if (cache_image_mount(file_path, mount_point, *unit_id_out, - attach_backend)) { - (void)release_runtime_image_mount(file_path); - } else { - attached_unit_detach_state_t detach_state; - memset(&detach_state, 0, sizeof(detach_state)); - (void)detach_attached_unit(attach_backend, *unit_id_out, &detach_state); - } + (void)cancel_image_attachment(file_path, generation); + rollback_unpublished_attached_unit(attach_backend, *unit_id_out, + devname_out); errno = EIO; return false; } @@ -1074,7 +1106,7 @@ static bool try_exfat_lvd_type_fallback( .nested_backing_cache = false, }; if (!attach_image_device( - file_path, mount_point, IMAGE_FS_EXFAT, mount_read_only, file_size, + file_path, IMAGE_FS_EXFAT, mount_read_only, file_size, ATTACH_BACKEND_LVD, unit_id_out, devname_out, devname_size, &fallback_profile, LVD_ATTACH_IMAGE_TYPE_SINGLE, generation)) { return false; @@ -1228,8 +1260,8 @@ bool mount_image(const char *file_path, image_fs_type_t fs_type) { memset(devname, 0, sizeof(devname)); bool attached = attach_image_device( - file_path, mount_point, fs_type, mount_read_only, st.st_size, - attach_backend, &unit_id, devname, sizeof(devname), &profile, + file_path, fs_type, mount_read_only, st.st_size, attach_backend, + &unit_id, devname, sizeof(devname), &profile, get_lvd_image_type(file_path, fs_type, profile.legacy), attachment_generation); int attach_errno = errno; @@ -1320,7 +1352,7 @@ static bool unmount_child_image_mounts_for_container( : unmount_runtime_image(cached_entry.path, cached_entry.unit_id, cached_entry.backend); if (unmount_completed(result)) { - (void)invalidate_image_cache_entry(k, cached_entry.generation); + (void)invalidate_matching_image_cache_entry(k, &cached_entry); continue; } @@ -1335,26 +1367,18 @@ static bool unmount_child_image_mounts_for_container( } static bool detach_cached_image_device(const char *file_path, + uint64_t generation, attach_backend_t backend, int unit_id) { image_cache_entry_t cached_entry; - bool cached_device = claim_image_cache_detach( - file_path, unit_id, backend, &cached_entry); - if (!cached_device && errno == EINPROGRESS) + if (!claim_image_cache_detach(file_path, generation, unit_id, backend, + &cached_entry)) return false; - attached_unit_detach_state_t detach_state; - memset(&detach_state, 0, sizeof(detach_state)); - bool detach_was_requested = false; - if (cached_device) { - detach_state = cached_entry.detach_state; - detach_was_requested = detach_state.requested; - } + attached_unit_detach_state_t detach_state = cached_entry.detach_state; + bool detach_was_requested = detach_state.requested; - bool detached = detach_attached_unit( - backend, unit_id, cached_device ? &detach_state : NULL); + bool detached = detach_attached_unit(backend, unit_id, &detach_state); int detach_errno = errno; - if (!cached_device) - return detached; if (!finish_image_cache_detach_attempt( file_path, cached_entry.generation, unit_id, backend, @@ -1402,6 +1426,7 @@ static unmount_result_t unmount_image_impl(const char *file_path, int unit_id, build_image_mount_point_for_fs(file_path, fs_type, mount_point); int resolved_unit = unit_id; attach_backend_t resolved_backend = backend; + uint64_t tracked_generation = 0; if (resolved_unit < 0 || resolved_backend == ATTACH_BACKEND_NONE) { if (!resolve_device_from_mount(mount_point, &resolved_backend, @@ -1412,9 +1437,11 @@ static unmount_result_t unmount_image_impl(const char *file_path, int unit_id, } image_cache_entry_t tracked_entry; - if (find_image_cache_entry(file_path, &tracked_entry, NULL) && + bool has_tracked_device = + find_image_cache_entry(file_path, &tracked_entry, NULL) && tracked_entry.backend != ATTACH_BACKEND_NONE && - tracked_entry.unit_id >= 0) { + tracked_entry.unit_id >= 0; + if (has_tracked_device) { if (tracked_entry.unit_id != resolved_unit || tracked_entry.backend != resolved_backend) { result.error = ESTALE; @@ -1429,10 +1456,19 @@ static unmount_result_t unmount_image_impl(const char *file_path, int unit_id, if (!mark_image_cache_detach_requested( tracked_entry.path, tracked_entry.generation, tracked_entry.unit_id, tracked_entry.backend)) { - result.error = ESTALE; + result.error = errno != 0 ? errno : ESTALE; + log_debug(" [IMG][%s] unmount deferred before detach reservation: " + "source=%s unit=%d error=%s", + attach_backend_name(tracked_entry.backend), tracked_entry.path, + tracked_entry.unit_id, strerror(result.error)); errno = result.error; return result; } + tracked_generation = tracked_entry.generation; + } else if (resolved_backend != ATTACH_BACKEND_NONE && resolved_unit >= 0) { + result.error = ESTALE; + errno = result.error; + return result; } log_debug(" [IMG][%s] unmount start: source=%s mount=%s unit=%d", @@ -1482,7 +1518,7 @@ static unmount_result_t unmount_image_impl(const char *file_path, int unit_id, result.device_released = true; if (resolved_backend != ATTACH_BACKEND_NONE && resolved_unit >= 0) { result.device_released = detach_cached_image_device( - file_path, resolved_backend, resolved_unit); + file_path, tracked_generation, resolved_backend, resolved_unit); if (!result.device_released) result.error = errno != 0 ? errno : EIO; } @@ -1530,16 +1566,11 @@ static bool release_runtime_image_attachment(const char *file_path, } if (entry.backend == ATTACH_BACKEND_NONE || entry.unit_id < 0) return cancel_image_attachment(file_path, generation); - if (!mark_image_cache_detach_requested( - entry.path, generation, entry.unit_id, entry.backend)) { - errno = ESTALE; - return false; - } unmount_result_t result = unmount_runtime_image(entry.path, entry.unit_id, entry.backend); if (!unmount_completed(result)) return false; - (void)invalidate_image_cache_entry(index, generation); + (void)invalidate_matching_image_cache_entry(index, &entry); return true; } @@ -1554,15 +1585,11 @@ bool release_runtime_image_mount(const char *file_path) { } if (entry.backend == ATTACH_BACKEND_NONE || entry.unit_id < 0) return true; - if (!mark_image_cache_detach_requested( - entry.path, entry.generation, entry.unit_id, entry.backend)) { - return false; - } unmount_result_t result = unmount_runtime_image(entry.path, entry.unit_id, entry.backend); if (!unmount_completed(result)) return false; - (void)invalidate_image_cache_entry(index, entry.generation); + (void)invalidate_matching_image_cache_entry(index, &entry); return true; } @@ -1585,7 +1612,7 @@ bool release_runtime_image_mounts(void) { unmount_result_t result = unmount_runtime_image(entry.path, entry.unit_id, entry.backend); if (unmount_completed(result)) { - (void)invalidate_image_cache_entry(k, entry.generation); + (void)invalidate_matching_image_cache_entry(k, &entry); progress = true; } else { any_remaining = true; @@ -1608,7 +1635,7 @@ static bool service_pending_image_detach( unmount_result_t result = unmount_runtime_image(entry->path, entry->unit_id, entry->backend); if (unmount_completed(result)) - (void)invalidate_image_cache_entry(cache_index, entry->generation); + (void)invalidate_matching_image_cache_entry(cache_index, entry); return true; } @@ -1655,7 +1682,7 @@ void cleanup_stale_image_mounts(void) { unmount_image(cached_entry.path, cached_entry.unit_id, cached_entry.backend); if (unmount_completed(result)) - (void)invalidate_image_cache_entry(k, cached_entry.generation); + (void)invalidate_matching_image_cache_entry(k, &cached_entry); continue; } @@ -1714,7 +1741,7 @@ void cleanup_stale_image_mounts_for_root(const char *root) { unmount_image(cached_entry.path, cached_entry.unit_id, cached_entry.backend); if (unmount_completed(result)) - (void)invalidate_image_cache_entry(k, cached_entry.generation); + (void)invalidate_matching_image_cache_entry(k, &cached_entry); continue; } @@ -1764,7 +1791,7 @@ bool unmount_usb_image_mounts_for_suspend(void) { if (unmount_completed(result)) { log_debug(" [IMG][%s] USB suspend unmounted: %s", attach_backend_name(cached_entry.backend), cached_entry.path); - (void)invalidate_image_cache_entry(k, cached_entry.generation); + (void)invalidate_matching_image_cache_entry(k, &cached_entry); continue; } @@ -1868,7 +1895,7 @@ bool shutdown_image_mounts(void) { unmount_result_t result = unmount_runtime_image( cached_entry.path, cached_entry.unit_id, cached_entry.backend); if (unmount_completed(result)) { - (void)invalidate_image_cache_entry(k, cached_entry.generation); + (void)invalidate_matching_image_cache_entry(k, &cached_entry); progress = true; continue; } diff --git a/src/sm_image_cache.c b/src/sm_image_cache.c index 3071054..87ecf95 100644 --- a/src/sm_image_cache.c +++ b/src/sm_image_cache.c @@ -31,20 +31,25 @@ static uint64_t next_cache_generation(void) { return g_image_cache_generation; } +static bool attach_backend_is_valid(attach_backend_t backend) { + return backend == ATTACH_BACKEND_MD || backend == ATTACH_BACKEND_LVD; +} + static bool device_identity_matches( const attached_unit_detach_state_t *left, const attached_unit_detach_state_t *right) { if (!left->node_identity_valid || !right->node_identity_valid) - return true; + return false; return left->node_device == right->node_device && left->node_inode == right->node_inode && left->node_rdev == right->node_rdev; } -static void capture_attached_unit_identity(struct ImageCache *entry) { - memset(&entry->detach_state, 0, sizeof(entry->detach_state)); - if (entry->backend == ATTACH_BACKEND_NONE || entry->unit_id < 0) - return; +static bool refresh_attached_unit_identity(struct ImageCache *entry) { + if (!attach_backend_is_valid(entry->backend) || entry->unit_id < 0) + return false; + if (entry->detach_state.node_identity_valid) + return true; const char *prefix = entry->backend == ATTACH_BACKEND_MD ? "/dev/md" : "/dev/lvd"; @@ -53,12 +58,18 @@ static void capture_attached_unit_identity(struct ImageCache *entry) { struct stat st; if (stat(devname, &st) != 0) - return; + return false; entry->detach_state.node_identity_valid = true; entry->detach_state.node_device = (uint64_t)st.st_dev; entry->detach_state.node_inode = (uint64_t)st.st_ino; entry->detach_state.node_rdev = (uint64_t)st.st_rdev; + return true; +} + +static void reset_attached_unit_identity(struct ImageCache *entry) { + memset(&entry->detach_state, 0, sizeof(entry->detach_state)); + (void)refresh_attached_unit_identity(entry); } static void copy_cache_entry(int index, image_cache_entry_t *entry_out) { @@ -285,7 +296,8 @@ bool cancel_image_attachment(const char *path, uint64_t generation) { bool publish_image_attachment(const char *path, uint64_t generation, int unit_id, attach_backend_t backend) { if (!path || generation == 0 || unit_id < 0 || - backend == ATTACH_BACKEND_NONE) { + !attach_backend_is_valid(backend)) { + errno = EINVAL; return false; } @@ -302,15 +314,18 @@ bool publish_image_attachment(const char *path, uint64_t generation, struct ImageCache *entry = &g_image_cache[index]; entry->unit_id = unit_id; entry->backend = backend; - capture_attached_unit_identity(entry); + (void)refresh_attached_unit_identity(entry); pthread_mutex_unlock(&g_image_cache_mutex); return true; } bool complete_image_attachment(const char *path, uint64_t generation, int unit_id, attach_backend_t backend) { - if (!path || generation == 0) + if (!path || generation == 0 || unit_id < 0 || + !attach_backend_is_valid(backend)) { + errno = EINVAL; return false; + } pthread_mutex_lock(&g_image_cache_mutex); int index = find_source_path_index(path); @@ -323,13 +338,19 @@ bool complete_image_attachment(const char *path, uint64_t generation, } g_image_cache[index].state = ATTACHED_DEVICE_MOUNTED; - capture_attached_unit_identity(&g_image_cache[index]); + (void)refresh_attached_unit_identity(&g_image_cache[index]); pthread_mutex_unlock(&g_image_cache_mutex); return true; } bool cache_image_mount(const char *path, const char *mount_point, int unit_id, attach_backend_t backend) { + if (!path || !mount_point || unit_id < 0 || + !attach_backend_is_valid(backend)) { + errno = EINVAL; + return false; + } + pthread_mutex_lock(&g_image_cache_mutex); int entry_index = upsert_image_source_mapping(path, mount_point); if (entry_index < 0) { @@ -343,7 +364,7 @@ bool cache_image_mount(const char *path, const char *mount_point, int unit_id, entry->backend = backend; entry->state = ATTACHED_DEVICE_MOUNTED; entry->detach_attempt_active = false; - capture_attached_unit_identity(entry); + reset_attached_unit_identity(entry); pthread_mutex_unlock(&g_image_cache_mutex); return true; } @@ -381,47 +402,62 @@ bool find_image_cache_entry(const char *path, image_cache_entry_t *entry_out, bool mark_image_cache_detach_requested(const char *path, uint64_t generation, int unit_id, attach_backend_t backend) { - if (!path || generation == 0) + if (!path || generation == 0 || unit_id < 0 || + !attach_backend_is_valid(backend)) { + errno = EINVAL; return false; + } pthread_mutex_lock(&g_image_cache_mutex); struct ImageCache *entry = find_attachment_record( path, generation, unit_id, backend); if (!entry) { pthread_mutex_unlock(&g_image_cache_mutex); + errno = ESTALE; + return false; + } + entry->state = ATTACHED_DEVICE_DETACH_REQUESTED; + if (!refresh_attached_unit_identity(entry)) { + pthread_mutex_unlock(&g_image_cache_mutex); + errno = EAGAIN; return false; } - entry->state = ATTACHED_DEVICE_DETACH_REQUESTED; pthread_mutex_unlock(&g_image_cache_mutex); return true; } -bool claim_image_cache_detach(const char *path, int unit_id, - attach_backend_t backend, +bool claim_image_cache_detach(const char *path, uint64_t generation, + int unit_id, attach_backend_t backend, image_cache_entry_t *entry_out) { - if (!path || !entry_out) { + if (!path || generation == 0 || unit_id < 0 || + !attach_backend_is_valid(backend) || !entry_out) { errno = EINVAL; return false; } pthread_mutex_lock(&g_image_cache_mutex); - int index = find_source_path_index(path); - if (index < 0 || g_image_cache[index].unit_id != unit_id || - g_image_cache[index].backend != backend) { + struct ImageCache *entry = + find_attachment_record(path, generation, unit_id, backend); + if (!entry) { pthread_mutex_unlock(&g_image_cache_mutex); - errno = ENOENT; + errno = ESTALE; return false; } - if (g_image_cache[index].detach_attempt_active) { + if (entry->detach_attempt_active) { pthread_mutex_unlock(&g_image_cache_mutex); errno = EINPROGRESS; return false; } + if (!refresh_attached_unit_identity(entry)) { + pthread_mutex_unlock(&g_image_cache_mutex); + errno = EAGAIN; + return false; + } - g_image_cache[index].state = ATTACHED_DEVICE_DETACH_REQUESTED; - g_image_cache[index].detach_attempt_active = true; - copy_cache_entry(index, entry_out); + entry->state = ATTACHED_DEVICE_DETACH_REQUESTED; + entry->detach_attempt_active = true; + copy_cache_entry((int)(entry - g_image_cache), entry_out); pthread_mutex_unlock(&g_image_cache_mutex); return true; } @@ -430,8 +466,12 @@ bool finish_image_cache_detach_attempt( const char *path, uint64_t generation, int unit_id, attach_backend_t backend, const attached_unit_detach_state_t *detach_state) { - if (!path || !detach_state) + if (!path || generation == 0 || unit_id < 0 || + !attach_backend_is_valid(backend) || !detach_state || + !detach_state->node_identity_valid) { + errno = EINVAL; return false; + } pthread_mutex_lock(&g_image_cache_mutex); struct ImageCache *entry = find_attachment_record( @@ -439,6 +479,7 @@ bool finish_image_cache_detach_attempt( if (!entry || !entry->detach_attempt_active || !device_identity_matches(&entry->detach_state, detach_state)) { pthread_mutex_unlock(&g_image_cache_mutex); + errno = ESTALE; return false; } @@ -449,12 +490,32 @@ bool finish_image_cache_detach_attempt( return true; } -bool invalidate_image_cache_entry(int index, uint64_t generation) { +bool invalidate_image_cache_entry(int index, + const image_cache_entry_t *expected) { + if (!expected) { + errno = EINVAL; + return false; + } + pthread_mutex_lock(&g_image_cache_mutex); if (index < 0 || index >= MAX_IMAGE_MOUNTS || - !g_image_cache[index].valid || - g_image_cache[index].generation != generation) { + !g_image_cache[index].valid) { pthread_mutex_unlock(&g_image_cache_mutex); + errno = ESTALE; + return false; + } + const struct ImageCache *entry = &g_image_cache[index]; + if (entry->generation != expected->generation || + strcmp(entry->path, expected->path) != 0 || + strcmp(entry->mount_point, expected->mount_point) != 0 || + entry->unit_id != expected->unit_id || + entry->backend != expected->backend || + entry->state != expected->state || entry->detach_attempt_active || + entry->detach_state.requested != expected->detach_state.requested || + !device_identity_matches(&entry->detach_state, + &expected->detach_state)) { + pthread_mutex_unlock(&g_image_cache_mutex); + errno = ESTALE; return false; } g_image_cache[index].state = ATTACHED_DEVICE_RELEASED; diff --git a/src/sm_mount_device.c b/src/sm_mount_device.c index f5995d6..b52e8fd 100644 --- a/src/sm_mount_device.c +++ b/src/sm_mount_device.c @@ -212,6 +212,11 @@ static bool device_node_is_mounted(const char *devname, static bool detach_target_is_released_or_reused( attach_backend_t backend, int unit_id, const char *devname, const attached_unit_detach_state_t *state) { + if (!state || !state->node_identity_valid) { + errno = EAGAIN; + return false; + } + struct stat st; if (stat(devname, &st) != 0) { if (errno != ENOENT && errno != ENOTDIR) @@ -221,10 +226,9 @@ static bool detach_target_is_released_or_reused( return true; } - if (state && state->node_identity_valid && - (state->node_device != (uint64_t)st.st_dev || - state->node_inode != (uint64_t)st.st_ino || - state->node_rdev != (uint64_t)st.st_rdev)) { + if (state->node_device != (uint64_t)st.st_dev || + state->node_inode != (uint64_t)st.st_ino || + state->node_rdev != (uint64_t)st.st_rdev) { log_debug(" [IMG][%s] detach target released: unit=%d node identity " "changed", attach_backend_name(backend), unit_id); @@ -252,43 +256,39 @@ static attached_unit_detach_prepare_t prepare_attached_unit_detach( const attached_unit_detach_state_t *state, char devname[64], attached_unit_detach_state_t *request_state) { build_attached_unit_devname(backend, unit_id, devname); + *request_state = *state; + if (!request_state->node_identity_valid && + !read_device_node_identity(devname, request_state)) { + log_debug(" [IMG][%s] detach deferred without device identity: unit=%d", + attach_backend_name(backend), unit_id); + errno = EAGAIN; + return ATTACHED_UNIT_DETACH_PENDING; + } + // A freshly attached unit may not have published its device node yet. An // absent node is only proof of release after the node identity was observed // or a detach request was accepted. - bool release_can_be_observed = - !state || state->requested || state->node_identity_valid; - if (release_can_be_observed && - detach_target_is_released_or_reused(backend, unit_id, devname, state)) { + if (detach_target_is_released_or_reused(backend, unit_id, devname, + request_state)) { return ATTACHED_UNIT_DETACH_COMPLETE; } - if (!release_can_be_observed) { - struct stat st; - char mount_point[MNAMELEN]; - if (stat(devname, &st) == 0 && - device_node_is_mounted(devname, mount_point)) { - log_debug(" [IMG][%s] detach target released: unit=%d reused at %s", - attach_backend_name(backend), unit_id, mount_point); - return ATTACHED_UNIT_DETACH_COMPLETE; - } - } - if (state && state->requested) { + if (request_state->requested) { errno = EINPROGRESS; return ATTACHED_UNIT_DETACH_PENDING; } - if (state) - *request_state = *state; - else - memset(request_state, 0, sizeof(*request_state)); - if (!request_state->node_identity_valid) - (void)read_device_node_identity(devname, request_state); return ATTACHED_UNIT_DETACH_REQUEST; } bool wait_for_attached_unit_release( attach_backend_t backend, int unit_id, const attached_unit_detach_state_t *state) { + if (!state || !state->node_identity_valid) { + errno = EAGAIN; + return false; + } + char devname[64]; build_attached_unit_devname(backend, unit_id, devname); if (wait_for_dev_node_state(devname, false)) @@ -303,17 +303,12 @@ bool wait_for_attached_unit_release( } static bool finish_accepted_detach( - attach_backend_t backend, int unit_id, attached_unit_detach_state_t *state, attached_unit_detach_state_t *request_state) { request_state->requested = true; - if (state) { - *state = *request_state; - errno = EINPROGRESS; - return false; - } - - return wait_for_attached_unit_release(backend, unit_id, request_state); + *state = *request_state; + errno = EINPROGRESS; + return false; } static bool finish_failed_detach(attach_backend_t backend, int unit_id, @@ -322,7 +317,7 @@ static bool finish_failed_detach(attach_backend_t backend, int unit_id, int detach_errno) { // Before a detach request is accepted, an unobserved node may still belong // to the freshly attached unit. Keep it queued when the ioctl itself fails. - if (state && state->node_identity_valid && + if (state->node_identity_valid && detach_target_is_released_or_reused(backend, unit_id, devname, state)) { return true; } @@ -332,9 +327,6 @@ static bool finish_failed_detach(attach_backend_t backend, int unit_id, static bool detach_lvd_unit(int unit_id, attached_unit_detach_state_t *state) { - if (unit_id < 0) - return true; - char devname[64]; attached_unit_detach_state_t request_state; attached_unit_detach_prepare_t prepare = prepare_attached_unit_detach( @@ -367,15 +359,11 @@ static bool detach_lvd_unit(int unit_id, } close(fd); - return finish_accepted_detach(ATTACH_BACKEND_LVD, unit_id, state, - &request_state); + return finish_accepted_detach(state, &request_state); } static bool detach_md_unit(int unit_id, attached_unit_detach_state_t *state) { - if (unit_id < 0) - return true; - char devname[64]; attached_unit_detach_state_t request_state; attached_unit_detach_prepare_t prepare = prepare_attached_unit_detach( @@ -409,15 +397,17 @@ static bool detach_md_unit(int unit_id, } close(fd); - return finish_accepted_detach(ATTACH_BACKEND_MD, unit_id, state, - &request_state); + return finish_accepted_detach(state, &request_state); } bool detach_attached_unit(attach_backend_t backend, int unit_id, attached_unit_detach_state_t *state) { + if (!state || unit_id < 0 || + (backend != ATTACH_BACKEND_MD && backend != ATTACH_BACKEND_LVD)) { + errno = EINVAL; + return false; + } if (backend == ATTACH_BACKEND_MD) return detach_md_unit(unit_id, state); - else if (backend == ATTACH_BACKEND_LVD) - return detach_lvd_unit(unit_id, state); - return true; + return detach_lvd_unit(unit_id, state); }