From: Daan De Meyer Date: Wed, 18 May 2022 11:35:21 +0000 (+0200) Subject: dissect-image: Explicitly remove partitions when done with image X-Git-Tag: v252-rc1~936 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=75d7e04eb4662a814c26010d447eed8a862f5ec1;p=thirdparty%2Fsystemd.git dissect-image: Explicitly remove partitions when done with image When closing a loop device, the kernel will asynchronously remove the probed partitions. This can lead to race conditions where we try to reuse a partition device that still needs to be removed by the kernel. To avoid such issues, let's explicitly try to remove any partitions using BLKPG_DEL_PARTITION when we're done with an image. To make sure we don't try to remove partitions when we want them to remain (e.g. systemd-dissect --mount), we add dissected_image_relinquish() in a similar vein to loop_device_relinquish() and decrypted_image_relinquish(). --- diff --git a/src/core/namespace.c b/src/core/namespace.c index 926aa961741..ffa236cc887 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -2426,6 +2426,7 @@ int setup_namespace( } } + dissected_image_relinquish(dissected_image); loop_device_relinquish(loop_device); } else if (root_directory) { diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index bd94a755db7..0239d9b394f 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -648,6 +648,7 @@ static int action_mount(DissectedImage *m, LoopDevice *d) { return log_error_errno(r, "Failed to relinquish DM devices: %m"); } + dissected_image_relinquish(m); loop_device_relinquish(d); return 0; } @@ -700,6 +701,7 @@ static int action_copy(DissectedImage *m, LoopDevice *d) { return log_error_errno(r, "Failed to relinquish DM devices: %m"); } + dissected_image_relinquish(m); loop_device_relinquish(d); if (arg_action == ACTION_COPY_FROM) { diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c index 589a2cc5822..33e1a20de6f 100644 --- a/src/gpt-auto-generator/gpt-auto-generator.c +++ b/src/gpt-auto-generator/gpt-auto-generator.c @@ -768,6 +768,8 @@ static int enumerate_partitions(dev_t devnum) { r = k; } + dissected_image_relinquish(m); + return r; } diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 1ab88839aac..6885b14bb25 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -148,11 +148,45 @@ static void check_partition_flags( log_debug("Unexpected partition flag %llu set on %s!", bit, node); } } + +static int ioctl_partition_remove(int fd, const char *name, int nr) { + assert(fd >= 0); + assert(name); + assert(nr > 0); + + struct blkpg_partition bp = { + .pno = nr, + }; + + struct blkpg_ioctl_arg ba = { + .op = BLKPG_DEL_PARTITION, + .data = &bp, + .datalen = sizeof(bp), + }; + + if (strlen(name) >= sizeof(bp.devname)) + return -EINVAL; + + strcpy(bp.devname, name); + + return RET_NERRNO(ioctl(fd, BLKPG, &ba)); +} #endif -static void dissected_partition_done(DissectedPartition *p) { +static void dissected_partition_done(int fd, DissectedPartition *p) { + assert(fd >= 0); assert(p); +#if HAVE_BLKID + if (p->node && p->partno > 0 && !p->relinquished) { + int r; + + r = ioctl_partition_remove(fd, p->node, p->partno); + if (r < 0) + log_debug_errno(r, "BLKPG_DEL_PARTITION failed, ignoring: %m"); + } +#endif + free(p->fstype); free(p->node); free(p->label); @@ -332,9 +366,14 @@ int dissect_image( return -ENOMEM; *m = (DissectedImage) { + .fd = -1, .has_init_system = -1, }; + m->fd = fcntl(fd, F_DUPFD_CLOEXEC, 3); + if (m->fd < 0) + return -errno; + r = sd_device_get_sysname(d, &sysname); if (r < 0) return log_debug_errno(r, "Failed to get device sysname: %m"); @@ -790,10 +829,14 @@ int dissect_image( * scheme in OS images. */ if (!PARTITION_DESIGNATOR_VERSIONED(designator) || - strverscmp_improved(m->partitions[designator].label, label) >= 0) + strverscmp_improved(m->partitions[designator].label, label) >= 0) { + r = ioctl_partition_remove(fd, node, nr); + if (r < 0) + log_debug_errno(r, "BLKPG_DEL_PARTITION failed, ignoring: %m"); continue; + } - dissected_partition_done(m->partitions + designator); + dissected_partition_done(fd, m->partitions + designator); } if (fstype) { @@ -863,8 +906,12 @@ int dissect_image( const char *sid, *options = NULL; /* First one wins */ - if (m->partitions[PARTITION_XBOOTLDR].found) + if (m->partitions[PARTITION_XBOOTLDR].found) { + r = ioctl_partition_remove(fd, node, nr); + if (r < 0) + log_debug_errno(r, "BLKPG_DEL_PARTITION failed, ignoring: %m"); continue; + } sid = blkid_partition_get_uuid(pp); if (sid) @@ -1178,8 +1225,9 @@ DissectedImage* dissected_image_unref(DissectedImage *m) { return NULL; for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) - dissected_partition_done(m->partitions + i); + dissected_partition_done(m->fd, m->partitions + i); + safe_close(m->fd); free(m->image_name); free(m->hostname); strv_free(m->machine_info); @@ -1189,6 +1237,16 @@ DissectedImage* dissected_image_unref(DissectedImage *m) { return mfree(m); } +void dissected_image_relinquish(DissectedImage *m) { + assert(m); + + /* Partitions are automatically removed when the underlying loop device is closed. We just need to + * make sure we don't try to remove the partitions early. */ + + for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) + m->partitions[i].relinquished = true; +} + static int is_loop_device(const char *path) { char s[SYS_BLOCK_PATH_MAX("/../loop/")]; struct stat st; @@ -3023,6 +3081,7 @@ int mount_image_privately_interactively( return log_error_errno(r, "Failed to relinquish DM devices: %m"); } + dissected_image_relinquish(dissected_image); loop_device_relinquish(d); *ret_directory = TAKE_PTR(created_dir); @@ -3183,6 +3242,7 @@ int verity_dissect_and_mount( return log_debug_errno(r, "Failed to relinquish decrypted image: %m"); } + dissected_image_relinquish(dissected_image); loop_device_relinquish(loop_device); return 0; diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index 55bb8a1dadc..5230189c163 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -31,6 +31,7 @@ struct DissectedPartition { char *mount_options; uint64_t size; uint64_t offset; + bool relinquished; }; typedef enum PartitionDesignator { @@ -203,6 +204,8 @@ typedef enum DissectImageFlags { } DissectImageFlags; struct DissectedImage { + int fd; /* Backing fd */ + bool encrypted:1; bool has_verity:1; /* verity available in image, but not necessarily used */ bool has_verity_sig:1; /* pkcs#7 signature embedded in image */ @@ -258,6 +261,7 @@ int dissect_image_and_warn(int fd, const char *name, const VeritySettings *verit DissectedImage* dissected_image_unref(DissectedImage *m); DEFINE_TRIVIAL_CLEANUP_FUNC(DissectedImage*, dissected_image_unref); +void dissected_image_relinquish(DissectedImage *m); int dissected_image_decrypt(DissectedImage *m, const char *passphrase, const VeritySettings *verity, DissectImageFlags flags, DecryptedImage **ret); int dissected_image_decrypt_interactively(DissectedImage *m, const char *passphrase, const VeritySettings *verity, DissectImageFlags flags, DecryptedImage **ret); diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c index 364af195e0d..002ebb09f8d 100644 --- a/src/sysext/sysext.c +++ b/src/sysext/sysext.c @@ -584,6 +584,7 @@ static int merge_subprocess(Hashmap *images, const char *workspace) { return log_error_errno(r, "Failed to relinquish DM devices: %m"); } + dissected_image_relinquish(m); loop_device_relinquish(d); break; }