| @@ -1,372 +1,369 @@ | | | @@ -1,372 +1,369 @@ |
1 | /* $NetBSD: exfat.c,v 1.3 2020/02/08 12:56:56 fox Exp $ */ | | 1 | /* $NetBSD: exfat.c,v 1.4 2021/01/24 14:37:32 tkusumi Exp $ */ |
2 | | | 2 | |
3 | /* | | 3 | /* |
4 | * Copyright (c) 2017 Conrad Meyer <cem@FreeBSD.org> | | 4 | * Copyright (c) 2017 Conrad Meyer <cem@FreeBSD.org> |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * Redistribution and use in source and binary forms, with or without | | 7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions | | 8 | * modification, are permitted provided that the following conditions |
9 | * are met: | | 9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright | | 10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. | | 11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright | | 12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the | | 13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. | | 14 | * documentation and/or other materials provided with the distribution. |
15 | * | | 15 | * |
16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | | 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | 18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | | 19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
20 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | 20 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | | 21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
22 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | | 22 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
23 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | 23 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
24 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | 24 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
25 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | | 25 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
26 | * SUCH DAMAGE. | | 26 | * SUCH DAMAGE. |
27 | */ | | 27 | */ |
28 | #include <sys/cdefs.h> | | 28 | #include <sys/cdefs.h> |
29 | __RCSID("$NetBSD: exfat.c,v 1.3 2020/02/08 12:56:56 fox Exp $"); | | 29 | __RCSID("$NetBSD: exfat.c,v 1.4 2021/01/24 14:37:32 tkusumi Exp $"); |
30 | | | 30 | |
31 | #include <sys/param.h> | | 31 | #include <sys/param.h> |
32 | #include <sys/endian.h> | | 32 | #include <sys/endian.h> |
33 | | | 33 | |
34 | #include <assert.h> | | 34 | #include <assert.h> |
35 | #include <err.h> | | 35 | #include <err.h> |
36 | #include <errno.h> | | 36 | #include <errno.h> |
37 | #include <iconv.h> | | 37 | #include <iconv.h> |
38 | #include <stdbool.h> | | 38 | #include <stdbool.h> |
39 | #include <stdint.h> | | 39 | #include <stdint.h> |
40 | #include <stdio.h> | | 40 | #include <stdio.h> |
41 | #include <stdlib.h> | | 41 | #include <stdlib.h> |
42 | #include <string.h> | | 42 | #include <string.h> |
43 | | | 43 | |
44 | #include "fstyp.h" | | 44 | #include "fstyp.h" |
45 | | | 45 | |
46 | /* | | 46 | /* |
47 | * https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification | | 47 | * https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification |
48 | */ | | 48 | */ |
49 | | | 49 | |
50 | struct exfat_vbr { | | 50 | struct exfat_vbr { |
51 | char ev_jmp[3]; | | 51 | char ev_jmp[3]; |
52 | char ev_fsname[8]; | | 52 | char ev_fsname[8]; |
53 | char ev_zeros[53]; | | 53 | char ev_zeros[53]; |
54 | uint64_t ev_part_offset; | | 54 | uint64_t ev_part_offset; |
55 | uint64_t ev_vol_length; | | 55 | uint64_t ev_vol_length; |
56 | uint32_t ev_fat_offset; | | 56 | uint32_t ev_fat_offset; |
57 | uint32_t ev_fat_length; | | 57 | uint32_t ev_fat_length; |
58 | uint32_t ev_cluster_offset; | | 58 | uint32_t ev_cluster_offset; |
59 | uint32_t ev_cluster_count; | | 59 | uint32_t ev_cluster_count; |
60 | uint32_t ev_rootdir_cluster; | | 60 | uint32_t ev_rootdir_cluster; |
61 | uint32_t ev_vol_serial; | | 61 | uint32_t ev_vol_serial; |
62 | uint16_t ev_fs_revision; | | 62 | uint16_t ev_fs_revision; |
63 | uint16_t ev_vol_flags; | | 63 | uint16_t ev_vol_flags; |
64 | uint8_t ev_log_bytes_per_sect; | | 64 | uint8_t ev_log_bytes_per_sect; |
65 | uint8_t ev_log_sect_per_clust; | | 65 | uint8_t ev_log_sect_per_clust; |
66 | uint8_t ev_num_fats; | | 66 | uint8_t ev_num_fats; |
67 | uint8_t ev_drive_sel; | | 67 | uint8_t ev_drive_sel; |
68 | uint8_t ev_percent_used; | | 68 | uint8_t ev_percent_used; |
69 | } __packed; | | 69 | } __packed; |
70 | | | 70 | |
71 | struct exfat_dirent { | | 71 | struct exfat_dirent { |
72 | uint8_t xde_type; | | 72 | uint8_t xde_type; |
73 | #define XDE_TYPE_INUSE_MASK 0x80 /* 1=in use */ | | 73 | #define XDE_TYPE_INUSE_MASK 0x80 /* 1=in use */ |
74 | #define XDE_TYPE_INUSE_SHIFT 7 | | 74 | #define XDE_TYPE_INUSE_SHIFT 7 |
75 | #define XDE_TYPE_CATEGORY_MASK 0x40 /* 0=primary */ | | 75 | #define XDE_TYPE_CATEGORY_MASK 0x40 /* 0=primary */ |
76 | #define XDE_TYPE_CATEGORY_SHIFT 6 | | 76 | #define XDE_TYPE_CATEGORY_SHIFT 6 |
77 | #define XDE_TYPE_IMPORTNC_MASK 0x20 /* 0=critical */ | | 77 | #define XDE_TYPE_IMPORTNC_MASK 0x20 /* 0=critical */ |
78 | #define XDE_TYPE_IMPORTNC_SHIFT 5 | | 78 | #define XDE_TYPE_IMPORTNC_SHIFT 5 |
79 | #define XDE_TYPE_CODE_MASK 0x1f | | 79 | #define XDE_TYPE_CODE_MASK 0x1f |
80 | /* InUse=0, ..., TypeCode=0: EOD. */ | | 80 | /* InUse=0, ..., TypeCode=0: EOD. */ |
81 | #define XDE_TYPE_EOD 0x00 | | 81 | #define XDE_TYPE_EOD 0x00 |
82 | #define XDE_TYPE_ALLOC_BITMAP (XDE_TYPE_INUSE_MASK | 0x01) | | 82 | #define XDE_TYPE_ALLOC_BITMAP (XDE_TYPE_INUSE_MASK | 0x01) |
83 | #define XDE_TYPE_UPCASE_TABLE (XDE_TYPE_INUSE_MASK | 0x02) | | 83 | #define XDE_TYPE_UPCASE_TABLE (XDE_TYPE_INUSE_MASK | 0x02) |
84 | #define XDE_TYPE_VOL_LABEL (XDE_TYPE_INUSE_MASK | 0x03) | | 84 | #define XDE_TYPE_VOL_LABEL (XDE_TYPE_INUSE_MASK | 0x03) |
85 | #define XDE_TYPE_FILE (XDE_TYPE_INUSE_MASK | 0x05) | | 85 | #define XDE_TYPE_FILE (XDE_TYPE_INUSE_MASK | 0x05) |
86 | #define XDE_TYPE_VOL_GUID (XDE_TYPE_INUSE_MASK | XDE_TYPE_IMPORTNC_MASK) | | 86 | #define XDE_TYPE_VOL_GUID (XDE_TYPE_INUSE_MASK | XDE_TYPE_IMPORTNC_MASK) |
87 | #define XDE_TYPE_STREAM_EXT (XDE_TYPE_INUSE_MASK | XDE_TYPE_CATEGORY_MASK) | | 87 | #define XDE_TYPE_STREAM_EXT (XDE_TYPE_INUSE_MASK | XDE_TYPE_CATEGORY_MASK) |
88 | #define XDE_TYPE_FILE_NAME (XDE_TYPE_INUSE_MASK | XDE_TYPE_CATEGORY_MASK | 0x01) | | 88 | #define XDE_TYPE_FILE_NAME (XDE_TYPE_INUSE_MASK | XDE_TYPE_CATEGORY_MASK | 0x01) |
89 | #define XDE_TYPE_VENDOR (XDE_TYPE_INUSE_MASK | XDE_TYPE_CATEGORY_MASK | XDE_TYPE_IMPORTNC_MASK) | | 89 | #define XDE_TYPE_VENDOR (XDE_TYPE_INUSE_MASK | XDE_TYPE_CATEGORY_MASK | XDE_TYPE_IMPORTNC_MASK) |
90 | #define XDE_TYPE_VENDOR_ALLOC (XDE_TYPE_INUSE_MASK | XDE_TYPE_CATEGORY_MASK | XDE_TYPE_IMPORTNC_MASK | 0x01) | | 90 | #define XDE_TYPE_VENDOR_ALLOC (XDE_TYPE_INUSE_MASK | XDE_TYPE_CATEGORY_MASK | XDE_TYPE_IMPORTNC_MASK | 0x01) |
91 | union { | | 91 | union { |
92 | uint8_t xde_generic_[19]; | | 92 | uint8_t xde_generic_[19]; |
93 | struct exde_primary { | | 93 | struct exde_primary { |
94 | /* | | 94 | /* |
95 | * Count of "secondary" dirents following this one. | | 95 | * Count of "secondary" dirents following this one. |
96 | * | | 96 | * |
97 | * A single logical entity may be composed of a | | 97 | * A single logical entity may be composed of a |
98 | * sequence of several dirents, starting with a primary | | 98 | * sequence of several dirents, starting with a primary |
99 | * one; the rest are secondary dirents. | | 99 | * one; the rest are secondary dirents. |
100 | */ | | 100 | */ |
101 | uint8_t xde_secondary_count_; | | 101 | uint8_t xde_secondary_count_; |
102 | uint16_t xde_set_chksum_; | | 102 | uint16_t xde_set_chksum_; |
103 | uint16_t xde_prim_flags_; | | 103 | uint16_t xde_prim_flags_; |
104 | uint8_t xde_prim_generic_[14]; | | 104 | uint8_t xde_prim_generic_[14]; |
105 | } __packed xde_primary_; | | 105 | } __packed xde_primary_; |
106 | struct exde_secondary { | | 106 | struct exde_secondary { |
107 | uint8_t xde_sec_flags_; | | 107 | uint8_t xde_sec_flags_; |
108 | uint8_t xde_sec_generic_[18]; | | 108 | uint8_t xde_sec_generic_[18]; |
109 | } __packed xde_secondary_; | | 109 | } __packed xde_secondary_; |
110 | } u; | | 110 | } u; |
111 | uint32_t xde_first_cluster; | | 111 | uint32_t xde_first_cluster; |
112 | uint64_t xde_data_len; | | 112 | uint64_t xde_data_len; |
113 | } __packed; | | 113 | } __packed; |
114 | #define xde_generic u.xde_generic_ | | 114 | #define xde_generic u.xde_generic_ |
115 | #define xde_secondary_count u.xde_primary_.xde_secondary_count | | 115 | #define xde_secondary_count u.xde_primary_.xde_secondary_count |
116 | #define xde_set_chksum u.xde_primary_.xde_set_chksum_ | | 116 | #define xde_set_chksum u.xde_primary_.xde_set_chksum_ |
117 | #define xde_prim_flags u.xde_primary_.xde_prim_flags_ | | 117 | #define xde_prim_flags u.xde_primary_.xde_prim_flags_ |
118 | #define xde_sec_flags u.xde_secondary_.xde_sec_flags_ | | 118 | #define xde_sec_flags u.xde_secondary_.xde_sec_flags_ |
119 | _Static_assert(sizeof(struct exfat_dirent) == 32, "spec"); | | 119 | _Static_assert(sizeof(struct exfat_dirent) == 32, "spec"); |
120 | | | 120 | |
121 | struct exfat_de_label { | | 121 | struct exfat_de_label { |
122 | uint8_t xdel_type; /* XDE_TYPE_VOL_LABEL */ | | 122 | uint8_t xdel_type; /* XDE_TYPE_VOL_LABEL */ |
123 | uint8_t xdel_char_cnt; /* Length of UCS-2 label */ | | 123 | uint8_t xdel_char_cnt; /* Length of UCS-2 label */ |
124 | uint16_t xdel_vol_lbl[11]; | | 124 | uint16_t xdel_vol_lbl[11]; |
125 | uint8_t xdel_reserved[8]; | | 125 | uint8_t xdel_reserved[8]; |
126 | } __packed; | | 126 | } __packed; |
127 | _Static_assert(sizeof(struct exfat_de_label) == 32, "spec"); | | 127 | _Static_assert(sizeof(struct exfat_de_label) == 32, "spec"); |
128 | | | 128 | |
129 | #define MAIN_BOOT_REGION_SECT 0 | | 129 | #define MAIN_BOOT_REGION_SECT 0 |
130 | #define BACKUP_BOOT_REGION_SECT 12 | | 130 | #define BACKUP_BOOT_REGION_SECT 12 |
131 | | | 131 | |
132 | #define SUBREGION_CHKSUM_SECT 11 | | 132 | #define SUBREGION_CHKSUM_SECT 11 |
133 | | | 133 | |
134 | #define FIRST_CLUSTER 2 | | 134 | #define FIRST_CLUSTER 2 |
135 | #define BAD_BLOCK_SENTINEL 0xfffffff7u | | 135 | #define BAD_BLOCK_SENTINEL 0xfffffff7u |
136 | #define END_CLUSTER_SENTINEL 0xffffffffu | | 136 | #define END_CLUSTER_SENTINEL 0xffffffffu |
137 | | | 137 | |
138 | static inline void * | | 138 | static inline void * |
139 | read_sectn(FILE *fp, off_t sect, unsigned count, unsigned bytespersec) | | 139 | read_sectn(FILE *fp, off_t sect, unsigned count, unsigned bytespersec) |
140 | { | | 140 | { |
141 | return (read_buf(fp, sect * bytespersec, bytespersec * count)); | | 141 | return (read_buf(fp, sect * bytespersec, bytespersec * count)); |
142 | } | | 142 | } |
143 | | | 143 | |
144 | static inline void * | | 144 | static inline void * |
145 | read_sect(FILE *fp, off_t sect, unsigned bytespersec) | | 145 | read_sect(FILE *fp, off_t sect, unsigned bytespersec) |
146 | { | | 146 | { |
147 | return (read_sectn(fp, sect, 1, bytespersec)); | | 147 | return (read_sectn(fp, sect, 1, bytespersec)); |
148 | } | | 148 | } |
149 | | | 149 | |
150 | /* | | 150 | /* |
151 | * Compute the byte-by-byte multi-sector checksum of the given boot region | | 151 | * Compute the byte-by-byte multi-sector checksum of the given boot region |
152 | * (MAIN or BACKUP), for a given bytespersec (typically 512 or 4096). | | 152 | * (MAIN or BACKUP), for a given bytespersec (typically 512 or 4096). |
153 | * | | 153 | * |
154 | * Endian-safe; result is host endian. | | 154 | * Endian-safe; result is host endian. |
155 | */ | | 155 | */ |
156 | static int | | 156 | static int |
157 | exfat_compute_boot_chksum(FILE *fp, unsigned region, unsigned bytespersec, | | 157 | exfat_compute_boot_chksum(FILE *fp, unsigned region, unsigned bytespersec, |
158 | uint32_t *result) | | 158 | uint32_t *result) |
159 | { | | 159 | { |
160 | unsigned char *sector; | | 160 | unsigned char *sector; |
161 | unsigned n, sect; | | 161 | unsigned n, sect; |
162 | uint32_t checksum; | | 162 | uint32_t checksum; |
163 | | | 163 | |
164 | checksum = 0; | | 164 | checksum = 0; |
165 | for (sect = 0; sect < 11; sect++) { | | 165 | for (sect = 0; sect < 11; sect++) { |
166 | sector = read_sect(fp, region + sect, bytespersec); | | 166 | sector = read_sect(fp, region + sect, bytespersec); |
167 | if (sector == NULL) | | 167 | if (sector == NULL) |
168 | return (ENXIO); | | 168 | return (ENXIO); |
169 | for (n = 0; n < bytespersec; n++) { | | 169 | for (n = 0; n < bytespersec; n++) { |
170 | if (sect == 0) { | | 170 | if (sect == 0) { |
171 | switch (n) { | | 171 | switch (n) { |
172 | case 106: | | 172 | case 106: |
173 | case 107: | | 173 | case 107: |
174 | case 112: | | 174 | case 112: |
175 | continue; | | 175 | continue; |
176 | } | | 176 | } |
177 | } | | 177 | } |
178 | checksum = ((checksum & 1) ? 0x80000000u : 0u) + | | 178 | checksum = ((checksum & 1) ? 0x80000000u : 0u) + |
179 | (checksum >> 1) + (uint32_t)sector[n]; | | 179 | (checksum >> 1) + (uint32_t)sector[n]; |
180 | } | | 180 | } |
181 | free(sector); | | 181 | free(sector); |
182 | } | | 182 | } |
183 | | | 183 | |
184 | *result = checksum; | | 184 | *result = checksum; |
185 | return (0); | | 185 | return (0); |
186 | } | | 186 | } |
187 | | | 187 | |
188 | static void | | 188 | static void |
189 | convert_label(const uint16_t *ucs2label /* LE */, unsigned ucs2len, char | | 189 | convert_label(const uint16_t *ucs2label /* LE */, unsigned ucs2len, char |
190 | *label_out, size_t label_sz) | | 190 | *label_out, size_t label_sz) |
191 | { | | 191 | { |
192 | const char *label; | | 192 | const char *label; |
193 | char *label_out_orig; | | 193 | char *label_out_orig; |
194 | iconv_t cd; | | 194 | iconv_t cd; |
195 | size_t srcleft, rc; | | 195 | size_t srcleft, rc; |
196 | | | 196 | |
197 | /* Currently hardcoded in fstyp.c as 256 or so. */ | | 197 | /* Currently hardcoded in fstyp.c as 256 or so. */ |
198 | assert(label_sz > 1); | | 198 | assert(label_sz > 1); |
199 | | | 199 | |
200 | if (ucs2len == 0) { | | 200 | if (ucs2len == 0) { |
201 | /* | | 201 | /* |
202 | * Kind of seems bogus, but the spec allows an empty label | | 202 | * Kind of seems bogus, but the spec allows an empty label |
203 | * entry with the same meaning as no label. | | 203 | * entry with the same meaning as no label. |
204 | */ | | 204 | */ |
205 | return; | | 205 | return; |
206 | } | | 206 | } |
207 | | | 207 | |
208 | if (ucs2len > 11) { | | 208 | if (ucs2len > 11) { |
209 | warnx("exfat: Bogus volume label length: %u", ucs2len); | | 209 | warnx("exfat: Bogus volume label length: %u", ucs2len); |
210 | return; | | 210 | return; |
211 | } | | 211 | } |
212 | | | 212 | |
213 | /* dstname="" means convert to the current locale. */ | | 213 | /* dstname="" means convert to the current locale. */ |
214 | cd = iconv_open("", EXFAT_ENC); | | 214 | cd = iconv_open("", EXFAT_ENC); |
215 | if (cd == (iconv_t)-1) { | | 215 | if (cd == (iconv_t)-1) { |
216 | warn("exfat: Could not open iconv"); | | 216 | warn("exfat: Could not open iconv"); |
217 | return; | | 217 | return; |
218 | } | | 218 | } |
219 | | | 219 | |
220 | label_out_orig = label_out; | | 220 | label_out_orig = label_out; |
221 | | | 221 | |
222 | /* Dummy up the byte pointer and byte length iconv's API wants. */ | | 222 | /* Dummy up the byte pointer and byte length iconv's API wants. */ |
223 | label = (const void *)ucs2label; | | 223 | label = (const void *)ucs2label; |
224 | srcleft = ucs2len * sizeof(*ucs2label); | | 224 | srcleft = ucs2len * sizeof(*ucs2label); |
225 | | | 225 | |
226 | rc = iconv(cd, __UNCONST(&label), &srcleft, &label_out, | | 226 | rc = iconv(cd, __UNCONST(&label), &srcleft, &label_out, |
227 | &label_sz); | | 227 | &label_sz); |
228 | if (rc == (size_t)-1) { | | 228 | if (rc == (size_t)-1) { |
229 | warn("exfat: iconv()"); | | 229 | warn("exfat: iconv()"); |
230 | *label_out_orig = '\0'; | | 230 | *label_out_orig = '\0'; |
231 | } else { | | 231 | } else { |
232 | /* NUL-terminate result (iconv advances label_out). */ | | 232 | /* NUL-terminate result (iconv advances label_out). */ |
233 | if (label_sz == 0) | | 233 | if (label_sz == 0) |
234 | label_out--; | | 234 | label_out--; |
235 | *label_out = '\0'; | | 235 | *label_out = '\0'; |
236 | } | | 236 | } |
237 | | | 237 | |
238 | iconv_close(cd); | | 238 | iconv_close(cd); |
239 | } | | 239 | } |
240 | | | 240 | |
241 | /* | | 241 | /* |
242 | * Using the FAT table, look up the next cluster in this chain. | | 242 | * Using the FAT table, look up the next cluster in this chain. |
243 | */ | | 243 | */ |
244 | static uint32_t | | 244 | static uint32_t |
245 | exfat_fat_next(FILE *fp, const struct exfat_vbr *ev, unsigned BPS, | | 245 | exfat_fat_next(FILE *fp, const struct exfat_vbr *ev, unsigned BPS, |
246 | uint32_t cluster) | | 246 | uint32_t cluster) |
247 | { | | 247 | { |
248 | uint32_t fat_offset_sect, clsect, clsectoff; | | 248 | uint32_t fat_offset_sect, clsect, clsectoff; |
249 | uint32_t *fatsect, nextclust; | | 249 | uint32_t *fatsect, nextclust; |
250 | | | 250 | |
251 | fat_offset_sect = le32toh(ev->ev_fat_offset); | | 251 | fat_offset_sect = le32toh(ev->ev_fat_offset); |
252 | clsect = fat_offset_sect + (cluster / (BPS / (uint32_t)sizeof(cluster))); | | 252 | clsect = fat_offset_sect + (cluster / (BPS / (uint32_t)sizeof(cluster))); |
253 | clsectoff = (cluster % (BPS / (uint32_t)sizeof(cluster))); | | 253 | clsectoff = (cluster % (BPS / (uint32_t)sizeof(cluster))); |
254 | | | 254 | |
255 | /* XXX This is pretty wasteful without a block cache for the FAT. */ | | 255 | /* XXX This is pretty wasteful without a block cache for the FAT. */ |
256 | fatsect = read_sect(fp, clsect, BPS); | | 256 | fatsect = read_sect(fp, clsect, BPS); |
257 | nextclust = le32toh(fatsect[clsectoff]); | | 257 | nextclust = le32toh(fatsect[clsectoff]); |
258 | free(fatsect); | | 258 | free(fatsect); |
259 | | | 259 | |
260 | return (nextclust); | | 260 | return (nextclust); |
261 | } | | 261 | } |
262 | | | 262 | |
263 | static void | | 263 | static void |
264 | exfat_find_label(FILE *fp, const struct exfat_vbr *ev, unsigned BPS, | | 264 | exfat_find_label(FILE *fp, const struct exfat_vbr *ev, unsigned BPS, |
265 | char *label_out, size_t label_sz) | | 265 | char *label_out, size_t label_sz) |
266 | { | | 266 | { |
267 | uint32_t rootdir_cluster, sects_per_clust, cluster_offset_sect; | | 267 | uint32_t rootdir_cluster, sects_per_clust, cluster_offset_sect; |
268 | off_t rootdir_sect; | | 268 | off_t rootdir_sect; |
269 | struct exfat_dirent *declust, *it; | | 269 | struct exfat_dirent *declust, *it; |
270 | | | 270 | |
271 | cluster_offset_sect = le32toh(ev->ev_cluster_offset); | | 271 | cluster_offset_sect = le32toh(ev->ev_cluster_offset); |
272 | rootdir_cluster = le32toh(ev->ev_rootdir_cluster); | | 272 | rootdir_cluster = le32toh(ev->ev_rootdir_cluster); |
273 | sects_per_clust = (1u << ev->ev_log_sect_per_clust); | | 273 | sects_per_clust = (1u << ev->ev_log_sect_per_clust); |
274 | | | 274 | |
275 | if (rootdir_cluster < FIRST_CLUSTER) { | | 275 | if (rootdir_cluster < FIRST_CLUSTER) { |
276 | warnx("%s: invalid rootdir cluster %u < %d", __func__, | | 276 | warnx("%s: invalid rootdir cluster %u < %d", __func__, |
277 | rootdir_cluster, FIRST_CLUSTER); | | 277 | rootdir_cluster, FIRST_CLUSTER); |
278 | return; | | 278 | return; |
279 | } | | 279 | } |
280 | | | 280 | |
281 | | | 281 | |
282 | for (; rootdir_cluster != END_CLUSTER_SENTINEL; | | 282 | for (; rootdir_cluster != END_CLUSTER_SENTINEL; |
283 | rootdir_cluster = exfat_fat_next(fp, ev, BPS, rootdir_cluster)) { | | 283 | rootdir_cluster = exfat_fat_next(fp, ev, BPS, rootdir_cluster)) { |
284 | if (rootdir_cluster == BAD_BLOCK_SENTINEL) { | | 284 | if (rootdir_cluster == BAD_BLOCK_SENTINEL) { |
285 | warnx("%s: Bogus bad block in root directory chain", | | 285 | warnx("%s: Bogus bad block in root directory chain", |
286 | __func__); | | 286 | __func__); |
287 | return; | | 287 | return; |
288 | } | | 288 | } |
289 | | | 289 | |
290 | rootdir_sect = (rootdir_cluster - FIRST_CLUSTER) * | | 290 | rootdir_sect = (rootdir_cluster - FIRST_CLUSTER) * |
291 | sects_per_clust + cluster_offset_sect; | | 291 | sects_per_clust + cluster_offset_sect; |
292 | declust = read_sectn(fp, rootdir_sect, sects_per_clust, BPS); | | 292 | declust = read_sectn(fp, rootdir_sect, sects_per_clust, BPS); |
293 | for (it = declust; | | 293 | for (it = declust; |
294 | it < declust + (sects_per_clust * BPS / sizeof(*it)); it++) { | | 294 | it < declust + (sects_per_clust * BPS / sizeof(*it)); it++) { |
295 | bool eod = false; | | 295 | bool eod = false; |
296 | | | 296 | |
297 | /* | | 297 | /* |
298 | * Simplistic directory traversal; doesn't do any | | 298 | * Simplistic directory traversal; doesn't do any |
299 | * validation of "MUST" requirements in spec. | | 299 | * validation of "MUST" requirements in spec. |
300 | */ | | 300 | */ |
301 | switch (it->xde_type) { | | 301 | switch (it->xde_type) { |
302 | case XDE_TYPE_EOD: | | 302 | case XDE_TYPE_EOD: |
303 | eod = true; | | 303 | eod = true; |
304 | break; | | 304 | break; |
305 | case XDE_TYPE_VOL_LABEL: { | | 305 | case XDE_TYPE_VOL_LABEL: { |
306 | struct exfat_de_label *lde = (void*)it; | | 306 | struct exfat_de_label *lde = (void*)it; |
307 | convert_label(lde->xdel_vol_lbl, | | 307 | convert_label(lde->xdel_vol_lbl, |
308 | lde->xdel_char_cnt, label_out, label_sz); | | 308 | lde->xdel_char_cnt, label_out, label_sz); |
309 | free(declust); | | 309 | free(declust); |
310 | return; | | 310 | return; |
311 | } | | 311 | } |
312 | } | | 312 | } |
313 | | | 313 | |
314 | if (eod) | | 314 | if (eod) |
315 | break; | | 315 | break; |
316 | } | | 316 | } |
317 | free(declust); | | 317 | free(declust); |
318 | } | | 318 | } |
319 | } | | 319 | } |
320 | | | 320 | |
321 | int | | 321 | int |
322 | fstyp_exfat(FILE *fp, char *label, size_t size) | | 322 | fstyp_exfat(FILE *fp, char *label, size_t size) |
323 | { | | 323 | { |
324 | struct exfat_vbr *ev; | | 324 | struct exfat_vbr *ev; |
325 | uint32_t *cksect; | | 325 | uint32_t *cksect; |
326 | unsigned bytespersec; | | 326 | unsigned bytespersec; |
327 | uint32_t chksum; | | 327 | uint32_t chksum; |
328 | int error; | | 328 | int error; |
329 | | | 329 | |
| | | 330 | error = 1; |
330 | cksect = NULL; | | 331 | cksect = NULL; |
331 | | | | |
332 | ev = (struct exfat_vbr *)read_buf(fp, 0, 512); | | 332 | ev = (struct exfat_vbr *)read_buf(fp, 0, 512); |
333 | if (ev == NULL || strncmp(ev->ev_fsname, "EXFAT ", 8) != 0) | | 333 | if (ev == NULL || strncmp(ev->ev_fsname, "EXFAT ", 8) != 0) |
334 | goto fail; | | 334 | goto out; |
335 | | | 335 | |
336 | if (ev->ev_log_bytes_per_sect < 9 || ev->ev_log_bytes_per_sect > 12) { | | 336 | if (ev->ev_log_bytes_per_sect < 9 || ev->ev_log_bytes_per_sect > 12) { |
337 | warnx("exfat: Invalid BytesPerSectorShift"); | | 337 | warnx("exfat: Invalid BytesPerSectorShift"); |
338 | goto done; | | 338 | goto out; |
339 | } | | 339 | } |
340 | | | 340 | |
341 | bytespersec = (1u << ev->ev_log_bytes_per_sect); | | 341 | bytespersec = (1u << ev->ev_log_bytes_per_sect); |
342 | | | 342 | |
343 | error = exfat_compute_boot_chksum(fp, MAIN_BOOT_REGION_SECT, | | 343 | error = exfat_compute_boot_chksum(fp, MAIN_BOOT_REGION_SECT, |
344 | bytespersec, &chksum); | | 344 | bytespersec, &chksum); |
345 | if (error != 0) | | 345 | if (error != 0) |
346 | goto done; | | 346 | goto out; |
347 | | | 347 | |
348 | cksect = read_sect(fp, MAIN_BOOT_REGION_SECT + SUBREGION_CHKSUM_SECT, | | 348 | cksect = read_sect(fp, MAIN_BOOT_REGION_SECT + SUBREGION_CHKSUM_SECT, |
349 | bytespersec); | | 349 | bytespersec); |
350 | | | 350 | |
351 | /* | | 351 | /* |
352 | * Technically the entire sector should be full of repeating 4-byte | | 352 | * Technically the entire sector should be full of repeating 4-byte |
353 | * checksum pattern, but we only verify the first. | | 353 | * checksum pattern, but we only verify the first. |
354 | */ | | 354 | */ |
355 | if (chksum != le32toh(cksect[0])) { | | 355 | if (chksum != le32toh(cksect[0])) { |
356 | warnx("exfat: Found checksum 0x%08x != computed 0x%08x", | | 356 | warnx("exfat: Found checksum 0x%08x != computed 0x%08x", |
357 | le32toh(cksect[0]), chksum); | | 357 | le32toh(cksect[0]), chksum); |
358 | goto done; | | 358 | error = 1; |
| | | 359 | goto out; |
359 | } | | 360 | } |
360 | | | 361 | |
361 | if (show_label) | | 362 | if (show_label) |
362 | exfat_find_label(fp, ev, bytespersec, label, size); | | 363 | exfat_find_label(fp, ev, bytespersec, label, size); |
363 | | | 364 | |
364 | done: | | 365 | out: |
365 | free(cksect); | | 366 | free(cksect); |
366 | free(ev); | | 367 | free(ev); |
367 | return (0); | | 368 | return (error != 0); |
368 | | | | |
369 | fail: | | | |
370 | free(ev); | | | |
371 | return (1); | | | |
372 | } | | 369 | } |