]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libblkid: exfat: add checksum support
authorThomas Weißschuh <thomas@t-8ch.de>
Sat, 10 Sep 2022 10:18:14 +0000 (12:18 +0200)
committerThomas Weißschuh <thomas@t-8ch.de>
Sat, 10 Sep 2022 10:21:25 +0000 (12:21 +0200)
Signed-off-by: Thomas Weißschuh <thomas@t-8ch.de>
libblkid/src/superblocks/exfat.c

index fd2b8565529180982e700dfb956aab8afea346b9..101a2321e37ae59ee769fb94f044674fb16b0fa4 100644 (file)
@@ -114,6 +114,47 @@ static struct exfat_entry_label *find_label(blkid_probe pr,
        return NULL;
 }
 
+/* From https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification#34-main-and-backup-boot-checksum-sub-regions */
+static uint32_t exfat_boot_checksum(unsigned char *sectors,
+                                   size_t sector_size)
+{
+       uint32_t n_bytes = sector_size * 11;
+       uint32_t checksum = 0;
+
+       for (size_t i = 0; i < n_bytes; i++) {
+               if ((i == 106) || (i == 107) || (i == 112))
+                       continue;
+
+               checksum = ((checksum & 1) ? 0x80000000 : 0) + (checksum >> 1)
+                       + (uint32_t) sectors[i];
+       }
+
+       return checksum;
+}
+
+static int exfat_validate_checksum(blkid_probe pr,
+               const struct exfat_super_block *sb)
+{
+       size_t sector_size = BLOCK_SIZE(sb);
+       /* 11 sectors will be checksummed, the 12th contains the expected */
+       unsigned char *data = blkid_probe_get_buffer(pr, 0, sector_size * 12);
+       if (!data)
+               return 0;
+
+       uint32_t checksum = exfat_boot_checksum(data, sector_size);
+
+       /* The expected checksum is repeated, check all of them */
+       for (size_t i = 0; i < sector_size / sizeof(uint32_t); i++) {
+               size_t offset = sector_size * 11 + i * 4;
+               uint32_t *expected_addr = (uint32_t *) &data[offset];
+               uint32_t expected = le32_to_cpu(*expected_addr);
+               if (!blkid_probe_verify_csum(pr, checksum, expected))
+                       return 0;
+       };
+
+       return 1;
+}
+
 static int probe_exfat(blkid_probe pr, const struct blkid_idmag *mag)
 {
        struct exfat_super_block *sb;
@@ -123,6 +164,9 @@ static int probe_exfat(blkid_probe pr, const struct blkid_idmag *mag)
        if (!sb || !CLUSTER_SIZE(sb))
                return errno ? -errno : BLKID_PROBE_NONE;
 
+       if (!exfat_validate_checksum(pr, sb))
+               return BLKID_PROBE_NONE;
+
        label = find_label(pr, sb);
        if (label)
                blkid_probe_set_utf8label(pr, label->name,