]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/shared/dissect-image.c
Merge pull request #16678 from poettering/loop-configure
[thirdparty/systemd.git] / src / shared / dissect-image.c
index 24be6de6c546fa84e39afa693ff26051ebb8d6b7..d3f183b50c54cb3ac94034a86d761253dca1bb72 100644 (file)
 #include "hexdecoct.h"
 #include "hostname-util.h"
 #include "id128-util.h"
+#include "mkdir.h"
 #include "mount-util.h"
 #include "mountpoint-util.h"
+#include "namespace-util.h"
 #include "nulstr-util.h"
 #include "os-util.h"
 #include "path-util.h"
@@ -213,22 +215,18 @@ static int wait_for_partitions_to_appear(
                         break;
                 r = -errno;
                 if (r == -EINVAL) {
-                        struct loop_info64 info;
+                        /* If we are running on a block 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 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) {
-#if HAVE_VALGRIND_MEMCHECK_H
-                                /* Valgrind currently doesn't know LOOP_GET_STATUS64. Remove this once it does */
-                                VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info));
-#endif
+                        r = blockdev_partscan_enabled(fd);
+                        if (r < 0)
+                                return r;
+                        if (r == 0)
+                                return log_debug_errno(EPROTONOSUPPORT,
+                                                       "Device is a loop device and partition scanning is off!");
 
-                                if ((info.lo_flags & LO_FLAGS_PARTSCAN) == 0)
-                                        return log_debug_errno(EPROTONOSUPPORT,
-                                                               "Device is a loop device and partition scanning is off!");
-                        }
+                        return -EINVAL; /* original error */
                 }
                 if (r != -EBUSY)
                         return r;
