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

Archive Download this file

Revision: 2853