]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
dissect-image: Explicitly remove partitions when done with image
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Wed, 18 May 2022 11:35:21 +0000 (13:35 +0200)
committerLuca Boccassi <luca.boccassi@gmail.com>
Mon, 23 May 2022 17:15:16 +0000 (18:15 +0100)
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().

src/core/namespace.c
src/dissect/dissect.c
src/gpt-auto-generator/gpt-auto-generator.c
src/shared/dissect-image.c
src/shared/dissect-image.h
src/sysext/sysext.c

index 926aa961741ae70fc5c5e3e923b95f448d548605..ffa236cc887deea7a10dcb58f3f9c5a36342e6f8 100644 (file)
@@ -2426,6 +2426,7 @@ int setup_namespace(
                         }
                 }
 
+                dissected_image_relinquish(dissected_image);
                 loop_device_relinquish(loop_device);
 
         } else if (root_directory) {
index bd94a755db7029cdb6dd03112b0501f45b501b76..0239d9b394f84f9eda6b8c348abc657cf011abf1 100644 (file)
@@ -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) {
index 589a2cc5822b90441f6447f4bb1c477137f9b93e..33e1a20de6f9e776cfacca81e0b7902cf71b7258 100644 (file)
@@ -768,6 +768,8 @@ static int enumerate_partitions(dev_t devnum) {
                         r = k;
         }
 
+        dissected_image_relinquish(m);
+
         return r;
 }
 
index 1ab88839aacfb9f8b9e33215bbbcc0b5305e2c65..6885b14bb25b59df581179f09f6129489a7005ca 100644 (file)
@@ -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;
index 55bb8a1dadc27ccc61370774a051476078881617..5230189c1635a655c3292381fdb775bd2d179799 100644 (file)
@@ -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);
index 364af195e0dc631c8a48910183cf001a2378d921..002ebb09f8dea42bef3f90fbdb2d7589e22f5304 100644 (file)
@@ -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;
                 }