]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/shared/dissect-image.c
tree-wide: use TAKE_PTR() and TAKE_FD() macros
[thirdparty/systemd.git] / src / shared / dissect-image.c
index 08f1e98640f610ffd22bd066d3ffd5b4dd5112c6..e2baa4497c0a5ca9f5db3f3fac1fbce33e705d57 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#if HAVE_LIBCRYPTSETUP
-#include <libcryptsetup.h>
-#ifndef CRYPT_LUKS
-#define CRYPT_LUKS NULL
-#endif
-#endif
 #include <sys/mount.h>
 #include <sys/prctl.h>
 #include <sys/wait.h>
 
+#include "sd-id128.h"
+
 #include "architecture.h"
 #include "ask-password-api.h"
 #include "blkid-util.h"
+#include "blockdev-util.h"
 #include "copy.h"
+#include "crypt-util.h"
 #include "def.h"
+#include "device-nodes.h"
 #include "dissect-image.h"
 #include "fd-util.h"
 #include "fileio.h"
@@ -42,6 +41,7 @@
 #include "hostname-util.h"
 #include "id128-util.h"
 #include "linux-3.13/dm-ioctl.h"
+#include "missing.h"
 #include "mount-util.h"
 #include "path-util.h"
 #include "process-util.h"
 #include "string-util.h"
 #include "strv.h"
 #include "udev-util.h"
+#include "user-util.h"
 #include "xattr-util.h"
 
-_unused_ static int probe_filesystem(const char *node, char **ret_fstype) {
+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 ambigous results, and an
+         * different error otherwise. */
+
 #if HAVE_BLKID
         _cleanup_blkid_free_probe_ blkid_probe b = NULL;
         const char *fstype;
         int r;
 
+        errno = 0;
         b = blkid_new_probe_from_filename(node);
         if (!b)
-                return -ENOMEM;
+                return -errno ?: -ENOMEM;
 
         blkid_probe_enable_superblocks(b, 1);
         blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
 
         errno = 0;
         r = blkid_do_safeprobe(b);
-        if (IN_SET(r, -2, 1)) {
-                log_debug("Failed to identify any partition type on partition %s", node);
+        if (r == 1) {
+                log_debug("No type detected on partition %s", node);
                 goto not_found;
         }
+        if (r == -2) {
+                log_debug("Results ambiguous for partition %s", node);
+                return -EUCLEAN;
+        }
         if (r != 0)
                 return -errno ?: -EIO;
 
@@ -98,7 +108,20 @@ not_found:
 #endif
 }
 
-int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DissectedImage **ret) {
+/* Detect RPMB and Boot partitions, which are not listed by blkid.
+ * See https://github.com/systemd/systemd/issues/5806. */
+static bool device_is_mmc_special_partition(struct udev_device *d) {
+        const char *sysname = udev_device_get_sysname(d);
+        return (sysname && startswith(sysname, "mmcblk") &&
+                (endswith(sysname, "rpmb") || endswith(sysname, "boot0") || endswith(sysname, "boot1")));
+}
+
+int dissect_image(
+                int fd,
+                const void *root_hash,
+                size_t root_hash_size,
+                DissectImageFlags flags,
+                DissectedImage **ret) {
 
 #if HAVE_BLKID
         sd_id128_t root_uuid = SD_ID128_NULL, verity_uuid = SD_ID128_NULL;
@@ -206,16 +229,13 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
                                 .rw = true,
                                 .partno = -1,
                                 .architecture = _ARCHITECTURE_INVALID,
-                                .fstype = t,
-                                .node = n,
+                                .fstype = TAKE_PTR(t),
+                                .node = TAKE_PTR(n),
                         };
 
-                        t = n = NULL;
-
                         m->encrypted = streq(fstype, "crypto_LUKS");
 
-                        *ret = m;
-                        m = NULL;
+                        *ret = TAKE_PTR(m);
 
                         return 0;
                 }
@@ -267,8 +287,17 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
                 /* Count the partitions enumerated by the kernel */
                 n = 0;
                 first = udev_enumerate_get_list_entry(e);
-                udev_list_entry_foreach(item, first)
+                udev_list_entry_foreach(item, first) {
+                        _cleanup_udev_device_unref_ struct udev_device *q;
+
+                        q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
+                        if (!q)
+                                return -errno;
+
+                        if (device_is_mmc_special_partition(q))
+                                continue;
                         n++;
+                }
 
                 /* Count the partitions enumerated by blkid */
                 z = blkid_partlist_numof_partitions(pl);
