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