1 | /*␊ |
2 | * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.␊ |
3 | *␊ |
4 | * @APPLE_LICENSE_HEADER_START@␊ |
5 | * ␊ |
6 | * Copyright (c) 1999-2004 Apple Computer, Inc. All Rights Reserved.␊ |
7 | * ␊ |
8 | * This file contains Original Code and/or Modifications of Original Code␊ |
9 | * as defined in and that are subject to the Apple Public Source License␊ |
10 | * Version 2.0 (the 'License'). You may not use this file except in␊ |
11 | * compliance with the License. Please obtain a copy of the License at␊ |
12 | * http://www.opensource.apple.com/apsl/ and read it before using this␊ |
13 | * file.␊ |
14 | * ␊ |
15 | * The Original Code and all software distributed under the License are␊ |
16 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER␊ |
17 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,␊ |
18 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,␊ |
19 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.␊ |
20 | * Please see the License for the specific language governing rights and␊ |
21 | * limitations under the License.␊ |
22 | * ␊ |
23 | * @APPLE_LICENSE_HEADER_END@␊ |
24 | */␊ |
25 | /*␊ |
26 | * dmazar, 14/7/2011␊ |
27 | * support for EXFAT volume label reading␊ |
28 | * EXFAT info from: http://www.ntfs.com/exfat-overview.htm␊ |
29 | *␊ |
30 | * Zenith432, Nov 30 2014␊ |
31 | * support for reading files␊ |
32 | *␊ |
33 | * EXFAT shares partition type with NTFS (0x7) and easiest way of␊ |
34 | * adding it was through ntfs.c module. All functions here are called␊ |
35 | * from similar ntfs.c funcs as fallback (if not NTFS, maybe it's EXFAT).␊ |
36 | */␊ |
37 | ␊ |
38 | #include "libsaio.h"␊ |
39 | #include "sl.h"␊ |
40 | ␊ |
41 | #pragma mark -␊ |
42 | #pragma mark Preprocessor Definitions␊ |
43 | #pragma mark -␊ |
44 | ␊ |
45 | #ifndef DEBUG_EXFAT␊ |
46 | #define DEBUG_EXFAT 0␊ |
47 | #endif␊ |
48 | ␊ |
49 | #if DEBUG_EXFAT␊ |
50 | #define DBG(x...)␉␉printf(x)␊ |
51 | #define PAUSE()␉␉␉getchar()␊ |
52 | #else␊ |
53 | #define DBG(x...)␊ |
54 | #define PAUSE()␊ |
55 | #endif␊ |
56 | ␊ |
57 | #define␉EXFAT_BBID␉&gExfatID[0]␊ |
58 | #define␉EXFAT_BBIDLEN␉8␊ |
59 | ␊ |
60 | #define MIN_BLOCK_SIZE_SHIFT␉␉9␊ |
61 | #define MAX_BLOCK_SIZE_SHIFT␉␉12␊ |
62 | #define MAX_BLOCK_SIZE␉␉␉(1 << MAX_BLOCK_SIZE_SHIFT)␊ |
63 | #define MAX_CLUSTER_SIZE_SHIFT␉␉25␊ |
64 | #define CLUST_FIRST␉␉␉2␉␉/* reserved cluster range */␊ |
65 | #define CLUST_RSRVD␉␉␉0xfffffff7␉/* reserved cluster range */␊ |
66 | #define INVALID_FAT_ADDRESS␉␉0xffffffff␊ |
67 | #define ATTR_DIRECTORY␉␉␉0x10␉␉/* entry is a directory name */␊ |
68 | #define SEFLAG_ALLOCATION_POSSIBLE␉1␊ |
69 | #define SEFLAG_INVALID_FAT_CHAIN␉2␊ |
70 | #define SEFLAG_PSEUDO_ROOTDIR␉␉0x80␊ |
71 | #define COMPONENT_MAX_CHARS␉␉255␉␉/* Max # characters in path name single component */␊ |
72 | ␊ |
73 | #pragma mark -␊ |
74 | #pragma mark Static Data␊ |
75 | #pragma mark -␊ |
76 | ␊ |
77 | static CICell gCurrentIH = NULL;␊ |
78 | static uint8_t gBPSShift = 0;␉␉␉/* log_2(Bytes-Per-Sector) */␊ |
79 | static uint8_t gSPCShift = 0;␉␉␉/* log_2(Sectors-Per-Cluster) */␊ |
80 | static uint32_t gFATOffset = 0;␉␉␉/* in sectors */␊ |
81 | static uint32_t gFATLength = 0;␉␉␉/* in sectors */␊ |
82 | static uint32_t gCLOffset = 0;␉␉␉/* in sectors */␊ |
83 | static uint32_t gCLCount = 0;␉␉␉/* in clusters */␊ |
84 | static uint32_t gRDCl = 0;␉␉␉/* Root Directory Cluster Number */␊ |
85 | static uint8_t* gFATCacheBuffer = NULL;␊ |
86 | static uint32_t gCachedFATBlockAddress = 0;␊ |
87 | static uint16_t* gUPCase = NULL;␉␉/* If loaded, should be exactly 2^16 * sizeof(uint16_t) bytes long */␊ |
88 | static uint8_t gBPCBShift = 0;␉␉␉/* log_2(Bytes-Per-Cache-Block) */␊ |
89 | ␊ |
90 | static char const gExfatID[] = "EXFAT ";␊ |
91 | ␊ |
92 | #pragma mark -␊ |
93 | #pragma mark Helper Structures␊ |
94 | #pragma mark -␊ |
95 | ␊ |
96 | struct exfat_dir_iterator␊ |
97 | {␊ |
98 | ␉uint64_t lsa;␉␉␉␉/* Current sector address */␊ |
99 | ␉uint64_t lsa_end;␉␉␉/* Last sector address + 1 */␊ |
100 | ␉uint8_t* buffer;␊ |
101 | ␉uint32_t cluster;␉␉␉/* Next cluster number */␊ |
102 | ␉uint16_t residue;␉␉␉/* Number of sectors in last cluster */␊ |
103 | ␉uint16_t entry_offset;␉␉␉/* Offset of next entry in buffer */␊ |
104 | };␊ |
105 | ␊ |
106 | struct exfat_inode␊ |
107 | {␊ |
108 | ␉uint64_t valid_length;␉␉␉/* File/Directory length */␊ |
109 | ␉uint32_t first_cluster;␉␉␉/* First cluster number */␊ |
110 | ␉uint8_t attributes;␉␉␉/* From direntry_file::attributes (truncated to 8 bits) */␊ |
111 | ␉uint8_t stream_extension_flags;␉␉/* From direntry_stream_extension::flags */␊ |
112 | };␊ |
113 | ␊ |
114 | #pragma mark -␊ |
115 | #pragma mark exFAT on-disk structures␊ |
116 | #pragma mark -␊ |
117 | ␊ |
118 | /*␊ |
119 | * boot sector of the partition␊ |
120 | * http://www.ntfs.com/exfat-boot-sector.htm␊ |
121 | */␊ |
122 | struct exfatbootfile {␊ |
123 | ␉uint8_t␉␉reserved1[3];␉␉/* JumpBoot: 0xEB7690 */␊ |
124 | ␉uint8_t␉␉bf_sysid[8];␉␉/* FileSystemName: 'EXFAT ' */␊ |
125 | ␉uint8_t␉␉reserved2[53];␉␉/* MustBeZero */␊ |
126 | ␉uint64_t␉bf_prtoff;␉␉/* PartitionOffset: In sectors; if 0, shall be ignored */␊ |
127 | ␉uint64_t␉bf_vollen;␉␉/* VolumeLength: Size of exFAT volume in sectors */␊ |
128 | ␉uint32_t␉bf_fatoff;␉␉/* FatOffset: In sectors */␊ |
129 | ␉uint32_t␉bf_fatlen;␉␉/* FatLength: In sectors. May exceed the required space in order to align the second FAT */␊ |
130 | ␉uint32_t␉bf_cloff;␉␉/* ClusterHeapOffset: In sectors. */␊ |
131 | ␉uint32_t␉bf_clcnt;␉␉/* ClusterCount: 2^32-11 is the maximum number of clusters could be described. */␊ |
132 | ␉uint32_t␉bf_rdircl;␉␉/* RootDirectoryCluster. */␊ |
133 | ␉uint32_t␉bf_volsn;␉␉/* VolumeSerialNumber. */␊ |
134 | ␉uint16_t␉bf_fsrev;␉␉/* FileSystemRevision: as MAJOR.minor, major revision is high byte, minor is low byte; currently 01.00. */␊ |
135 | ␉uint16_t␉bf_volflags;␉␉/* VolumeFlags. */␊ |
136 | ␉uint8_t␉␉bf_bpss;␉␉/* BytesPerSectorShift: Power of 2. Minimum 9 (512 bytes per sector), maximum 12 (4096 bytes per sector) */␊ |
137 | |
138 | ␉uint8_t␉␉bf_nfats;␉␉/* NumberOfFats: 2 is for TexFAT only */␊ |
139 | ␉uint8_t␉␉bf_drvs;␉␉/* DriveSelect: Extended INT 13h drive number; typically 0x80 */␊ |
140 | |
141 | ␉uint8_t␉␉reserved3[7];␉␉/* Reserved */␊ |
142 | ␉uint8_t␉␉bootcode[390];␉␉/* BootCode */␊ |
143 | ␉uint16_t␉bf_bsig;␉␉/* BootSignature: 0xAA55 */␊ |
144 | };␊ |
145 | ␊ |
146 | struct direntry_label {␊ |
147 | #define DIRENTRY_TYPE_LABEL␉␉((uint8_t) 0x83)␊ |
148 | ␉uint8_t␉␉type;␉␉␉/* EntryType: 0x83 (or 0x03 if label is empty) */␊ |
149 | ␉uint8_t␉␉llen;␉␉␉/* CharacterCount: Length in Unicode characters (max 11) */␊ |
150 | #define VOLUME_LABEL_MAX_CHARS␉11␊ |
151 | ␉uint16_t␉label[11];␉␉/* VolumeLabel: Unicode characters (max 11) */␊ |
152 | ␉uint8_t␉␉reserved1[8];␉␉/* Reserved */␊ |
153 | };␊ |
154 | ␊ |
155 | #if UNUSED␊ |
156 | struct direntry_allocation_bitmap␊ |
157 | {␊ |
158 | ␉uint8_t␉␉type;␉␉␉/* EntryType: 0x81 (or 0x01 if entry is empty) */␊ |
159 | ␉uint8_t␉␉bitmap_flags;␉␉/* bit 0: 0 1st bitmap, 1 2nd bitmap */␊ |
160 | ␉uint8_t␉␉reserved1[18];␉␉/* Reserved */␊ |
161 | ␉uint32_t␉first_cluster;␉␉/* Cluster address of 1st data block */␊ |
162 | ␉uint64_t␉data_length;␉␉/* Length of the data */␊ |
163 | };␊ |
164 | ␊ |
165 | struct direntry_upcase_table␊ |
166 | {␊ |
167 | ␉uint8_t␉␉type;␉␉␉/* EntryType: 0x82 (or 0x02 if entry is empty) */␊ |
168 | ␉uint8_t␉␉reserved1[3];␉␉/* Reserved */␊ |
169 | ␉uint32_t␉checksum;␉␉/* Table Checksum */␊ |
170 | ␉uint8_t␉␉reserved2[12];␉␉/* Reserved */␊ |
171 | ␉uint32_t␉first_cluster;␉␉/* Cluster address of 1st data block */␊ |
172 | ␉uint64_t␉data_length;␉␉/* Length of the data */␊ |
173 | };␊ |
174 | /*␊ |
175 | * Skipped:␊ |
176 | * Volume GUID direntry 0xA0␊ |
177 | * TexFAT Padding direntry 0xA1␊ |
178 | * Windows CE Access Control Table 0xE2␊ |
179 | */␊ |
180 | #endif /* UNUSED */␊ |
181 | ␊ |
182 | struct direntry_file␊ |
183 | {␊ |
184 | #define DIRENTRY_TYPE_FILE␉␉((uint8_t) 0x85)␊ |
185 | ␉uint8_t␉␉type;␉␉␉/* EntryType: 0x85 (or 0x05 if entry is empty) */␊ |
186 | ␉uint8_t␉␉count2;␉␉␉/* Secondary Count */␊ |
187 | ␉uint16_t␉checksum;␉␉/* Set Checksum */␊ |
188 | ␉uint16_t␉attributes;␉␉/* File Attributes, 1 - R, 2 - H, 4 - S, 16 - D, 32 - A */␊ |
189 | ␉uint8_t␉␉reserved1[2];␉␉/* Reserved */␊ |
190 | ␉uint32_t␉create_time;␉␉/* Create Time, DOS Timestamp Format */␊ |
191 | ␉uint32_t␉mod_time;␉␉/* Last Modified Time, DOS Timestamp Format */␊ |
192 | ␉uint32_t␉access_time;␉␉/* Last Accessed Time, DOS Timestamp Format */␊ |
193 | ␉uint8_t␉␉create_10ms;␉␉/* 10ms increments range 0 - 199 */␊ |
194 | ␉uint8_t␉␉mod_10ms;␉␉/* 10ms increments range 0 - 199 */␊ |
195 | ␉uint8_t␉␉create_tzoff;␉␉/* TZ Offset, difference to UTC in 15 min increments */␊ |
196 | ␉uint8_t␉␉mod_tzoff;␉␉/* TZ Offset, difference to UTC in 15 min increments */␊ |
197 | ␉uint8_t␉␉access_tzoff;␉␉/* TZ Offset, difference to UTC in 15 min increments */␊ |
198 | ␉uint8_t␉␉reserved2[7];␉␉/* Reserved */␊ |
199 | };␊ |
200 | ␊ |
201 | struct direntry_stream_extension␊ |
202 | {␊ |
203 | #define DIRENTRY_TYPE_ST_EX␉␉((uint8_t) 0xC0)␊ |
204 | ␉uint8_t␉␉type;␉␉␉/* EntryType: 0xC0 (or 0x40 if entry is empty) */␊ |
205 | ␉uint8_t␉␉flags;␉␉␉/* bit 0 - Allocation Possible (1 Yes/0 No), bit 1 - No FAT Chain (1 Invalid/0 Valid) */␊ |
206 | ␉uint8_t␉␉reserved1;␉␉/* Reserved */␊ |
207 | ␉uint8_t␉␉name_length;␉␉/* Name Length */␊ |
208 | ␉uint16_t␉name_hash;␉␉/* Name Hash */␊ |
209 | ␉uint8_t␉␉reserved2[2];␉␉/* Reserved */␊ |
210 | ␉uint64_t␉valid_length;␉␉/* Valid Data Length */␊ |
211 | ␉uint8_t␉␉reserved3[4];␉␉/* Reserved */␊ |
212 | ␉uint32_t␉first_cluster;␉␉/* Cluster address of 1st data block */␊ |
213 | ␉uint64_t␉data_length;␉␉/* Length of the data */␊ |
214 | };␊ |
215 | ␊ |
216 | struct direntry_name_extension␊ |
217 | {␊ |
218 | #define DIRENTRY_TYPE_NA_EX␉␉((uint8_t) 0xC1)␊ |
219 | ␉uint8_t␉␉type;␉␉␉/* EntryType: 0xC1 (or 0x41 if entry is empty) */␊ |
220 | ␉uint8_t␉␉reserved1;␉␉/* Reserved */␊ |
221 | #define LABEL_MAX_CHARS 15␊ |
222 | ␉uint16_t␉label[15];␉␉/* 15 characters of file name (UTF16LE) */␊ |
223 | };␊ |
224 | ␊ |
225 | #pragma mark -␊ |
226 | #pragma mark FATCache␊ |
227 | #pragma mark -␊ |
228 | ␊ |
229 | static␊ |
230 | int FATCacheInit(void)␊ |
231 | {␊ |
232 | ␉if (!gFATCacheBuffer)␊ |
233 | ␉{␊ |
234 | ␉␉gFATCacheBuffer = (uint8_t*) malloc(MAX_BLOCK_SIZE);␊ |
235 | ␉␉if (!gFATCacheBuffer)␊ |
236 | ␉␉{␊ |
237 | ␉␉␉return -1;␊ |
238 | ␉␉}␊ |
239 | ␉}␊ |
240 | ␉gCachedFATBlockAddress = INVALID_FAT_ADDRESS;␊ |
241 | ␉return 0;␊ |
242 | }␊ |
243 | ␊ |
244 | static inline␊ |
245 | void FATCacheInvalidate(void)␊ |
246 | {␊ |
247 | ␉gCachedFATBlockAddress = INVALID_FAT_ADDRESS;␊ |
248 | }␊ |
249 | ␊ |
250 | static inline␊ |
251 | uint16_t CacheBlockSize(void)␊ |
252 | {␊ |
253 | ␉return (uint16_t) (1 << gBPCBShift);␊ |
254 | }␊ |
255 | ␊ |
256 | static␊ |
257 | int getRange(uint32_t cluster, uint32_t maxContiguousClusters, uint32_t* pNextCluster, uint32_t* pNumContiguousClusters)␊ |
258 | {␊ |
259 | ␉uint32_t count, lcba;␊ |
260 | ␉uint16_t mask;␊ |
261 | ␉uint8_t shift;␊ |
262 | ␊ |
263 | ␉if (!pNextCluster || !pNumContiguousClusters)␊ |
264 | ␉{␊ |
265 | ␉␉return -1;␊ |
266 | ␉}␊ |
267 | ␉count = 0;␊ |
268 | ␉shift = gBPCBShift - 2;␊ |
269 | ␉mask = (uint16_t) ((1 << shift) - 1);␊ |
270 | ␉while (cluster >= CLUST_FIRST && cluster < CLUST_RSRVD && count < maxContiguousClusters)␊ |
271 | ␉{␊ |
272 | ␉␉++count;␊ |
273 | ␉␉lcba = cluster >> shift;␊ |
274 | ␉␉if (lcba != gCachedFATBlockAddress)␊ |
275 | ␉␉{␊ |
276 | ␉␉␉Seek(gCurrentIH, (((long long) gFATOffset) << gBPSShift) + (((long long) lcba) << gBPCBShift));␊ |
277 | ␉␉␉Read(gCurrentIH, (long) gFATCacheBuffer, CacheBlockSize());␊ |
278 | ␉␉␉gCachedFATBlockAddress = lcba;␊ |
279 | ␉␉}␊ |
280 | ␉␉lcba = cluster + 1;␊ |
281 | ␉␉cluster = OSSwapLittleToHostInt32(((uint32_t const*) gFATCacheBuffer)[cluster & mask]);␊ |
282 | ␉␉if (cluster != lcba)␊ |
283 | ␉␉␉break;␊ |
284 | ␉}␊ |
285 | ␉*pNextCluster = cluster;␊ |
286 | ␉*pNumContiguousClusters = count;␊ |
287 | ␉return 0;␊ |
288 | }␊ |
289 | ␊ |
290 | #pragma mark -␊ |
291 | #pragma mark Directory Iterator␊ |
292 | #pragma mark -␊ |
293 | ␊ |
294 | static␊ |
295 | void InitIteratorFromRoot(struct exfat_dir_iterator* pIter)␊ |
296 | {␊ |
297 | ␉pIter->lsa = 0;␊ |
298 | ␉pIter->lsa_end = 0;␊ |
299 | ␉pIter->cluster = gRDCl;␊ |
300 | ␉pIter->residue = 0;␊ |
301 | ␉pIter->entry_offset = CacheBlockSize();␊ |
302 | }␊ |
303 | ␊ |
304 | static inline␊ |
305 | uint64_t RoundUp(uint64_t val, uint8_t shift)␊ |
306 | {␊ |
307 | ␉return (val + (1 << shift) - 1) >> shift;␉␉/* == RoundUpToInt(val/(2^shift)) */␊ |
308 | }␊ |
309 | ␊ |
310 | static inline␊ |
311 | uint16_t Residue(uint64_t val, uint8_t shift)␊ |
312 | {␊ |
313 | ␉return (-(int16_t) val) & (int16_t) ((1 << shift) - 1);␉/* == (-val) mod (2^shift) */␊ |
314 | }␊ |
315 | ␊ |
316 | static inline␊ |
317 | uint64_t ClusterToLSA(uint32_t cluster)␊ |
318 | {␊ |
319 | ␉return (((uint64_t) (cluster - CLUST_FIRST)) << gSPCShift) + gCLOffset;␊ |
320 | }␊ |
321 | ␊ |
322 | static␊ |
323 | void InitIteratorFromInode(struct exfat_dir_iterator* pIter, struct exfat_inode const* pInode)␊ |
324 | {␊ |
325 | ␉if (pInode->stream_extension_flags & SEFLAG_ALLOCATION_POSSIBLE)␊ |
326 | ␉{␊ |
327 | ␉␉uint64_t sector_length = RoundUp(pInode->valid_length, gBPSShift);␊ |
328 | ␉␉if (pInode->stream_extension_flags & SEFLAG_INVALID_FAT_CHAIN)␊ |
329 | ␉␉{␊ |
330 | ␉␉␉pIter->lsa = ClusterToLSA(pInode->first_cluster);␊ |
331 | ␉␉␉pIter->lsa_end = pIter->lsa + sector_length;␊ |
332 | ␉␉␉pIter->cluster = CLUST_RSRVD;␊ |
333 | ␉␉}␊ |
334 | ␉␉else␊ |
335 | ␉␉{␊ |
336 | ␉␉␉pIter->lsa = 0;␊ |
337 | ␉␉␉pIter->lsa_end = 0;␊ |
338 | ␉␉␉pIter->cluster = pInode->first_cluster;␊ |
339 | ␉␉␉pIter->residue = Residue(sector_length, gSPCShift);␊ |
340 | ␉␉}␊ |
341 | ␉}␊ |
342 | ␉else␊ |
343 | ␉{␊ |
344 | ␉␉pIter->lsa = 0;␊ |
345 | ␉␉pIter->lsa_end = 0;␊ |
346 | ␉␉pIter->cluster = CLUST_RSRVD;␊ |
347 | ␉}␊ |
348 | ␉pIter->entry_offset = CacheBlockSize();␊ |
349 | }␊ |
350 | ␊ |
351 | static␊ |
352 | uint8_t const* nextDirEntry(struct exfat_dir_iterator* pIter)␊ |
353 | {␊ |
354 | ␉uint8_t const* ret;␊ |
355 | ␉uint16_t toRead;␊ |
356 | ␊ |
357 | ␉if (pIter->entry_offset >= CacheBlockSize())␊ |
358 | ␉{␊ |
359 | ␉␉if (pIter->lsa == pIter->lsa_end) {␊ |
360 | ␉␉␉uint32_t next_cluster, contig;␊ |
361 | ␊ |
362 | ␉␉␉getRange(pIter->cluster, CLUST_RSRVD, &next_cluster, &contig);␊ |
363 | ␉␉␉if (!contig)␊ |
364 | ␉␉␉{␊ |
365 | ␉␉␉␉return NULL;␊ |
366 | ␉␉␉}␊ |
367 | ␉␉␉pIter->lsa = ClusterToLSA(pIter->cluster);␊ |
368 | ␉␉␉pIter->lsa_end = pIter->lsa + (((uint64_t) contig) << gSPCShift);␊ |
369 | ␉␉␉if (next_cluster >= CLUST_RSRVD)␊ |
370 | ␉␉␉{␊ |
371 | ␉␉␉␉pIter->lsa_end -= pIter->residue;␊ |
372 | ␉␉␉}␊ |
373 | ␉␉␉pIter->cluster = next_cluster;␊ |
374 | ␉␉}␊ |
375 | ␉␉toRead = (uint16_t) (1 << (gBPCBShift - gBPSShift));␊ |
376 | ␉␉if (pIter->lsa + toRead > pIter->lsa_end)␊ |
377 | ␉␉␉toRead = (uint16_t) (pIter->lsa_end - pIter->lsa);␊ |
378 | ␉␉Seek(gCurrentIH, (long long) (pIter->lsa << gBPSShift));␊ |
379 | ␉␉Read(gCurrentIH, (long) pIter->buffer, ((long) toRead) << gBPSShift);␊ |
380 | ␉␉pIter->lsa += toRead;␊ |
381 | ␉␉pIter->entry_offset = 0;␊ |
382 | ␉}␊ |
383 | ␉ret = pIter->buffer + pIter->entry_offset;␊ |
384 | ␉pIter->entry_offset += sizeof(struct direntry_file);␊ |
385 | ␉return ret;␊ |
386 | }␊ |
387 | ␊ |
388 | #pragma mark -␊ |
389 | #pragma mark Path Search␊ |
390 | #pragma mark -␊ |
391 | ␊ |
392 | static inline␊ |
393 | int32_t ToUpper(uint16_t ch)␊ |
394 | {␊ |
395 | ␉if (gUPCase)␊ |
396 | ␉{␊ |
397 | ␉␉return gUPCase[ch];␊ |
398 | ␉}␊ |
399 | ␉if (ch >= 128)␊ |
400 | ␉{␊ |
401 | ␉␉return -1;␊ |
402 | ␉}␊ |
403 | ␉if ((uint8_t) ch >= 'a' && (uint8_t) ch <= 'z')␊ |
404 | ␉{␊ |
405 | ␉␉ch &= ~0x20;␊ |
406 | ␉}␊ |
407 | ␉return ch;␊ |
408 | }␊ |
409 | ␊ |
410 | static␊ |
411 | int32_t NameHash(uint16_t const* component_utf16le, uint16_t numChars)␊ |
412 | {␊ |
413 | ␉int32_t ch;␊ |
414 | ␉uint16_t hash = 0;␊ |
415 | ␊ |
416 | ␉for (; numChars; ++component_utf16le, --numChars)␊ |
417 | ␉{␊ |
418 | ␉␉ch = ToUpper(OSSwapLittleToHostInt16(*component_utf16le));␊ |
419 | ␉␉if (ch < 0)␊ |
420 | ␉␉{␊ |
421 | ␉␉␉return ch;␊ |
422 | ␉␉}␊ |
423 | ␉␉hash = (hash << 15) | (hash >> 1) + (uint8_t) ch;␊ |
424 | ␉␉hash = (hash << 15) | (hash >> 1) + (uint8_t) (ch >> 8);␊ |
425 | ␉}␊ |
426 | ␉return hash;␊ |
427 | }␊ |
428 | ␊ |
429 | static␊ |
430 | int ComponentToInode(struct exfat_dir_iterator* iterator, uint16_t const* component_utf16le, uint16_t numChars, struct exfat_inode* out_file)␊ |
431 | {␊ |
432 | ␉union {␊ |
433 | ␉␉uint8_t const* nde;␊ |
434 | ␉␉struct direntry_file const* fe;␊ |
435 | ␉␉struct direntry_stream_extension const* fse;␊ |
436 | ␉␉struct direntry_name_extension const* ne;␊ |
437 | ␉} u;␊ |
438 | ␉int32_t computed_hash;␊ |
439 | ␉uint8_t count2, name_length;␊ |
440 | ␊ |
441 | ␉computed_hash = NameHash(component_utf16le, numChars);␊ |
442 | ␉while ((u.nde = nextDirEntry(iterator)))␊ |
443 | ␉{␊ |
444 | ␉␉if (!*u.nde)␊ |
445 | ␉␉{␊ |
446 | ␉␉␉break;␊ |
447 | ␉␉}␊ |
448 | ␉redo:␊ |
449 | ␉␉if (*u.nde != DIRENTRY_TYPE_FILE)␊ |
450 | ␉␉␉continue;␊ |
451 | ␉␉count2 = u.fe->count2;␊ |
452 | ␉␉if (count2 < 2)␊ |
453 | ␉␉{␊ |
454 | ␉␉␉continue;␊ |
455 | ␉␉}␊ |
456 | ␉␉out_file->attributes = (uint8_t) OSSwapLittleToHostInt16(u.fe->attributes);␊ |
457 | ␉␉u.nde = nextDirEntry(iterator);␊ |
458 | ␉␉if (!u.nde || !*u.nde)␊ |
459 | ␉␉␉break;␊ |
460 | ␉␉if (*u.nde != DIRENTRY_TYPE_ST_EX)␊ |
461 | ␉␉␉goto redo;␊ |
462 | ␉␉out_file->stream_extension_flags = u.fse->flags;␊ |
463 | ␉␉name_length = u.fse->name_length;␊ |
464 | ␉␉if (name_length != numChars)␊ |
465 | ␉␉␉continue;␊ |
466 | ␉␉if (computed_hash >= 0 && computed_hash != OSSwapLittleToHostInt16(u.fse->name_hash))␊ |
467 | ␉␉␉continue;␊ |
468 | ␉␉out_file->valid_length = OSSwapLittleToHostInt64(u.fse->valid_length);␊ |
469 | ␉␉out_file->first_cluster = OSSwapLittleToHostInt32(u.fse->first_cluster);␊ |
470 | ␉␉for (--count2; count2 && name_length; --count2) {␊ |
471 | ␉␉␉int32_t ch1, ch2;␊ |
472 | ␉␉␉uint16_t const* q;␊ |
473 | ␉␉␉uint8_t t;␊ |
474 | ␊ |
475 | ␉␉␉u.nde = nextDirEntry(iterator);␊ |
476 | ␉␉␉if (!u.nde || !*u.nde)␊ |
477 | ␉␉␉␉goto outta_bad;␊ |
478 | ␉␉␉if (*u.nde != DIRENTRY_TYPE_NA_EX)␊ |
479 | ␉␉␉␉goto redo;␊ |
480 | ␉␉␉t = name_length > LABEL_MAX_CHARS ? LABEL_MAX_CHARS : name_length;␊ |
481 | ␉␉␉q = &u.ne->label[0];␊ |
482 | ␉␉␉for (; t; ++q, ++component_utf16le, --t, --name_length) {␊ |
483 | ␉␉␉␉ch1 = ToUpper(OSSwapLittleToHostInt16(*component_utf16le));␊ |
484 | ␉␉␉␉ch2 = ToUpper(OSSwapLittleToHostInt16(*q));␊ |
485 | ␉␉␉␉if (ch1 != ch2)␊ |
486 | ␉␉␉␉␉goto abort_comparison;␊ |
487 | ␉␉␉}␊ |
488 | ␉␉}␊ |
489 | ␉␉return 0;␊ |
490 | ␉abort_comparison:;␊ |
491 | ␉}␊ |
492 | outta_bad:␊ |
493 | ␉return -1;␊ |
494 | }␊ |
495 | ␊ |
496 | static␊ |
497 | int ExtractDirEntry(struct exfat_dir_iterator* iterator, char** name, long* flags, u_int32_t* time, long* infoValid)␊ |
498 | {␊ |
499 | ␉union {␊ |
500 | ␉␉uint8_t const* nde;␊ |
501 | ␉␉struct direntry_file const* fe;␊ |
502 | ␉␉struct direntry_stream_extension const* fse;␊ |
503 | ␉␉struct direntry_name_extension const* ne;␊ |
504 | ␉} u;␊ |
505 | ␉uint8_t count2, name_length, t;␊ |
506 | ␉uint16_t component_full[COMPONENT_MAX_CHARS], *cp_ptr;␊ |
507 | ␊ |
508 | ␉while ((u.nde = nextDirEntry(iterator))) {␊ |
509 | ␉␉if (!*u.nde)␊ |
510 | ␉␉␉break;␊ |
511 | ␉redo:␊ |
512 | ␉␉if (*u.nde != DIRENTRY_TYPE_FILE)␊ |
513 | ␉␉␉continue;␊ |
514 | ␉␉count2 = u.fe->count2;␊ |
515 | ␉␉if (count2 < 2)␊ |
516 | ␉␉␉continue;␊ |
517 | ␉␉if (flags)␊ |
518 | ␉␉␉*flags = (OSSwapLittleToHostInt16(u.fe->attributes) & ATTR_DIRECTORY) ? kFileTypeDirectory : kFileTypeFlat;␊ |
519 | ␉␉if (time)␊ |
520 | ␉␉␉*time = OSSwapLittleToHostInt32(u.fe->mod_time);␊ |
521 | ␉␉if (!name)␊ |
522 | ␉␉␉goto info_valid;␊ |
523 | ␉␉u.nde = nextDirEntry(iterator);␊ |
524 | ␉␉if (!u.nde || !*u.nde)␊ |
525 | ␉␉␉break;␊ |
526 | ␉␉if (*u.nde != DIRENTRY_TYPE_ST_EX)␊ |
527 | ␉␉␉goto redo;␊ |
528 | ␉␉name_length = u.fse->name_length;␊ |
529 | ␉␉cp_ptr = &component_full[0];␊ |
530 | ␉␉for (--count2; count2 && name_length; --count2) {␊ |
531 | ␉␉␉u.nde = nextDirEntry(iterator);␊ |
532 | ␉␉␉if (!u.nde || !*u.nde)␊ |
533 | ␉␉␉␉goto outta_bad;␊ |
534 | ␉␉␉if (*u.nde != DIRENTRY_TYPE_NA_EX)␊ |
535 | ␉␉␉␉goto redo;␊ |
536 | ␉␉␉t = name_length > LABEL_MAX_CHARS ? LABEL_MAX_CHARS : name_length;␊ |
537 | ␉␉␉memcpy(cp_ptr, &u.ne->label[0], t * sizeof(uint16_t));␊ |
538 | ␉␉␉cp_ptr += t;␊ |
539 | ␉␉␉name_length -= t;␊ |
540 | ␉␉}␊ |
541 | ␉␉/*␊ |
542 | ␉␉ * Note: for ASCII can allocate exactly name_length + 1,␊ |
543 | ␉␉ * but in case of multibyte characters, allow more space.␊ |
544 | ␉␉ */␊ |
545 | ␉␉*name = (char*) malloc(COMPONENT_MAX_CHARS + 1);␊ |
546 | ␉␉if (*name)␊ |
547 | ␉␉␉utf_encodestr(&component_full[0], cp_ptr - &component_full[0], (uint8_t*) *name, COMPONENT_MAX_CHARS, OSLittleEndian);␊ |
548 | ␉info_valid:␊ |
549 | ␉␉if (infoValid)␊ |
550 | ␉␉␉*infoValid = 1;␊ |
551 | ␉␉return 0;␊ |
552 | ␉}␊ |
553 | outta_bad:␊ |
554 | ␉return -1;␊ |
555 | }␊ |
556 | ␊ |
557 | static␊ |
558 | int PathToInode(char const* path, struct exfat_inode* out_file, uint8_t* buffer /* size CacheBlockSize() bytes */)␊ |
559 | {␊ |
560 | ␉struct exfat_dir_iterator iterator;␊ |
561 | ␉uint8_t *ptr, *slash, ch;␊ |
562 | ␉uint16_t path_utf16le[COMPONENT_MAX_CHARS];␊ |
563 | ␉uint16_t numChars;␊ |
564 | ␉char have_prev_inode;␊ |
565 | ␊ |
566 | ␉InitIteratorFromRoot(&iterator);␊ |
567 | ␉iterator.buffer = buffer;␊ |
568 | ␉ptr = (uint8_t*) path;␉/* Note: const_cast */␊ |
569 | ␉have_prev_inode = 0;␊ |
570 | ␉do {␊ |
571 | ␉␉do {␊ |
572 | ␉␉␉for (slash = ptr; *slash && *slash != '/'; ++slash);␊ |
573 | ␉␉␉ch = *slash;␊ |
574 | ␉␉␉if (slash == ptr) {␊ |
575 | ␉␉␉␉if (!ch) {␊ |
576 | ␉␉␉␉␉if (!have_prev_inode) {␊ |
577 | ␉␉␉␉␉␉/*␊ |
578 | ␉␉␉␉␉␉ * Fill in pseudo-inode for Root Directory␊ |
579 | ␉␉␉␉␉␉ */␊ |
580 | ␉␉␉␉␉␉out_file->valid_length = 0;␉/* Unknown */␊ |
581 | ␉␉␉␉␉␉out_file->first_cluster = gRDCl;␊ |
582 | ␉␉␉␉␉␉out_file->attributes = ATTR_DIRECTORY;␊ |
583 | ␉␉␉␉␉␉out_file->stream_extension_flags = SEFLAG_ALLOCATION_POSSIBLE | SEFLAG_PSEUDO_ROOTDIR;␊ |
584 | ␉␉␉␉␉}␊ |
585 | ␉␉␉␉␉return 0;␊ |
586 | ␉␉␉␉}␊ |
587 | ␉␉␉␉++ptr;␊ |
588 | ␉␉␉␉continue;␊ |
589 | ␉␉␉}␊ |
590 | ␉␉␉break;␊ |
591 | ␉␉} while (1);␊ |
592 | ␉␉*slash = 0;␊ |
593 | ␉␉utf_decodestr(ptr, &path_utf16le[0], &numChars, sizeof path_utf16le, OSLittleEndian);␊ |
594 | ␉␉numChars = OSSwapLittleToHostInt16(numChars);␊ |
595 | ␉␉*slash = ch;␊ |
596 | ␉␉ptr = slash + 1;␊ |
597 | ␉␉if (have_prev_inode)␊ |
598 | ␉␉␉InitIteratorFromInode(&iterator, out_file);␊ |
599 | ␉␉if (ComponentToInode(&iterator, &path_utf16le[0], numChars, out_file) < 0)␊ |
600 | ␉␉␉break;␊ |
601 | ␉␉if (!ch)␉/* was last component - done */␊ |
602 | ␉␉␉return 0;␊ |
603 | ␉␉if (!(out_file->attributes & ATTR_DIRECTORY))␉/* not a directory and not last component - error */␊ |
604 | ␉␉␉return -1;␊ |
605 | ␉␉have_prev_inode = 1;␊ |
606 | ␉} while (1);␊ |
607 | ␉return -1;␊ |
608 | }␊ |
609 | ␊ |
610 | #pragma mark -␊ |
611 | #pragma mark exFAT Implementation␊ |
612 | #pragma mark -␊ |
613 | ␊ |
614 | void␊ |
615 | EXFATFree(CICell ih)␊ |
616 | {␊ |
617 | ␉if (gCurrentIH == ih) {␊ |
618 | ␉␉gCurrentIH = NULL;␊ |
619 | ␉␉FATCacheInvalidate();␊ |
620 | ␉}␊ |
621 | ␉free(ih);␊ |
622 | }␊ |
623 | ␊ |
624 | long␊ |
625 | EXFATInitPartition(CICell ih)␊ |
626 | {␊ |
627 | ␉uint8_t *buffer, bpss, spcs;␊ |
628 | ␉struct exfatbootfile const* boot;␊ |
629 | ␊ |
630 | ␉if (!ih)␊ |
631 | ␉␉return -1;␊ |
632 | ␉if (gCurrentIH == ih)␊ |
633 | ␉␉return FATCacheInit();␊ |
634 | ␊ |
635 | ␉buffer = (uint8_t*) malloc(BPS);␊ |
636 | ␉if (!buffer)␊ |
637 | ␉␉return -1;␊ |
638 | ␊ |
639 | ␉/*␊ |
640 | ␉ * Read the boot sector of the filesystem, and then check the␊ |
641 | ␉ * boot signature. If not a boot sector then error out.␊ |
642 | ␉ */␊ |
643 | ␊ |
644 | ␉Seek(ih, 0);␊ |
645 | ␉Read(ih, (long) buffer, BPS);␊ |
646 | ␊ |
647 | ␉boot = (struct exfatbootfile const*) buffer;␊ |
648 | ␊ |
649 | ␉/* Looking for EXFAT signature. */␊ |
650 | ␉if (memcmp((char const*) &boot->bf_sysid[0], EXFAT_BBID, EXFAT_BBIDLEN)) {␊ |
651 | ␉␉free(buffer);␊ |
652 | ␉␉return -1;␊ |
653 | ␉}␊ |
654 | ␊ |
655 | ␉/*␊ |
656 | ␉ * Make sure the bytes per sector and sectors per cluster are within reasonable ranges.␊ |
657 | ␉ */␊ |
658 | ␉bpss = boot->bf_bpss;␊ |
659 | ␉if (bpss < MIN_BLOCK_SIZE_SHIFT || bpss > MAX_BLOCK_SIZE_SHIFT) {␊ |
660 | ␉␉free(buffer);␊ |
661 | ␉␉return -1;␊ |
662 | ␉}␊ |
663 | ␊ |
664 | ␉spcs = boot->bf_spcs;␊ |
665 | ␉if (spcs > (MAX_CLUSTER_SIZE_SHIFT - bpss)) {␊ |
666 | ␉␉free(buffer);␊ |
667 | ␉␉return -1;␊ |
668 | ␉}␊ |
669 | ␊ |
670 | ␉if (FATCacheInit() < 0) {␊ |
671 | ␉␉free(buffer);␊ |
672 | ␉␉return -1;␊ |
673 | ␉}␊ |
674 | ␊ |
675 | ␉gBPSShift = bpss;␊ |
676 | ␉gSPCShift = spcs;␊ |
677 | ␉gFATOffset = OSSwapLittleToHostInt32(boot->bf_fatoff);␊ |
678 | ␉gFATLength = OSSwapLittleToHostInt32(boot->bf_fatlen);␊ |
679 | ␉gCLOffset = OSSwapLittleToHostInt32(boot->bf_cloff);␊ |
680 | ␉gCLCount = OSSwapLittleToHostInt32(boot->bf_clcnt);␊ |
681 | ␉gRDCl = OSSwapLittleToHostInt32(boot->bf_rdircl);␊ |
682 | ␉gBPCBShift = bpss + spcs;␊ |
683 | ␉if (gBPCBShift > MAX_BLOCK_SIZE_SHIFT)␊ |
684 | ␉␉gBPCBShift = MAX_BLOCK_SIZE_SHIFT;␊ |
685 | ␊ |
686 | ␉gCurrentIH = ih;␊ |
687 | ␊ |
688 | ␉free(buffer);␊ |
689 | ␉return 0;␊ |
690 | }␊ |
691 | ␊ |
692 | long␊ |
693 | EXFATGetDirEntry(CICell ih, char * dirPath, long long * dirIndex,␊ |
694 | ␉␉␉␉ char ** name, long * flags, u_int32_t * time,␊ |
695 | ␉␉␉␉ FinderInfo * finderInfo, long * infoValid)␊ |
696 | {␊ |
697 | ␉struct exfat_dir_iterator* iterator;␊ |
698 | ␊ |
699 | ␉if (!dirPath || !dirIndex)␊ |
700 | ␉␉return -1;␊ |
701 | ␊ |
702 | ␉if (EXFATInitPartition(ih) < 0)␊ |
703 | ␉␉return -1;␊ |
704 | ␊ |
705 | ␉if (*dirPath == '/')␊ |
706 | ␉␉++dirPath;␊ |
707 | ␊ |
708 | ␉iterator = (struct exfat_dir_iterator*) (long) *dirIndex;␊ |
709 | ␉if (!iterator) {␊ |
710 | ␉␉struct exfat_inode inode;␊ |
711 | ␉␉uint8_t* buffer;␊ |
712 | ␊ |
713 | ␉␉buffer = (uint8_t*) malloc(CacheBlockSize() + sizeof *iterator);␊ |
714 | ␉␉if (!buffer)␊ |
715 | ␉␉␉return -1;␊ |
716 | ␉␉iterator = (struct exfat_dir_iterator*) (buffer + CacheBlockSize());␊ |
717 | ␉␉if (PathToInode(dirPath, &inode, buffer) < 0 ||␊ |
718 | ␉␉␉!(inode.attributes & ATTR_DIRECTORY)) {␊ |
719 | ␉␉␉free(buffer);␊ |
720 | ␉␉␉return -1;␊ |
721 | ␉␉}␊ |
722 | ␉␉if (inode.stream_extension_flags & SEFLAG_PSEUDO_ROOTDIR)␊ |
723 | ␉␉␉InitIteratorFromRoot(iterator);␊ |
724 | ␉␉else␊ |
725 | ␉␉␉InitIteratorFromInode(iterator, &inode);␊ |
726 | ␉␉iterator->buffer = buffer;␊ |
727 | ␉␉*dirIndex = (long long) (long) iterator;␊ |
728 | ␉}␊ |
729 | ␉if (ExtractDirEntry(iterator, name, flags, time, infoValid) < 0) {␊ |
730 | ␉␉free(iterator->buffer);␊ |
731 | ␉␉*dirIndex = 0;␊ |
732 | ␉␉return -1;␊ |
733 | ␉}␊ |
734 | ␉return 0;␊ |
735 | }␊ |
736 | ␊ |
737 | long␊ |
738 | EXFATReadFile(CICell ih, char * filePath, void *base, uint64_t offset, uint64_t length)␊ |
739 | {␊ |
740 | ␉uint64_t size, toRead, leftToRead;␊ |
741 | ␉struct exfat_inode inode;␊ |
742 | ␉uint8_t* buffer;␊ |
743 | ␉uint32_t cluster;␊ |
744 | ␊ |
745 | ␉if (EXFATInitPartition(ih) < 0)␊ |
746 | ␉␉return -1;␊ |
747 | ␊ |
748 | ␉if (*filePath == '/')␊ |
749 | ␉␉++filePath;␊ |
750 | ␊ |
751 | ␉buffer = (uint8_t*) malloc(CacheBlockSize());␊ |
752 | ␉if (!buffer)␊ |
753 | ␉␉return -1;␊ |
754 | ␊ |
755 | ␉if (PathToInode(filePath, &inode, buffer) < 0 ||␊ |
756 | ␉␉(inode.attributes & ATTR_DIRECTORY) != 0) {␊ |
757 | ␉␉free(buffer);␊ |
758 | ␉␉return -1;␊ |
759 | ␉}␊ |
760 | ␉free(buffer);␊ |
761 | ␉if (!(inode.stream_extension_flags & SEFLAG_ALLOCATION_POSSIBLE))␊ |
762 | ␉␉return (!offset && !length) ? 0 : -1;␊ |
763 | ␉cluster = inode.first_cluster;␊ |
764 | ␉size = inode.valid_length;␊ |
765 | ␉if (size == offset && !length)␊ |
766 | ␉␉return 0;␊ |
767 | ␉if (size <= offset)␊ |
768 | ␉␉return -1;␊ |
769 | ␉toRead = size - offset;␊ |
770 | ␉if (length && length < toRead)␊ |
771 | ␉␉toRead = length;␊ |
772 | ␉if (inode.stream_extension_flags & SEFLAG_INVALID_FAT_CHAIN) {␊ |
773 | ␉␉Seek(ih, (long long) ((ClusterToLSA(cluster) << gBPSShift) + offset));␊ |
774 | ␉␉Read(ih, (long) base, (long) toRead);␊ |
775 | ␉␉return (long) toRead;␊ |
776 | ␉}␊ |
777 | ␉leftToRead = toRead;␊ |
778 | ␉do {␊ |
779 | ␉␉uint64_t chunk, canRead;␊ |
780 | ␉␉uint32_t next_cluster, contig;␊ |
781 | ␊ |
782 | ␉␉getRange(cluster, CLUST_RSRVD, &next_cluster, &contig);␊ |
783 | ␉␉if (!contig)␊ |
784 | ␉␉␉break;␊ |
785 | ␉␉chunk = ((uint64_t) contig) << (gBPSShift + gSPCShift);␊ |
786 | ␉␉if (offset >= chunk) {␊ |
787 | ␉␉␉offset -= chunk;␊ |
788 | ␉␉␉cluster = next_cluster;␊ |
789 | ␉␉␉continue;␊ |
790 | ␉␉}␊ |
791 | ␉␉canRead = chunk - offset;␊ |
792 | ␉␉if (canRead > leftToRead)␊ |
793 | ␉␉␉canRead = leftToRead;␊ |
794 | ␉␉Seek(ih, (long long) ((ClusterToLSA(cluster) << gBPSShift) + offset));␊ |
795 | ␉␉Read(ih, (long) base, (long) canRead);␊ |
796 | ␉␉base = ((uint8_t*) base) + canRead;␊ |
797 | ␉␉cluster = next_cluster;␊ |
798 | ␉␉offset = 0;␊ |
799 | ␉␉leftToRead -= canRead;␊ |
800 | ␉} while (leftToRead);␊ |
801 | ␉return (long) (toRead - leftToRead);␊ |
802 | }␊ |
803 | ␊ |
804 | long␊ |
805 | EXFATGetFileBlock(CICell ih, char *filePath, unsigned long long *firstBlock)␊ |
806 | {␊ |
807 | ␉uint8_t* buffer;␊ |
808 | ␉struct exfat_inode inode;␊ |
809 | ␉uint32_t cluster;␊ |
810 | ␊ |
811 | ␉if (EXFATInitPartition(ih) < 0)␊ |
812 | ␉␉return -1;␊ |
813 | ␊ |
814 | ␉if (*filePath == '/')␊ |
815 | ␉␉++filePath;␊ |
816 | ␊ |
817 | ␉buffer = (uint8_t*) malloc(CacheBlockSize());␊ |
818 | ␉if (!buffer)␊ |
819 | ␉␉return -1;␊ |
820 | ␊ |
821 | ␉if (PathToInode(filePath, &inode, buffer) < 0 ||␊ |
822 | ␉␉(inode.attributes & ATTR_DIRECTORY) != 0 ||␊ |
823 | ␉␉!(inode.stream_extension_flags & SEFLAG_ALLOCATION_POSSIBLE)) {␊ |
824 | ␉␉free(buffer);␊ |
825 | ␉␉return -1;␊ |
826 | ␉}␊ |
827 | ␉free(buffer);␊ |
828 | ␉cluster = inode.first_cluster;␊ |
829 | ␉if (cluster < CLUST_FIRST || cluster >= CLUST_RSRVD)␊ |
830 | ␉␉return -1;␊ |
831 | ␉*firstBlock = ClusterToLSA(cluster);␊ |
832 | ␉return 0;␊ |
833 | }␊ |
834 | ␊ |
835 | long␊ |
836 | EXFATLoadFile(CICell ih, char * filePath)␊ |
837 | {␊ |
838 | ␉return EXFATReadFile(ih, filePath, (void *)gFSLoadAddress, 0, 0);␊ |
839 | }␊ |
840 | ␊ |
841 | /**␊ |
842 | * Reads volume label into str.␊ |
843 | * Reads boot sector, performs some checking, loads root dir␊ |
844 | * and parses volume label.␊ |
845 | */␊ |
846 | void␊ |
847 | EXFATGetDescription(CICell ih, char *str, long strMaxLen)␊ |
848 | {␊ |
849 | struct exfatbootfile *boot;␊ |
850 | uint8_t bpss, spcs;␊ |
851 | long long rdirOffset = 0;␊ |
852 | char *buf = NULL;␊ |
853 | struct direntry_label *dire = NULL;␊ |
854 | int loopControl = 0;␊ |
855 | ␊ |
856 | DBG("EXFAT: start %x:%x\n", ih->biosdev, ih->part_no);␊ |
857 | ␊ |
858 | buf = (char *)malloc(MAX_BLOCK_SIZE);␊ |
859 | if (buf == 0)␊ |
860 | {␊ |
861 | goto error;␊ |
862 | }␊ |
863 | ␊ |
864 | /*␊ |
865 | * Read the boot sector, check signatures, and do some minimal␊ |
866 | * sanity checking. NOTE: the size of the read below is intended␊ |
867 | * to be a multiple of all supported block sizes, so we don't␊ |
868 | * have to determine or change the device's block size.␊ |
869 | */␊ |
870 | Seek(ih, 0);␊ |
871 | Read(ih, (long)buf, MAX_BLOCK_SIZE);␊ |
872 | ␊ |
873 | // take our boot structure␊ |
874 | boot = (struct exfatbootfile *) buf;␊ |
875 | ␊ |
876 | /*␊ |
877 | * The first three bytes are an Intel x86 jump instruction. I assume it␊ |
878 | * can be the same forms as DOS FAT:␊ |
879 | * 0xE9 0x?? 0x??␊ |
880 | * 0xEC 0x?? 0x90␊ |
881 | * where 0x?? means any byte value is OK.␊ |
882 | */␊ |
883 | if (boot->reserved1[0] != 0xE9␊ |
884 | && (boot->reserved1[0] != 0xEB || boot->reserved1[2] != 0x90))␊ |
885 | {␊ |
886 | goto error;␊ |
887 | }␊ |
888 | ␊ |
889 | // Check the "EXFAT " signature.␊ |
890 | if (memcmp((const char *)boot->bf_sysid, EXFAT_BBID, EXFAT_BBIDLEN) != 0)␊ |
891 | {␊ |
892 | goto error;␊ |
893 | }␊ |
894 | ␊ |
895 | /*␊ |
896 | * Make sure the bytes per sector and sectors per cluster are␊ |
897 | * powers of two, and within reasonable ranges.␊ |
898 | */␊ |
899 | bpss = boot->bf_bpss;␉/* Just one byte; no swapping needed */␊ |
900 | DBG("EXFAT: bpss=%d, bytesPerSector=%d\n", bpss, (1 << bpss));␊ |
901 | if (bpss < MIN_BLOCK_SIZE_SHIFT || bpss > MAX_BLOCK_SIZE_SHIFT)␊ |
902 | {␊ |
903 | DBG("EXFAT: invalid bytes per sector shift(%d)\n", bpss);␊ |
904 | goto error;␊ |
905 | }␊ |
906 | ␊ |
907 | spcs = boot->bf_spcs;␉/* Just one byte; no swapping needed */␊ |
908 | DBG("EXFAT: spcs=%d, sectorsPerCluster=%d\n", spcs, (1 << spcs));␊ |
909 | if (spcs > (MAX_CLUSTER_SIZE_SHIFT - bpss))␊ |
910 | {␊ |
911 | DBG("EXFAT: invalid sectors per cluster shift (%d)\n", spcs);␊ |
912 | goto error;␊ |
913 | }␊ |
914 | ␊ |
915 | // calculate root dir cluster offset␊ |
916 | rdirOffset = OSSwapLittleToHostInt32(boot->bf_cloff) + ((long long) (OSSwapLittleToHostInt32(boot->bf_rdircl) - 2) << spcs);␊ |
917 | DBG("EXFAT: rdirOffset=%d\n", (int) rdirOffset);␊ |
918 | ␊ |
919 | // load MAX_BLOCK_SIZE bytes of root dir␊ |
920 | Seek(ih, rdirOffset << bpss);␊ |
921 | Read(ih, (long)buf, MAX_BLOCK_SIZE);␊ |
922 | DBG("buf 0 1 2 = %x %x %x\n", 0x00ff & buf[0], 0x00ff & buf[1], 0x00ff & buf[2]);␊ |
923 | ␊ |
924 | str[0] = '\0';␊ |
925 | ␊ |
926 | /*␊ |
927 | * Search for volume label dir entry (type 0x83), convert from unicode and put to str.␊ |
928 | * Set loopControl var to avoid searching outside of buf.␊ |
929 | */␊ |
930 | loopControl = MAX_BLOCK_SIZE / sizeof(struct direntry_label);␊ |
931 | dire = (struct direntry_label *)buf;␊ |
932 | while (loopControl && dire->type && dire->type != DIRENTRY_TYPE_LABEL)␊ |
933 | {␊ |
934 | dire++;␊ |
935 | loopControl--;␊ |
936 | }␊ |
937 | if (dire->type == DIRENTRY_TYPE_LABEL && dire->llen > 0 && dire->llen <= VOLUME_LABEL_MAX_CHARS)␊ |
938 | {␊ |
939 | utf_encodestr( dire->label, (int)dire->llen, (u_int8_t *)str, strMaxLen, OSLittleEndian );␊ |
940 | }␊ |
941 | DBG("EXFAT: label=%s\n", str);␊ |
942 | ␊ |
943 | free(buf);␊ |
944 | PAUSE();␊ |
945 | return;␊ |
946 | ␊ |
947 | error:␊ |
948 | ␉if (buf)␊ |
949 | ␉{␊ |
950 | ␉␉free(buf);␊ |
951 | ␉}␊ |
952 | ␊ |
953 | ␉DBG("EXFAT: error\n");␊ |
954 | ␉PAUSE();␊ |
955 | ␉return;␊ |
956 | }␊ |
957 | ␊ |
958 | /**␊ |
959 | * Sets UUID to uuidStr.␊ |
960 | * Reads the boot sector, does some checking, generates UUID␊ |
961 | * (like the one you get on Windows???)␊ |
962 | */␊ |
963 | long EXFATGetUUID(CICell ih, char *uuidStr)␊ |
964 | {␊ |
965 | ␉uint32_t volsn;␊ |
966 | ␉struct exfatbootfile *boot;␊ |
967 | ␉void *buf = malloc(MAX_BLOCK_SIZE);␊ |
968 | ␉if ( !buf )␊ |
969 | ␉␉return -1;␊ |
970 | ␊ |
971 | ␉/*␊ |
972 | ␉ * Read the boot sector, check signatures, and do some minimal␊ |
973 | ␉ * sanity checking.␉ NOTE: the size of the read below is intended␊ |
974 | ␉ * to be a multiple of all supported block sizes, so we don't␊ |
975 | ␉ * have to determine or change the device's block size.␊ |
976 | ␉ */␊ |
977 | ␉Seek(ih, 0);␊ |
978 | ␉Read(ih, (long)buf, MAX_BLOCK_SIZE);␊ |
979 | ␊ |
980 | ␉boot = (struct exfatbootfile *) buf;␊ |
981 | ␊ |
982 | ␉/*␊ |
983 | ␉ * Check the "EXFAT " signature.␊ |
984 | ␉ */␊ |
985 | ␉if (memcmp((const char *)boot->bf_sysid, EXFAT_BBID, EXFAT_BBIDLEN) != 0)␊ |
986 | ␉{␊ |
987 | ␉␉return -1;␊ |
988 | ␉}␊ |
989 | ␊ |
990 | ␉// Check for non-null volume serial number␊ |
991 | ␉volsn = OSSwapLittleToHostInt32(boot->bf_volsn);␊ |
992 | ␉if( !volsn )␊ |
993 | ␉{␊ |
994 | ␉␉return -1;␊ |
995 | ␉}␊ |
996 | ␊ |
997 | ␉// Use UUID like the one you get on Windows␊ |
998 | ␉sprintf(uuidStr, "%04X-%04X", (unsigned short)(volsn >> 16) & 0xFFFF,␊ |
999 | (unsigned short)volsn & 0xFFFF);␊ |
1000 | ␊ |
1001 | ␉DBG("EXFATGetUUID: %x:%x = %s\n", ih->biosdev, ih->part_no, uuidStr);␊ |
1002 | ␉return 0;␊ |
1003 | }␊ |
1004 | ␊ |
1005 | /**␊ |
1006 | * Returns true if given buffer is the boot rec of the EXFAT volume.␊ |
1007 | */␊ |
1008 | bool EXFATProbe(const void * buffer)␊ |
1009 | {␊ |
1010 | ␉bool result = false;␊ |
1011 | ␊ |
1012 | ␉// boot sector structure␊ |
1013 | ␉const struct exfatbootfile␉* boot = buffer;␊ |
1014 | ␊ |
1015 | ␉// Looking for EXFAT signature.␊ |
1016 | ␉if (memcmp((const char *)boot->bf_sysid, EXFAT_BBID, EXFAT_BBIDLEN) == 0)␊ |
1017 | ␉{␊ |
1018 | ␉␉result = true;␊ |
1019 | ␉}␊ |
1020 | ␊ |
1021 | ␉DBG("EXFATProbe: %d\n", result ? 1 : 0);␊ |
1022 | ␉return result;␊ |
1023 | }␊ |
1024 | |