]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/shared/dissect-image.c
dissect: rename mount_options_from_part() → mount_options_from_designator()
[thirdparty/systemd.git] / src / shared / dissect-image.c
index f41d1a0e485f7cc6e95fd8effea854d94e20ee3c..385b1247d71866edadf57c8988f301f85f3c362a 100644 (file)
@@ -19,7 +19,7 @@
 #include "blkid-util.h"
 #include "blockdev-util.h"
 #include "copy.h"
-#include "crypt-util.h"
+#include "cryptsetup-util.h"
 #include "def.h"
 #include "device-nodes.h"
 #include "device-util.h"
@@ -54,6 +54,9 @@
 #include "user-util.h"
 #include "xattr-util.h"
 
+/* how many times to wait for the device nodes to appear */
+#define N_DEVICE_NODE_LIST_ATTEMPTS 10
+
 int probe_filesystem(const char *node, char **ret_fstype) {
         /* Try to find device content type and return it in *ret_fstype. If nothing is found,
          * 0/NULL will be returned. -EUCLEAN will be returned for ambiguous results, and an
@@ -154,9 +157,6 @@ static int enumerator_for_parent(sd_device *d, sd_device_enumerator **ret) {
         return 0;
 }
 
-/* how many times to wait for the device nodes to appear */
-#define N_DEVICE_NODE_LIST_ATTEMPTS 10
-
 static int wait_for_partitions_to_appear(
                 int fd,
                 sd_device *d,
@@ -420,7 +420,7 @@ int dissect_image(
                         m->verity = root_hash && verity_data;
                         m->can_verity = !!verity_data;
 
-                        options = mount_options_from_part(mount_options, 0);
+                        options = mount_options_from_designator(mount_options, PARTITION_ROOT);
                         if (options) {
                                 o = strdup(options);
                                 if (!o)
@@ -716,7 +716,7 @@ int dissect_image(
                                 if (!n)
                                         return -ENOMEM;
 
-                                options = mount_options_from_part(mount_options, nr);
+                                options = mount_options_from_designator(mount_options, designator);
                                 if (options) {
                                         o = strdup(options);
                                         if (!o)
@@ -773,7 +773,7 @@ int dissect_image(
                                 if (!n)
                                         return -ENOMEM;
 
-                                options = mount_options_from_part(mount_options, nr);
+                                options = mount_options_from_designator(mount_options, PARTITION_XBOOTLDR);
                                 if (options) {
                                         o = strdup(options);
                                         if (!o)
@@ -813,8 +813,8 @@ int dissect_image(
                         _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 */
+                        /* If the root hash was set, then we won't fall back to a generic node, because the
+                         * root hash decides. */
                         if (root_hash)
                                 return -EADDRNOTAVAIL;
 
@@ -827,7 +827,7 @@ int dissect_image(
                         if (multiple_generic)
                                 return -ENOTUNIQ;
 
-                        options = mount_options_from_part(mount_options, generic_nr);
+                        options = mount_options_from_designator(mount_options, PARTITION_ROOT);
                         if (options) {
                                 o = strdup(options);
                                 if (!o)
@@ -998,14 +998,17 @@ static int mount_partition(
         assert(m);
         assert(where);
 
+        /* Use decrypted node and matching fstype if available, otherwise use the original device */
         node = m->decrypted_node ?: m->node;
-        fstype = m->decrypted_fstype ?: m->fstype;
+        fstype = m->decrypted_node ? m->decrypted_fstype: m->fstype;
 
-        if (!m->found || !node || !fstype)
+        if (!m->found || !node)
                 return 0;
+        if (!fstype)
+                return -EAFNOSUPPORT;
 
         /* We are looking at an encrypted partition? This either means stacked encryption, or the caller didn't call dissected_image_decrypt() beforehand. Let's return a recognizable error for this case. */
-        if (streq_ptr(fstype, "crypto_LUKS"))
+        if (streq(fstype, "crypto_LUKS"))
                 return -EUNATCH;
 
         rw = m->rw && !(flags & DISSECT_IMAGE_READ_ONLY);
@@ -1017,6 +1020,13 @@ static int mount_partition(
         }
 
         if (directory) {
+                if (!FLAGS_SET(flags, DISSECT_IMAGE_READ_ONLY)) {
+                        /* Automatically create missing mount points, if necessary. */
+                        r = mkdir_p_root(where, directory, uid_shift, (gid_t) uid_shift, 0755);
+                        if (r < 0)
+                                return r;
+                }
+
                 r = chase_symlinks(directory, where, CHASE_PREFIX_ROOT, &chased, NULL);
                 if (r < 0)
                         return r;
@@ -1062,7 +1072,7 @@ static int mount_partition(
 }
 
 int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift, DissectImageFlags flags) {
-        int r, boot_mounted;
+        int r, xbootldr_mounted;
 
         assert(m);
         assert(where);
@@ -1074,6 +1084,7 @@ int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift,
          *  -EUNATCH      → Encrypted partition found for which no dm-crypt was set up yet
          *  -EUCLEAN      → fsck for file system failed
          *  -EBUSY        → File system already mounted/used elsewhere (kernel)
+         *  -EAFNOSUPPORT → File system type not supported or not known
          */
 
         if (!m->partitions[PARTITION_ROOT].found)
@@ -1116,30 +1127,48 @@ int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift,
         if (r < 0)
                 return r;
 
-        boot_mounted = mount_partition(m->partitions + PARTITION_XBOOTLDR, where, "/boot", uid_shift, flags);
-        if (boot_mounted < 0)
-                return boot_mounted;
+        xbootldr_mounted = mount_partition(m->partitions + PARTITION_XBOOTLDR, where, "/boot", uid_shift, flags);
+        if (xbootldr_mounted < 0)
+                return xbootldr_mounted;
 
         if (m->partitions[PARTITION_ESP].found) {
+                int esp_done = false;
+
                 /* Mount the ESP to /efi if it exists. If it doesn't exist, use /boot instead, but only if it
                  * exists and is empty, and we didn't already mount the XBOOTLDR partition into it. */
 
                 r = chase_symlinks("/efi", where, CHASE_PREFIX_ROOT, NULL, NULL);
-                if (r >= 0) {
-                        r = mount_partition(m->partitions + PARTITION_ESP, where, "/efi", uid_shift, flags);
-                        if (r < 0)
+                if (r < 0) {
+                        if (r != -ENOENT)
                                 return r;
 
-                } else if (boot_mounted <= 0) {
-                        _cleanup_free_ char *p = NULL;
+                        /* /efi doesn't exist. Let's see if /boot is suitable then */
 
-                        r = chase_symlinks("/boot", where, CHASE_PREFIX_ROOT, &p, NULL);
-                        if (r >= 0 && dir_is_empty(p) > 0) {
-                                r = mount_partition(m->partitions + PARTITION_ESP, where, "/boot", uid_shift, flags);
-                                if (r < 0)
-                                        return r;
+                        if (!xbootldr_mounted) {
+                                _cleanup_free_ char *p = NULL;
+
+                                r = chase_symlinks("/boot", where, CHASE_PREFIX_ROOT, &p, NULL);
+                                if (r < 0) {
+                                        if (r != -ENOENT)
+                                                return r;
+                                } else if (dir_is_empty(p) > 0) {
+                                        /* It exists and is an empty directory. Let's mount the ESP there. */
+                                        r = mount_partition(m->partitions + PARTITION_ESP, where, "/boot", uid_shift, flags);
+                                        if (r < 0)
+                                                return r;
+
+                                        esp_done = true;
+                                }
                         }
                 }
+
+                if (!esp_done) {
+                        /* OK, let's mount the ESP now to /efi (possibly creating the dir if missing) */
+
+                        r = mount_partition(m->partitions + PARTITION_ESP, where, "/efi", uid_shift, flags);
+                        if (r < 0)
+                                return r;
+                }
         }
 
         return 0;
@@ -1162,6 +1191,8 @@ int dissected_image_mount_and_warn(DissectedImage *m, const char *where, uid_t u
                 return log_error_errno(r, "File system check on image failed.");
         if (r == -EBUSY)
                 return log_error_errno(r, "File system already mounted elsewhere.");
+        if (r == -EAFNOSUPPORT)
+                return log_error_errno(r, "File system type not supported or not known.");
         if (r < 0)
                 return log_error_errno(r, "Failed to mount image: %m");
 
@@ -1194,13 +1225,13 @@ DecryptedImage* decrypted_image_unref(DecryptedImage* d) {
                 DecryptedPartition *p = d->decrypted + i;
 
                 if (p->device && p->name && !p->relinquished) {
-                        r = crypt_deactivate(p->device, p->name);
+                        r = sym_crypt_deactivate_by_name(p->device, p->name, 0);
                         if (r < 0)
                                 log_debug_errno(r, "Failed to deactivate encrypted partition %s", p->name);
                 }
 
                 if (p->device)
-                        crypt_free(p->device);
+                        sym_crypt_free(p->device);
                 free(p->name);
         }
 
@@ -1234,7 +1265,7 @@ static int make_dm_name_and_node(const void *original_node, const char *suffix,
         if (!filename_is_valid(name))
                 return -EINVAL;
 
-        node = path_join(crypt_get_dir(), name);
+        node = path_join(sym_crypt_get_dir(), name);
         if (!node)
                 return -ENOMEM;
 
@@ -1251,7 +1282,7 @@ static int decrypt_partition(
                 DecryptedImage *d) {
 
         _cleanup_free_ char *node = NULL, *name = NULL;
-        _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+        _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
         int r;
 
         assert(m);
@@ -1266,6 +1297,10 @@ static int decrypt_partition(
         if (!passphrase)
                 return -ENOKEY;
 
+        r = dlopen_cryptsetup();
+        if (r < 0)
+                return r;
+
         r = make_dm_name_and_node(m->node, "-decrypted", &name, &node);
         if (r < 0)
                 return r;
@@ -1273,19 +1308,19 @@ static int decrypt_partition(
         if (!GREEDY_REALLOC0(d->decrypted, d->n_allocated, d->n_decrypted + 1))
                 return -ENOMEM;
 
-        r = crypt_init(&cd, m->node);
+        r = sym_crypt_init(&cd, m->node);
         if (r < 0)
                 return log_debug_errno(r, "Failed to initialize dm-crypt: %m");
 
         cryptsetup_enable_logging(cd);
 
-        r = crypt_load(cd, CRYPT_LUKS, NULL);
+        r = sym_crypt_load(cd, CRYPT_LUKS, NULL);
         if (r < 0)
                 return log_debug_errno(r, "Failed to load LUKS metadata: %m");
 
-        r = crypt_activate_by_passphrase(cd, name, CRYPT_ANY_SLOT, passphrase, strlen(passphrase),
-                                         ((flags & DISSECT_IMAGE_READ_ONLY) ? CRYPT_ACTIVATE_READONLY : 0) |
-                                         ((flags & DISSECT_IMAGE_DISCARD_ON_CRYPTO) ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0));
+        r = sym_crypt_activate_by_passphrase(cd, name, CRYPT_ANY_SLOT, passphrase, strlen(passphrase),
+                                             ((flags & DISSECT_IMAGE_READ_ONLY) ? CRYPT_ACTIVATE_READONLY : 0) |
+                                             ((flags & DISSECT_IMAGE_DISCARD_ON_CRYPTO) ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0));
         if (r < 0) {
                 log_debug_errno(r, "Failed to activate LUKS device: %m");
                 return r == -EPERM ? -EKEYREJECTED : r;
@@ -1303,23 +1338,26 @@ static int decrypt_partition(
 static int verity_can_reuse(const void *root_hash, size_t root_hash_size, bool has_sig, const char *name, struct crypt_device **ret_cd) {
         /* If the same volume was already open, check that the root hashes match, and reuse it if they do */
         _cleanup_free_ char *root_hash_existing = NULL;
-        _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+        _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
         struct crypt_params_verity crypt_params = {};
         size_t root_hash_existing_size = root_hash_size;
         int r;
 
         assert(ret_cd);
 
-        r = crypt_init_by_name(&cd, name);
+        r = sym_crypt_init_by_name(&cd, name);
         if (r < 0)
                 return log_debug_errno(r, "Error opening verity device, crypt_init_by_name failed: %m");
-        r = crypt_get_verity_info(cd, &crypt_params);
+
+        r = sym_crypt_get_verity_info(cd, &crypt_params);
         if (r < 0)
                 return log_debug_errno(r, "Error opening verity device, crypt_get_verity_info failed: %m");
+
         root_hash_existing = malloc0(root_hash_size);
         if (!root_hash_existing)
                 return -ENOMEM;
-        r = crypt_volume_key_get(cd, CRYPT_ANY_SLOT, root_hash_existing, &root_hash_existing_size, NULL, 0);
+
+        r = sym_crypt_volume_key_get(cd, CRYPT_ANY_SLOT, root_hash_existing, &root_hash_existing_size, NULL, 0);
         if (r < 0)
                 return log_debug_errno(r, "Error opening verity device, crypt_volume_key_get failed: %m");
         if (root_hash_size != root_hash_existing_size || memcmp(root_hash_existing, root_hash, root_hash_size) != 0)
@@ -1339,7 +1377,8 @@ static int verity_can_reuse(const void *root_hash, size_t root_hash_size, bool h
 static inline void dm_deferred_remove_clean(char *name) {
         if (!name)
                 return;
-        (void) crypt_deactivate_by_name(NULL, name, CRYPT_DEACTIVATE_DEFERRED);
+
+        (void) sym_crypt_deactivate_by_name(NULL, name, CRYPT_DEACTIVATE_DEFERRED);
         free(name);
 }
 DEFINE_TRIVIAL_CLEANUP_FUNC(char *, dm_deferred_remove_clean);
@@ -1357,7 +1396,7 @@ static int verity_partition(
                 DecryptedImage *d) {
 
         _cleanup_free_ char *node = NULL, *name = NULL, *hash_sig_from_file = NULL;
-        _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+        _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
         _cleanup_(dm_deferred_remove_cleanp) char *restore_deferred_remove = NULL;
         int r;
 
@@ -1377,11 +1416,17 @@ static int verity_partition(
                         return 0;
         }
 
+        r = dlopen_cryptsetup();
+        if (r < 0)
+                return r;
+
         if (FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE)) {
                 /* Use the roothash, which is unique per volume, as the device node name, so that it can be reused */
                 _cleanup_free_ char *root_hash_encoded = NULL;
+
                 root_hash_encoded = hexmem(root_hash, root_hash_size);
                 if (!root_hash_encoded)
+
                         return -ENOMEM;
                 r = make_dm_name_and_node(root_hash_encoded, "-verity", &name, &node);
         } else
@@ -1395,17 +1440,17 @@ static int verity_partition(
                         return r;
         }
 
-        r = crypt_init(&cd, verity_data ?: v->node);
+        r = sym_crypt_init(&cd, verity_data ?: v->node);
         if (r < 0)
                 return r;
 
         cryptsetup_enable_logging(cd);
 
-        r = crypt_load(cd, CRYPT_VERITY, NULL);
+        r = sym_crypt_load(cd, CRYPT_VERITY, NULL);
         if (r < 0)
                 return r;
 
-        r = crypt_set_data_device(cd, m->node);
+        r = sym_crypt_set_data_device(cd, m->node);
         if (r < 0)
                 return r;
 
@@ -1418,15 +1463,15 @@ static int verity_partition(
         for (unsigned i = 0; i < N_DEVICE_NODE_LIST_ATTEMPTS; i++) {
                 if (root_hash_sig || hash_sig_from_file) {
 #if HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
-                        r = crypt_activate_by_signed_key(cd, name, root_hash, root_hash_size, root_hash_sig ?: hash_sig_from_file, root_hash_sig_size, CRYPT_ACTIVATE_READONLY);
+                        r = sym_crypt_activate_by_signed_key(cd, name, root_hash, root_hash_size, root_hash_sig ?: hash_sig_from_file, root_hash_sig_size, CRYPT_ACTIVATE_READONLY);
 #else
                         r = log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "activation of verity device with signature requested, but not supported by cryptsetup due to missing crypt_activate_by_signed_key()");
 #endif
                 } else
-                        r = crypt_activate_by_volume_key(cd, name, root_hash, root_hash_size, CRYPT_ACTIVATE_READONLY);
+                        r = sym_crypt_activate_by_volume_key(cd, name, root_hash, root_hash_size, CRYPT_ACTIVATE_READONLY);
                 /* libdevmapper can return EINVAL when the device is already in the activation stage.
                  * There's no way to distinguish this situation from a genuine error due to invalid
-                 * parameters, so immediately fallback to activating the device with a unique name.
+                 * parameters, so immediately fall back to activating the device with a unique name.
                  * Improvements in libcrypsetup can ensure this never happens: https://gitlab.com/cryptsetup/cryptsetup/-/merge_requests/96 */
                 if (r == -EINVAL && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE))
                         return verity_partition(m, v, root_hash, root_hash_size, verity_data, NULL, root_hash_sig ?: hash_sig_from_file, root_hash_sig_size, flags & ~DISSECT_IMAGE_VERITY_SHARE, d);
@@ -1469,7 +1514,7 @@ static int verity_partition(
                                         return r;
 
                                 if (cd)
-                                        crypt_free(cd);
+                                        sym_crypt_free(cd);
                                 cd = existing_cd;
                         }
                 }
@@ -1633,7 +1678,7 @@ int decrypted_image_relinquish(DecryptedImage *d) {
                 if (p->relinquished)
                         continue;
 
-                r = crypt_deactivate_by_name(NULL, p->name, CRYPT_DEACTIVATE_DEFERRED);
+                r = sym_crypt_deactivate_by_name(NULL, p->name, CRYPT_DEACTIVATE_DEFERRED);
                 if (r < 0)
                         return log_debug_errno(r, "Failed to mark %s for auto-removal: %m", p->name);
 
@@ -2023,11 +2068,11 @@ MountOptions* mount_options_free_all(MountOptions *options) {
         return NULL;
 }
 
-const char* mount_options_from_part(const MountOptions *options, unsigned int partition_number) {
-        MountOptions *m;
+const char* mount_options_from_designator(const MountOptions *options, int designator) {
+        const MountOptions *m;
 
-        LIST_FOREACH(mount_options, m, (MountOptions *)options)
-                if (partition_number == m->partition_number && !isempty(m->options))
+        LIST_FOREACH(mount_options, m, options)
+                if (designator == m->partition_designator && !isempty(m->options))
                         return m->options;
 
         return NULL;