Chameleon

Chameleon Svn Source Tree

Root/trunk/i386/libsaio/exfat.c

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#defineEXFAT_BBID&gExfatID[0]
58#defineEXFAT_BBIDLEN8
59
60#define MIN_BLOCK_SIZE_SHIFT9
61#define MAX_BLOCK_SIZE_SHIFT12
62#define MAX_BLOCK_SIZE(1 << MAX_BLOCK_SIZE_SHIFT)
63#define MAX_CLUSTER_SIZE_SHIFT25
64#define CLUST_FIRST2/* reserved cluster range */
65#define CLUST_RSRVD0xfffffff7/* reserved cluster range */
66#define INVALID_FAT_ADDRESS0xffffffff
67#define ATTR_DIRECTORY0x10/* entry is a directory name */
68#define SEFLAG_ALLOCATION_POSSIBLE1
69#define SEFLAG_INVALID_FAT_CHAIN2
70#define SEFLAG_PSEUDO_ROOTDIR0x80
71#define COMPONENT_MAX_CHARS255/* Max # characters in path name single component */
72
73#pragma mark -
74#pragma mark Static Data
75#pragma mark -
76
77static CICell gCurrentIH = NULL;
78static uint8_t gBPSShift = 0;/* log_2(Bytes-Per-Sector) */
79static uint8_t gSPCShift = 0;/* log_2(Sectors-Per-Cluster) */
80static uint32_t gFATOffset = 0;/* in sectors */
81static uint32_t gFATLength = 0;/* in sectors */
82static uint32_t gCLOffset = 0;/* in sectors */
83static uint32_t gCLCount = 0;/* in clusters */
84static uint32_t gRDCl = 0;/* Root Directory Cluster Number */
85static uint8_t* gFATCacheBuffer = NULL;
86static uint32_t gCachedFATBlockAddress = 0;
87static uint16_t* gUPCase = NULL;/* If loaded, should be exactly 2^16 * sizeof(uint16_t) bytes long */
88static uint8_t gBPCBShift = 0;/* log_2(Bytes-Per-Cache-Block) */
89
90static char const gExfatID[] = "EXFAT ";
91
92#pragma mark -
93#pragma mark Helper Structures
94#pragma mark -
95
96struct exfat_dir_iterator
97{
98uint64_t lsa;/* Current sector address */
99uint64_t lsa_end;/* Last sector address + 1 */
100uint8_t* buffer;
101uint32_t cluster;/* Next cluster number */
102uint16_t residue;/* Number of sectors in last cluster */
103uint16_t entry_offset;/* Offset of next entry in buffer */
104};
105
106struct exfat_inode
107{
108uint64_t valid_length;/* File/Directory length */
109uint32_t first_cluster;/* First cluster number */
110uint8_t attributes;/* From direntry_file::attributes (truncated to 8 bits) */
111uint8_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 */
122struct exfatbootfile {
123uint8_treserved1[3];/* JumpBoot: 0xEB7690 */
124uint8_tbf_sysid[8];/* FileSystemName: 'EXFAT ' */
125uint8_treserved2[53];/* MustBeZero */
126uint64_tbf_prtoff;/* PartitionOffset: In sectors; if 0, shall be ignored */
127uint64_tbf_vollen;/* VolumeLength: Size of exFAT volume in sectors */
128uint32_tbf_fatoff;/* FatOffset: In sectors */
129uint32_tbf_fatlen;/* FatLength: In sectors. May exceed the required space in order to align the second FAT */
130uint32_tbf_cloff;/* ClusterHeapOffset: In sectors. */
131uint32_tbf_clcnt;/* ClusterCount: 2^32-11 is the maximum number of clusters could be described. */
132uint32_tbf_rdircl;/* RootDirectoryCluster. */
133uint32_tbf_volsn;/* VolumeSerialNumber. */
134uint16_tbf_fsrev;/* FileSystemRevision: as MAJOR.minor, major revision is high byte, minor is low byte; currently 01.00. */
135uint16_tbf_volflags;/* VolumeFlags. */
136uint8_tbf_bpss;/* BytesPerSectorShift: Power of 2. Minimum 9 (512 bytes per sector), maximum 12 (4096 bytes per sector) */
137
138uint8_tbf_nfats;/* NumberOfFats: 2 is for TexFAT only */
139uint8_tbf_drvs;/* DriveSelect: Extended INT 13h drive number; typically 0x80 */
140
141uint8_treserved3[7];/* Reserved */
142uint8_tbootcode[390];/* BootCode */
143uint16_tbf_bsig;/* BootSignature: 0xAA55 */
144};
145
146struct direntry_label {
147#define DIRENTRY_TYPE_LABEL((uint8_t) 0x83)
148uint8_ttype;/* EntryType: 0x83 (or 0x03 if label is empty) */
149uint8_tllen;/* CharacterCount: Length in Unicode characters (max 11) */
150#define VOLUME_LABEL_MAX_CHARS11
151uint16_tlabel[11];/* VolumeLabel: Unicode characters (max 11) */
152uint8_treserved1[8];/* Reserved */
153};
154
155#if UNUSED
156struct direntry_allocation_bitmap
157{
158uint8_ttype;/* EntryType: 0x81 (or 0x01 if entry is empty) */
159uint8_tbitmap_flags;/* bit 0: 0 1st bitmap, 1 2nd bitmap */
160uint8_treserved1[18];/* Reserved */
161uint32_tfirst_cluster;/* Cluster address of 1st data block */
162uint64_tdata_length;/* Length of the data */
163};
164
165struct direntry_upcase_table
166{
167uint8_ttype;/* EntryType: 0x82 (or 0x02 if entry is empty) */
168uint8_treserved1[3];/* Reserved */
169uint32_tchecksum;/* Table Checksum */
170uint8_treserved2[12];/* Reserved */
171uint32_tfirst_cluster;/* Cluster address of 1st data block */
172uint64_tdata_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
182struct direntry_file
183{
184#define DIRENTRY_TYPE_FILE((uint8_t) 0x85)
185uint8_ttype;/* EntryType: 0x85 (or 0x05 if entry is empty) */
186uint8_tcount2;/* Secondary Count */
187uint16_tchecksum;/* Set Checksum */
188uint16_tattributes;/* File Attributes, 1 - R, 2 - H, 4 - S, 16 - D, 32 - A */
189uint8_treserved1[2];/* Reserved */
190uint32_tcreate_time;/* Create Time, DOS Timestamp Format */
191uint32_tmod_time;/* Last Modified Time, DOS Timestamp Format */
192uint32_taccess_time;/* Last Accessed Time, DOS Timestamp Format */
193uint8_tcreate_10ms;/* 10ms increments range 0 - 199 */
194uint8_tmod_10ms;/* 10ms increments range 0 - 199 */
195uint8_tcreate_tzoff;/* TZ Offset, difference to UTC in 15 min increments */
196uint8_tmod_tzoff;/* TZ Offset, difference to UTC in 15 min increments */
197uint8_taccess_tzoff;/* TZ Offset, difference to UTC in 15 min increments */
198uint8_treserved2[7];/* Reserved */
199};
200
201struct direntry_stream_extension
202{
203#define DIRENTRY_TYPE_ST_EX((uint8_t) 0xC0)
204uint8_ttype;/* EntryType: 0xC0 (or 0x40 if entry is empty) */
205uint8_tflags;/* bit 0 - Allocation Possible (1 Yes/0 No), bit 1 - No FAT Chain (1 Invalid/0 Valid) */
206uint8_treserved1;/* Reserved */
207uint8_tname_length;/* Name Length */
208uint16_tname_hash;/* Name Hash */
209uint8_treserved2[2];/* Reserved */
210uint64_tvalid_length;/* Valid Data Length */
211uint8_treserved3[4];/* Reserved */
212uint32_tfirst_cluster;/* Cluster address of 1st data block */
213uint64_tdata_length;/* Length of the data */
214};
215
216struct direntry_name_extension
217{
218#define DIRENTRY_TYPE_NA_EX((uint8_t) 0xC1)
219uint8_ttype;/* EntryType: 0xC1 (or 0x41 if entry is empty) */
220uint8_treserved1;/* Reserved */
221#define LABEL_MAX_CHARS 15
222uint16_tlabel[15];/* 15 characters of file name (UTF16LE) */
223};
224
225#pragma mark -
226#pragma mark FATCache
227#pragma mark -
228
229static
230int FATCacheInit(int invalidate)
231{
232if (!gFATCacheBuffer)
233{
234gFATCacheBuffer = (uint8_t*) malloc(MAX_BLOCK_SIZE);
235if (!gFATCacheBuffer)
236{
237return -1;
238}
239invalidate = 1;
240}
241if (invalidate)
242{
243gCachedFATBlockAddress = INVALID_FAT_ADDRESS;
244}
245return 0;
246}
247
248static inline
249void FATCacheInvalidate(void)
250{
251gCachedFATBlockAddress = INVALID_FAT_ADDRESS;
252}
253
254static inline
255uint16_t CacheBlockSize(void)
256{
257return (uint16_t) (1 << gBPCBShift);
258}
259
260static
261int getRange(uint32_t cluster, uint32_t maxContiguousClusters, uint32_t* pNextCluster, uint32_t* pNumContiguousClusters)
262{
263uint32_t count, lcba;
264uint16_t mask;
265uint8_t shift;
266
267if (!pNextCluster || !pNumContiguousClusters)
268{
269return -1;
270}
271count = 0;
272shift = gBPCBShift - 2;
273mask = (uint16_t) ((1 << shift) - 1);
274while (cluster >= CLUST_FIRST && cluster < CLUST_RSRVD && count < maxContiguousClusters)
275{
276++count;
277lcba = cluster >> shift;
278if (lcba != gCachedFATBlockAddress)
279{
280CacheRead(gCurrentIH,
281 (char*) gFATCacheBuffer,
282 (((long long) gFATOffset) << gBPSShift) + (((long long) lcba) << gBPCBShift),
283 CacheBlockSize(),
284 1);
285gCachedFATBlockAddress = lcba;
286}
287lcba = cluster + 1;
288cluster = OSSwapLittleToHostInt32(((uint32_t const*) gFATCacheBuffer)[cluster & mask]);
289if (cluster != lcba)
290break;
291}
292*pNextCluster = cluster;
293*pNumContiguousClusters = count;
294return 0;
295}
296
297#pragma mark -
298#pragma mark Directory Iterator
299#pragma mark -
300
301static
302void InitIteratorFromRoot(struct exfat_dir_iterator* pIter)
303{
304pIter->lsa = 0;
305pIter->lsa_end = 0;
306pIter->cluster = gRDCl;
307pIter->residue = 0;
308pIter->entry_offset = CacheBlockSize();
309}
310
311static inline
312uint64_t RoundUp(uint64_t val, uint8_t shift)
313{
314return (val + (1 << shift) - 1) >> shift;/* == RoundUpToInt(val/(2^shift)) */
315}
316
317static inline
318uint16_t Residue(uint64_t val, uint8_t shift)
319{
320return (-(int16_t) val) & (int16_t) ((1 << shift) - 1);/* == (-val) mod (2^shift) */
321}
322
323static inline
324uint64_t ClusterToLSA(uint32_t cluster)
325{
326return (((uint64_t) (cluster - CLUST_FIRST)) << gSPCShift) + gCLOffset;
327}
328
329static
330void InitIteratorFromInode(struct exfat_dir_iterator* pIter, struct exfat_inode const* pInode)
331{
332if (pInode->stream_extension_flags & SEFLAG_ALLOCATION_POSSIBLE)
333{
334uint64_t sector_length = RoundUp(pInode->valid_length, gBPSShift);
335if (pInode->stream_extension_flags & SEFLAG_INVALID_FAT_CHAIN)
336{
337pIter->lsa = ClusterToLSA(pInode->first_cluster);
338pIter->lsa_end = pIter->lsa + sector_length;
339pIter->cluster = CLUST_RSRVD;
340}
341else
342{
343pIter->lsa = 0;
344pIter->lsa_end = 0;
345pIter->cluster = pInode->first_cluster;
346pIter->residue = Residue(sector_length, gSPCShift);
347}
348}
349else
350{
351pIter->lsa = 0;
352pIter->lsa_end = 0;
353pIter->cluster = CLUST_RSRVD;
354}
355pIter->entry_offset = CacheBlockSize();
356}
357
358static
359uint8_t const* nextDirEntry(struct exfat_dir_iterator* pIter)
360{
361uint8_t const* ret;
362uint16_t toRead;
363
364if (pIter->entry_offset >= CacheBlockSize())
365{
366if (pIter->lsa == pIter->lsa_end) {
367uint32_t next_cluster, contig;
368
369getRange(pIter->cluster, CLUST_RSRVD, &next_cluster, &contig);
370if (!contig)
371{
372return NULL;
373}
374pIter->lsa = ClusterToLSA(pIter->cluster);
375pIter->lsa_end = pIter->lsa + (((uint64_t) contig) << gSPCShift);
376if (next_cluster >= CLUST_RSRVD)
377{
378pIter->lsa_end -= pIter->residue;
379}
380pIter->cluster = next_cluster;
381}
382toRead = (uint16_t) (1 << (gBPCBShift - gBPSShift));
383if (pIter->lsa + toRead > pIter->lsa_end)
384toRead = (uint16_t) (pIter->lsa_end - pIter->lsa);
385CacheRead(gCurrentIH,
386 (char*) pIter->buffer,
387 (long long) (pIter->lsa << gBPSShift),
388 ((uint32_t) toRead) << gBPSShift,
389 1);
390pIter->lsa += toRead;
391pIter->entry_offset = 0;
392}
393ret = pIter->buffer + pIter->entry_offset;
394pIter->entry_offset += sizeof(struct direntry_file);
395return ret;
396}
397
398#pragma mark -
399#pragma mark Path Search
400#pragma mark -
401
402static inline
403int32_t ToUpper(uint16_t ch)
404{
405if (gUPCase)
406{
407return gUPCase[ch];
408}
409if (ch >= 128)
410{
411return -1;
412}
413if ((uint8_t) ch >= 'a' && (uint8_t) ch <= 'z')
414{
415ch &= ~0x20;
416}
417return ch;
418}
419
420static
421int32_t NameHash(uint16_t const* component_utf16le, uint16_t numChars)
422{
423int32_t ch;
424uint16_t hash = 0;
425
426for (; numChars; ++component_utf16le, --numChars)
427{
428ch = ToUpper(OSSwapLittleToHostInt16(*component_utf16le));
429if (ch < 0)
430{
431return ch;
432}
433hash = (hash << 15) | (hash >> 1) + (uint8_t) ch;
434hash = (hash << 15) | (hash >> 1) + (uint8_t) (ch >> 8);
435}
436return hash;
437}
438
439static
440int ComponentToInode(struct exfat_dir_iterator* iterator, uint16_t const* component_utf16le, uint16_t numChars, struct exfat_inode* out_file)
441{
442union {
443uint8_t const* nde;
444struct direntry_file const* fe;
445struct direntry_stream_extension const* fse;
446struct direntry_name_extension const* ne;
447} u;
448int32_t computed_hash;
449uint8_t count2, name_length;
450
451computed_hash = NameHash(component_utf16le, numChars);
452while ((u.nde = nextDirEntry(iterator)))
453{
454if (!*u.nde)
455{
456break;
457}
458redo:
459if (*u.nde != DIRENTRY_TYPE_FILE)
460continue;
461count2 = u.fe->count2;
462if (count2 < 2)
463{
464continue;
465}
466out_file->attributes = (uint8_t) OSSwapLittleToHostInt16(u.fe->attributes);
467u.nde = nextDirEntry(iterator);
468if (!u.nde || !*u.nde)
469break;
470if (*u.nde != DIRENTRY_TYPE_ST_EX)
471goto redo;
472out_file->stream_extension_flags = u.fse->flags;
473name_length = u.fse->name_length;
474if (name_length != numChars)
475continue;
476if (computed_hash >= 0 && computed_hash != OSSwapLittleToHostInt16(u.fse->name_hash))
477continue;
478out_file->valid_length = OSSwapLittleToHostInt64(u.fse->valid_length);
479out_file->first_cluster = OSSwapLittleToHostInt32(u.fse->first_cluster);
480for (--count2; count2 && name_length; --count2) {
481int32_t ch1, ch2;
482uint16_t const* q;
483uint8_t t;
484
485u.nde = nextDirEntry(iterator);
486if (!u.nde || !*u.nde)
487goto outta_bad;
488if (*u.nde != DIRENTRY_TYPE_NA_EX)
489goto redo;
490t = name_length > LABEL_MAX_CHARS ? LABEL_MAX_CHARS : name_length;
491q = &u.ne->label[0];
492for (; t; ++q, ++component_utf16le, --t, --name_length) {
493ch1 = ToUpper(OSSwapLittleToHostInt16(*component_utf16le));
494ch2 = ToUpper(OSSwapLittleToHostInt16(*q));
495if (ch1 != ch2)
496goto abort_comparison;
497}
498}
499return 0;
500abort_comparison:;
501}
502outta_bad:
503return -1;
504}
505
506static
507int ExtractDirEntry(struct exfat_dir_iterator* iterator, char** name, long* flags, u_int32_t* time, long* infoValid)
508{
509union {
510uint8_t const* nde;
511struct direntry_file const* fe;
512struct direntry_stream_extension const* fse;
513struct direntry_name_extension const* ne;
514} u;
515uint8_t count2, name_length, t;
516uint16_t component_full[COMPONENT_MAX_CHARS], *cp_ptr;
517
518while ((u.nde = nextDirEntry(iterator))) {
519if (!*u.nde)
520break;
521redo:
522if (*u.nde != DIRENTRY_TYPE_FILE)
523continue;
524count2 = u.fe->count2;
525if (count2 < 2)
526continue;
527if (flags)
528*flags = (OSSwapLittleToHostInt16(u.fe->attributes) & ATTR_DIRECTORY) ? kFileTypeDirectory : kFileTypeFlat;
529if (time)
530*time = OSSwapLittleToHostInt32(u.fe->mod_time);
531if (!name)
532goto info_valid;
533u.nde = nextDirEntry(iterator);
534if (!u.nde || !*u.nde)
535break;
536if (*u.nde != DIRENTRY_TYPE_ST_EX)
537goto redo;
538name_length = u.fse->name_length;
539cp_ptr = &component_full[0];
540for (--count2; count2 && name_length; --count2) {
541u.nde = nextDirEntry(iterator);
542if (!u.nde || !*u.nde)
543goto outta_bad;
544if (*u.nde != DIRENTRY_TYPE_NA_EX)
545goto redo;
546t = name_length > LABEL_MAX_CHARS ? LABEL_MAX_CHARS : name_length;
547memcpy(cp_ptr, &u.ne->label[0], t * sizeof(uint16_t));
548cp_ptr += t;
549name_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);
556if (*name)
557utf_encodestr(&component_full[0], cp_ptr - &component_full[0], (uint8_t*) *name, COMPONENT_MAX_CHARS, OSLittleEndian);
558info_valid:
559if (infoValid)
560*infoValid = 1;
561return 0;
562}
563outta_bad:
564return -1;
565}
566
567static
568int PathToInode(char const* path, struct exfat_inode* out_file, uint8_t* buffer /* size CacheBlockSize() bytes */)
569{
570struct exfat_dir_iterator iterator;
571uint8_t *ptr, *slash, ch;
572uint16_t path_utf16le[COMPONENT_MAX_CHARS];
573uint16_t numChars;
574char have_prev_inode;
575
576InitIteratorFromRoot(&iterator);
577iterator.buffer = buffer;
578ptr = (uint8_t*) path;/* Note: const_cast */
579have_prev_inode = 0;
580do {
581do {
582for (slash = ptr; *slash && *slash != '/'; ++slash);
583ch = *slash;
584if (slash == ptr) {
585if (!ch) {
586if (!have_prev_inode) {
587/*
588 * Fill in pseudo-inode for Root Directory
589 */
590out_file->valid_length = 0;/* Unknown */
591out_file->first_cluster = gRDCl;
592out_file->attributes = ATTR_DIRECTORY;
593out_file->stream_extension_flags = SEFLAG_ALLOCATION_POSSIBLE | SEFLAG_PSEUDO_ROOTDIR;
594}
595return 0;
596}
597++ptr;
598continue;
599}
600break;
601} while (1);
602*slash = 0;
603utf_decodestr(ptr, &path_utf16le[0], &numChars, sizeof path_utf16le, OSLittleEndian);
604numChars = OSSwapLittleToHostInt16(numChars);
605*slash = ch;
606ptr = slash + 1;
607if (have_prev_inode)
608InitIteratorFromInode(&iterator, out_file);
609if (ComponentToInode(&iterator, &path_utf16le[0], numChars, out_file) < 0)
610break;
611if (!ch)/* was last component - done */
612return 0;
613if (!(out_file->attributes & ATTR_DIRECTORY))/* not a directory and not last component - error */
614return -1;
615have_prev_inode = 1;
616} while (1);
617return -1;
618}
619
620#pragma mark -
621#pragma mark exFAT Implementation
622#pragma mark -
623
624void
625EXFATFree(CICell ih)
626{
627if (gCurrentIH == ih) {
628gCurrentIH = NULL;
629FATCacheInvalidate();
630}
631free(ih);
632}
633
634long
635EXFATInitPartition(CICell ih)
636{
637uint8_t *buffer, bpss, spcs;
638struct exfatbootfile const* boot;
639
640if (!ih)
641return -1;
642if (gCurrentIH == ih)
643{
644CacheInit(ih, CacheBlockSize());
645return FATCacheInit(0);
646}
647
648buffer = (uint8_t*) malloc(BPS);
649if (!buffer)
650return -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
657Seek(ih, 0);
658Read(ih, (long) buffer, BPS);
659
660boot = (struct exfatbootfile const*) buffer;
661
662/* Looking for EXFAT signature. */
663if (memcmp((char const*) &boot->bf_sysid[0], EXFAT_BBID, EXFAT_BBIDLEN)) {
664free(buffer);
665return -1;
666}
667
668/*
669 * Make sure the bytes per sector and sectors per cluster are within reasonable ranges.
670 */
671bpss = boot->bf_bpss;
672if (bpss < MIN_BLOCK_SIZE_SHIFT || bpss > MAX_BLOCK_SIZE_SHIFT) {
673free(buffer);
674return -1;
675}
676
677spcs = boot->bf_spcs;
678if (spcs > (MAX_CLUSTER_SIZE_SHIFT - bpss)) {
679free(buffer);
680return -1;
681}
682
683if (FATCacheInit(1) < 0)
684{
685free(buffer);
686return -1;
687}
688
689gBPSShift = bpss;
690gSPCShift = spcs;
691gFATOffset = OSSwapLittleToHostInt32(boot->bf_fatoff);
692gFATLength = OSSwapLittleToHostInt32(boot->bf_fatlen);
693gCLOffset = OSSwapLittleToHostInt32(boot->bf_cloff);
694gCLCount = OSSwapLittleToHostInt32(boot->bf_clcnt);
695gRDCl = OSSwapLittleToHostInt32(boot->bf_rdircl);
696gBPCBShift = bpss + spcs;
697if (gBPCBShift > MAX_BLOCK_SIZE_SHIFT)
698gBPCBShift = MAX_BLOCK_SIZE_SHIFT;
699
700gCurrentIH = ih;
701
702CacheInit(ih, CacheBlockSize());
703
704free(buffer);
705return 0;
706}
707
708long
709EXFATGetDirEntry(CICell ih, char * dirPath, long long * dirIndex,
710 char ** name, long * flags, u_int32_t * time,
711 FinderInfo * finderInfo, long * infoValid)
712{
713struct exfat_dir_iterator* iterator;
714
715if (!dirPath || !dirIndex)
716return -1;
717
718if (EXFATInitPartition(ih) < 0)
719return -1;
720
721if (*dirPath == '/')
722++dirPath;
723
724iterator = (struct exfat_dir_iterator*) (long) *dirIndex;
725if (!iterator) {
726struct exfat_inode inode;
727uint8_t* buffer;
728
729buffer = (uint8_t*) malloc(CacheBlockSize() + sizeof *iterator);
730if (!buffer)
731return -1;
732iterator = (struct exfat_dir_iterator*) (buffer + CacheBlockSize());
733if (PathToInode(dirPath, &inode, buffer) < 0 ||
734!(inode.attributes & ATTR_DIRECTORY)) {
735free(buffer);
736return -1;
737}
738if (inode.stream_extension_flags & SEFLAG_PSEUDO_ROOTDIR)
739InitIteratorFromRoot(iterator);
740else
741InitIteratorFromInode(iterator, &inode);
742iterator->buffer = buffer;
743*dirIndex = (long long) (long) iterator;
744}
745if (ExtractDirEntry(iterator, name, flags, time, infoValid) < 0) {
746free(iterator->buffer);
747*dirIndex = 0;
748return -1;
749}
750return 0;
751}
752
753long
754EXFATReadFile(CICell ih, char * filePath, void *base, uint64_t offset, uint64_t length)
755{
756uint64_t size, toRead, leftToRead;
757struct exfat_inode inode;
758uint8_t* buffer;
759uint32_t cluster;
760
761if (EXFATInitPartition(ih) < 0)
762return -1;
763
764if (*filePath == '/')
765++filePath;
766
767buffer = (uint8_t*) malloc(CacheBlockSize());
768if (!buffer)
769return -1;
770
771if (PathToInode(filePath, &inode, buffer) < 0 ||
772(inode.attributes & ATTR_DIRECTORY) != 0) {
773free(buffer);
774return -1;
775}
776free(buffer);
777if (!(inode.stream_extension_flags & SEFLAG_ALLOCATION_POSSIBLE))
778return (!offset && !length) ? 0 : -1;
779cluster = inode.first_cluster;
780size = inode.valid_length;
781if (size == offset && !length)
782return 0;
783if (size <= offset)
784return -1;
785toRead = size - offset;
786if (length && length < toRead)
787toRead = length;
788if (inode.stream_extension_flags & SEFLAG_INVALID_FAT_CHAIN) {
789Seek(ih, (long long) ((ClusterToLSA(cluster) << gBPSShift) + offset));
790Read(ih, (long) base, (long) toRead);
791return (long) toRead;
792}
793leftToRead = toRead;
794do {
795uint64_t chunk, canRead;
796uint32_t next_cluster, contig;
797
798getRange(cluster, CLUST_RSRVD, &next_cluster, &contig);
799if (!contig)
800break;
801chunk = ((uint64_t) contig) << (gBPSShift + gSPCShift);
802if (offset >= chunk) {
803offset -= chunk;
804cluster = next_cluster;
805continue;
806}
807canRead = chunk - offset;
808if (canRead > leftToRead)
809canRead = leftToRead;
810Seek(ih, (long long) ((ClusterToLSA(cluster) << gBPSShift) + offset));
811Read(ih, (long) base, (long) canRead);
812base = ((uint8_t*) base) + canRead;
813cluster = next_cluster;
814offset = 0;
815leftToRead -= canRead;
816} while (leftToRead);
817return (long) (toRead - leftToRead);
818}
819
820long
821EXFATGetFileBlock(CICell ih, char *filePath, unsigned long long *firstBlock)
822{
823uint8_t* buffer;
824struct exfat_inode inode;
825uint32_t cluster;
826
827if (EXFATInitPartition(ih) < 0)
828return -1;
829
830if (*filePath == '/')
831++filePath;
832
833buffer = (uint8_t*) malloc(CacheBlockSize());
834if (!buffer)
835return -1;
836
837if (PathToInode(filePath, &inode, buffer) < 0 ||
838(inode.attributes & ATTR_DIRECTORY) != 0 ||
839!(inode.stream_extension_flags & SEFLAG_ALLOCATION_POSSIBLE)) {
840free(buffer);
841return -1;
842}
843free(buffer);
844cluster = inode.first_cluster;
845if (cluster < CLUST_FIRST || cluster >= CLUST_RSRVD)
846return -1;
847*firstBlock = ClusterToLSA(cluster);
848return 0;
849}
850
851long
852EXFATLoadFile(CICell ih, char * filePath)
853{
854return 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 */
862void
863EXFATGetDescription(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:
964if (buf)
965{
966free(buf);
967}
968
969DBG("EXFAT: error\n");
970PAUSE();
971return;
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 */
979long EXFATGetUUID(CICell ih, char *uuidStr)
980{
981uint32_t volsn;
982struct exfatbootfile *boot;
983void *buf = malloc(MAX_BLOCK_SIZE);
984if ( !buf )
985return -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 */
993Seek(ih, 0);
994Read(ih, (long)buf, MAX_BLOCK_SIZE);
995
996boot = (struct exfatbootfile *) buf;
997
998/*
999 * Check the "EXFAT " signature.
1000 */
1001if (memcmp((const char *)boot->bf_sysid, EXFAT_BBID, EXFAT_BBIDLEN) != 0)
1002{
1003return -1;
1004}
1005
1006// Check for non-null volume serial number
1007volsn = OSSwapLittleToHostInt32(boot->bf_volsn);
1008if( !volsn )
1009{
1010return -1;
1011}
1012
1013// Use UUID like the one you get on Windows
1014sprintf(uuidStr, "%04X-%04X", (unsigned short)(volsn >> 16) & 0xFFFF,
1015 (unsigned short)volsn & 0xFFFF);
1016
1017DBG("EXFATGetUUID: %x:%x = %s\n", ih->biosdev, ih->part_no, uuidStr);
1018return 0;
1019}
1020
1021/**
1022 * Returns true if given buffer is the boot rec of the EXFAT volume.
1023 */
1024bool EXFATProbe(const void * buffer)
1025{
1026bool result = false;
1027
1028// boot sector structure
1029const struct exfatbootfile* boot = buffer;
1030
1031// Looking for EXFAT signature.
1032if (memcmp((const char *)boot->bf_sysid, EXFAT_BBID, EXFAT_BBIDLEN) == 0)
1033{
1034result = true;
1035}
1036
1037DBG("EXFATProbe: %d\n", result ? 1 : 0);
1038return result;
1039}
1040

Archive Download this file

Revision: 2759