@@ -308,6 +306,7 @@ int dissect_image(
                 const void *root_hash,
                 size_t root_hash_size,
                 const char *verity_data,
+                const MountOptions *mount_options,
                 DissectImageFlags flags,
                 DissectedImage **ret) {
 
@@ -400,8 +399,8 @@ int dissect_image(
 
                 (void) blkid_probe_lookup_value(b, "USAGE", &usage, NULL);
                 if (STRPTR_IN_SET(usage, "filesystem", "crypto")) {
-                        _cleanup_free_ char *t = NULL, *n = NULL;
-                        const char *fstype = NULL;
+                        _cleanup_free_ char *t = NULL, *n = NULL, *o = NULL;
+                        const char *fstype = NULL, *options = NULL;
 
                         /* OK, we have found a file system, that's our root partition then. */
                         (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
@@ -420,6 +419,13 @@ int dissect_image(
                         m->verity = root_hash && verity_data;
                         m->can_verity = !!verity_data;
 
+                        options = mount_options_from_part(mount_options, 0);
+                        if (options) {
+                                o = strdup(options);
+                                if (!o)
+                                        return -ENOMEM;
+                        }
+
                         m->partitions[PARTITION_ROOT] = (DissectedPartition) {
                                 .found = true,
                                 .rw = !m->verity,
@@ -427,6 +433,7 @@ int dissect_image(
                                 .architecture = _ARCHITECTURE_INVALID,
                                 .fstype = TAKE_PTR(t),
                                 .node = TAKE_PTR(n),
+                                .mount_options = TAKE_PTR(o),
                         };
 
                         m->encrypted = streq_ptr(fstype, "crypto_LUKS");
@@ -691,7 +698,8 @@ int dissect_image(
                         }
 
                         if (designator != _PARTITION_DESIGNATOR_INVALID) {
-                                _cleanup_free_ char *t = NULL, *n = NULL;
+                                _cleanup_free_ char *t = NULL, *n = NULL, *o = NULL;
+                                const char *options = NULL;
 
                                 /* First one wins */
                                 if (m->partitions[designator].found)
@@ -707,6 +715,13 @@ int dissect_image(
                                 if (!n)
                                         return -ENOMEM;
 
+                                options = mount_options_from_part(mount_options, nr);
+                                if (options) {
+                                        o = strdup(options);
+                                        if (!o)
+                                                return -ENOMEM;
+                                }
+
                                 m->partitions[designator] = (DissectedPartition) {
                                         .found = true,
                                         .partno = nr,
@@ -715,6 +730,7 @@ int dissect_image(
                                         .node = TAKE_PTR(n),
                                         .fstype = TAKE_PTR(t),
                                         .uuid = id,
+                                        .mount_options = TAKE_PTR(o),
                                 };
                         }
 
@@ -740,9 +756,9 @@ int dissect_image(
                                 break;
 
                         case 0xEA: { /* Boot Loader Spec extended $BOOT partition */
-                                _cleanup_free_ char *n = NULL;
+                                _cleanup_free_ char *n = NULL, *o = NULL;
                                 sd_id128_t id = SD_ID128_NULL;
-                                const char *sid;
+                                const char *sid, *options = NULL;
 
                                 /* First one wins */
                                 if (m->partitions[PARTITION_XBOOTLDR].found)
@@ -756,6 +772,13 @@ int dissect_image(
                                 if (!n)
                                         return -ENOMEM;
 
+                                options = mount_options_from_part(mount_options, nr);
+                                if (options) {
+                                        o = strdup(options);
+                                        if (!o)
+                                                return -ENOMEM;
+                                }
+
                                 m->partitions[PARTITION_XBOOTLDR] = (DissectedPartition) {
                                         .found = true,
                                         .partno = nr,
@@ -763,6 +786,7 @@ int dissect_image(
                                         .architecture = _ARCHITECTURE_INVALID,
                                         .node = TAKE_PTR(n),
                                         .uuid = id,
+                                        .mount_options = TAKE_PTR(o),
                                 };
 
                                 break;
@@ -785,6 +809,8 @@ int dissect_image(
                         zero(m->partitions[PARTITION_ROOT_SECONDARY_VERITY]);
 
                 } else if (flags & DISSECT_IMAGE_REQUIRE_ROOT) {
+                        _cleanup_free_ char *o = NULL;
+                        const char *options = NULL;
 
                         /* If the root has was set, then we won't fallback to a generic node, because the root hash
                          * decides */
@@ -800,6 +826,13 @@ int dissect_image(
                         if (multiple_generic)
                                 return -ENOTUNIQ;
 
+                        options = mount_options_from_part(mount_options, generic_nr);
+                        if (options) {
+                                o = strdup(options);
+                                if (!o)
+                                        return -ENOMEM;
+                        }
+
                         m->partitions[PARTITION_ROOT] = (DissectedPartition) {
                                 .found = true,
                                 .rw = generic_rw,
@@ -807,6 +840,7 @@ int dissect_image(
                                 .architecture = _ARCHITECTURE_INVALID,
                                 .node = TAKE_PTR(generic_node),
                                 .uuid = generic_uuid,
+                                .mount_options = TAKE_PTR(o),
                         };
                 }
         }
@@ -869,6 +903,7 @@ DissectedImage* dissected_image_unref(DissectedImage *m) {
                 free(m->partitions[i].node);
                 free(m->partitions[i].decrypted_fstype);
                 free(m->partitions[i].decrypted_node);
+                free(m->partitions[i].mount_options);
         }
 
         free(m->hostname);
@@ -1008,6 +1043,10 @@ static int mount_partition(
                         return -ENOMEM;
         }
 
+        if (!isempty(m->mount_options))
+                if (!strextend_with_separator(&options, ",", m->mount_options, NULL))
+                        return -ENOMEM;
+
         r = mount_verbose(LOG_DEBUG, node, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), options);
         if (r < 0)
                 return r;
@@ -1819,6 +1858,7 @@ int dissect_image_and_warn(
                 const void *root_hash,
                 size_t root_hash_size,
                 const char *verity_data,
+                const MountOptions *mount_options,
                 DissectImageFlags flags,
                 DissectedImage **ret) {
 
@@ -1833,7 +1873,7 @@ int dissect_image_and_warn(
                 name = buffer;
         }
 
-        r = dissect_image(fd, root_hash, root_hash_size, verity_data, flags, ret);
+        r = dissect_image(fd, root_hash, root_hash_size, verity_data, mount_options, flags, ret);
 
         switch (r) {
 
@@ -1880,6 +1920,102 @@ bool dissected_image_has_verity(const DissectedImage *image, unsigned partition_
         return k >= 0 && image->partitions[k].found;
 }
 
+MountOptions* mount_options_free_all(MountOptions *options) {
+        MountOptions *m;
+
+        while ((m = options)) {
+                LIST_REMOVE(mount_options, options, m);
+                free(m->options);
+                free(m);
+        }
+
+        return NULL;
+}
+
+const char* mount_options_from_part(const MountOptions *options, unsigned int partition_number) {
+        MountOptions *m;
+
+        LIST_FOREACH(mount_options, m, (MountOptions *)options)
+                if (partition_number == m->partition_number && !isempty(m->options))
+                        return m->options;
+
+        return NULL;
+}
+
+int mount_image_privately_interactively(
+                const char *image,
+                DissectImageFlags flags,
+                char **ret_directory,
+                LoopDevice **ret_loop_device,
+                DecryptedImage **ret_decrypted_image) {
+
+        _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
+        _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
+        _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
+        _cleanup_(rmdir_and_freep) char *created_dir = NULL;
+        _cleanup_free_ char *temp = NULL;
+        int r;
+
+        /* Mounts an OS image at a temporary place, inside a newly created mount namespace of our own. This
+         * is used by tools such as systemd-tmpfiles or systemd-firstboot to operate on some disk image
+         * easily. */
+
+        assert(image);
+        assert(ret_directory);
+        assert(ret_loop_device);
+        assert(ret_decrypted_image);
+
+        r = tempfn_random_child(NULL, program_invocation_short_name, &temp);
+        if (r < 0)
+                return log_error_errno(r, "Failed to generate temporary mount directory: %m");
+
+        r = loop_device_make_by_path(
+                        image,
+                        FLAGS_SET(flags, DISSECT_IMAGE_READ_ONLY) ? O_RDONLY : O_RDWR,
+                        FLAGS_SET(flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
+                        &d);
+        if (r < 0)
+                return log_error_errno(r, "Failed to set up loopback device: %m");
+
+        r = dissect_image_and_warn(d->fd, image, NULL, 0, NULL, NULL, flags, &dissected_image);
+        if (r < 0)
+                return r;
+
+        r = dissected_image_decrypt_interactively(dissected_image, NULL, NULL, 0, NULL, NULL, NULL, 0, flags, &decrypted_image);
+        if (r < 0)
+                return r;
+
+        r = detach_mount_namespace();
+        if (r < 0)
+                return log_error_errno(r, "Failed to detach mount namespace: %m");
+
+        r = mkdir_p(temp, 0700);
+        if (r < 0)
+                return log_error_errno(r, "Failed to create mount point: %m");
+
+        created_dir = TAKE_PTR(temp);
+
+        r = dissected_image_mount(dissected_image, created_dir, UID_INVALID, flags);
+        if (r == -EUCLEAN)
+                return log_error_errno(r, "File system check on image failed: %m");
+        if (r < 0)
+                return log_error_errno(r, "Failed to mount image: %m");
+
+        if (decrypted_image) {
+                r = decrypted_image_relinquish(decrypted_image);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to relinquish DM devices: %m");
+        }
+
+        loop_device_relinquish(d);
+
+        *ret_directory = TAKE_PTR(created_dir);
+        *ret_loop_device = TAKE_PTR(d);
+        *ret_decrypted_image = TAKE_PTR(decrypted_image);
+
+        return 0;
+}
+
 static const char *const partition_designator_table[] = {
         [PARTITION_ROOT] = "root",
         [PARTITION_ROOT_SECONDARY] = "root-secondary",