@@ -327,7 +356,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
                 _cleanup_udev_device_unref_ struct udev_device *q;
                 unsigned long long pflags;
                 blkid_partition pp;
-                const char *node, *sysname;
+                const char *node;
                 dev_t qn;
                 int nr;
 
@@ -342,10 +371,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
                 if (st.st_rdev == qn)
                         continue;
 
-                /* Filter out weird MMC RPMB partitions, which cannot reasonably be read, see
-                 * https://github.com/systemd/systemd/issues/5806 */
-                sysname = udev_device_get_sysname(q);
-                if (sysname && startswith(sysname, "mmcblk") && endswith(sysname, "rpmb"))
+                if (device_is_mmc_special_partition(q))
                         continue;
 
                 node = udev_device_get_devnode(q);
@@ -512,12 +538,10 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
                                         .partno = nr,
                                         .rw = rw,
                                         .architecture = architecture,
-                                        .node = n,
-                                        .fstype = t,
+                                        .node = TAKE_PTR(n),
+                                        .fstype = TAKE_PTR(t),
                                         .uuid = id,
                                 };
-
-                                n = t = NULL;
                         }
 
                 } else if (is_mbr) {
@@ -575,11 +599,9 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
                                 .rw = generic_rw,
                                 .partno = generic_nr,
                                 .architecture = _ARCHITECTURE_INVALID,
-                                .node = generic_node,
+                                .node = TAKE_PTR(generic_node),
                                 .uuid = generic_uuid,
                         };
-
-                        generic_node = NULL;
                 }
         }
 
@@ -611,7 +633,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
 
                 if (!p->fstype && p->node) {
                         r = probe_filesystem(p->node, &p->fstype);
-                        if (r < 0)
+                        if (r < 0 && r != -EUCLEAN)
                                 return r;
                 }
 
@@ -622,8 +644,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
                         p->rw = false;
         }
 
-        *ret = m;
-        m = NULL;
+        *ret = TAKE_PTR(m);
 
         return 0;
 #else
@@ -648,12 +669,11 @@ DissectedImage* dissected_image_unref(DissectedImage *m) {
         strv_free(m->machine_info);
         strv_free(m->os_release);
 
-        free(m);
-        return NULL;
+        return mfree(m);
 }
 
 static int is_loop_device(const char *path) {
-        char s[strlen("/sys/dev/block/") + DECIMAL_STR_MAX(dev_t) + 1 + DECIMAL_STR_MAX(dev_t) + strlen("/../loop/")];
+        char s[SYS_BLOCK_PATH_MAX("/../loop/")];
         struct stat st;
 
         assert(path);
@@ -664,13 +684,13 @@ static int is_loop_device(const char *path) {
         if (!S_ISBLK(st.st_mode))
                 return -ENOTBLK;
 
-        xsprintf(s, "/sys/dev/block/%u:%u/loop/", major(st.st_rdev), minor(st.st_rdev));
+        xsprintf_sys_block_path(s, "/loop/", st.st_dev);
         if (access(s, F_OK) < 0) {
                 if (errno != ENOENT)
                         return -errno;
 
                 /* The device itself isn't a loop device, but maybe it's a partition and its parent is? */
-                xsprintf(s, "/sys/dev/block/%u:%u/../loop/", major(st.st_rdev), minor(st.st_rdev));
+                xsprintf_sys_block_path(s, "/../loop/", st.st_dev);
                 if (access(s, F_OK) < 0)
                         return errno == ENOENT ? false : -errno;
         }
@@ -682,10 +702,11 @@ static int mount_partition(
                 DissectedPartition *m,
                 const char *where,
                 const char *directory,
+                uid_t uid_shift,
                 DissectImageFlags flags) {
 
-        const char *p, *options = NULL, *node, *fstype;
-        _cleanup_free_ char *chased = NULL;
+        _cleanup_free_ char *chased = NULL, *options = NULL;
+        const char *p, *node, *fstype;
         bool rw;
         int r;
 
@@ -716,13 +737,26 @@ static int mount_partition(
         /* If requested, turn on discard support. */
         if (fstype_can_discard(fstype) &&
             ((flags & DISSECT_IMAGE_DISCARD) ||
-             ((flags & DISSECT_IMAGE_DISCARD_ON_LOOP) && is_loop_device(m->node))))
-                options = "discard";
+             ((flags & DISSECT_IMAGE_DISCARD_ON_LOOP) && is_loop_device(m->node)))) {
+                options = strdup("discard");
+                if (!options)
+                        return -ENOMEM;
+        }
+
+        if (uid_is_valid(uid_shift) && uid_shift != 0 && fstype_can_uid_gid(fstype)) {
+                _cleanup_free_ char *uid_option = NULL;
+
+                if (asprintf(&uid_option, "uid=" UID_FMT ",gid=" GID_FMT, uid_shift, (gid_t) uid_shift) < 0)
+                        return -ENOMEM;
+
+                if (!strextend_with_separator(&options, ",", uid_option, NULL))
+                        return -ENOMEM;
+        }
 
         return mount_verbose(LOG_DEBUG, node, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), options);
 }
 
