xref: /netbsd-src/usr.sbin/fstyp/exfat.c (revision ce58ffd338fadaf7de9a4ee9bc8f24033111977c)
1*ce58ffd3Schristos /*	$NetBSD: exfat.c,v 1.6 2021/09/17 21:06:35 christos Exp $	*/
27575c8cfStkusumi 
37575c8cfStkusumi /*
47575c8cfStkusumi  * Copyright (c) 2017 Conrad Meyer <cem@FreeBSD.org>
57575c8cfStkusumi  * All rights reserved.
67575c8cfStkusumi  *
77575c8cfStkusumi  * Redistribution and use in source and binary forms, with or without
87575c8cfStkusumi  * modification, are permitted provided that the following conditions
97575c8cfStkusumi  * are met:
107575c8cfStkusumi  * 1. Redistributions of source code must retain the above copyright
117575c8cfStkusumi  *    notice, this list of conditions and the following disclaimer.
127575c8cfStkusumi  * 2. Redistributions in binary form must reproduce the above copyright
137575c8cfStkusumi  *    notice, this list of conditions and the following disclaimer in the
147575c8cfStkusumi  *    documentation and/or other materials provided with the distribution.
157575c8cfStkusumi  *
167575c8cfStkusumi  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
177575c8cfStkusumi  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
187575c8cfStkusumi  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
197575c8cfStkusumi  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
207575c8cfStkusumi  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
217575c8cfStkusumi  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
227575c8cfStkusumi  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
237575c8cfStkusumi  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
247575c8cfStkusumi  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
257575c8cfStkusumi  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
267575c8cfStkusumi  * SUCH DAMAGE.
277575c8cfStkusumi  */
287575c8cfStkusumi #include <sys/cdefs.h>
29*ce58ffd3Schristos __RCSID("$NetBSD: exfat.c,v 1.6 2021/09/17 21:06:35 christos Exp $");
307575c8cfStkusumi 
310cd59d67Stkusumi #include <sys/param.h>
320cd59d67Stkusumi #include <sys/endian.h>
330cd59d67Stkusumi 
340cd59d67Stkusumi #include <assert.h>
350cd59d67Stkusumi #include <err.h>
360cd59d67Stkusumi #include <errno.h>
370cd59d67Stkusumi #include <iconv.h>
380cd59d67Stkusumi #include <stdbool.h>
397575c8cfStkusumi #include <stdint.h>
407575c8cfStkusumi #include <stdio.h>
417575c8cfStkusumi #include <stdlib.h>
427575c8cfStkusumi #include <string.h>
437575c8cfStkusumi 
447575c8cfStkusumi #include "fstyp.h"
457575c8cfStkusumi 
460cd59d67Stkusumi /*
470cd59d67Stkusumi  * https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification
480cd59d67Stkusumi  */
490cd59d67Stkusumi 
507575c8cfStkusumi struct exfat_vbr {
517575c8cfStkusumi 	char		ev_jmp[3];
527575c8cfStkusumi 	char		ev_fsname[8];
537575c8cfStkusumi 	char		ev_zeros[53];
547575c8cfStkusumi 	uint64_t	ev_part_offset;
557575c8cfStkusumi 	uint64_t	ev_vol_length;
567575c8cfStkusumi 	uint32_t	ev_fat_offset;
577575c8cfStkusumi 	uint32_t	ev_fat_length;
587575c8cfStkusumi 	uint32_t	ev_cluster_offset;
597575c8cfStkusumi 	uint32_t	ev_cluster_count;
607575c8cfStkusumi 	uint32_t	ev_rootdir_cluster;
617575c8cfStkusumi 	uint32_t	ev_vol_serial;
627575c8cfStkusumi 	uint16_t	ev_fs_revision;
637575c8cfStkusumi 	uint16_t	ev_vol_flags;
647575c8cfStkusumi 	uint8_t		ev_log_bytes_per_sect;
657575c8cfStkusumi 	uint8_t		ev_log_sect_per_clust;
667575c8cfStkusumi 	uint8_t		ev_num_fats;
677575c8cfStkusumi 	uint8_t		ev_drive_sel;
687575c8cfStkusumi 	uint8_t		ev_percent_used;
697575c8cfStkusumi } __packed;
707575c8cfStkusumi 
710cd59d67Stkusumi struct exfat_dirent {
720cd59d67Stkusumi 	uint8_t		xde_type;
730cd59d67Stkusumi #define	XDE_TYPE_INUSE_MASK	0x80	/* 1=in use */
740cd59d67Stkusumi #define	XDE_TYPE_INUSE_SHIFT	7
750cd59d67Stkusumi #define	XDE_TYPE_CATEGORY_MASK	0x40	/* 0=primary */
760cd59d67Stkusumi #define	XDE_TYPE_CATEGORY_SHIFT	6
770cd59d67Stkusumi #define	XDE_TYPE_IMPORTNC_MASK	0x20	/* 0=critical */
780cd59d67Stkusumi #define	XDE_TYPE_IMPORTNC_SHIFT	5
790cd59d67Stkusumi #define	XDE_TYPE_CODE_MASK	0x1f
800cd59d67Stkusumi /* InUse=0, ..., TypeCode=0: EOD. */
810cd59d67Stkusumi #define	XDE_TYPE_EOD		0x00
820cd59d67Stkusumi #define	XDE_TYPE_ALLOC_BITMAP	(XDE_TYPE_INUSE_MASK | 0x01)
830cd59d67Stkusumi #define	XDE_TYPE_UPCASE_TABLE	(XDE_TYPE_INUSE_MASK | 0x02)
840cd59d67Stkusumi #define	XDE_TYPE_VOL_LABEL	(XDE_TYPE_INUSE_MASK | 0x03)
850cd59d67Stkusumi #define	XDE_TYPE_FILE		(XDE_TYPE_INUSE_MASK | 0x05)
860cd59d67Stkusumi #define	XDE_TYPE_VOL_GUID	(XDE_TYPE_INUSE_MASK | XDE_TYPE_IMPORTNC_MASK)
870cd59d67Stkusumi #define	XDE_TYPE_STREAM_EXT	(XDE_TYPE_INUSE_MASK | XDE_TYPE_CATEGORY_MASK)
880cd59d67Stkusumi #define	XDE_TYPE_FILE_NAME	(XDE_TYPE_INUSE_MASK | XDE_TYPE_CATEGORY_MASK | 0x01)
890cd59d67Stkusumi #define	XDE_TYPE_VENDOR		(XDE_TYPE_INUSE_MASK | XDE_TYPE_CATEGORY_MASK | XDE_TYPE_IMPORTNC_MASK)
900cd59d67Stkusumi #define	XDE_TYPE_VENDOR_ALLOC	(XDE_TYPE_INUSE_MASK | XDE_TYPE_CATEGORY_MASK | XDE_TYPE_IMPORTNC_MASK | 0x01)
910cd59d67Stkusumi 	union {
920cd59d67Stkusumi 		uint8_t	xde_generic_[19];
930cd59d67Stkusumi 		struct exde_primary {
940cd59d67Stkusumi 			/*
950cd59d67Stkusumi 			 * Count of "secondary" dirents following this one.
960cd59d67Stkusumi 			 *
970cd59d67Stkusumi 			 * A single logical entity may be composed of a
980cd59d67Stkusumi 			 * sequence of several dirents, starting with a primary
990cd59d67Stkusumi 			 * one; the rest are secondary dirents.
1000cd59d67Stkusumi 			 */
1010cd59d67Stkusumi 			uint8_t		xde_secondary_count_;
1020cd59d67Stkusumi 			uint16_t	xde_set_chksum_;
1030cd59d67Stkusumi 			uint16_t	xde_prim_flags_;
1040cd59d67Stkusumi 			uint8_t		xde_prim_generic_[14];
1050cd59d67Stkusumi 		} __packed xde_primary_;
1060cd59d67Stkusumi 		struct exde_secondary {
1070cd59d67Stkusumi 			uint8_t		xde_sec_flags_;
1080cd59d67Stkusumi 			uint8_t		xde_sec_generic_[18];
1090cd59d67Stkusumi 		} __packed xde_secondary_;
1100cd59d67Stkusumi 	} u;
1110cd59d67Stkusumi 	uint32_t	xde_first_cluster;
1120cd59d67Stkusumi 	uint64_t	xde_data_len;
1130cd59d67Stkusumi } __packed;
1140cd59d67Stkusumi #define	xde_generic		u.xde_generic_
1150cd59d67Stkusumi #define	xde_secondary_count	u.xde_primary_.xde_secondary_count
1160cd59d67Stkusumi #define	xde_set_chksum		u.xde_primary_.xde_set_chksum_
1170cd59d67Stkusumi #define	xde_prim_flags		u.xde_primary_.xde_prim_flags_
1180cd59d67Stkusumi #define	xde_sec_flags		u.xde_secondary_.xde_sec_flags_
1190cd59d67Stkusumi _Static_assert(sizeof(struct exfat_dirent) == 32, "spec");
1200cd59d67Stkusumi 
1210cd59d67Stkusumi struct exfat_de_label {
1220cd59d67Stkusumi 	uint8_t		xdel_type;	/* XDE_TYPE_VOL_LABEL */
1230cd59d67Stkusumi 	uint8_t		xdel_char_cnt;	/* Length of UCS-2 label */
1240cd59d67Stkusumi 	uint16_t	xdel_vol_lbl[11];
1250cd59d67Stkusumi 	uint8_t		xdel_reserved[8];
1260cd59d67Stkusumi } __packed;
1270cd59d67Stkusumi _Static_assert(sizeof(struct exfat_de_label) == 32, "spec");
1280cd59d67Stkusumi 
1290cd59d67Stkusumi #define	MAIN_BOOT_REGION_SECT	0
1300cd59d67Stkusumi #define	BACKUP_BOOT_REGION_SECT	12
1310cd59d67Stkusumi 
1320cd59d67Stkusumi #define	SUBREGION_CHKSUM_SECT	11
1330cd59d67Stkusumi 
1340cd59d67Stkusumi #define	FIRST_CLUSTER		2
1350cd59d67Stkusumi #define	BAD_BLOCK_SENTINEL	0xfffffff7u
1360cd59d67Stkusumi #define	END_CLUSTER_SENTINEL	0xffffffffu
1370cd59d67Stkusumi 
1380cd59d67Stkusumi static inline void *
read_sectn(FILE * fp,off_t sect,unsigned count,unsigned bytespersec)1390cd59d67Stkusumi read_sectn(FILE *fp, off_t sect, unsigned count, unsigned bytespersec)
1400cd59d67Stkusumi {
1410cd59d67Stkusumi 	return (read_buf(fp, sect * bytespersec, bytespersec * count));
1420cd59d67Stkusumi }
1430cd59d67Stkusumi 
1440cd59d67Stkusumi static inline void *
read_sect(FILE * fp,off_t sect,unsigned bytespersec)1450cd59d67Stkusumi read_sect(FILE *fp, off_t sect, unsigned bytespersec)
1460cd59d67Stkusumi {
1470cd59d67Stkusumi 	return (read_sectn(fp, sect, 1, bytespersec));
1480cd59d67Stkusumi }
1490cd59d67Stkusumi 
1500cd59d67Stkusumi /*
1510cd59d67Stkusumi  * Compute the byte-by-byte multi-sector checksum of the given boot region
1520cd59d67Stkusumi  * (MAIN or BACKUP), for a given bytespersec (typically 512 or 4096).
1530cd59d67Stkusumi  *
1540cd59d67Stkusumi  * Endian-safe; result is host endian.
1550cd59d67Stkusumi  */
1560cd59d67Stkusumi static int
exfat_compute_boot_chksum(FILE * fp,unsigned region,unsigned bytespersec,uint32_t * result)1570cd59d67Stkusumi exfat_compute_boot_chksum(FILE *fp, unsigned region, unsigned bytespersec,
1580cd59d67Stkusumi     uint32_t *result)
1590cd59d67Stkusumi {
1600cd59d67Stkusumi 	unsigned char *sector;
1610cd59d67Stkusumi 	unsigned n, sect;
1620cd59d67Stkusumi 	uint32_t checksum;
1630cd59d67Stkusumi 
1640cd59d67Stkusumi 	checksum = 0;
1650cd59d67Stkusumi 	for (sect = 0; sect < 11; sect++) {
1660cd59d67Stkusumi 		sector = read_sect(fp, region + sect, bytespersec);
1670cd59d67Stkusumi 		if (sector == NULL)
1680cd59d67Stkusumi 			return (ENXIO);
1690cd59d67Stkusumi 		for (n = 0; n < bytespersec; n++) {
1700cd59d67Stkusumi 			if (sect == 0) {
1710cd59d67Stkusumi 				switch (n) {
1720cd59d67Stkusumi 				case 106:
1730cd59d67Stkusumi 				case 107:
1740cd59d67Stkusumi 				case 112:
1750cd59d67Stkusumi 					continue;
1760cd59d67Stkusumi 				}
1770cd59d67Stkusumi 			}
1780cd59d67Stkusumi 			checksum = ((checksum & 1) ? 0x80000000u : 0u) +
1790cd59d67Stkusumi 			    (checksum >> 1) + (uint32_t)sector[n];
1800cd59d67Stkusumi 		}
1810cd59d67Stkusumi 		free(sector);
1820cd59d67Stkusumi 	}
1830cd59d67Stkusumi 
1840cd59d67Stkusumi 	*result = checksum;
1850cd59d67Stkusumi 	return (0);
1860cd59d67Stkusumi }
1870cd59d67Stkusumi 
1880cd59d67Stkusumi static void
convert_label(const uint16_t * ucs2label,unsigned ucs2len,char * label_out,size_t label_sz)1890cd59d67Stkusumi convert_label(const uint16_t *ucs2label /* LE */, unsigned ucs2len, char
1900cd59d67Stkusumi     *label_out, size_t label_sz)
1910cd59d67Stkusumi {
1920cd59d67Stkusumi 	const char *label;
1930cd59d67Stkusumi 	char *label_out_orig;
1940cd59d67Stkusumi 	iconv_t cd;
1950cd59d67Stkusumi 	size_t srcleft, rc;
1960cd59d67Stkusumi 
1970cd59d67Stkusumi 	/* Currently hardcoded in fstyp.c as 256 or so. */
1980cd59d67Stkusumi 	assert(label_sz > 1);
1990cd59d67Stkusumi 
2000cd59d67Stkusumi 	if (ucs2len == 0) {
2010cd59d67Stkusumi 		/*
2020cd59d67Stkusumi 		 * Kind of seems bogus, but the spec allows an empty label
2030cd59d67Stkusumi 		 * entry with the same meaning as no label.
2040cd59d67Stkusumi 		 */
2050cd59d67Stkusumi 		return;
2060cd59d67Stkusumi 	}
2070cd59d67Stkusumi 
2080cd59d67Stkusumi 	if (ucs2len > 11) {
2090cd59d67Stkusumi 		warnx("exfat: Bogus volume label length: %u", ucs2len);
2100cd59d67Stkusumi 		return;
2110cd59d67Stkusumi 	}
2120cd59d67Stkusumi 
2130cd59d67Stkusumi 	/* dstname="" means convert to the current locale. */
2140cd59d67Stkusumi 	cd = iconv_open("", EXFAT_ENC);
2150cd59d67Stkusumi 	if (cd == (iconv_t)-1) {
2160cd59d67Stkusumi 		warn("exfat: Could not open iconv");
2170cd59d67Stkusumi 		return;
2180cd59d67Stkusumi 	}
2190cd59d67Stkusumi 
2200cd59d67Stkusumi 	label_out_orig = label_out;
2210cd59d67Stkusumi 
2220cd59d67Stkusumi 	/* Dummy up the byte pointer and byte length iconv's API wants. */
2230cd59d67Stkusumi 	label = (const void *)ucs2label;
2240cd59d67Stkusumi 	srcleft = ucs2len * sizeof(*ucs2label);
2250cd59d67Stkusumi 
2260cd59d67Stkusumi 	rc = iconv(cd, __UNCONST(&label), &srcleft, &label_out,
2270cd59d67Stkusumi 	    &label_sz);
2280cd59d67Stkusumi 	if (rc == (size_t)-1) {
2290cd59d67Stkusumi 		warn("exfat: iconv()");
2300cd59d67Stkusumi 		*label_out_orig = '\0';
2310cd59d67Stkusumi 	} else {
2320cd59d67Stkusumi 		/* NUL-terminate result (iconv advances label_out). */
2330cd59d67Stkusumi 		if (label_sz == 0)
2340cd59d67Stkusumi 			label_out--;
2350cd59d67Stkusumi 		*label_out = '\0';
2360cd59d67Stkusumi 	}
2370cd59d67Stkusumi 
2380cd59d67Stkusumi 	iconv_close(cd);
2390cd59d67Stkusumi }
2400cd59d67Stkusumi 
2410cd59d67Stkusumi /*
2420cd59d67Stkusumi  * Using the FAT table, look up the next cluster in this chain.
2430cd59d67Stkusumi  */
2440cd59d67Stkusumi static uint32_t
exfat_fat_next(FILE * fp,const struct exfat_vbr * ev,unsigned BPS,uint32_t cluster)2450cd59d67Stkusumi exfat_fat_next(FILE *fp, const struct exfat_vbr *ev, unsigned BPS,
2460cd59d67Stkusumi     uint32_t cluster)
2470cd59d67Stkusumi {
2480cd59d67Stkusumi 	uint32_t fat_offset_sect, clsect, clsectoff;
2490cd59d67Stkusumi 	uint32_t *fatsect, nextclust;
2500cd59d67Stkusumi 
2510cd59d67Stkusumi 	fat_offset_sect = le32toh(ev->ev_fat_offset);
2520cd59d67Stkusumi 	clsect = fat_offset_sect + (cluster / (BPS / (uint32_t)sizeof(cluster)));
2535f234ad7Sfox 	clsectoff = (cluster % (BPS / (uint32_t)sizeof(cluster)));
2540cd59d67Stkusumi 
2550cd59d67Stkusumi 	/* XXX This is pretty wasteful without a block cache for the FAT. */
2560cd59d67Stkusumi 	fatsect = read_sect(fp, clsect, BPS);
2570cd59d67Stkusumi 	nextclust = le32toh(fatsect[clsectoff]);
2580cd59d67Stkusumi 	free(fatsect);
2590cd59d67Stkusumi 
2600cd59d67Stkusumi 	return (nextclust);
2610cd59d67Stkusumi }
2620cd59d67Stkusumi 
2630cd59d67Stkusumi static void
exfat_find_label(FILE * fp,const struct exfat_vbr * ev,unsigned BPS,char * label_out,size_t label_sz)2640cd59d67Stkusumi exfat_find_label(FILE *fp, const struct exfat_vbr *ev, unsigned BPS,
2650cd59d67Stkusumi     char *label_out, size_t label_sz)
2660cd59d67Stkusumi {
2670cd59d67Stkusumi 	uint32_t rootdir_cluster, sects_per_clust, cluster_offset_sect;
2680cd59d67Stkusumi 	off_t rootdir_sect;
2690cd59d67Stkusumi 	struct exfat_dirent *declust, *it;
2700cd59d67Stkusumi 
2710cd59d67Stkusumi 	cluster_offset_sect = le32toh(ev->ev_cluster_offset);
2720cd59d67Stkusumi 	rootdir_cluster = le32toh(ev->ev_rootdir_cluster);
2730cd59d67Stkusumi 	sects_per_clust = (1u << ev->ev_log_sect_per_clust);
2740cd59d67Stkusumi 
2750cd59d67Stkusumi 	if (rootdir_cluster < FIRST_CLUSTER) {
2760cd59d67Stkusumi 		warnx("%s: invalid rootdir cluster %u < %d", __func__,
2770cd59d67Stkusumi 		    rootdir_cluster, FIRST_CLUSTER);
2780cd59d67Stkusumi 		return;
2790cd59d67Stkusumi 	}
2800cd59d67Stkusumi 
2810cd59d67Stkusumi 
2820cd59d67Stkusumi 	for (; rootdir_cluster != END_CLUSTER_SENTINEL;
2830cd59d67Stkusumi 	    rootdir_cluster = exfat_fat_next(fp, ev, BPS, rootdir_cluster)) {
2840cd59d67Stkusumi 		if (rootdir_cluster == BAD_BLOCK_SENTINEL) {
2850cd59d67Stkusumi 			warnx("%s: Bogus bad block in root directory chain",
2860cd59d67Stkusumi 			    __func__);
2870cd59d67Stkusumi 			return;
2880cd59d67Stkusumi 		}
2890cd59d67Stkusumi 
2900cd59d67Stkusumi 		rootdir_sect = (rootdir_cluster - FIRST_CLUSTER) *
2910cd59d67Stkusumi 		    sects_per_clust + cluster_offset_sect;
2920cd59d67Stkusumi 		declust = read_sectn(fp, rootdir_sect, sects_per_clust, BPS);
2930cd59d67Stkusumi 		for (it = declust;
2940cd59d67Stkusumi 		    it < declust + (sects_per_clust * BPS / sizeof(*it)); it++) {
2950cd59d67Stkusumi 			bool eod = false;
2960cd59d67Stkusumi 
2970cd59d67Stkusumi 			/*
2980cd59d67Stkusumi 			 * Simplistic directory traversal; doesn't do any
2990cd59d67Stkusumi 			 * validation of "MUST" requirements in spec.
3000cd59d67Stkusumi 			 */
3010cd59d67Stkusumi 			switch (it->xde_type) {
3020cd59d67Stkusumi 			case XDE_TYPE_EOD:
3030cd59d67Stkusumi 				eod = true;
3040cd59d67Stkusumi 				break;
3050cd59d67Stkusumi 			case XDE_TYPE_VOL_LABEL: {
3060cd59d67Stkusumi 				struct exfat_de_label *lde = (void*)it;
3070cd59d67Stkusumi 				convert_label(lde->xdel_vol_lbl,
3080cd59d67Stkusumi 				    lde->xdel_char_cnt, label_out, label_sz);
3090cd59d67Stkusumi 				free(declust);
3100cd59d67Stkusumi 				return;
3110cd59d67Stkusumi 				}
3120cd59d67Stkusumi 			}
3130cd59d67Stkusumi 
3140cd59d67Stkusumi 			if (eod)
3150cd59d67Stkusumi 				break;
3160cd59d67Stkusumi 		}
3170cd59d67Stkusumi 		free(declust);
3180cd59d67Stkusumi 	}
3190cd59d67Stkusumi }
3200cd59d67Stkusumi 
3217575c8cfStkusumi int
fstyp_exfat(FILE * fp,char * label,size_t size)3227575c8cfStkusumi fstyp_exfat(FILE *fp, char *label, size_t size)
3237575c8cfStkusumi {
3247575c8cfStkusumi 	struct exfat_vbr *ev;
3250cd59d67Stkusumi 	uint32_t *cksect;
3260cd59d67Stkusumi 	unsigned bytespersec;
3270cd59d67Stkusumi 	uint32_t chksum;
3280cd59d67Stkusumi 	int error;
3290cd59d67Stkusumi 
330ed5947a0Stkusumi 	error = 1;
3310cd59d67Stkusumi 	cksect = NULL;
3327575c8cfStkusumi 	ev = (struct exfat_vbr *)read_buf(fp, 0, 512);
3337575c8cfStkusumi 	if (ev == NULL || strncmp(ev->ev_fsname, "EXFAT   ", 8) != 0)
334ed5947a0Stkusumi 		goto out;
3357575c8cfStkusumi 
3360cd59d67Stkusumi 	if (ev->ev_log_bytes_per_sect < 9 || ev->ev_log_bytes_per_sect > 12) {
3370cd59d67Stkusumi 		warnx("exfat: Invalid BytesPerSectorShift");
338ed5947a0Stkusumi 		goto out;
3390cd59d67Stkusumi 	}
3400cd59d67Stkusumi 
3410cd59d67Stkusumi 	bytespersec = (1u << ev->ev_log_bytes_per_sect);
3420cd59d67Stkusumi 
3430cd59d67Stkusumi 	error = exfat_compute_boot_chksum(fp, MAIN_BOOT_REGION_SECT,
3440cd59d67Stkusumi 	    bytespersec, &chksum);
3450cd59d67Stkusumi 	if (error != 0)
346ed5947a0Stkusumi 		goto out;
3470cd59d67Stkusumi 
3480cd59d67Stkusumi 	cksect = read_sect(fp, MAIN_BOOT_REGION_SECT + SUBREGION_CHKSUM_SECT,
3490cd59d67Stkusumi 	    bytespersec);
3500cd59d67Stkusumi 
3517575c8cfStkusumi 	/*
3520cd59d67Stkusumi 	 * Technically the entire sector should be full of repeating 4-byte
3530cd59d67Stkusumi 	 * checksum pattern, but we only verify the first.
3547575c8cfStkusumi 	 */
3550cd59d67Stkusumi 	if (chksum != le32toh(cksect[0])) {
3560cd59d67Stkusumi 		warnx("exfat: Found checksum 0x%08x != computed 0x%08x",
3570cd59d67Stkusumi 		    le32toh(cksect[0]), chksum);
358ed5947a0Stkusumi 		error = 1;
359ed5947a0Stkusumi 		goto out;
3600cd59d67Stkusumi 	}
3610cd59d67Stkusumi 
3620cd59d67Stkusumi 	if (show_label)
3630cd59d67Stkusumi 		exfat_find_label(fp, ev, bytespersec, label, size);
3640cd59d67Stkusumi 
365ed5947a0Stkusumi out:
3660cd59d67Stkusumi 	free(cksect);
3677575c8cfStkusumi 	free(ev);
368ed5947a0Stkusumi 	return (error != 0);
3697575c8cfStkusumi }
370