mirror of
https://github.com/drakmor/ShadowMountPlus.git
synced 2026-10-22 22:52:23 -05:00
Prevent detach and cache invalidation reuse races
This commit is contained in:
1 parent
06576fabe3
commit
01ea403b88
5 files changed
+210
-131
No files matched your search
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
+77
-50
@@ -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;
|
||||
}
|
||||
|
||||
+89
-28
@@ -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;
|
||||
|
||||
+37
-47
@@ -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);
|
||||
}
|
||||
Reference in new issue
Block a user