From 74dd4636e493a8bc758bcbccca8616720bf577d9 Mon Sep 17 00:00:00 2001 From: Silke Hofstra Date: Sat, 4 Jul 2020 14:38:35 +0200 Subject: [PATCH] Add support for non-ext filesystems Calling stat() on some filesystems, like Btrfs, results in a dev_t with no relation to the backing device(s), and subsequent failure to detect the root fs. Add support such filesystems by retrieving the block device from `/proc/self/mounts` and working on from there. Resolves #61, #182 and #193. --- src/lib/blkid_stub.c | 14 +++++++++++++ src/lib/blkid_stub.h | 4 ++++ src/lib/files.c | 35 ++++++++++++++++++++++++++++----- src/lib/files.h | 8 ++++++++ src/lib/probe.c | 6 +++--- src/lib/probe.h | 1 - src/lib/system_stub.c | 6 ++++++ src/lib/system_stub.h | 6 ++++++ tests/blkid-harness.h | 15 ++++++++++++++ tests/check-legacy.c | 9 +++++++++ tests/check-probe.c | 12 +++++++++++ tests/check-select-bootloader.c | 12 +++++++++++ tests/check-syslinux.c | 9 +++++++++ tests/system-harness.h | 8 +++++++- 14 files changed, 135 insertions(+), 10 deletions(-) diff --git a/src/lib/blkid_stub.c b/src/lib/blkid_stub.c index da58d12b..3ec96472 100644 --- a/src/lib/blkid_stub.c +++ b/src/lib/blkid_stub.c @@ -37,6 +37,7 @@ static CbmBlkidOps default_blkid_ops = { .probe_set_superblocks_flags = blkid_probe_set_superblocks_flags, .probe_enable_partitions = blkid_probe_enable_partitions, .probe_set_partitions_flags = blkid_probe_set_partitions_flags, + .probe_get_wholedisk_devno = blkid_probe_get_wholedisk_devno, .probe_lookup_value = blkid_probe_lookup_value, .do_safeprobe = blkid_do_safeprobe, .free_probe = blkid_free_probe, @@ -54,6 +55,7 @@ static CbmBlkidOps default_blkid_ops = { /* Misc */ .devno_to_wholedisk = cbm_blkid_devno_to_wholedisk_wrapped, + .devno_to_devname = blkid_devno_to_devname, }; /** @@ -79,6 +81,7 @@ void cbm_blkid_set_vtable(CbmBlkidOps *ops) assert(blkid_ops->probe_set_superblocks_flags != NULL); assert(blkid_ops->probe_enable_partitions != NULL); assert(blkid_ops->probe_set_partitions_flags != NULL); + assert(blkid_ops->probe_get_wholedisk_devno != NULL); assert(blkid_ops->probe_lookup_value != NULL); assert(blkid_ops->do_safeprobe != NULL); assert(blkid_ops->free_probe != NULL); @@ -96,6 +99,7 @@ void cbm_blkid_set_vtable(CbmBlkidOps *ops) /* misc */ assert(blkid_ops->devno_to_wholedisk != NULL); + assert(blkid_ops->devno_to_devname != NULL); } /** @@ -141,6 +145,11 @@ void cbm_blkid_free_probe(blkid_probe pr) blkid_ops->free_probe(pr); } +dev_t cbm_probe_get_wholedisk_devno(blkid_probe pr) +{ + return blkid_ops->probe_get_wholedisk_devno(pr); +} + /** * Partition functions */ @@ -193,6 +202,11 @@ int cbm_blkid_devno_to_wholedisk(dev_t dev, char *diskname, size_t len, dev_t *d return blkid_ops->devno_to_wholedisk(dev, diskname, len, diskdevno); } +char *cbm_blkid_devno_to_devname(dev_t dev) +{ + return blkid_ops->devno_to_devname(dev); +} + /* * Editor modelines - https://www.wireshark.org/tools/modelines.html * diff --git a/src/lib/blkid_stub.h b/src/lib/blkid_stub.h index aa965863..7ee7cb8b 100644 --- a/src/lib/blkid_stub.h +++ b/src/lib/blkid_stub.h @@ -28,6 +28,7 @@ typedef struct CbmBlkidOps { int (*probe_lookup_value)(blkid_probe pr, const char *name, const char **data, size_t *len); int (*do_safeprobe)(blkid_probe pr); void (*free_probe)(blkid_probe pr); + dev_t (*probe_get_wholedisk_devno)(blkid_probe pr); /* Partition functions */ blkid_partlist (*probe_get_partitions)(blkid_probe pr); @@ -42,6 +43,7 @@ typedef struct CbmBlkidOps { /* Misc functions */ int (*devno_to_wholedisk)(dev_t dev, char *diskname, size_t len, dev_t *diskdevno); + char *(*devno_to_devname)(dev_t dev); } CbmBlkidOps; /** @@ -109,6 +111,7 @@ int cbm_blkid_probe_set_partitions_flags(blkid_probe pr, int flags); int cbm_blkid_do_safeprobe(blkid_probe pr); int cbm_blkid_probe_lookup_value(blkid_probe pr, const char *name, const char **data, size_t *len); void cbm_blkid_free_probe(blkid_probe pr); +dev_t cbm_probe_get_wholedisk_devno(blkid_probe pr); /** * Partition related wrappers @@ -129,6 +132,7 @@ const char *cbm_blkid_parttable_get_type(blkid_parttable tab); * Misc related wrappers */ int cbm_blkid_devno_to_wholedisk(dev_t dev, char *diskname, size_t len, dev_t *diskdevno); +char *cbm_blkid_devno_to_devname(dev_t dev); /* * Editor modelines - https://www.wireshark.org/tools/modelines.html diff --git a/src/lib/files.c b/src/lib/files.c index 052fb3d3..6e499ce4 100644 --- a/src/lib/files.c +++ b/src/lib/files.c @@ -155,17 +155,22 @@ char *get_boot_device() static bool get_parent_disk_devno(char *path, dev_t *diskdevno) { struct stat st = { 0 }; - dev_t ret; + autofree(char) *dev_path = NULL; if (stat(path, &st) != 0) { return false; } - if (cbm_blkid_devno_to_wholedisk(st.st_dev, NULL, 0, &ret) < 0) { + dev_path = cbm_system_get_device_for_mountpoint(path); + blkid_probe pr = cbm_blkid_new_probe_from_filename(dev_path); + if (cbm_blkid_do_safeprobe(pr) != 0) { LOG_ERROR("Invalid block device: %s", path); + cbm_blkid_free_probe(pr); return false; } - *diskdevno = ret; + + *diskdevno = cbm_probe_get_wholedisk_devno(pr); + cbm_blkid_free_probe(pr); return true; } @@ -199,13 +204,12 @@ char *get_parent_disk(char *path) { dev_t devt; autofree(char) *node = NULL; - const char *devfs = cbm_system_get_devfs_path(); if (!get_parent_disk_devno(path, &devt)) { return NULL; } - node = string_printf("%s/block/%u:%u", devfs, major(devt), minor(devt)); + node = cbm_blkid_devno_to_devname(devt); return realpath(node, NULL); } @@ -518,6 +522,27 @@ char *cbm_get_mountpoint_for_device(const char *device) return NULL; } +char *cbm_get_device_for_mountpoint(const char *mount) +{ + autofree(char) *dev_path = NULL; + autofree(FILE_MNT) *tab = NULL; + struct mntent *mnt = NULL; + + tab = setmntent("/proc/self/mounts", "r"); + if (tab == NULL) { + return NULL; + } + + while ((mnt = getmntent(tab)) != NULL) { + if (strcmp(mnt->mnt_dir, mount) == 0) { + dev_path = strdup(mnt->mnt_fsname); + break; + } + } + + return realpath(dev_path, NULL); +} + bool cbm_system_has_uefi() { autofree(char) *p = NULL; diff --git a/src/lib/files.h b/src/lib/files.h index 3afe662d..a00cca97 100644 --- a/src/lib/files.h +++ b/src/lib/files.h @@ -142,6 +142,14 @@ bool cbm_is_mounted(const char *path); */ char *cbm_get_mountpoint_for_device(const char *device); +/** + * Determine the device for the given mountpoint + * + * @param mount Mount point to get the device for + * @return Device path for the mount point, or NULL if none was found. + */ +char *cbm_get_device_for_mountpoint(const char *mount); + /** * Determine whether the system is booted using UEFI */ diff --git a/src/lib/probe.c b/src/lib/probe.c index a6ffc70e..60b7b223 100644 --- a/src/lib/probe.c +++ b/src/lib/probe.c @@ -203,17 +203,17 @@ CbmDeviceProbe *cbm_probe_path(const char *path) LOG_ERROR("Path does not exist: %s", path); return NULL; } - probe.dev = st.st_dev; - devnode = cbm_system_devnode_to_devpath(probe.dev); + devnode = cbm_system_get_device_for_mountpoint(path); if (!devnode) { + LOG_ERROR("No device for path: %s", path); DECLARE_OOM(); return NULL; } blk_probe = cbm_blkid_new_probe_from_filename(devnode); if (!blk_probe) { - fprintf(stderr, "Unable to probe %u:%u", major(st.st_dev), minor(st.st_dev)); + fprintf(stderr, "Unable to probe device %s", devnode); return NULL; } diff --git a/src/lib/probe.h b/src/lib/probe.h index 1aab949c..4fc7b4fa 100644 --- a/src/lib/probe.h +++ b/src/lib/probe.h @@ -26,7 +26,6 @@ typedef struct CbmDeviceProbe { char *uuid; /**< UUID for all partition types */ char *part_uuid; /**< PartUUID for GPT partitions */ char *luks_uuid; /**< Parent LUKS UUID for the partition */ - dev_t dev; /**< The device itself */ bool gpt; /**get_mountpoint_for_device(device); } +char *cbm_system_get_device_for_mountpoint(const char *device) +{ + return system_ops->get_device_for_mountpoint(device); +} + char *cbm_system_devnode_to_devpath(dev_t d) { return system_ops->devnode_to_devpath(d); diff --git a/src/lib/system_stub.h b/src/lib/system_stub.h index fff3b6b0..78403c46 100644 --- a/src/lib/system_stub.h +++ b/src/lib/system_stub.h @@ -30,6 +30,7 @@ typedef struct CbmSystemOps { /* wrap cbm lib functions */ bool (*is_mounted)(const char *target); char *(*get_mountpoint_for_device)(const char *device); + char *(*get_device_for_mountpoint)(const char *mount); /* exec family */ int (*system)(const char *command); @@ -70,6 +71,11 @@ bool cbm_system_is_mounted(const char *target); */ char *cbm_system_get_mountpoint_for_device(const char *device); +/** + * Get the device path for a given mountpoint + */ +char *cbm_system_get_device_for_mountpoint(const char *device); + /** * Wrap the umount syscall */ diff --git a/tests/blkid-harness.h b/tests/blkid-harness.h index 093e295d..1cba5198 100644 --- a/tests/blkid-harness.h +++ b/tests/blkid-harness.h @@ -12,6 +12,7 @@ #pragma once #include "blkid_stub.h" +#include const char *DEFAULT_UUID = "Test-UUID"; const char *DEFAULT_PART_UUID = "Test-PartUUID"; @@ -48,6 +49,12 @@ static inline int test_blkid_probe_set_partitions_flags(__cbm_unused__ blkid_pro return 0; } +static inline dev_t test_blkid_probe_get_wholedisk_devno(__cbm_unused__ blkid_probe pr) +{ + /* Prevent legacy testing */ + return makedev(0, 0); +} + static inline int test_blkid_do_safeprobe(__cbm_unused__ blkid_probe pr) { return 0; @@ -118,6 +125,12 @@ static inline int test_blkid_devno_to_wholedisk(__cbm_unused__ dev_t dev, return -1; } +static inline char *test_blkid_devno_to_devname(dev_t dev) +{ + return string_printf("%s/dev/block/%u:%u", + TOP_BUILD_DIR "/tests/update_playground", major(dev), minor(dev)); +} + static inline blkid_parttable test_blkid_partlist_get_table(__cbm_unused__ blkid_partlist ls) { /* Return a "valid" partition table */ @@ -140,6 +153,7 @@ CbmBlkidOps BlkidTestOps = { .probe_set_superblocks_flags = test_blkid_probe_set_superblocks_flags, .probe_enable_partitions = test_blkid_probe_enable_partitions, .probe_set_partitions_flags = test_blkid_probe_set_partitions_flags, + .probe_get_wholedisk_devno = test_blkid_probe_get_wholedisk_devno, .probe_lookup_value = test_blkid_probe_lookup_value, .do_safeprobe = test_blkid_do_safeprobe, .free_probe = test_blkid_free_probe, @@ -157,6 +171,7 @@ CbmBlkidOps BlkidTestOps = { /* Misc */ .devno_to_wholedisk = test_blkid_devno_to_wholedisk, + .devno_to_devname = test_blkid_devno_to_devname, }; /* diff --git a/tests/check-legacy.c b/tests/check-legacy.c index 07f10dd7..d2bf39bc 100644 --- a/tests/check-legacy.c +++ b/tests/check-legacy.c @@ -43,6 +43,14 @@ static inline int legacy_devno_to_wholedisk(__cbm_unused__ dev_t dev, __cbm_unus return 0; } +/** + * Coerce legacy lookup + */ +static inline dev_t legacy_probe_get_wholedisk_devno(__cbm_unused__ blkid_probe pr) +{ + return makedev(8, 8); +} + /** * Forces detection of GPT legacy boot partition */ @@ -290,6 +298,7 @@ int main(void) /* override test ops for legacy testing */ CbmBlkidOps blkid_ops = BlkidTestOps; blkid_ops.devno_to_wholedisk = legacy_devno_to_wholedisk; + blkid_ops.probe_get_wholedisk_devno = legacy_probe_get_wholedisk_devno; blkid_ops.partition_get_flags = legacy_partition_get_flags; blkid_ops.partition_get_uuid = legacy_partition_get_uuid; diff --git a/tests/check-probe.c b/tests/check-probe.c index 321ab6d0..94b1f185 100644 --- a/tests/check-probe.c +++ b/tests/check-probe.c @@ -66,6 +66,14 @@ static inline int gpt_devno_to_wholedisk(__cbm_unused__ dev_t dev, __cbm_unused_ return 0; } +/** + * Coerce legacy lookup + */ +static inline dev_t gpt_probe_get_wholedisk_devno(__cbm_unused__ blkid_probe pr) +{ + return makedev(8, 8); +} + /** * This will force the tests to use the GPT detection codepaths */ @@ -75,6 +83,7 @@ static CbmBlkidOps gpt_blkid_ops = { .probe_set_superblocks_flags = test_blkid_probe_set_superblocks_flags, .probe_enable_partitions = test_blkid_probe_enable_partitions, .probe_set_partitions_flags = test_blkid_probe_set_partitions_flags, + .probe_get_wholedisk_devno = gpt_probe_get_wholedisk_devno, .probe_lookup_value = test_blkid_probe_lookup_value, .do_safeprobe = test_blkid_do_safeprobe, .free_probe = test_blkid_free_probe, @@ -86,6 +95,7 @@ static CbmBlkidOps gpt_blkid_ops = { .partlist_get_table = test_blkid_partlist_get_table, .parttable_get_type = test_blkid_parttable_get_type, .devno_to_wholedisk = gpt_devno_to_wholedisk, + .devno_to_devname = test_blkid_devno_to_devname, }; static inline const char *mbr_parttable_get_type(__cbm_unused__ blkid_parttable tab) @@ -102,6 +112,7 @@ static CbmBlkidOps mbr_blkid_ops = { .probe_set_superblocks_flags = test_blkid_probe_set_superblocks_flags, .probe_enable_partitions = test_blkid_probe_enable_partitions, .probe_set_partitions_flags = test_blkid_probe_set_partitions_flags, + .probe_get_wholedisk_devno = test_blkid_probe_get_wholedisk_devno, .probe_lookup_value = test_blkid_probe_lookup_value, .do_safeprobe = test_blkid_do_safeprobe, .free_probe = test_blkid_free_probe, @@ -113,6 +124,7 @@ static CbmBlkidOps mbr_blkid_ops = { .partlist_get_table = test_blkid_partlist_get_table, .parttable_get_type = mbr_parttable_get_type, .devno_to_wholedisk = gpt_devno_to_wholedisk, + .devno_to_devname = test_blkid_devno_to_devname, }; static void bootman_probe_set_gpt_vtables(void) diff --git a/tests/check-select-bootloader.c b/tests/check-select-bootloader.c index 6c13c2a0..b0f5cfb0 100644 --- a/tests/check-select-bootloader.c +++ b/tests/check-select-bootloader.c @@ -181,6 +181,14 @@ static inline int legacy_devno_to_wholedisk(__cbm_unused__ dev_t dev, __cbm_unus return 0; } +/** + * Coerce legacy lookup + */ +static inline dev_t legacy_probe_get_wholedisk_devno(__cbm_unused__ blkid_probe pr) +{ + return makedev(8, 8); +} + /** * Forces detection of GPT legacy boot partition */ @@ -206,6 +214,7 @@ static CbmBlkidOps legacy_blkid_ops = { .probe_set_superblocks_flags = test_blkid_probe_set_superblocks_flags, .probe_enable_partitions = test_blkid_probe_enable_partitions, .probe_set_partitions_flags = test_blkid_probe_set_partitions_flags, + .probe_get_wholedisk_devno = legacy_probe_get_wholedisk_devno, .probe_lookup_value = test_blkid_probe_lookup_value, .do_safeprobe = test_blkid_do_safeprobe, .free_probe = test_blkid_free_probe, @@ -217,6 +226,7 @@ static CbmBlkidOps legacy_blkid_ops = { .partlist_get_table = test_blkid_partlist_get_table, .parttable_get_type = test_blkid_parttable_get_type, .devno_to_wholedisk = legacy_devno_to_wholedisk, + .devno_to_devname = test_blkid_devno_to_devname, }; static void bootman_select_set_legacy_vtables(void) @@ -303,6 +313,7 @@ static CbmBlkidOps grub2_blkid_ops = { .probe_set_superblocks_flags = test_blkid_probe_set_superblocks_flags, .probe_enable_partitions = test_blkid_probe_enable_partitions, .probe_set_partitions_flags = test_blkid_probe_set_partitions_flags, + .probe_get_wholedisk_devno = test_blkid_probe_get_wholedisk_devno, .probe_lookup_value = grub2_blkid_probe_lookup_value, .do_safeprobe = test_blkid_do_safeprobe, .free_probe = test_blkid_free_probe, @@ -314,6 +325,7 @@ static CbmBlkidOps grub2_blkid_ops = { .partlist_get_table = test_blkid_partlist_get_table, .parttable_get_type = test_blkid_parttable_get_type, .devno_to_wholedisk = test_blkid_devno_to_wholedisk, + .devno_to_devname = test_blkid_devno_to_devname, }; static void bootman_select_set_grub2_vtables(void) diff --git a/tests/check-syslinux.c b/tests/check-syslinux.c index 80c81e15..83898854 100644 --- a/tests/check-syslinux.c +++ b/tests/check-syslinux.c @@ -43,6 +43,14 @@ static inline int legacy_devno_to_wholedisk(__cbm_unused__ dev_t dev, __cbm_unus return 0; } +/** + * Coerce legacy lookup + */ +static inline dev_t legacy_probe_get_wholedisk_devno(__cbm_unused__ blkid_probe pr) +{ + return makedev(8, 8); +} + /** * Forces detection of GPT legacy boot partition */ @@ -290,6 +298,7 @@ int main(void) /* override test ops for legacy testing */ CbmBlkidOps blkid_ops = BlkidTestOps; blkid_ops.devno_to_wholedisk = legacy_devno_to_wholedisk; + blkid_ops.probe_get_wholedisk_devno = legacy_probe_get_wholedisk_devno; blkid_ops.partition_get_flags = legacy_partition_get_flags; blkid_ops.partition_get_uuid = legacy_partition_get_uuid; diff --git a/tests/system-harness.h b/tests/system-harness.h index 0b7836c0..94137a1d 100644 --- a/tests/system-harness.h +++ b/tests/system-harness.h @@ -41,11 +41,16 @@ static inline char *test_get_mountpoint_for_device(__cbm_unused__ const char *de return NULL; } -static inline char *test_devnode_to_devpath(__cbm_unused__ dev_t d) +static inline char *test_get_device_for_mountpoint(__cbm_unused__ const char *device) { return string_printf("%s/dev/testRoot", TOP_BUILD_DIR "/tests/update_playground"); } +static inline char *test_devnode_to_devpath(__cbm_unused__ dev_t d) +{ + return test_get_device_for_mountpoint(NULL); +} + static inline const char *test_get_sysfs_path(void) { return TOP_BUILD_DIR "/tests/update_playground/sys"; @@ -66,6 +71,7 @@ CbmSystemOps SystemTestOps = { .system = test_system, .is_mounted = test_is_mounted, .get_mountpoint_for_device = test_get_mountpoint_for_device, + .get_device_for_mountpoint = test_get_device_for_mountpoint, .devnode_to_devpath = test_devnode_to_devpath, .get_sysfs_path = test_get_sysfs_path, .get_devfs_path = test_get_devfs_path,