Chameleon

Chameleon Svn Source Tree

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

Archive Download this file

Revision: 2517