xref: /freebsd-src/usr.sbin/fstyp/exfat.c (revision 4d65a7c6951cea0333f1a0c1b32c38489cdfa6c5)
1e2660756SConrad Meyer /*
2e2660756SConrad Meyer  * Copyright (c) 2017 Conrad Meyer <cem@FreeBSD.org>
3e2660756SConrad Meyer  * All rights reserved.
4e2660756SConrad Meyer  *
5e2660756SConrad Meyer  * Redistribution and use in source and binary forms, with or without
6e2660756SConrad Meyer  * modification, are permitted provided that the following conditions
7e2660756SConrad Meyer  * are met:
8e2660756SConrad Meyer  * 1. Redistributions of source code must retain the above copyright
9e2660756SConrad Meyer  *    notice, this list of conditions and the following disclaimer.
10e2660756SConrad Meyer  * 2. Redistributions in binary form must reproduce the above copyright
11e2660756SConrad Meyer  *    notice, this list of conditions and the following disclaimer in the
12e2660756SConrad Meyer  *    documentation and/or other materials provided with the distribution.
13e2660756SConrad Meyer  *
14e2660756SConrad Meyer  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15e2660756SConrad Meyer  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16e2660756SConrad Meyer  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17e2660756SConrad Meyer  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18e2660756SConrad Meyer  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19e2660756SConrad Meyer  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20e2660756SConrad Meyer  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21e2660756SConrad Meyer  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22e2660756SConrad Meyer  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23e2660756SConrad Meyer  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24e2660756SConrad Meyer  * SUCH DAMAGE.
25e2660756SConrad Meyer  */
26e2660756SConrad Meyer 
2785b4c344SConrad Meyer #include <sys/param.h>
2885b4c344SConrad Meyer #include <sys/endian.h>
2985b4c344SConrad Meyer 
3085b4c344SConrad Meyer #include <assert.h>
3185b4c344SConrad Meyer #include <err.h>
3285b4c344SConrad Meyer #include <errno.h>
335ab1cb52SConrad Meyer #ifdef WITH_ICONV
3485b4c344SConrad Meyer #include <iconv.h>
355ab1cb52SConrad Meyer #endif
3685b4c344SConrad Meyer #include <stdbool.h>
37e2660756SConrad Meyer #include <stdint.h>
38e2660756SConrad Meyer #include <stdio.h>
39e2660756SConrad Meyer #include <stdlib.h>
40e2660756SConrad Meyer #include <string.h>
41e2660756SConrad Meyer 
42e2660756SConrad Meyer #include "fstyp.h"
43e2660756SConrad Meyer 
4485b4c344SConrad Meyer /*
4585b4c344SConrad Meyer  * https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification
4685b4c344SConrad Meyer  */
4785b4c344SConrad Meyer 
48e2660756SConrad Meyer struct exfat_vbr {
49e2660756SConrad Meyer 	char		ev_jmp[3];
50e2660756SConrad Meyer 	char		ev_fsname[8];
51e2660756SConrad Meyer 	char		ev_zeros[53];
52e2660756SConrad Meyer 	uint64_t	ev_part_offset;
53e2660756SConrad Meyer 	uint64_t	ev_vol_length;
54e2660756SConrad Meyer 	uint32_t	ev_fat_offset;
55e2660756SConrad Meyer 	uint32_t	ev_fat_length;
56e2660756SConrad Meyer 	uint32_t	ev_cluster_offset;
57e2660756SConrad Meyer 	uint32_t	ev_cluster_count;
58e2660756SConrad Meyer 	uint32_t	ev_rootdir_cluster;
59e2660756SConrad Meyer 	uint32_t	ev_vol_serial;
60e2660756SConrad Meyer 	uint16_t	ev_fs_revision;
61e2660756SConrad Meyer 	uint16_t	ev_vol_flags;
62e2660756SConrad Meyer 	uint8_t		ev_log_bytes_per_sect;
63e2660756SConrad Meyer 	uint8_t		ev_log_sect_per_clust;
64e2660756SConrad Meyer 	uint8_t		ev_num_fats;
65e2660756SConrad Meyer 	uint8_t		ev_drive_sel;
66e2660756SConrad Meyer 	uint8_t		ev_percent_used;
67e2660756SConrad Meyer } __packed;
68e2660756SConrad Meyer 
6985b4c344SConrad Meyer struct exfat_dirent {
7085b4c344SConrad Meyer 	uint8_t		xde_type;
7185b4c344SConrad Meyer #define	XDE_TYPE_INUSE_MASK	0x80	/* 1=in use */
7285b4c344SConrad Meyer #define	XDE_TYPE_INUSE_SHIFT	7
7385b4c344SConrad Meyer #define	XDE_TYPE_CATEGORY_MASK	0x40	/* 0=primary */
7485b4c344SConrad Meyer #define	XDE_TYPE_CATEGORY_SHIFT	6
7585b4c344SConrad Meyer #define	XDE_TYPE_IMPORTNC_MASK	0x20	/* 0=critical */
7685b4c344SConrad Meyer #define	XDE_TYPE_IMPORTNC_SHIFT	5
7785b4c344SConrad Meyer #define	XDE_TYPE_CODE_MASK	0x1f
7885b4c344SConrad Meyer /* InUse=0, ..., TypeCode=0: EOD. */
7985b4c344SConrad Meyer #define	XDE_TYPE_EOD		0x00
8085b4c344SConrad Meyer #define	XDE_TYPE_ALLOC_BITMAP	(XDE_TYPE_INUSE_MASK | 0x01)
8185b4c344SConrad Meyer #define	XDE_TYPE_UPCASE_TABLE	(XDE_TYPE_INUSE_MASK | 0x02)
8285b4c344SConrad Meyer #define	XDE_TYPE_VOL_LABEL	(XDE_TYPE_INUSE_MASK | 0x03)
8385b4c344SConrad Meyer #define	XDE_TYPE_FILE		(XDE_TYPE_INUSE_MASK | 0x05)
8485b4c344SConrad Meyer #define	XDE_TYPE_VOL_GUID	(XDE_TYPE_INUSE_MASK | XDE_TYPE_IMPORTNC_MASK)
8585b4c344SConrad Meyer #define	XDE_TYPE_STREAM_EXT	(XDE_TYPE_INUSE_MASK | XDE_TYPE_CATEGORY_MASK)
8685b4c344SConrad Meyer #define	XDE_TYPE_FILE_NAME	(XDE_TYPE_INUSE_MASK | XDE_TYPE_CATEGORY_MASK | 0x01)
8785b4c344SConrad Meyer #define	XDE_TYPE_VENDOR		(XDE_TYPE_INUSE_MASK | XDE_TYPE_CATEGORY_MASK | XDE_TYPE_IMPORTNC_MASK)
8885b4c344SConrad Meyer #define	XDE_TYPE_VENDOR_ALLOC	(XDE_TYPE_INUSE_MASK | XDE_TYPE_CATEGORY_MASK | XDE_TYPE_IMPORTNC_MASK | 0x01)
8985b4c344SConrad Meyer 	union {
9085b4c344SConrad Meyer 		uint8_t	xde_generic_[19];
9185b4c344SConrad Meyer 		struct exde_primary {
9285b4c344SConrad Meyer 			/*
9385b4c344SConrad Meyer 			 * Count of "secondary" dirents following this one.
9485b4c344SConrad Meyer 			 *
9585b4c344SConrad Meyer 			 * A single logical entity may be composed of a
9685b4c344SConrad Meyer 			 * sequence of several dirents, starting with a primary
9785b4c344SConrad Meyer 			 * one; the rest are secondary dirents.
9885b4c344SConrad Meyer 			 */
9985b4c344SConrad Meyer 			uint8_t		xde_secondary_count_;
10085b4c344SConrad Meyer 			uint16_t	xde_set_chksum_;
10185b4c344SConrad Meyer 			uint16_t	xde_prim_flags_;
10285b4c344SConrad Meyer 			uint8_t		xde_prim_generic_[14];
10385b4c344SConrad Meyer 		} __packed xde_primary_;
10485b4c344SConrad Meyer 		struct exde_secondary {
10585b4c344SConrad Meyer 			uint8_t		xde_sec_flags_;
10685b4c344SConrad Meyer 			uint8_t		xde_sec_generic_[18];
10785b4c344SConrad Meyer 		} __packed xde_secondary_;
10885b4c344SConrad Meyer 	} u;
10985b4c344SConrad Meyer 	uint32_t	xde_first_cluster;
11085b4c344SConrad Meyer 	uint64_t	xde_data_len;
111*58862c0bSJohn Baldwin };
11285b4c344SConrad Meyer #define	xde_generic		u.xde_generic_
11385b4c344SConrad Meyer #define	xde_secondary_count	u.xde_primary_.xde_secondary_count
11485b4c344SConrad Meyer #define	xde_set_chksum		u.xde_primary_.xde_set_chksum_
11585b4c344SConrad Meyer #define	xde_prim_flags		u.xde_primary_.xde_prim_flags_
11685b4c344SConrad Meyer #define	xde_sec_flags		u.xde_secondary_.xde_sec_flags_
11785b4c344SConrad Meyer _Static_assert(sizeof(struct exfat_dirent) == 32, "spec");
11885b4c344SConrad Meyer 
11985b4c344SConrad Meyer struct exfat_de_label {
12085b4c344SConrad Meyer 	uint8_t		xdel_type;	/* XDE_TYPE_VOL_LABEL */
12185b4c344SConrad Meyer 	uint8_t		xdel_char_cnt;	/* Length of UCS-2 label */
12285b4c344SConrad Meyer 	uint16_t	xdel_vol_lbl[11];
12385b4c344SConrad Meyer 	uint8_t		xdel_reserved[8];
124*58862c0bSJohn Baldwin };
12585b4c344SConrad Meyer _Static_assert(sizeof(struct exfat_de_label) == 32, "spec");
12685b4c344SConrad Meyer 
12785b4c344SConrad Meyer #define	MAIN_BOOT_REGION_SECT	0
12885b4c344SConrad Meyer #define	BACKUP_BOOT_REGION_SECT	12
12985b4c344SConrad Meyer 
13085b4c344SConrad Meyer #define	SUBREGION_CHKSUM_SECT	11
13185b4c344SConrad Meyer 
13285b4c344SConrad Meyer #define	FIRST_CLUSTER		2
13385b4c344SConrad Meyer #define	BAD_BLOCK_SENTINEL	0xfffffff7u
13485b4c344SConrad Meyer #define	END_CLUSTER_SENTINEL	0xffffffffu
13585b4c344SConrad Meyer 
13685b4c344SConrad Meyer static inline void *
read_sectn(FILE * fp,off_t sect,unsigned count,unsigned bytespersec)13785b4c344SConrad Meyer read_sectn(FILE *fp, off_t sect, unsigned count, unsigned bytespersec)
13885b4c344SConrad Meyer {
13985b4c344SConrad Meyer 	return (read_buf(fp, sect * bytespersec, bytespersec * count));
14085b4c344SConrad Meyer }
14185b4c344SConrad Meyer 
14285b4c344SConrad Meyer static inline void *
read_sect(FILE * fp,off_t sect,unsigned bytespersec)14385b4c344SConrad Meyer read_sect(FILE *fp, off_t sect, unsigned bytespersec)
14485b4c344SConrad Meyer {
14585b4c344SConrad Meyer 	return (read_sectn(fp, sect, 1, bytespersec));
14685b4c344SConrad Meyer }
14785b4c344SConrad Meyer 
14885b4c344SConrad Meyer /*
14985b4c344SConrad Meyer  * Compute the byte-by-byte multi-sector checksum of the given boot region
15085b4c344SConrad Meyer  * (MAIN or BACKUP), for a given bytespersec (typically 512 or 4096).
15185b4c344SConrad Meyer  *
15285b4c344SConrad Meyer  * Endian-safe; result is host endian.
15385b4c344SConrad Meyer  */
15485b4c344SConrad Meyer static int
exfat_compute_boot_chksum(FILE * fp,unsigned region,unsigned bytespersec,uint32_t * result)15585b4c344SConrad Meyer exfat_compute_boot_chksum(FILE *fp, unsigned region, unsigned bytespersec,
15685b4c344SConrad Meyer     uint32_t *result)
15785b4c344SConrad Meyer {
15885b4c344SConrad Meyer 	unsigned char *sector;
15985b4c344SConrad Meyer 	unsigned n, sect;
16085b4c344SConrad Meyer 	uint32_t checksum;
16185b4c344SConrad Meyer 
16285b4c344SConrad Meyer 	checksum = 0;
16385b4c344SConrad Meyer 	for (sect = 0; sect < 11; sect++) {
16485b4c344SConrad Meyer 		sector = read_sect(fp, region + sect, bytespersec);
16585b4c344SConrad Meyer 		if (sector == NULL)
16685b4c344SConrad Meyer 			return (ENXIO);
16785b4c344SConrad Meyer 		for (n = 0; n < bytespersec; n++) {
16885b4c344SConrad Meyer 			if (sect == 0) {
16985b4c344SConrad Meyer 				switch (n) {
17085b4c344SConrad Meyer 				case 106:
17185b4c344SConrad Meyer 				case 107:
17285b4c344SConrad Meyer 				case 112:
17385b4c344SConrad Meyer 					continue;
17485b4c344SConrad Meyer 				}
17585b4c344SConrad Meyer 			}
17685b4c344SConrad Meyer 			checksum = ((checksum & 1) ? 0x80000000u : 0u) +
17785b4c344SConrad Meyer 			    (checksum >> 1) + (uint32_t)sector[n];
17885b4c344SConrad Meyer 		}
17985b4c344SConrad Meyer 		free(sector);
18085b4c344SConrad Meyer 	}
18185b4c344SConrad Meyer 
18285b4c344SConrad Meyer 	*result = checksum;
18385b4c344SConrad Meyer 	return (0);
18485b4c344SConrad Meyer }
18585b4c344SConrad Meyer 
1865ab1cb52SConrad Meyer #ifdef WITH_ICONV
18785b4c344SConrad Meyer static void
convert_label(const uint16_t * ucs2label,unsigned ucs2len,char * label_out,size_t label_sz)18885b4c344SConrad Meyer convert_label(const uint16_t *ucs2label /* LE */, unsigned ucs2len, char
18985b4c344SConrad Meyer     *label_out, size_t label_sz)
19085b4c344SConrad Meyer {
19185b4c344SConrad Meyer 	const char *label;
19285b4c344SConrad Meyer 	char *label_out_orig;
19385b4c344SConrad Meyer 	iconv_t cd;
19485b4c344SConrad Meyer 	size_t srcleft, rc;
19585b4c344SConrad Meyer 
19685b4c344SConrad Meyer 	/* Currently hardcoded in fstyp.c as 256 or so. */
19785b4c344SConrad Meyer 	assert(label_sz > 1);
19885b4c344SConrad Meyer 
19985b4c344SConrad Meyer 	if (ucs2len == 0) {
20085b4c344SConrad Meyer 		/*
20185b4c344SConrad Meyer 		 * Kind of seems bogus, but the spec allows an empty label
20285b4c344SConrad Meyer 		 * entry with the same meaning as no label.
20385b4c344SConrad Meyer 		 */
20485b4c344SConrad Meyer 		return;
20585b4c344SConrad Meyer 	}
20685b4c344SConrad Meyer 
20785b4c344SConrad Meyer 	if (ucs2len > 11) {
20885b4c344SConrad Meyer 		warnx("exfat: Bogus volume label length: %u", ucs2len);
20985b4c344SConrad Meyer 		return;
21085b4c344SConrad Meyer 	}
21185b4c344SConrad Meyer 
21285b4c344SConrad Meyer 	/* dstname="" means convert to the current locale. */
21385b4c344SConrad Meyer 	cd = iconv_open("", EXFAT_ENC);
21485b4c344SConrad Meyer 	if (cd == (iconv_t)-1) {
21585b4c344SConrad Meyer 		warn("exfat: Could not open iconv");
21685b4c344SConrad Meyer 		return;
21785b4c344SConrad Meyer 	}
21885b4c344SConrad Meyer 
21985b4c344SConrad Meyer 	label_out_orig = label_out;
22085b4c344SConrad Meyer 
22185b4c344SConrad Meyer 	/* Dummy up the byte pointer and byte length iconv's API wants. */
22285b4c344SConrad Meyer 	label = (const void *)ucs2label;
22385b4c344SConrad Meyer 	srcleft = ucs2len * sizeof(*ucs2label);
22485b4c344SConrad Meyer 
22585b4c344SConrad Meyer 	rc = iconv(cd, __DECONST(char **, &label), &srcleft, &label_out,
22685b4c344SConrad Meyer 	    &label_sz);
22785b4c344SConrad Meyer 	if (rc == (size_t)-1) {
22885b4c344SConrad Meyer 		warn("exfat: iconv()");
22985b4c344SConrad Meyer 		*label_out_orig = '\0';
23085b4c344SConrad Meyer 	} else {
23185b4c344SConrad Meyer 		/* NUL-terminate result (iconv advances label_out). */
23285b4c344SConrad Meyer 		if (label_sz == 0)
23385b4c344SConrad Meyer 			label_out--;
23485b4c344SConrad Meyer 		*label_out = '\0';
23585b4c344SConrad Meyer 	}
23685b4c344SConrad Meyer 
23785b4c344SConrad Meyer 	iconv_close(cd);
23885b4c344SConrad Meyer }
23985b4c344SConrad Meyer 
24085b4c344SConrad Meyer /*
24185b4c344SConrad Meyer  * Using the FAT table, look up the next cluster in this chain.
24285b4c344SConrad Meyer  */
24385b4c344SConrad Meyer static uint32_t
exfat_fat_next(FILE * fp,const struct exfat_vbr * ev,unsigned BPS,uint32_t cluster)24485b4c344SConrad Meyer exfat_fat_next(FILE *fp, const struct exfat_vbr *ev, unsigned BPS,
24585b4c344SConrad Meyer     uint32_t cluster)
24685b4c344SConrad Meyer {
24785b4c344SConrad Meyer 	uint32_t fat_offset_sect, clsect, clsectoff;
24885b4c344SConrad Meyer 	uint32_t *fatsect, nextclust;
24985b4c344SConrad Meyer 
25085b4c344SConrad Meyer 	fat_offset_sect = le32toh(ev->ev_fat_offset);
25185b4c344SConrad Meyer 	clsect = fat_offset_sect + (cluster / (BPS / sizeof(cluster)));
25285b4c344SConrad Meyer 	clsectoff = (cluster % (BPS / sizeof(cluster)));
25385b4c344SConrad Meyer 
25485b4c344SConrad Meyer 	/* XXX This is pretty wasteful without a block cache for the FAT. */
25585b4c344SConrad Meyer 	fatsect = read_sect(fp, clsect, BPS);
25685b4c344SConrad Meyer 	nextclust = le32toh(fatsect[clsectoff]);
25785b4c344SConrad Meyer 	free(fatsect);
25885b4c344SConrad Meyer 
25985b4c344SConrad Meyer 	return (nextclust);
26085b4c344SConrad Meyer }
26185b4c344SConrad Meyer 
26285b4c344SConrad Meyer static void
exfat_find_label(FILE * fp,const struct exfat_vbr * ev,unsigned BPS,char * label_out,size_t label_sz)26385b4c344SConrad Meyer exfat_find_label(FILE *fp, const struct exfat_vbr *ev, unsigned BPS,
26485b4c344SConrad Meyer     char *label_out, size_t label_sz)
26585b4c344SConrad Meyer {
26685b4c344SConrad Meyer 	uint32_t rootdir_cluster, sects_per_clust, cluster_offset_sect;
26785b4c344SConrad Meyer 	off_t rootdir_sect;
26885b4c344SConrad Meyer 	struct exfat_dirent *declust, *it;
26985b4c344SConrad Meyer 
27085b4c344SConrad Meyer 	cluster_offset_sect = le32toh(ev->ev_cluster_offset);
27185b4c344SConrad Meyer 	rootdir_cluster = le32toh(ev->ev_rootdir_cluster);
27285b4c344SConrad Meyer 	sects_per_clust = (1u << ev->ev_log_sect_per_clust);
27385b4c344SConrad Meyer 
27485b4c344SConrad Meyer 	if (rootdir_cluster < FIRST_CLUSTER) {
27585b4c344SConrad Meyer 		warnx("%s: invalid rootdir cluster %u < %d", __func__,
27685b4c344SConrad Meyer 		    rootdir_cluster, FIRST_CLUSTER);
27785b4c344SConrad Meyer 		return;
27885b4c344SConrad Meyer 	}
27985b4c344SConrad Meyer 
28085b4c344SConrad Meyer 
28185b4c344SConrad Meyer 	for (; rootdir_cluster != END_CLUSTER_SENTINEL;
28285b4c344SConrad Meyer 	    rootdir_cluster = exfat_fat_next(fp, ev, BPS, rootdir_cluster)) {
28385b4c344SConrad Meyer 		if (rootdir_cluster == BAD_BLOCK_SENTINEL) {
28485b4c344SConrad Meyer 			warnx("%s: Bogus bad block in root directory chain",
28585b4c344SConrad Meyer 			    __func__);
28685b4c344SConrad Meyer 			return;
28785b4c344SConrad Meyer 		}
28885b4c344SConrad Meyer 
28985b4c344SConrad Meyer 		rootdir_sect = (rootdir_cluster - FIRST_CLUSTER) *
29085b4c344SConrad Meyer 		    sects_per_clust + cluster_offset_sect;
29185b4c344SConrad Meyer 		declust = read_sectn(fp, rootdir_sect, sects_per_clust, BPS);
29285b4c344SConrad Meyer 		for (it = declust;
29385b4c344SConrad Meyer 		    it < declust + (sects_per_clust * BPS / sizeof(*it)); it++) {
29485b4c344SConrad Meyer 			bool eod = false;
29585b4c344SConrad Meyer 
29685b4c344SConrad Meyer 			/*
29785b4c344SConrad Meyer 			 * Simplistic directory traversal; doesn't do any
29885b4c344SConrad Meyer 			 * validation of "MUST" requirements in spec.
29985b4c344SConrad Meyer 			 */
30085b4c344SConrad Meyer 			switch (it->xde_type) {
30185b4c344SConrad Meyer 			case XDE_TYPE_EOD:
30285b4c344SConrad Meyer 				eod = true;
30385b4c344SConrad Meyer 				break;
30485b4c344SConrad Meyer 			case XDE_TYPE_VOL_LABEL: {
30585b4c344SConrad Meyer 				struct exfat_de_label *lde = (void*)it;
30685b4c344SConrad Meyer 				convert_label(lde->xdel_vol_lbl,
30785b4c344SConrad Meyer 				    lde->xdel_char_cnt, label_out, label_sz);
30885b4c344SConrad Meyer 				free(declust);
30985b4c344SConrad Meyer 				return;
31085b4c344SConrad Meyer 				}
31185b4c344SConrad Meyer 			}
31285b4c344SConrad Meyer 
31385b4c344SConrad Meyer 			if (eod)
31485b4c344SConrad Meyer 				break;
31585b4c344SConrad Meyer 		}
31685b4c344SConrad Meyer 		free(declust);
31785b4c344SConrad Meyer 	}
31885b4c344SConrad Meyer }
3195ab1cb52SConrad Meyer #endif /* WITH_ICONV */
32085b4c344SConrad Meyer 
321e2660756SConrad Meyer int
fstyp_exfat(FILE * fp,char * label,size_t size)322e2660756SConrad Meyer fstyp_exfat(FILE *fp, char *label, size_t size)
323e2660756SConrad Meyer {
324e2660756SConrad Meyer 	struct exfat_vbr *ev;
32585b4c344SConrad Meyer 	uint32_t *cksect;
32685b4c344SConrad Meyer 	unsigned bytespersec;
32785b4c344SConrad Meyer 	uint32_t chksum;
32885b4c344SConrad Meyer 	int error;
329e2660756SConrad Meyer 
330ddf61156SConrad Meyer 	error = 1;
33185b4c344SConrad Meyer 	cksect = NULL;
332e2660756SConrad Meyer 	ev = (struct exfat_vbr *)read_buf(fp, 0, 512);
333e2660756SConrad Meyer 	if (ev == NULL || strncmp(ev->ev_fsname, "EXFAT   ", 8) != 0)
334ddf61156SConrad Meyer 		goto out;
335e2660756SConrad Meyer 
33685b4c344SConrad Meyer 	if (ev->ev_log_bytes_per_sect < 9 || ev->ev_log_bytes_per_sect > 12) {
33785b4c344SConrad Meyer 		warnx("exfat: Invalid BytesPerSectorShift");
338ddf61156SConrad Meyer 		goto out;
33985b4c344SConrad Meyer 	}
34085b4c344SConrad Meyer 
34185b4c344SConrad Meyer 	bytespersec = (1u << ev->ev_log_bytes_per_sect);
34285b4c344SConrad Meyer 
34385b4c344SConrad Meyer 	error = exfat_compute_boot_chksum(fp, MAIN_BOOT_REGION_SECT,
34485b4c344SConrad Meyer 	    bytespersec, &chksum);
34585b4c344SConrad Meyer 	if (error != 0)
346ddf61156SConrad Meyer 		goto out;
34785b4c344SConrad Meyer 
34885b4c344SConrad Meyer 	cksect = read_sect(fp, MAIN_BOOT_REGION_SECT + SUBREGION_CHKSUM_SECT,
34985b4c344SConrad Meyer 	    bytespersec);
35085b4c344SConrad Meyer 
351e2660756SConrad Meyer 	/*
35285b4c344SConrad Meyer 	 * Technically the entire sector should be full of repeating 4-byte
35385b4c344SConrad Meyer 	 * checksum pattern, but we only verify the first.
354e2660756SConrad Meyer 	 */
35585b4c344SConrad Meyer 	if (chksum != le32toh(cksect[0])) {
35685b4c344SConrad Meyer 		warnx("exfat: Found checksum 0x%08x != computed 0x%08x",
35785b4c344SConrad Meyer 		    le32toh(cksect[0]), chksum);
358ddf61156SConrad Meyer 		error = 1;
359ddf61156SConrad Meyer 		goto out;
36085b4c344SConrad Meyer 	}
36185b4c344SConrad Meyer 
3625ab1cb52SConrad Meyer #ifdef WITH_ICONV
36385b4c344SConrad Meyer 	if (show_label)
36485b4c344SConrad Meyer 		exfat_find_label(fp, ev, bytespersec, label, size);
3653513df4bSEd Maste #else
3663513df4bSEd Maste 	if (show_label) {
3673513df4bSEd Maste 		warnx("label not available without iconv support");
3683513df4bSEd Maste 		memset(label, 0, size);
3693513df4bSEd Maste 	}
3705ab1cb52SConrad Meyer #endif
37185b4c344SConrad Meyer 
372ddf61156SConrad Meyer out:
37385b4c344SConrad Meyer 	free(cksect);
374e2660756SConrad Meyer 	free(ev);
375ddf61156SConrad Meyer 	return (error != 0);
376e2660756SConrad Meyer }
377