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