+ return ret;
+}
+
+static int restore_boot_region(struct exfat_blk_dev *bd, unsigned int sect_size)
+{
+ int i;
+ char *sector;
+ int ret;
+
+ sector = malloc(sect_size);
+ if (!sector)
+ return -ENOMEM;
+
+ for (i = 0; i < 12; i++) {
+ if (exfat_read(bd->dev_fd, sector, sect_size,
+ BACKUP_BOOT_SEC_IDX * sect_size +
+ i * sect_size) !=
+ (ssize_t)sect_size) {
+ ret = -EIO;
+ goto free_sector;
+ }
+ if (i == 0)
+ ((struct pbr *)sector)->bsx.perc_in_use = 0xff;
+
+ if (exfat_write(bd->dev_fd, sector, sect_size,
+ BOOT_SEC_IDX * sect_size +
+ i * sect_size) !=
+ (ssize_t)sect_size) {
+ ret = -EIO;
+ goto free_sector;
+ }
+ }
+
+ if (fsync(bd->dev_fd)) {
+ ret = -EIO;
+ goto free_sector;
+ }
+ ret = 0;
+
+free_sector:
+ free(sector);
+ return ret;
+}
+
+static int exfat_boot_region_check(struct exfat *exfat, struct pbr **bs)
+{
+ struct pbr *boot_sect;
+ unsigned int sect_size;
+ int ret;
+
+ /* First, find out the exfat sector size */
+ boot_sect = malloc(sizeof(*boot_sect));
+ if (boot_sect == NULL)
+ return -ENOMEM;
+
+ if (exfat_read(exfat->blk_dev->dev_fd, boot_sect,
+ sizeof(*boot_sect), 0) != (ssize_t)sizeof(*boot_sect)) {
+ exfat_err("failed to read Main boot sector\n");
+ return -EIO;
+ }
+
+ sect_size = 1 << boot_sect->bsx.sect_size_bits;
+ free(boot_sect);
+
+ /* check boot regions */
+ ret = read_boot_region(exfat->blk_dev, bs,
+ BOOT_SEC_IDX, sect_size, true);
+ if (ret == -EINVAL && exfat_repair_ask(exfat, ER_BS_BOOT_REGION,
+ "boot region is corrupted. try to restore the region from backup"
+ )) {
+ const unsigned int sector_sizes[] = {512, 4096, 1024, 2048};
+ unsigned int i;
+
+ if (sect_size >= 512 && sect_size <= EXFAT_MAX_SECTOR_SIZE) {
+ ret = read_boot_region(exfat->blk_dev, bs,
+ BACKUP_BOOT_SEC_IDX, sect_size,
+ false);
+ if (!ret)
+ goto restore;
+ }
+
+ for (i = 0; i < sizeof(sector_sizes)/sizeof(sector_sizes[0]); i++) {
+ if (sector_sizes[i] == sect_size)
+ continue;
+
+ ret = read_boot_region(exfat->blk_dev, bs,
+ BACKUP_BOOT_SEC_IDX,
+ sector_sizes[i], false);
+ if (!ret) {
+ sect_size = sector_sizes[i];
+ goto restore;
+ }
+ }
+ exfat_err("backup boot region is also corrupted\n");
+ }
+
+ return ret;
+restore:
+ ret = restore_boot_region(exfat->blk_dev, sect_size);
+ if (ret) {
+ exfat_err("failed to restore boot region from backup\n");
+ free(*bs);
+ *bs = NULL;
+ }
+ return ret;