-int dissected_image_mount(DissectedImage *m, const char *where, DissectImageFlags flags) {
+int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift, DissectImageFlags flags) {
         int r;
 
         assert(m);
@@ -731,15 +765,20 @@ int dissected_image_mount(DissectedImage *m, const char *where, DissectImageFlag
         if (!m->partitions[PARTITION_ROOT].found)
                 return -ENXIO;
 
-        r = mount_partition(m->partitions + PARTITION_ROOT, where, NULL, flags);
-        if (r < 0)
-                return r;
+        if ((flags & DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY) == 0) {
+                r = mount_partition(m->partitions + PARTITION_ROOT, where, NULL, uid_shift, flags);
+                if (r < 0)
+                        return r;
+        }
+
+        if ((flags & DISSECT_IMAGE_MOUNT_ROOT_ONLY))
+                return 0;
 
-        r = mount_partition(m->partitions + PARTITION_HOME, where, "/home", flags);
+        r = mount_partition(m->partitions + PARTITION_HOME, where, "/home", uid_shift, flags);
         if (r < 0)
                 return r;
 
-        r = mount_partition(m->partitions + PARTITION_SRV, where, "/srv", flags);
+        r = mount_partition(m->partitions + PARTITION_SRV, where, "/srv", uid_shift, flags);
         if (r < 0)
                 return r;
 
@@ -757,7 +796,7 @@ int dissected_image_mount(DissectedImage *m, const char *where, DissectImageFlag
 
                         r = dir_is_empty(p);
                         if (r > 0) {
-                                r = mount_partition(m->partitions + PARTITION_ESP, where, mp, flags);
+                                r = mount_partition(m->partitions + PARTITION_ESP, where, mp, uid_shift, flags);
                                 if (r < 0)
                                         return r;
                         }
@@ -836,10 +875,9 @@ static int make_dm_name_and_node(const void *original_node, const char *suffix,
         if (!node)
                 return -ENOMEM;
 
-        *ret_name = name;
-        *ret_node = node;
+        *ret_name = TAKE_PTR(name);
+        *ret_node = TAKE_PTR(node);
 
-        name = node = NULL;
         return 0;
 }
 
@@ -850,7 +888,7 @@ static int decrypt_partition(
                 DecryptedImage *d) {
 
         _cleanup_free_ char *node = NULL, *name = NULL;
-        struct crypt_device *cd;
+        _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
         int r;
 
         assert(m);
@@ -862,6 +900,9 @@ static int decrypt_partition(
         if (!streq(m->fstype, "crypto_LUKS"))
                 return 0;
 
+        if (!passphrase)
+                return -ENOKEY;
+
         r = make_dm_name_and_node(m->node, "-decrypted", &name, &node);
         if (r < 0)
                 return r;
@@ -874,37 +915,24 @@ static int decrypt_partition(
                 return log_debug_errno(r, "Failed to initialize dm-crypt: %m");
 
         r = crypt_load(cd, CRYPT_LUKS, NULL);
-        if (r < 0) {
-                log_debug_errno(r, "Failed to load LUKS metadata: %m");
-                goto fail;
-        }
+        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));
-        if (r < 0)
+        if (r < 0) {
                 log_debug_errno(r, "Failed to activate LUKS device: %m");
-        if (r == -EPERM) {
-                r = -EKEYREJECTED;
-                goto fail;
+                return r == -EPERM ? -EKEYREJECTED : r;
         }
-        if (r < 0)
-                goto fail;
-
-        d->decrypted[d->n_decrypted].name = name;
-        name = NULL;
 
-        d->decrypted[d->n_decrypted].device = cd;
+        d->decrypted[d->n_decrypted].name = TAKE_PTR(name);
+        d->decrypted[d->n_decrypted].device = TAKE_PTR(cd);
         d->n_decrypted++;
 
-        m->decrypted_node = node;
-        node = NULL;
+        m->decrypted_node = TAKE_PTR(node);
 
         return 0;
-
-fail:
-        crypt_free(cd);
-        return r;
 }
 
 static int verity_partition(
@@ -916,7 +944,7 @@ static int verity_partition(
                 DecryptedImage *d) {
 
         _cleanup_free_ char *node = NULL, *name = NULL;
-        struct crypt_device *cd;
+        _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
         int r;
 
         assert(m);
@@ -946,30 +974,23 @@ static int verity_partition(
 
         r = crypt_load(cd, CRYPT_VERITY, NULL);
         if (r < 0)
-                goto fail;
+                return r;
 
         r = crypt_set_data_device(cd, m->node);
         if (r < 0)
-                goto fail;
+                return r;
 
         r = crypt_activate_by_volume_key(cd, name, root_hash, root_hash_size, CRYPT_ACTIVATE_READONLY);
         if (r < 0)
-                goto fail;
-
-        d->decrypted[d->n_decrypted].name = name;
-        name = NULL;
+                return r;
 
-        d->decrypted[d->n_decrypted].device = cd;
+        d->decrypted[d->n_decrypted].name = TAKE_PTR(name);
+        d->decrypted[d->n_decrypted].device = TAKE_PTR(cd);
         d->n_decrypted++;
 
-        m->decrypted_node = node;
-        node = NULL;
+        m->decrypted_node = TAKE_PTR(node);
 
         return 0;
-
-fail:
-        crypt_free(cd);
-        return r;
 }
 #endif
 
@@ -981,8 +1002,8 @@ int dissected_image_decrypt(
                 DissectImageFlags flags,
                 DecryptedImage **ret) {
 
-        _cleanup_(decrypted_image_unrefp) DecryptedImage *d = NULL;
 #if HAVE_LIBCRYPTSETUP
+        _cleanup_(decrypted_image_unrefp) DecryptedImage *d = NULL;
         unsigned i;
         int r;
 #endif
@@ -1007,9 +1028,6 @@ int dissected_image_decrypt(
         }
 
 #if HAVE_LIBCRYPTSETUP
-        if (m->encrypted && !passphrase)
-                return -ENOKEY;
-
         d = new0(DecryptedImage, 1);
         if (!d)
                 return -ENOMEM;
@@ -1034,13 +1052,12 @@ int dissected_image_decrypt(
 
                 if (!p->decrypted_fstype && p->decrypted_node) {
                         r = probe_filesystem(p->decrypted_node, &p->decrypted_fstype);
-                        if (r < 0)
+                        if (r < 0 && r != -EUCLEAN)
                                 return r;
                 }
         }
 
-        *ret = d;
-        d = NULL;
+        *ret = TAKE_PTR(d);
 
         return 1;
 #else
@@ -1174,7 +1191,7 @@ int root_hash_load(const char *image, void **ret, size_t *ret_size) {
                 if (!IN_SET(r, -ENODATA, -EOPNOTSUPP, -ENOENT))
                         return r;
 
-                fn = newa(char, strlen(image) + strlen(".roothash") + 1);
+                fn = newa(char, strlen(image) + STRLEN(".roothash") + 1);
                 n = stpcpy(fn, image);
                 e = endswith(fn, ".raw");
                 if (e)
@@ -1198,11 +1215,9 @@ int root_hash_load(const char *image, void **ret, size_t *ret_size) {
         if (l < sizeof(sd_id128_t))
                 return -EINVAL;
 
-        *ret = k;
+        *ret = TAKE_PTR(k);
         *ret_size = l;
 
-        k = NULL;
-
         return 1;
 }
 
@@ -1230,7 +1245,6 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
         _cleanup_free_ char *hostname = NULL;
         unsigned n_meta_initialized = 0, k;
         int fds[2 * _META_MAX], r;
-        siginfo_t si;
 
         BLOCK_SIGNALS(SIGCHLD);
 
@@ -1246,23 +1260,15 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
         if (r < 0)
                 goto finish;
 
-        child = raw_clone(SIGCHLD|CLONE_NEWNS);
-        if (child < 0) {
-                r = -errno;
+        r = safe_fork("(sd-dissect)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_NEW_MOUNTNS, &child);
+        if (r < 0)
                 goto finish;
-        }
-
-        if (child == 0) {
-
-                (void) reset_all_signal_handlers();
-                (void) reset_signal_mask();
-                assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
-
+        if (r == 0) {
                 /* Make sure we never propagate to the host */
                 if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0)
                         _exit(EXIT_FAILURE);
 
-                r = dissected_image_mount(m, t, DISSECT_IMAGE_READ_ONLY);
+                r = dissected_image_mount(m, t, UID_INVALID, DISSECT_IMAGE_READ_ONLY);
                 if (r < 0)
                         _exit(EXIT_FAILURE);
 
@@ -1273,18 +1279,14 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
                         fds[2*k] = safe_close(fds[2*k]);
 
                         NULSTR_FOREACH(p, paths[k]) {
-                                _cleanup_free_ char *q = NULL;
-
-                                r = chase_symlinks(p, t, CHASE_PREFIX_ROOT, &q);
-                                if (r < 0)
-                                        continue;
-
-                                fd = open(q, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+                                fd = chase_symlinks_and_open(p, t, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL);
                                 if (fd >= 0)
                                         break;
                         }
-                        if (fd < 0)
+                        if (fd < 0) {
+                                log_debug_errno(fd, "Failed to read %s file of image, ignoring: %m", paths[k]);
                                 continue;
+                        }
 
                         r = copy_bytes(fd, fds[2*k+1], (uint64_t) -1, 0);
                         if (r < 0)
@@ -1352,15 +1354,12 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
                 }
         }
 
-        r = wait_for_terminate(child, &si);
-        if (r < 0)
-                goto finish;
+        r = wait_for_terminate_and_check("(sd-dissect)", child, 0);
         child = 0;
-
-        if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) {
-                r = -EPROTO;
+        if (r < 0)
                 goto finish;
-        }
+        if (r != EXIT_SUCCESS)
+                return -EPROTO;
 
         free_and_replace(m->hostname, hostname);
         m->machine_id = machine_id;
@@ -1374,6 +1373,55 @@ finish:
         return r;
 }
 
+int dissect_image_and_warn(
+                int fd,
+                const char *name,
+                const void *root_hash,
+                size_t root_hash_size,
+                DissectImageFlags flags,
+                DissectedImage **ret) {
+
+        _cleanup_free_ char *buffer = NULL;
+        int r;
+
+        if (!name) {
+                r = fd_get_path(fd, &buffer);
+                if (r < 0)
+                        return r;
+
+                name = buffer;
+        }
+
+        r = dissect_image(fd, root_hash, root_hash_size, flags, ret);
+
+        switch (r) {
+
+        case -EOPNOTSUPP:
+                return log_error_errno(r, "Dissecting images is not supported, compiled without blkid support.");
+
+        case -ENOPKG:
+                return log_error_errno(r, "Couldn't identify a suitable partition table or file system in '%s'.", name);
+
+        case -EADDRNOTAVAIL:
+                return log_error_errno(r, "No root partition for specified root hash found in '%s'.", name);
+
+        case -ENOTUNIQ:
+                return log_error_errno(r, "Multiple suitable root partitions found in image '%s'.", name);
+
+        case -ENXIO:
+                return log_error_errno(r, "No suitable root partition found in image '%s'.", name);
+
+        case -EPROTONOSUPPORT:
+                return log_error_errno(r, "Device '%s' is loopback block device with partition scanning turned off, please turn it on.", name);
+
+        default:
+                if (r < 0)
+                        return log_error_errno(r, "Failed to dissect image '%s': %m", name);
+
+                return r;
+        }
+}
+
 static const char *const partition_designator_table[] = {
         [PARTITION_ROOT] = "root",
         [PARTITION_ROOT_SECONDARY] = "root-secondary",