File mkfs-fix-boot-sector-checksum-when-the-sector-size-i.patch of Package exfatprogs.30205

From f570d7828ec8f993f947bccc8b65ef4aaed08bd7 Mon Sep 17 00:00:00 2001
From: Christophe Vu-Brugier <christophe.vu-brugier@seagate.com>
Date: Tue, 11 May 2021 09:40:49 +0200
Subject: [PATCH] mkfs: fix boot sector checksum when the sector size is 4 KB

"4K native" HDDs advertise a physical sector size and a logical sector
size of 4 KB. A sector size of 4 KB is supported according to the
exFAT specification. From section 3.1.14:

> The valid range of values for [BytesPerSectorShift] shall be:
> * At least 9 (sector size of 512 bytes), which is the smallest
>   sector possible for an exFAT volume
> * At most 12 (sector size of 4096 bytes), which is the memory page
>   size of CPUs common in personal computers

Unfortunately, several bugs in `mkfs.exfat` prevent it from properly
formatting devices whose logical sector size is 4 KB. The Linux kernel
reports the following error at `mount` time:

    $ sudo mount /dev/vda /mnt/
    [  262.063000] exFAT-fs (vda): Invalid boot checksum (boot checksum : 0x8455546b, checksum : 0xfedcaae5)
    [  262.068991] exFAT-fs (vda): invalid boot region
    [  262.071618] exFAT-fs (vda): failed to recognize exfat type
    mount: /mnt: wrong fs type, bad option, bad superblock on /dev/vda, missing codepage or helper program, or other error.

There are 3 issues in `mkfs.exfat:`

1. it truncates some volume structures that should fill an entire
   sector (4 KB for 4Kn HDDs) to 512 bytes when writing them.
2. it computes the main and backup boot checksums using only 512 bytes
   for some sectors. Hence the error reported by Linux.
3. it writes the extended boot signature at offset 510 bytes whatever
   the sector size is.

This is fixed by using `bd->sector_size` to allocate, clear, and
checksum the sectors. Moreover, the extended boot signature is now
placed in the last two bytes of the extended boot sectors.

This patch fixes issue #163.

Signed-off-by: Christophe Vu-Brugier <christophe.vu-brugier@seagate.com>
(cherry picked from commit 1e3163a68d3b73ad8d55df12c5a8b639003de188)
[ddiss: rebase without b4d9c9 ("exfatlabel: add get/set volume serial")]
Acked-by: David Disseldorp <ddiss@suse.de>
---
 include/exfat_ondisk.h | 11 -----------
 mkfs/mkfs.c            | 30 ++++++++++++++++++++----------
 2 files changed, 20 insertions(+), 21 deletions(-)

diff --git a/include/exfat_ondisk.h b/include/exfat_ondisk.h
index 163bef0..bfbdb1a 100644
--- a/include/exfat_ondisk.h
+++ b/include/exfat_ondisk.h
@@ -131,17 +131,6 @@ struct pbr {
 	__le16 signature;
 };
 
-/* Extended Boot Sector */
-struct exbs {
-	__u8 zero[510];
-	__le16 signature;
-};
-
-/* Extended Boot Record (8 sectors) */
-struct expbr {
-	struct exbs eb[8];
-};
-
 #define VOLUME_LABEL_MAX_LEN	11
 #define ENTRY_NAME_MAX		15
 
diff --git a/mkfs/mkfs.c b/mkfs/mkfs.c
index 9fff978..5b8e7fb 100644
--- a/mkfs/mkfs.c
+++ b/mkfs/mkfs.c
@@ -121,12 +121,12 @@ static int exfat_write_boot_sector(struct exfat_blk_dev *bd,
 	if (is_backup)
 		sec_idx += BACKUP_BOOT_SEC_IDX;
 
-	ppbr = malloc(sizeof(struct pbr));
+	ppbr = malloc(bd->sector_size);
 	if (!ppbr) {
 		exfat_err("Cannot allocate pbr: out of memory\n");
 		return -1;
 	}
-	memset(ppbr, 0, sizeof(struct pbr));
+	memset(ppbr, 0, bd->sector_size);
 
 	exfat_setup_boot_sector(ppbr, bd, ui);
 
@@ -138,7 +138,7 @@ static int exfat_write_boot_sector(struct exfat_blk_dev *bd,
 		goto free_ppbr;
 	}
 
-	boot_calc_checksum((unsigned char *)ppbr, sizeof(struct pbr),
+	boot_calc_checksum((unsigned char *)ppbr, bd->sector_size,
 		true, checksum);
 
 free_ppbr:
@@ -149,26 +149,36 @@ free_ppbr:
 static int exfat_write_extended_boot_sectors(struct exfat_blk_dev *bd,
 		unsigned int *checksum, bool is_backup)
 {
-	struct exbs eb;
+	char *peb;
+	__le16 *peb_signature;
+	int ret = 0;
 	int i;
 	unsigned int sec_idx = EXBOOT_SEC_IDX;
 
+	peb = malloc(bd->sector_size);
+	if (!peb)
+		return -1;
+
 	if (is_backup)
 		sec_idx += BACKUP_BOOT_SEC_IDX;
 
-	memset(&eb, 0, sizeof(struct exbs));
-	eb.signature = cpu_to_le16(PBR_SIGNATURE);
+	memset(peb, 0, bd->sector_size);
+	peb_signature = (__le16*) (peb + bd->sector_size - 2);
+	*peb_signature = cpu_to_le16(PBR_SIGNATURE);
 	for (i = 0; i < EXBOOT_SEC_NUM; i++) {
-		if (exfat_write_sector(bd, &eb, sec_idx++)) {
+		if (exfat_write_sector(bd, peb, sec_idx++)) {
 			exfat_err("extended boot sector write failed\n");
-			return -1;
+			ret = -1;
+			goto free_peb;
 		}
 
-		boot_calc_checksum((unsigned char *) &eb, sizeof(struct exbs),
+		boot_calc_checksum((unsigned char *) peb, bd->sector_size,
 			false, checksum);
 	}
 
-	return 0;
+free_peb:
+	free(peb);
+	return ret;
 }
 
 static int exfat_write_oem_sector(struct exfat_blk_dev *bd,
-- 
2.35.3

openSUSE Build Service is sponsored by