From a13479bc46f87d89e874621e09f05878db402ec9 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 13 May 2021 14:46:22 +0900 Subject: [PATCH] exfatprogs: tune: use sector size extracted from the boot sector Eric Sandeen reported exfat dump image is damaged when tune.exfat was executed against exfat dump image created in 4K native device. # fsck/fsck.exfat /root/test.img exfatprogs version : 1.1.1 /root/test.img: clean. directories 1, files 0 # tune/tune.exfat -I 0x1234 /root/test.img exfatprogs version : 1.1.1 New volume serial : 0x1234 # fsck/fsck.exfat /root/test.img exfatprogs version : 1.1.1 checksum of boot region is not correct. 0x3eedc5, but expected 0xe59577e3 boot region is corrupted. try to restore the region from backup. Fix (y/N)? n This patch read boot sector with 4KB(for 4K native) size, and use the sector size extracted from the boot sector. Reported-by: Eric Sandeen Signed-off-by: Namjae Jeon --- include/libexfat.h | 4 +++- label/label.c | 2 +- lib/libexfat.c | 29 ++++++++++++++++------------- tune/tune.c | 2 +- 4 files changed, 21 insertions(+), 16 deletions(-) diff --git a/include/libexfat.h b/include/libexfat.h index 7b567984..fecd7f23 100644 --- a/include/libexfat.h +++ b/include/libexfat.h @@ -43,6 +43,8 @@ #define EXFAT_GET_VOLUME_SERIAL 0x03 #define EXFAT_SET_VOLUME_SERIAL 0x04 +#define EXFAT_MAX_SECTOR_SIZE 4096 + enum { BOOT_SEC_IDX = 0, EXBOOT_SEC_IDX, @@ -107,7 +109,7 @@ int exfat_write_sector(struct exfat_blk_dev *bd, void *buf, int exfat_write_checksum_sector(struct exfat_blk_dev *bd, unsigned int checksum, bool is_backup); char *exfat_conv_volume_label(struct exfat_dentry *vol_entry); -int exfat_show_volume_serial(struct exfat_blk_dev *bd); +int exfat_show_volume_serial(int fd); int exfat_set_volume_serial(struct exfat_blk_dev *bd, struct exfat_user_input *ui); unsigned int exfat_clus_to_blk_dev_off(struct exfat_blk_dev *bd, diff --git a/label/label.c b/label/label.c index 66c54e8a..b41e8271 100644 --- a/label/label.c +++ b/label/label.c @@ -90,7 +90,7 @@ int main(int argc, char *argv[]) if (serial_mode) { /* Mode to change or display volume serial */ if (flags == EXFAT_GET_VOLUME_SERIAL) { - ret = exfat_show_volume_serial(&bd); + ret = exfat_show_volume_serial(bd.dev_fd); } else if (flags == EXFAT_SET_VOLUME_SERIAL) { ui.volume_serial = strtoul(argv[3], NULL, 0); ret = exfat_set_volume_serial(&bd, &ui); diff --git a/lib/libexfat.c b/lib/libexfat.c index 8f2ea0f8..16138639 100644 --- a/lib/libexfat.c +++ b/lib/libexfat.c @@ -358,26 +358,27 @@ off_t exfat_get_root_entry_offset(struct exfat_blk_dev *bd) { struct pbr *bs; int nbytes; - unsigned int cluster_size; + unsigned int cluster_size, sector_size; off_t root_clu_off; - bs = (struct pbr *)malloc(sizeof(struct pbr)); + bs = (struct pbr *)malloc(EXFAT_MAX_SECTOR_SIZE); if (!bs) { exfat_err("failed to allocate memory\n"); return -ENOMEM; } - nbytes = exfat_read(bd->dev_fd, bs, sizeof(struct pbr), 0); - if (nbytes != sizeof(struct pbr)) { + nbytes = exfat_read(bd->dev_fd, bs, EXFAT_MAX_SECTOR_SIZE, 0); + if (nbytes != EXFAT_MAX_SECTOR_SIZE) { exfat_err("boot sector read failed: %d\n", errno); free(bs); return -1; } - cluster_size = (1 << bs->bsx.sect_per_clus_bits) * bd->sector_size; - root_clu_off = le32_to_cpu(bs->bsx.clu_offset) * bd->sector_size + - le32_to_cpu(bs->bsx.root_cluster - EXFAT_RESERVED_CLUSTERS) - * cluster_size; + sector_size = 1 << bs->bsx.sect_size_bits; + cluster_size = (1 << bs->bsx.sect_per_clus_bits) * sector_size; + root_clu_off = le32_to_cpu(bs->bsx.clu_offset) * sector_size + + le32_to_cpu(bs->bsx.root_cluster - EXFAT_RESERVED_CLUSTERS) * + cluster_size; free(bs); return root_clu_off; @@ -528,19 +529,19 @@ int exfat_write_checksum_sector(struct exfat_blk_dev *bd, return ret; } -int exfat_show_volume_serial(struct exfat_blk_dev *bd) +int exfat_show_volume_serial(int fd) { struct pbr *ppbr; int ret; - ppbr = malloc(bd->sector_size); + ppbr = malloc(EXFAT_MAX_SECTOR_SIZE); if (!ppbr) { exfat_err("Cannot allocate pbr: out of memory\n"); return -1; } /* read main boot sector */ - ret = exfat_read_sector(bd, (char *)ppbr, BOOT_SEC_IDX); + ret = exfat_read(fd, (char *)ppbr, EXFAT_MAX_SECTOR_SIZE, 0); if (ret < 0) { exfat_err("main boot sector read failed\n"); ret = -1; @@ -600,20 +601,22 @@ int exfat_set_volume_serial(struct exfat_blk_dev *bd, int ret; struct pbr *ppbr; - ppbr = malloc(bd->sector_size); + ppbr = malloc(EXFAT_MAX_SECTOR_SIZE); if (!ppbr) { exfat_err("Cannot allocate pbr: out of memory\n"); return -1; } /* read main boot sector */ - ret = exfat_read_sector(bd, (char *)ppbr, BOOT_SEC_IDX); + ret = exfat_read(bd->dev_fd, (char *)ppbr, EXFAT_MAX_SECTOR_SIZE, + BOOT_SEC_IDX); if (ret < 0) { exfat_err("main boot sector read failed\n"); ret = -1; goto free_ppbr; } + bd->sector_size = 1 << ppbr->bsx.sect_size_bits; ppbr->bsx.vol_serial = ui->volume_serial; /* update main boot sector */ diff --git a/tune/tune.c b/tune/tune.c index bec9bb12..a53be597 100644 --- a/tune/tune.c +++ b/tune/tune.c @@ -102,7 +102,7 @@ int main(int argc, char *argv[]) /* Mode to change or display volume serial */ if (flags == EXFAT_GET_VOLUME_SERIAL) { - ret = exfat_show_volume_serial(&bd); + ret = exfat_show_volume_serial(bd.dev_fd); goto close_fd_out; } else if (flags == EXFAT_SET_VOLUME_SERIAL) { ret = exfat_set_volume_serial(&bd, &ui);