diff options
Diffstat (limited to 'src/shared/dissect-image.c')
-rw-r--r-- | src/shared/dissect-image.c | 333 |
1 files changed, 194 insertions, 139 deletions
diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index fa1cf26ee1..3a46faf769 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -4,6 +4,7 @@ #include <sys/prctl.h> #include <sys/wait.h> +#include "sd-device.h" #include "sd-id128.h" #include "architecture.h" @@ -14,7 +15,9 @@ #include "crypt-util.h" #include "def.h" #include "device-nodes.h" +#include "device-util.h" #include "dissect-image.h" +#include "env-file.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" @@ -25,6 +28,7 @@ #include "linux-3.13/dm-ioctl.h" #include "missing.h" #include "mount-util.h" +#include "mountpoint-util.h" #include "os-util.h" #include "path-util.h" #include "process-util.h" @@ -35,6 +39,7 @@ #include "string-table.h" #include "string-util.h" #include "strv.h" +#include "tmpfile-util.h" #include "udev-util.h" #include "user-util.h" #include "xattr-util.h" @@ -94,23 +99,173 @@ not_found: #if HAVE_BLKID /* 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) { +static bool device_is_mmc_special_partition(sd_device *d) { const char *sysname; - sysname = udev_device_get_sysname(d); - return sysname && startswith(sysname, "mmcblk") && + assert(d); + + if (sd_device_get_sysname(d, &sysname) < 0) + return false; + + return startswith(sysname, "mmcblk") && (endswith(sysname, "rpmb") || endswith(sysname, "boot0") || endswith(sysname, "boot1")); } -static bool device_is_block(struct udev_device *d) { +static bool device_is_block(sd_device *d) { const char *ss; - ss = udev_device_get_subsystem(d); - if (!ss) + assert(d); + + if (sd_device_get_subsystem(d, &ss) < 0) return false; return streq(ss, "block"); } + +static int enumerator_for_parent(sd_device *d, sd_device_enumerator **ret) { + _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; + int r; + + assert(d); + assert(ret); + + r = sd_device_enumerator_new(&e); + if (r < 0) + return r; + + r = sd_device_enumerator_allow_uninitialized(e); + if (r < 0) + return r; + + r = sd_device_enumerator_add_match_parent(e, d); + if (r < 0) + return r; + + *ret = TAKE_PTR(e); + 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, + unsigned num_partitions, + DissectImageFlags flags, + sd_device_enumerator **ret_enumerator) { + + _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; + sd_device *q; + unsigned n; + int r; + + assert(fd >= 0); + assert(d); + assert(ret_enumerator); + + r = enumerator_for_parent(d, &e); + if (r < 0) + return r; + + /* Count the partitions enumerated by the kernel */ + n = 0; + FOREACH_DEVICE(e, q) { + if (sd_device_get_devnum(q, NULL) < 0) + continue; + if (!device_is_block(q)) + continue; + if (device_is_mmc_special_partition(q)) + continue; + + if (!FLAGS_SET(flags, DISSECT_IMAGE_NO_UDEV)) { + r = device_wait_for_initialization(q, "block", NULL); + if (r < 0) + return r; + } + + n++; + } + + if (n == num_partitions + 1) { + *ret_enumerator = TAKE_PTR(e); + return 0; /* success! */ + } + if (n > num_partitions + 1) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), + "blkid and kernel partition lists do not match."); + + /* The kernel has probed fewer partitions than blkid? Maybe the kernel prober is still running or it + * got EBUSY because udev already opened the device. Let's reprobe the device, which is a synchronous + * call that waits until probing is complete. */ + + for (unsigned j = 0; ; j++) { + if (j++ > 20) + return -EBUSY; + + if (ioctl(fd, BLKRRPART, 0) >= 0) + break; + r = -errno; + if (r == -EINVAL) { + struct loop_info64 info; + + /* 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 && (info.lo_flags & LO_FLAGS_PARTSCAN) == 0) { + log_debug("Device is a loop device and partition scanning is off!"); + return -EPROTONOSUPPORT; + } + } + if (r != -EBUSY) + return r; + + /* If something else has the device open, such as an udev rule, the ioctl will return + * EBUSY. Since there's no way to wait until it isn't busy anymore, let's just wait a bit, + * and try again. + * + * This is really something they should fix in the kernel! */ + (void) usleep(50 * USEC_PER_MSEC); + + } + + return -EAGAIN; /* no success yet, try again */ +} + +static int loop_wait_for_partitions_to_appear( + int fd, + sd_device *d, + unsigned num_partitions, + DissectImageFlags flags, + sd_device_enumerator **ret_enumerator) { + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + int r; + + assert(fd >= 0); + assert(d); + assert(ret_enumerator); + + log_debug("Waiting for device (parent + %d partitions) to appear...", num_partitions); + + if (!FLAGS_SET(flags, DISSECT_IMAGE_NO_UDEV)) { + r = device_wait_for_initialization(d, "block", &device); + if (r < 0) + return r; + } else + device = sd_device_ref(d); + + for (unsigned i = 0; i < N_DEVICE_NODE_LIST_ATTEMPTS; i++) { + r = wait_for_partitions_to_appear(fd, device, num_partitions, flags, ret_enumerator); + if (r != -EAGAIN) + return r; + } + + return log_debug_errno(SYNTHETIC_ERRNO(ENXIO), + "Kernel partitions dit not appear within %d attempts", + N_DEVICE_NODE_LIST_ATTEMPTS); +} + #endif int dissect_image( @@ -122,19 +277,18 @@ int dissect_image( #if HAVE_BLKID sd_id128_t root_uuid = SD_ID128_NULL, verity_uuid = SD_ID128_NULL; - _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL; + _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; bool is_gpt, is_mbr, generic_rw, multiple_generic = false; - _cleanup_(udev_device_unrefp) struct udev_device *d = NULL; + _cleanup_(sd_device_unrefp) sd_device *d = NULL; _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL; _cleanup_(blkid_free_probep) blkid_probe b = NULL; - _cleanup_(udev_unrefp) struct udev *udev = NULL; _cleanup_free_ char *generic_node = NULL; sd_id128_t generic_uuid = SD_ID128_NULL; const char *pttype = NULL; - struct udev_list_entry *first, *item; blkid_partlist pl; int r, generic_nr; struct stat st; + sd_device *q; unsigned i; assert(fd >= 0); @@ -200,6 +354,10 @@ int dissect_image( if (!m) return -ENOMEM; + r = sd_device_new_from_devnum(&d, 'b', st.st_rdev); + if (r < 0) + return r; + if (!(flags & DISSECT_IMAGE_GPT_ONLY) && (flags & DISSECT_IMAGE_REQUIRE_ROOT)) { const char *usage = NULL; @@ -218,8 +376,9 @@ int dissect_image( return -ENOMEM; } - if (asprintf(&n, "/dev/block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0) - return -ENOMEM; + r = device_path_make_major_minor(st.st_mode, st.st_rdev, &n); + if (r < 0) + return r; m->partitions[PARTITION_ROOT] = (DissectedPartition) { .found = true, @@ -230,7 +389,11 @@ int dissect_image( .node = TAKE_PTR(n), }; - m->encrypted = streq(fstype, "crypto_LUKS"); + m->encrypted = streq_ptr(fstype, "crypto_LUKS"); + + r = loop_wait_for_partitions_to_appear(fd, d, 0, flags, &e); + if (r < 0) + return r; *ret = TAKE_PTR(m); @@ -253,124 +416,19 @@ int dissect_image( if (!pl) return -errno ?: -ENOMEM; - udev = udev_new(); - if (!udev) - return -errno; - - d = udev_device_new_from_devnum(udev, 'b', st.st_rdev); - if (!d) - return -ENOMEM; - - for (i = 0;; i++) { - int n, z; - - if (i >= 10) { - log_debug("Kernel partitions never appeared."); - return -ENXIO; - } - - e = udev_enumerate_new(udev); - if (!e) - return -errno; - - r = udev_enumerate_add_match_parent(e, d); - if (r < 0) - return r; - - r = udev_enumerate_scan_devices(e); - if (r < 0) - return r; - - /* Count the partitions enumerated by the kernel */ - n = 0; - first = udev_enumerate_get_list_entry(e); - udev_list_entry_foreach(item, first) { - _cleanup_(udev_device_unrefp) struct udev_device *q; - dev_t qn; - - q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); - if (!q) - return -errno; - - qn = udev_device_get_devnum(q); - if (major(qn) == 0) - continue; - - if (!device_is_block(q)) - continue; - - if (device_is_mmc_special_partition(q)) - continue; - n++; - } - - /* Count the partitions enumerated by blkid */ - z = blkid_partlist_numof_partitions(pl); - if (n == z + 1) - break; - if (n > z + 1) { - log_debug("blkid and kernel partition list do not match."); - return -EIO; - } - if (n < z + 1) { - unsigned j = 0; - - /* The kernel has probed fewer partitions than blkid? Maybe the kernel prober is still running - * or it got EBUSY because udev already opened the device. Let's reprobe the device, which is a - * synchronous call that waits until probing is complete. */ - - for (;;) { - if (j++ > 20) - return -EBUSY; - - if (ioctl(fd, BLKRRPART, 0) < 0) { - r = -errno; - - if (r == -EINVAL) { - struct loop_info64 info; - - /* 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 && (info.lo_flags & LO_FLAGS_PARTSCAN) == 0) { - log_debug("Device is loop device and partition scanning is off!"); - return -EPROTONOSUPPORT; - } - } - if (r != -EBUSY) - return r; - } else - break; - - /* If something else has the device open, such as an udev rule, the ioctl will return - * EBUSY. Since there's no way to wait until it isn't busy anymore, let's just wait a - * bit, and try again. - * - * This is really something they should fix in the kernel! */ - - (void) usleep(50 * USEC_PER_MSEC); - } - } - - e = udev_enumerate_unref(e); - } + r = loop_wait_for_partitions_to_appear(fd, d, blkid_partlist_numof_partitions(pl), flags, &e); + if (r < 0) + return r; - first = udev_enumerate_get_list_entry(e); - udev_list_entry_foreach(item, first) { - _cleanup_(udev_device_unrefp) struct udev_device *q; + FOREACH_DEVICE(e, q) { unsigned long long pflags; blkid_partition pp; const char *node; dev_t qn; int nr; - q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); - if (!q) - return -errno; - - qn = udev_device_get_devnum(q); - if (major(qn) == 0) + r = sd_device_get_devnum(q, &qn); + if (r < 0) continue; if (st.st_rdev == qn) @@ -382,8 +440,8 @@ int dissect_image( if (device_is_mmc_special_partition(q)) continue; - node = udev_device_get_devnode(q); - if (!node) + r = sd_device_get_devname(q, &node); + if (r < 0) continue; pp = blkid_partlist_devno_to_partition(pl, qn); @@ -787,7 +845,7 @@ int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift, } } - if ((flags & DISSECT_IMAGE_MOUNT_ROOT_ONLY)) + if (flags & DISSECT_IMAGE_MOUNT_ROOT_ONLY) return 0; r = mount_partition(m->partitions + PARTITION_HOME, where, "/home", uid_shift, flags); @@ -1101,19 +1159,16 @@ int dissected_image_decrypt_interactively( return r; if (r == -EKEYREJECTED) log_error_errno(r, "Incorrect passphrase, try again!"); - else if (r != -ENOKEY) { - log_error_errno(r, "Failed to decrypt image: %m"); - return r; - } + else if (r != -ENOKEY) + return log_error_errno(r, "Failed to decrypt image: %m"); - if (--n < 0) { - log_error("Too many retries."); - return -EKEYREJECTED; - } + if (--n < 0) + return log_error_errno(SYNTHETIC_ERRNO(EKEYREJECTED), + "Too many retries."); z = strv_free(z); - r = ask_password_auto("Please enter image passphrase!", NULL, "dissect", "dissect", USEC_INFINITY, 0, &z); + r = ask_password_auto("Please enter image passphrase:", NULL, "dissect", "dissect", USEC_INFINITY, 0, &z); if (r < 0) return log_error_errno(r, "Failed to query for passphrase: %m"); @@ -1317,7 +1372,7 @@ int dissected_image_acquire_metadata(DissectedImage *m) { fds[2*k+1] = safe_close(fds[2*k+1]); - f = fdopen(fds[2*k], "re"); + f = fdopen(fds[2*k], "r"); if (!f) { r = -errno; goto finish; @@ -1353,14 +1408,14 @@ int dissected_image_acquire_metadata(DissectedImage *m) { } case META_MACHINE_INFO: - r = load_env_file_pairs(f, "machine-info", NULL, &machine_info); + r = load_env_file_pairs(f, "machine-info", &machine_info); if (r < 0) log_debug_errno(r, "Failed to read /etc/machine-info: %m"); break; case META_OS_RELEASE: - r = load_env_file_pairs(f, "os-release", NULL, &os_release); + r = load_env_file_pairs(f, "os-release", &os_release); if (r < 0) log_debug_errno(r, "Failed to read OS release file: %m"); |