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(int invalidate)␊ |
231 | {␊ |
232 | ␉if (!gFATCacheBuffer)␊ |
233 | ␉{␊ |
234 | ␉␉gFATCacheBuffer = (uint8_t*) malloc(MAX_BLOCK_SIZE);␊ |
235 | ␉␉if (!gFATCacheBuffer)␊ |
236 | ␉␉{␊ |
237 | ␉␉␉return -1;␊ |
238 | ␉␉}␊ |
239 | ␉␉invalidate = 1;␊ |
240 | ␉}␊ |
241 | ␉if (invalidate)␊ |
242 | ␉{␊ |
243 | ␉␉gCachedFATBlockAddress = INVALID_FAT_ADDRESS;␊ |
244 | ␉}␊ |
245 | ␉return 0;␊ |
246 | }␊ |
247 | ␊ |
248 | static inline␊ |
249 | void FATCacheInvalidate(void)␊ |
250 | {␊ |
251 | ␉gCachedFATBlockAddress = INVALID_FAT_ADDRESS;␊ |
252 | }␊ |
253 | ␊ |
254 | static inline␊ |
255 | uint16_t CacheBlockSize(void)␊ |
256 | {␊ |
257 | ␉return (uint16_t) (1 << gBPCBShift);␊ |
258 | }␊ |
259 | ␊ |
260 | static␊ |
261 | int getRange(uint32_t cluster, uint32_t maxContiguousClusters, uint32_t* pNextCluster, uint32_t* pNumContiguousClusters)␊ |
262 | {␊ |
263 | ␉uint32_t count, lcba;␊ |
264 | ␉uint16_t mask;␊ |
265 | ␉uint8_t shift;␊ |
266 | ␊ |
267 | ␉if (!pNextCluster || !pNumContiguousClusters)␊ |
268 | ␉{␊ |
269 | ␉␉return -1;␊ |
270 | ␉}␊ |
271 | ␉count = 0;␊ |
272 | ␉shift = gBPCBShift - 2;␊ |
273 | ␉mask = (uint16_t) ((1 << shift) - 1);␊ |
274 | ␉while (cluster >= CLUST_FIRST && cluster < CLUST_RSRVD && count < maxContiguousClusters)␊ |
275 | ␉{␊ |
276 | ␉␉++count;␊ |
277 | ␉␉lcba = cluster >> shift;␊ |
278 | ␉␉if (lcba != gCachedFATBlockAddress)␊ |
279 | ␉␉{␊ |
280 | ␉␉␉CacheRead(gCurrentIH,␊ |
281 | ␉␉␉␉␉ (char*) gFATCacheBuffer,␊ |
282 | ␉␉␉␉␉ (((long long) gFATOffset) << gBPSShift) + (((long long) lcba) << gBPCBShift),␊ |
283 | ␉␉␉␉␉ CacheBlockSize(),␊ |
284 | ␉␉␉␉␉ 1);␊ |
285 | ␉␉␉gCachedFATBlockAddress = lcba;␊ |
286 | ␉␉}␊ |
287 | ␉␉lcba = cluster + 1;␊ |
288 | ␉␉cluster = OSSwapLittleToHostInt32(((uint32_t const*) gFATCacheBuffer)[cluster & mask]);␊ |
289 | ␉␉if (cluster != lcba)␊ |
290 | ␉␉␉break;␊ |
291 | ␉}␊ |
292 | ␉*pNextCluster = cluster;␊ |
293 | ␉*pNumContiguousClusters = count;␊ |
294 | ␉return 0;␊ |
295 | }␊ |
296 | ␊ |
297 | #pragma mark -␊ |
298 | #pragma mark Directory Iterator␊ |
299 | #pragma mark -␊ |
300 | ␊ |
301 | static␊ |
302 | void InitIteratorFromRoot(struct exfat_dir_iterator* pIter)␊ |
303 | {␊ |
304 | ␉pIter->lsa = 0;␊ |
305 | ␉pIter->lsa_end = 0;␊ |
306 | ␉pIter->cluster = gRDCl;␊ |
307 | ␉pIter->residue = 0;␊ |
308 | ␉pIter->entry_offset = CacheBlockSize();␊ |
309 | }␊ |
310 | ␊ |
311 | static inline␊ |
312 | uint64_t RoundUp(uint64_t val, uint8_t shift)␊ |
313 | {␊ |
314 | ␉return (val + (1 << shift) - 1) >> shift;␉␉/* == RoundUpToInt(val/(2^shift)) */␊ |
315 | }␊ |
316 | ␊ |
317 | static inline␊ |
318 | uint16_t Residue(uint64_t val, uint8_t shift)␊ |
319 | {␊ |
320 | ␉return (-(int16_t) val) & (int16_t) ((1 << shift) - 1);␉/* == (-val) mod (2^shift) */␊ |
321 | }␊ |
322 | ␊ |
323 | static inline␊ |
324 | uint64_t ClusterToLSA(uint32_t cluster)␊ |
325 | {␊ |
326 | ␉return (((uint64_t) (cluster - CLUST_FIRST)) << gSPCShift) + gCLOffset;␊ |
327 | }␊ |
328 | ␊ |
329 | static␊ |
330 | void InitIteratorFromInode(struct exfat_dir_iterator* pIter, struct exfat_inode const* pInode)␊ |
331 | {␊ |
332 | ␉if (pInode->stream_extension_flags & SEFLAG_ALLOCATION_POSSIBLE)␊ |
333 | ␉{␊ |
334 | ␉␉uint64_t sector_length = RoundUp(pInode->valid_length, gBPSShift);␊ |
335 | ␉␉if (pInode->stream_extension_flags & SEFLAG_INVALID_FAT_CHAIN)␊ |
336 | ␉␉{␊ |
337 | ␉␉␉pIter->lsa = ClusterToLSA(pInode->first_cluster);␊ |
338 | ␉␉␉pIter->lsa_end = pIter->lsa + sector_length;␊ |
339 | ␉␉␉pIter->cluster = CLUST_RSRVD;␊ |
340 | ␉␉}␊ |
341 | ␉␉else␊ |
342 | ␉␉{␊ |
343 | ␉␉␉pIter->lsa = 0;␊ |
344 | ␉␉␉pIter->lsa_end = 0;␊ |
345 | ␉␉␉pIter->cluster = pInode->first_cluster;␊ |
346 | ␉␉␉pIter->residue = Residue(sector_length, gSPCShift);␊ |
347 | ␉␉}␊ |
348 | ␉}␊ |
349 | ␉else␊ |
350 | ␉{␊ |
351 | ␉␉pIter->lsa = 0;␊ |
352 | ␉␉pIter->lsa_end = 0;␊ |
353 | ␉␉pIter->cluster = CLUST_RSRVD;␊ |
354 | ␉}␊ |
355 | ␉pIter->entry_offset = CacheBlockSize();␊ |
356 | }␊ |
357 | ␊ |
358 | static␊ |
359 | uint8_t const* nextDirEntry(struct exfat_dir_iterator* pIter)␊ |
360 | {␊ |
361 | ␉uint8_t const* ret;␊ |
362 | ␉uint16_t toRead;␊ |
363 | ␊ |
364 | ␉if (pIter->entry_offset >= CacheBlockSize())␊ |
365 | ␉{␊ |
366 | ␉␉if (pIter->lsa == pIter->lsa_end) {␊ |
367 | ␉␉␉uint32_t next_cluster, contig;␊ |
368 | ␊ |
369 | ␉␉␉getRange(pIter->cluster, CLUST_RSRVD, &next_cluster, &contig);␊ |
370 | ␉␉␉if (!contig)␊ |
371 | ␉␉␉{␊ |
372 | ␉␉␉␉return NULL;␊ |
373 | ␉␉␉}␊ |
374 | ␉␉␉pIter->lsa = ClusterToLSA(pIter->cluster);␊ |
375 | ␉␉␉pIter->lsa_end = pIter->lsa + (((uint64_t) contig) << gSPCShift);␊ |
376 | ␉␉␉if (next_cluster >= CLUST_RSRVD)␊ |
377 | ␉␉␉{␊ |
378 | ␉␉␉␉pIter->lsa_end -= pIter->residue;␊ |
379 | ␉␉␉}␊ |
380 | ␉␉␉pIter->cluster = next_cluster;␊ |
381 | ␉␉}␊ |
382 | ␉␉toRead = (uint16_t) (1 << (gBPCBShift - gBPSShift));␊ |
383 | ␉␉if (pIter->lsa + toRead > pIter->lsa_end)␊ |
384 | ␉␉␉toRead = (uint16_t) (pIter->lsa_end - pIter->lsa);␊ |
385 | ␉␉CacheRead(gCurrentIH,␊ |
386 | ␉␉␉␉ (char*) pIter->buffer,␊ |
387 | ␉␉␉␉ (long long) (pIter->lsa << gBPSShift),␊ |
388 | ␉␉␉␉ ((uint32_t) toRead) << gBPSShift,␊ |
389 | ␉␉␉␉ 1);␊ |
390 | ␉␉pIter->lsa += toRead;␊ |
391 | ␉␉pIter->entry_offset = 0;␊ |
392 | ␉}␊ |
393 | ␉ret = pIter->buffer + pIter->entry_offset;␊ |
394 | ␉pIter->entry_offset += sizeof(struct direntry_file);␊ |
395 | ␉return ret;␊ |
396 | }␊ |
397 | ␊ |
398 | #pragma mark -␊ |
399 | #pragma mark Path Search␊ |
400 | #pragma mark -␊ |
401 | ␊ |
402 | static inline␊ |
403 | int32_t ToUpper(uint16_t ch)␊ |
404 | {␊ |
405 | ␉if (gUPCase)␊ |
406 | ␉{␊ |
407 | ␉␉return gUPCase[ch];␊ |
408 | ␉}␊ |
409 | ␉if (ch >= 128)␊ |
410 | ␉{␊ |
411 | ␉␉return -1;␊ |
412 | ␉}␊ |
413 | ␉if ((uint8_t) ch >= 'a' && (uint8_t) ch <= 'z')␊ |
414 | ␉{␊ |
415 | ␉␉ch &= ~0x20;␊ |
416 | ␉}␊ |
417 | ␉return ch;␊ |
418 | }␊ |
419 | ␊ |
420 | static␊ |
421 | int32_t NameHash(uint16_t const* component_utf16le, uint16_t numChars)␊ |
422 | {␊ |
423 | ␉int32_t ch;␊ |
424 | ␉uint16_t hash = 0;␊ |
425 | ␊ |
426 | ␉for (; numChars; ++component_utf16le, --numChars)␊ |
427 | ␉{␊ |
428 | ␉␉ch = ToUpper(OSSwapLittleToHostInt16(*component_utf16le));␊ |
429 | ␉␉if (ch < 0)␊ |
430 | ␉␉{␊ |
431 | ␉␉␉return ch;␊ |
432 | ␉␉}␊ |
433 | ␉␉hash = (hash << 15) | (hash >> 1) + (uint8_t) ch;␊ |
434 | ␉␉hash = (hash << 15) | (hash >> 1) + (uint8_t) (ch >> 8);␊ |
435 | ␉}␊ |
436 | ␉return hash;␊ |
437 | }␊ |
438 | ␊ |
439 | static␊ |
440 | int ComponentToInode(struct exfat_dir_iterator* iterator, uint16_t const* component_utf16le, uint16_t numChars, struct exfat_inode* out_file)␊ |
441 | {␊ |
442 | ␉union {␊ |
443 | ␉␉uint8_t const* nde;␊ |
444 | ␉␉struct direntry_file const* fe;␊ |
445 | ␉␉struct direntry_stream_extension const* fse;␊ |
446 | ␉␉struct direntry_name_extension const* ne;␊ |
447 | ␉} u;␊ |
448 | ␉int32_t computed_hash;␊ |
449 | ␉uint8_t count2, name_length;␊ |
450 | ␊ |
451 | ␉computed_hash = NameHash(component_utf16le, numChars);␊ |
452 | ␉while ((u.nde = nextDirEntry(iterator)))␊ |
453 | ␉{␊ |
454 | ␉␉if (!*u.nde)␊ |
455 | ␉␉{␊ |
456 | ␉␉␉break;␊ |
457 | ␉␉}␊ |
458 | ␉redo:␊ |
459 | ␉␉if (*u.nde != DIRENTRY_TYPE_FILE)␊ |
460 | ␉␉␉continue;␊ |
461 | ␉␉count2 = u.fe->count2;␊ |
462 | ␉␉if (count2 < 2)␊ |
463 | ␉␉{␊ |
464 | ␉␉␉continue;␊ |
465 | ␉␉}␊ |
466 | ␉␉out_file->attributes = (uint8_t) OSSwapLittleToHostInt16(u.fe->attributes);␊ |
467 | ␉␉u.nde = nextDirEntry(iterator);␊ |
468 | ␉␉if (!u.nde || !*u.nde)␊ |
469 | ␉␉␉break;␊ |
470 | ␉␉if (*u.nde != DIRENTRY_TYPE_ST_EX)␊ |
471 | ␉␉␉goto redo;␊ |
472 | ␉␉out_file->stream_extension_flags = u.fse->flags;␊ |
473 | ␉␉name_length = u.fse->name_length;␊ |
474 | ␉␉if (name_length != numChars)␊ |
475 | ␉␉␉continue;␊ |
476 | ␉␉if (computed_hash >= 0 && computed_hash != OSSwapLittleToHostInt16(u.fse->name_hash))␊ |
477 | ␉␉␉continue;␊ |
478 | ␉␉out_file->valid_length = OSSwapLittleToHostInt64(u.fse->valid_length);␊ |
479 | ␉␉out_file->first_cluster = OSSwapLittleToHostInt32(u.fse->first_cluster);␊ |
480 | ␉␉for (--count2; count2 && name_length; --count2) {␊ |
481 | ␉␉␉int32_t ch1, ch2;␊ |
482 | ␉␉␉uint16_t const* q;␊ |
483 | ␉␉␉uint8_t t;␊ |
484 | ␊ |
485 | ␉␉␉u.nde = nextDirEntry(iterator);␊ |
486 | ␉␉␉if (!u.nde || !*u.nde)␊ |
487 | ␉␉␉␉goto outta_bad;␊ |
488 | ␉␉␉if (*u.nde != DIRENTRY_TYPE_NA_EX)␊ |
489 | ␉␉␉␉goto redo;␊ |
490 | ␉␉␉t = name_length > LABEL_MAX_CHARS ? LABEL_MAX_CHARS : name_length;␊ |
491 | ␉␉␉q = &u.ne->label[0];␊ |
492 | ␉␉␉for (; t; ++q, ++component_utf16le, --t, --name_length) {␊ |
493 | ␉␉␉␉ch1 = ToUpper(OSSwapLittleToHostInt16(*component_utf16le));␊ |
494 | ␉␉␉␉ch2 = ToUpper(OSSwapLittleToHostInt16(*q));␊ |
495 | ␉␉␉␉if (ch1 != ch2)␊ |
496 | ␉␉␉␉␉goto abort_comparison;␊ |
497 | ␉␉␉}␊ |
498 | ␉␉}␊ |
499 | ␉␉return 0;␊ |
500 | ␉abort_comparison:;␊ |
501 | ␉}␊ |
502 | outta_bad:␊ |
503 | ␉return -1;␊ |
504 | }␊ |
505 | ␊ |
506 | static␊ |
507 | int ExtractDirEntry(struct exfat_dir_iterator* iterator, char** name, long* flags, u_int32_t* time, long* infoValid)␊ |
508 | {␊ |
509 | ␉union {␊ |
510 | ␉␉uint8_t const* nde;␊ |
511 | ␉␉struct direntry_file const* fe;␊ |
512 | ␉␉struct direntry_stream_extension const* fse;␊ |
513 | ␉␉struct direntry_name_extension const* ne;␊ |
514 | ␉} u;␊ |
515 | ␉uint8_t count2, name_length, t;␊ |
516 | ␉uint16_t component_full[COMPONENT_MAX_CHARS], *cp_ptr;␊ |
517 | ␊ |
518 | ␉while ((u.nde = nextDirEntry(iterator))) {␊ |
519 | ␉␉if (!*u.nde)␊ |
520 | ␉␉␉break;␊ |
521 | ␉redo:␊ |
522 | ␉␉if (*u.nde != DIRENTRY_TYPE_FILE)␊ |
523 | ␉␉␉continue;␊ |
524 | ␉␉count2 = u.fe->count2;␊ |
525 | ␉␉if (count2 < 2)␊ |
526 | ␉␉␉continue;␊ |
527 | ␉␉if (flags)␊ |
528 | ␉␉␉*flags = (OSSwapLittleToHostInt16(u.fe->attributes) & ATTR_DIRECTORY) ? kFileTypeDirectory : kFileTypeFlat;␊ |
529 | ␉␉if (time)␊ |
530 | ␉␉␉*time = OSSwapLittleToHostInt32(u.fe->mod_time);␊ |
531 | ␉␉if (!name)␊ |
532 | ␉␉␉goto info_valid;␊ |
533 | ␉␉u.nde = nextDirEntry(iterator);␊ |
534 | ␉␉if (!u.nde || !*u.nde)␊ |
535 | ␉␉␉break;␊ |
536 | ␉␉if (*u.nde != DIRENTRY_TYPE_ST_EX)␊ |
537 | ␉␉␉goto redo;␊ |
538 | ␉␉name_length = u.fse->name_length;␊ |
539 | ␉␉cp_ptr = &component_full[0];␊ |
540 | ␉␉for (--count2; count2 && name_length; --count2) {␊ |
541 | ␉␉␉u.nde = nextDirEntry(iterator);␊ |
542 | ␉␉␉if (!u.nde || !*u.nde)␊ |
543 | ␉␉␉␉goto outta_bad;␊ |
544 | ␉␉␉if (*u.nde != DIRENTRY_TYPE_NA_EX)␊ |
545 | ␉␉␉␉goto redo;␊ |
546 | ␉␉␉t = name_length > LABEL_MAX_CHARS ? LABEL_MAX_CHARS : name_length;␊ |
547 | ␉␉␉memcpy(cp_ptr, &u.ne->label[0], t * sizeof(uint16_t));␊ |
548 | ␉␉␉cp_ptr += t;␊ |
549 | ␉␉␉name_length -= t;␊ |
550 | ␉␉}␊ |
551 | ␉␉/*␊ |
552 | ␉␉ * Note: for ASCII can allocate exactly name_length + 1,␊ |
553 | ␉␉ * but in case of multibyte characters, allow more space.␊ |
554 | ␉␉ */␊ |
555 | ␉␉*name = (char*) malloc(COMPONENT_MAX_CHARS + 1);␊ |
556 | ␉␉if (*name)␊ |
557 | ␉␉␉utf_encodestr(&component_full[0], cp_ptr - &component_full[0], (uint8_t*) *name, COMPONENT_MAX_CHARS, OSLittleEndian);␊ |
558 | ␉info_valid:␊ |
559 | ␉␉if (infoValid)␊ |
560 | ␉␉␉*infoValid = 1;␊ |
561 | ␉␉return 0;␊ |
562 | ␉}␊ |
563 | outta_bad:␊ |
564 | ␉return -1;␊ |
565 | }␊ |
566 | ␊ |
567 | static␊ |
568 | int PathToInode(char const* path, struct exfat_inode* out_file, uint8_t* buffer /* size CacheBlockSize() bytes */)␊ |
569 | {␊ |
570 | ␉struct exfat_dir_iterator iterator;␊ |
571 | ␉uint8_t *ptr, *slash, ch;␊ |
572 | ␉uint16_t path_utf16le[COMPONENT_MAX_CHARS];␊ |
573 | ␉uint16_t numChars;␊ |
574 | ␉char have_prev_inode;␊ |
575 | ␊ |
576 | ␉InitIteratorFromRoot(&iterator);␊ |
577 | ␉iterator.buffer = buffer;␊ |
578 | ␉ptr = (uint8_t*) path;␉/* Note: const_cast */␊ |
579 | ␉have_prev_inode = 0;␊ |
580 | ␉do {␊ |
581 | ␉␉do {␊ |
582 | ␉␉␉for (slash = ptr; *slash && *slash != '/'; ++slash);␊ |
583 | ␉␉␉ch = *slash;␊ |
584 | ␉␉␉if (slash == ptr) {␊ |
585 | ␉␉␉␉if (!ch) {␊ |
586 | ␉␉␉␉␉if (!have_prev_inode) {␊ |
587 | ␉␉␉␉␉␉/*␊ |
588 | ␉␉␉␉␉␉ * Fill in pseudo-inode for Root Directory␊ |
589 | ␉␉␉␉␉␉ */␊ |
590 | ␉␉␉␉␉␉out_file->valid_length = 0;␉/* Unknown */␊ |
591 | ␉␉␉␉␉␉out_file->first_cluster = gRDCl;␊ |
592 | ␉␉␉␉␉␉out_file->attributes = ATTR_DIRECTORY;␊ |
593 | ␉␉␉␉␉␉out_file->stream_extension_flags = SEFLAG_ALLOCATION_POSSIBLE | SEFLAG_PSEUDO_ROOTDIR;␊ |
594 | ␉␉␉␉␉}␊ |
595 | ␉␉␉␉␉return 0;␊ |
596 | ␉␉␉␉}␊ |
597 | ␉␉␉␉++ptr;␊ |
598 | ␉␉␉␉continue;␊ |
599 | ␉␉␉}␊ |
600 | ␉␉␉break;␊ |
601 | ␉␉} while (1);␊ |
602 | ␉␉*slash = 0;␊ |
603 | ␉␉utf_decodestr(ptr, &path_utf16le[0], &numChars, sizeof path_utf16le, OSLittleEndian);␊ |
604 | ␉␉numChars = OSSwapLittleToHostInt16(numChars);␊ |
605 | ␉␉*slash = ch;␊ |
606 | ␉␉ptr = slash + 1;␊ |
607 | ␉␉if (have_prev_inode)␊ |
608 | ␉␉␉InitIteratorFromInode(&iterator, out_file);␊ |
609 | ␉␉if (ComponentToInode(&iterator, &path_utf16le[0], numChars, out_file) < 0)␊ |
610 | ␉␉␉break;␊ |
611 | ␉␉if (!ch)␉/* was last component - done */␊ |
612 | ␉␉␉return 0;␊ |
613 | ␉␉if (!(out_file->attributes & ATTR_DIRECTORY))␉/* not a directory and not last component - error */␊ |
614 | ␉␉␉return -1;␊ |
615 | ␉␉have_prev_inode = 1;␊ |
616 | ␉} while (1);␊ |
617 | ␉return -1;␊ |
618 | }␊ |
619 | ␊ |
620 | #pragma mark -␊ |
621 | #pragma mark exFAT Implementation␊ |
622 | #pragma mark -␊ |
623 | ␊ |
624 | void␊ |
625 | EXFATFree(CICell ih)␊ |
626 | {␊ |
627 | ␉if (gCurrentIH == ih) {␊ |
628 | ␉␉gCurrentIH = NULL;␊ |
629 | ␉␉FATCacheInvalidate();␊ |
630 | ␉}␊ |
631 | ␉free(ih);␊ |
632 | }␊ |
633 | ␊ |
634 | long␊ |
635 | EXFATInitPartition(CICell ih)␊ |
636 | {␊ |
637 | ␉uint8_t *buffer, bpss, spcs;␊ |
638 | ␉struct exfatbootfile const* boot;␊ |
639 | ␊ |
640 | ␉if (!ih)␊ |
641 | ␉␉return -1;␊ |
642 | ␉if (gCurrentIH == ih)␊ |
643 | ␉{␊ |
644 | ␉␉CacheInit(ih, CacheBlockSize());␊ |
645 | ␉␉return FATCacheInit(0);␊ |
646 | ␉}␊ |
647 | ␊ |
648 | ␉buffer = (uint8_t*) malloc(BPS);␊ |
649 | ␉if (!buffer)␊ |
650 | ␉␉return -1;␊ |
651 | ␊ |
652 | ␉/*␊ |
653 | ␉ * Read the boot sector of the filesystem, and then check the␊ |
654 | ␉ * boot signature. If not a boot sector then error out.␊ |
655 | ␉ */␊ |
656 | ␊ |
657 | ␉Seek(ih, 0);␊ |
658 | ␉Read(ih, (long) buffer, BPS);␊ |
659 | ␊ |
660 | ␉boot = (struct exfatbootfile const*) buffer;␊ |
661 | ␊ |
662 | ␉/* Looking for EXFAT signature. */␊ |
663 | ␉if (memcmp((char const*) &boot->bf_sysid[0], EXFAT_BBID, EXFAT_BBIDLEN)) {␊ |
664 | ␉␉free(buffer);␊ |
665 | ␉␉return -1;␊ |
666 | ␉}␊ |
667 | ␊ |
668 | ␉/*␊ |
669 | ␉ * Make sure the bytes per sector and sectors per cluster are within reasonable ranges.␊ |
670 | ␉ */␊ |
671 | ␉bpss = boot->bf_bpss;␊ |
672 | ␉if (bpss < MIN_BLOCK_SIZE_SHIFT || bpss > MAX_BLOCK_SIZE_SHIFT) {␊ |
673 | ␉␉free(buffer);␊ |
674 | ␉␉return -1;␊ |
675 | ␉}␊ |
676 | ␊ |
677 | ␉spcs = boot->bf_spcs;␊ |
678 | ␉if (spcs > (MAX_CLUSTER_SIZE_SHIFT - bpss)) {␊ |
679 | ␉␉free(buffer);␊ |
680 | ␉␉return -1;␊ |
681 | ␉}␊ |
682 | ␊ |
683 | ␉if (FATCacheInit(1) < 0)␊ |
684 | ␉{␊ |
685 | ␉␉free(buffer);␊ |
686 | ␉␉return -1;␊ |
687 | ␉}␊ |
688 | ␊ |
689 | ␉gBPSShift = bpss;␊ |
690 | ␉gSPCShift = spcs;␊ |
691 | ␉gFATOffset = OSSwapLittleToHostInt32(boot->bf_fatoff);␊ |
692 | ␉gFATLength = OSSwapLittleToHostInt32(boot->bf_fatlen);␊ |
693 | ␉gCLOffset = OSSwapLittleToHostInt32(boot->bf_cloff);␊ |
694 | ␉gCLCount = OSSwapLittleToHostInt32(boot->bf_clcnt);␊ |
695 | ␉gRDCl = OSSwapLittleToHostInt32(boot->bf_rdircl);␊ |
696 | ␉gBPCBShift = bpss + spcs;␊ |
697 | ␉if (gBPCBShift > MAX_BLOCK_SIZE_SHIFT)␊ |
698 | ␉␉gBPCBShift = MAX_BLOCK_SIZE_SHIFT;␊ |
699 | ␊ |
700 | ␉gCurrentIH = ih;␊ |
701 | ␊ |
702 | ␉CacheInit(ih, CacheBlockSize());␊ |
703 | ␊ |
704 | ␉free(buffer);␊ |
705 | ␉return 0;␊ |
706 | }␊ |
707 | ␊ |
708 | long␊ |
709 | EXFATGetDirEntry(CICell ih, char * dirPath, long long * dirIndex,␊ |
710 | ␉␉␉␉ char ** name, long * flags, u_int32_t * time,␊ |
711 | ␉␉␉␉ FinderInfo * finderInfo, long * infoValid)␊ |
712 | {␊ |
713 | ␉struct exfat_dir_iterator* iterator;␊ |
714 | ␊ |
715 | ␉if (!dirPath || !dirIndex)␊ |
716 | ␉␉return -1;␊ |
717 | ␊ |
718 | ␉if (EXFATInitPartition(ih) < 0)␊ |
719 | ␉␉return -1;␊ |
720 | ␊ |
721 | ␉if (*dirPath == '/')␊ |
722 | ␉␉++dirPath;␊ |
723 | ␊ |
724 | ␉iterator = (struct exfat_dir_iterator*) (long) *dirIndex;␊ |
725 | ␉if (!iterator) {␊ |
726 | ␉␉struct exfat_inode inode;␊ |
727 | ␉␉uint8_t* buffer;␊ |
728 | ␊ |
729 | ␉␉buffer = (uint8_t*) malloc(CacheBlockSize() + sizeof *iterator);␊ |
730 | ␉␉if (!buffer)␊ |
731 | ␉␉␉return -1;␊ |
732 | ␉␉iterator = (struct exfat_dir_iterator*) (buffer + CacheBlockSize());␊ |
733 | ␉␉if (PathToInode(dirPath, &inode, buffer) < 0 ||␊ |
734 | ␉␉␉!(inode.attributes & ATTR_DIRECTORY)) {␊ |
735 | ␉␉␉free(buffer);␊ |
736 | ␉␉␉return -1;␊ |
737 | ␉␉}␊ |
738 | ␉␉if (inode.stream_extension_flags & SEFLAG_PSEUDO_ROOTDIR)␊ |
739 | ␉␉␉InitIteratorFromRoot(iterator);␊ |
740 | ␉␉else␊ |
741 | ␉␉␉InitIteratorFromInode(iterator, &inode);␊ |
742 | ␉␉iterator->buffer = buffer;␊ |
743 | ␉␉*dirIndex = (long long) (long) iterator;␊ |
744 | ␉}␊ |
745 | ␉if (ExtractDirEntry(iterator, name, flags, time, infoValid) < 0) {␊ |
746 | ␉␉free(iterator->buffer);␊ |
747 | ␉␉*dirIndex = 0;␊ |
748 | ␉␉return -1;␊ |
749 | ␉}␊ |
750 | ␉return 0;␊ |
751 | }␊ |
752 | ␊ |
753 | long␊ |
754 | EXFATReadFile(CICell ih, char * filePath, void *base, uint64_t offset, uint64_t length)␊ |
755 | {␊ |
756 | ␉uint64_t size, toRead, leftToRead;␊ |
757 | ␉struct exfat_inode inode;␊ |
758 | ␉uint8_t* buffer;␊ |
759 | ␉uint32_t cluster;␊ |
760 | ␊ |
761 | ␉if (EXFATInitPartition(ih) < 0)␊ |
762 | ␉␉return -1;␊ |
763 | ␊ |
764 | ␉if (*filePath == '/')␊ |
765 | ␉␉++filePath;␊ |
766 | ␊ |
767 | ␉buffer = (uint8_t*) malloc(CacheBlockSize());␊ |
768 | ␉if (!buffer)␊ |
769 | ␉␉return -1;␊ |
770 | ␊ |
771 | ␉if (PathToInode(filePath, &inode, buffer) < 0 ||␊ |
772 | ␉␉(inode.attributes & ATTR_DIRECTORY) != 0) {␊ |
773 | ␉␉free(buffer);␊ |
774 | ␉␉return -1;␊ |
775 | ␉}␊ |
776 | ␉free(buffer);␊ |
777 | ␉if (!(inode.stream_extension_flags & SEFLAG_ALLOCATION_POSSIBLE))␊ |
778 | ␉␉return (!offset && !length) ? 0 : -1;␊ |
779 | ␉cluster = inode.first_cluster;␊ |
780 | ␉size = inode.valid_length;␊ |
781 | ␉if (size == offset && !length)␊ |
782 | ␉␉return 0;␊ |
783 | ␉if (size <= offset)␊ |
784 | ␉␉return -1;␊ |
785 | ␉toRead = size - offset;␊ |
786 | ␉if (length && length < toRead)␊ |
787 | ␉␉toRead = length;␊ |
788 | ␉if (inode.stream_extension_flags & SEFLAG_INVALID_FAT_CHAIN) {␊ |
789 | ␉␉Seek(ih, (long long) ((ClusterToLSA(cluster) << gBPSShift) + offset));␊ |
790 | ␉␉Read(ih, (long) base, (long) toRead);␊ |
791 | ␉␉return (long) toRead;␊ |
792 | ␉}␊ |
793 | ␉leftToRead = toRead;␊ |
794 | ␉do {␊ |
795 | ␉␉uint64_t chunk, canRead;␊ |
796 | ␉␉uint32_t next_cluster, contig;␊ |
797 | ␊ |
798 | ␉␉getRange(cluster, CLUST_RSRVD, &next_cluster, &contig);␊ |
799 | ␉␉if (!contig)␊ |
800 | ␉␉␉break;␊ |
801 | ␉␉chunk = ((uint64_t) contig) << (gBPSShift + gSPCShift);␊ |
802 | ␉␉if (offset >= chunk) {␊ |
803 | ␉␉␉offset -= chunk;␊ |
804 | ␉␉␉cluster = next_cluster;␊ |
805 | ␉␉␉continue;␊ |
806 | ␉␉}␊ |
807 | ␉␉canRead = chunk - offset;␊ |
808 | ␉␉if (canRead > leftToRead)␊ |
809 | ␉␉␉canRead = leftToRead;␊ |
810 | ␉␉Seek(ih, (long long) ((ClusterToLSA(cluster) << gBPSShift) + offset));␊ |
811 | ␉␉Read(ih, (long) base, (long) canRead);␊ |
812 | ␉␉base = ((uint8_t*) base) + canRead;␊ |
813 | ␉␉cluster = next_cluster;␊ |
814 | ␉␉offset = 0;␊ |
815 | ␉␉leftToRead -= canRead;␊ |
816 | ␉} while (leftToRead);␊ |
817 | ␉return (long) (toRead - leftToRead);␊ |
818 | }␊ |
819 | ␊ |
820 | long␊ |
821 | EXFATGetFileBlock(CICell ih, char *filePath, unsigned long long *firstBlock)␊ |
822 | {␊ |
823 | ␉uint8_t* buffer;␊ |
824 | ␉struct exfat_inode inode;␊ |
825 | ␉uint32_t cluster;␊ |
826 | ␊ |
827 | ␉if (EXFATInitPartition(ih) < 0)␊ |
828 | ␉␉return -1;␊ |
829 | ␊ |
830 | ␉if (*filePath == '/')␊ |
831 | ␉␉++filePath;␊ |
832 | ␊ |
833 | ␉buffer = (uint8_t*) malloc(CacheBlockSize());␊ |
834 | ␉if (!buffer)␊ |
835 | ␉␉return -1;␊ |
836 | ␊ |
837 | ␉if (PathToInode(filePath, &inode, buffer) < 0 ||␊ |
838 | ␉␉(inode.attributes & ATTR_DIRECTORY) != 0 ||␊ |
839 | ␉␉!(inode.stream_extension_flags & SEFLAG_ALLOCATION_POSSIBLE)) {␊ |
840 | ␉␉free(buffer);␊ |
841 | ␉␉return -1;␊ |
842 | ␉}␊ |
843 | ␉free(buffer);␊ |
844 | ␉cluster = inode.first_cluster;␊ |
845 | ␉if (cluster < CLUST_FIRST || cluster >= CLUST_RSRVD)␊ |
846 | ␉␉return -1;␊ |
847 | ␉*firstBlock = ClusterToLSA(cluster);␊ |
848 | ␉return 0;␊ |
849 | }␊ |
850 | ␊ |
851 | long␊ |
852 | EXFATLoadFile(CICell ih, char * filePath)␊ |
853 | {␊ |
854 | ␉return EXFATReadFile(ih, filePath, (void *)gFSLoadAddress, 0, 0);␊ |
855 | }␊ |
856 | ␊ |
857 | /**␊ |
858 | * Reads volume label into str.␊ |
859 | * Reads boot sector, performs some checking, loads root dir␊ |
860 | * and parses volume label.␊ |
861 | */␊ |
862 | void␊ |
863 | EXFATGetDescription(CICell ih, char *str, long strMaxLen)␊ |
864 | {␊ |
865 | struct exfatbootfile *boot;␊ |
866 | uint8_t bpss, spcs;␊ |
867 | long long rdirOffset = 0;␊ |
868 | char *buf = NULL;␊ |
869 | struct direntry_label *dire = NULL;␊ |
870 | int loopControl = 0;␊ |
871 | ␊ |
872 | DBG("EXFAT: start %x:%x\n", ih->biosdev, ih->part_no);␊ |
873 | ␊ |
874 | buf = (char *)malloc(MAX_BLOCK_SIZE);␊ |
875 | if (buf == 0)␊ |
876 | {␊ |
877 | goto error;␊ |
878 | }␊ |
879 | ␊ |
880 | /*␊ |
881 | * Read the boot sector, check signatures, and do some minimal␊ |
882 | * sanity checking. NOTE: the size of the read below is intended␊ |
883 | * to be a multiple of all supported block sizes, so we don't␊ |
884 | * have to determine or change the device's block size.␊ |
885 | */␊ |
886 | Seek(ih, 0);␊ |
887 | Read(ih, (long)buf, MAX_BLOCK_SIZE);␊ |
888 | ␊ |
889 | // take our boot structure␊ |
890 | boot = (struct exfatbootfile *) buf;␊ |
891 | ␊ |
892 | /*␊ |
893 | * The first three bytes are an Intel x86 jump instruction. I assume it␊ |
894 | * can be the same forms as DOS FAT:␊ |
895 | * 0xE9 0x?? 0x??␊ |
896 | * 0xEC 0x?? 0x90␊ |
897 | * where 0x?? means any byte value is OK.␊ |
898 | */␊ |
899 | if (boot->reserved1[0] != 0xE9␊ |
900 | && (boot->reserved1[0] != 0xEB || boot->reserved1[2] != 0x90))␊ |
901 | {␊ |
902 | goto error;␊ |
903 | }␊ |
904 | ␊ |
905 | // Check the "EXFAT " signature.␊ |
906 | if (memcmp((const char *)boot->bf_sysid, EXFAT_BBID, EXFAT_BBIDLEN) != 0)␊ |
907 | {␊ |
908 | goto error;␊ |
909 | }␊ |
910 | ␊ |
911 | /*␊ |
912 | * Make sure the bytes per sector and sectors per cluster are␊ |
913 | * powers of two, and within reasonable ranges.␊ |
914 | */␊ |
915 | bpss = boot->bf_bpss;␉/* Just one byte; no swapping needed */␊ |
916 | DBG("EXFAT: bpss=%d, bytesPerSector=%d\n", bpss, (1 << bpss));␊ |
917 | if (bpss < MIN_BLOCK_SIZE_SHIFT || bpss > MAX_BLOCK_SIZE_SHIFT)␊ |
918 | {␊ |
919 | DBG("EXFAT: invalid bytes per sector shift(%d)\n", bpss);␊ |
920 | goto error;␊ |
921 | }␊ |
922 | ␊ |
923 | spcs = boot->bf_spcs;␉/* Just one byte; no swapping needed */␊ |
924 | DBG("EXFAT: spcs=%d, sectorsPerCluster=%d\n", spcs, (1 << spcs));␊ |
925 | if (spcs > (MAX_CLUSTER_SIZE_SHIFT - bpss))␊ |
926 | {␊ |
927 | DBG("EXFAT: invalid sectors per cluster shift (%d)\n", spcs);␊ |
928 | goto error;␊ |
929 | }␊ |
930 | ␊ |
931 | // calculate root dir cluster offset␊ |
932 | rdirOffset = OSSwapLittleToHostInt32(boot->bf_cloff) + ((long long) (OSSwapLittleToHostInt32(boot->bf_rdircl) - 2) << spcs);␊ |
933 | DBG("EXFAT: rdirOffset=%d\n", (int) rdirOffset);␊ |
934 | ␊ |
935 | // load MAX_BLOCK_SIZE bytes of root dir␊ |
936 | Seek(ih, rdirOffset << bpss);␊ |
937 | Read(ih, (long)buf, MAX_BLOCK_SIZE);␊ |
938 | DBG("buf 0 1 2 = %x %x %x\n", 0x00ff & buf[0], 0x00ff & buf[1], 0x00ff & buf[2]);␊ |
939 | ␊ |
940 | str[0] = '\0';␊ |
941 | ␊ |
942 | /*␊ |
943 | * Search for volume label dir entry (type 0x83), convert from unicode and put to str.␊ |
944 | * Set loopControl var to avoid searching outside of buf.␊ |
945 | */␊ |
946 | loopControl = MAX_BLOCK_SIZE / sizeof(struct direntry_label);␊ |
947 | dire = (struct direntry_label *)buf;␊ |
948 | while (loopControl && dire->type && dire->type != DIRENTRY_TYPE_LABEL)␊ |
949 | {␊ |
950 | dire++;␊ |
951 | loopControl--;␊ |
952 | }␊ |
953 | if (dire->type == DIRENTRY_TYPE_LABEL && dire->llen > 0 && dire->llen <= VOLUME_LABEL_MAX_CHARS)␊ |
954 | {␊ |
955 | utf_encodestr( dire->label, (int)dire->llen, (u_int8_t *)str, strMaxLen, OSLittleEndian );␊ |
956 | }␊ |
957 | DBG("EXFAT: label=%s\n", str);␊ |
958 | ␊ |
959 | free(buf);␊ |
960 | PAUSE();␊ |
961 | return;␊ |
962 | ␊ |
963 | error:␊ |
964 | ␉if (buf)␊ |
965 | ␉{␊ |
966 | ␉␉free(buf);␊ |
967 | ␉}␊ |
968 | ␊ |
969 | ␉DBG("EXFAT: error\n");␊ |
970 | ␉PAUSE();␊ |
971 | ␉return;␊ |
972 | }␊ |
973 | ␊ |
974 | /**␊ |
975 | * Sets UUID to uuidStr.␊ |
976 | * Reads the boot sector, does some checking, generates UUID␊ |
977 | * (like the one you get on Windows???)␊ |
978 | */␊ |
979 | long EXFATGetUUID(CICell ih, char *uuidStr)␊ |
980 | {␊ |
981 | ␉uint32_t volsn;␊ |
982 | ␉struct exfatbootfile *boot;␊ |
983 | ␉void *buf = malloc(MAX_BLOCK_SIZE);␊ |
984 | ␉if ( !buf )␊ |
985 | ␉␉return -1;␊ |
986 | ␊ |
987 | ␉/*␊ |
988 | ␉ * Read the boot sector, check signatures, and do some minimal␊ |
989 | ␉ * sanity checking.␉ NOTE: the size of the read below is intended␊ |
990 | ␉ * to be a multiple of all supported block sizes, so we don't␊ |
991 | ␉ * have to determine or change the device's block size.␊ |
992 | ␉ */␊ |
993 | ␉Seek(ih, 0);␊ |
994 | ␉Read(ih, (long)buf, MAX_BLOCK_SIZE);␊ |
995 | ␊ |
996 | ␉boot = (struct exfatbootfile *) buf;␊ |
997 | ␊ |
998 | ␉/*␊ |
999 | ␉ * Check the "EXFAT " signature.␊ |
1000 | ␉ */␊ |
1001 | ␉if (memcmp((const char *)boot->bf_sysid, EXFAT_BBID, EXFAT_BBIDLEN) != 0)␊ |
1002 | ␉{␊ |
1003 | ␉␉return -1;␊ |
1004 | ␉}␊ |
1005 | ␊ |
1006 | ␉// Check for non-null volume serial number␊ |
1007 | ␉volsn = OSSwapLittleToHostInt32(boot->bf_volsn);␊ |
1008 | ␉if( !volsn )␊ |
1009 | ␉{␊ |
1010 | ␉␉return -1;␊ |
1011 | ␉}␊ |
1012 | ␊ |
1013 | ␉// Use UUID like the one you get on Windows␊ |
1014 | ␉sprintf(uuidStr, "%04X-%04X", (unsigned short)(volsn >> 16) & 0xFFFF,␊ |
1015 | (unsigned short)volsn & 0xFFFF);␊ |
1016 | ␊ |
1017 | ␉DBG("EXFATGetUUID: %x:%x = %s\n", ih->biosdev, ih->part_no, uuidStr);␊ |
1018 | ␉return 0;␊ |
1019 | }␊ |
1020 | ␊ |
1021 | /**␊ |
1022 | * Returns true if given buffer is the boot rec of the EXFAT volume.␊ |
1023 | */␊ |
1024 | bool EXFATProbe(const void * buffer)␊ |
1025 | {␊ |
1026 | ␉bool result = false;␊ |
1027 | ␊ |
1028 | ␉// boot sector structure␊ |
1029 | ␉const struct exfatbootfile␉* boot = buffer;␊ |
1030 | ␊ |
1031 | ␉// Looking for EXFAT signature.␊ |
1032 | ␉if (memcmp((const char *)boot->bf_sysid, EXFAT_BBID, EXFAT_BBIDLEN) == 0)␊ |
1033 | ␉{␊ |
1034 | ␉␉result = true;␊ |
1035 | ␉}␊ |
1036 | ␊ |
1037 | ␉DBG("EXFATProbe: %d\n", result ? 1 : 0);␊ |
1038 | ␉return result;␊ |
1039 | }␊ |
1040 | |