Chameleon

Chameleon Commit Details

Date:2014-12-19 13:04:11 (9 years 4 months ago)
Author:ErmaC
Commit:2516
Parents: 2515
Message:Full implementation of exfat support for Chameleon's boot2 stage.
Changes:
M/trunk/i386/libsaio/fdisk.h
M/trunk/i386/libsaio/exfat.c
M/trunk/i386/libsaio/disk.c
M/trunk/CHANGES
M/trunk/i386/libsaio/exfat.h

File differences

trunk/i386/libsaio/exfat.c
2727
2828
2929
30
31
32
3033
3134
3235
......
3538
3639
3740
41
42
43
44
3845
3946
4047
......
4754
4855
4956
50
57
5158
5259
53
54
60
61
62
63
64
65
66
67
68
69
70
71
5572
56
73
74
75
5776
77
78
79
80
81
82
83
84
85
86
87
88
5889
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
59118
60119
61120
62121
63122
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
85144
86145
87146
88
89
90
91
147
148
149
150
151
152
92153
93154
155
156
157
158
159
160
161
162
163
94164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
95841
96842
97
843
98844
99845
100846
101847
102848
103849
104
105
850
106851
107852
108853
109854
110855
111856
112
857
113858
114859
115860
......
127872
128873
129874
130
875
131876
132877
133878
......
151896
152897
153898
154
155
156
899
900
901
157902
158
903
159904
160905
161906
162
163
164
907
908
909
165910
166
911
167912
168913
169
914
170915
171
172
916
917
173918
174919
175
920
176921
177922
178923
......
184929
185930
186931
187
932
188933
189934
190935
191936
192
937
193938
194939
195940
......
200945
201946
202947
203
204
205
206
948
949
950
951
952
953
954
955
207956
208957
209958
......
213962
214963
215964
216
217
218
219
965
966
967
968
969
220970
221
222
223
224
225
226
227
228
971
972
973
974
975
976
977
978
229979
230
980
231981
232
233
234
235
236
982
983
984
985
986
987
988
237989
238
239
240
990
991
992
993
994
995
241996
242
243
244
997
998
999
2451000
246
247
248
1001
1002
1003
2491004
2501005
2511006
2521007
2531008
2541009
255
1010
2561011
257
258
1012
1013
2591014
260
261
262
263
264
265
1015
1016
1017
1018
1019
1020
1021
1022
2661023
* support for EXFAT volume label reading
* EXFAT info from: http://www.ntfs.com/exfat-overview.htm
*
* Zenith432, Nov 30 2014
* support for reading files
*
* EXFAT shares partition type with NTFS (0x7) and easiest way of
* adding it was through ntfs.c module. All functions here are called
* from similar ntfs.c funcs as fallback (if not NTFS, maybe it's EXFAT).
#include "libsaio.h"
#include "sl.h"
#pragma mark -
#pragma mark Preprocessor Definitions
#pragma mark -
#ifndef DEBUG_EXFAT
#define DEBUG_EXFAT 0
#endif
#define PAUSE()
#endif
#defineEXFAT_BBID"EXFAT "
#defineEXFAT_BBID&gExfatID[0]
#defineEXFAT_BBIDLEN8
#define MAX_BLOCK_SIZE4096
#define MAX_CLUSTER_SIZE32 * 1024 * 1024
#define MIN_BLOCK_SIZE_SHIFT9
#define MAX_BLOCK_SIZE_SHIFT12
#define MAX_BLOCK_SIZE(1 << MAX_BLOCK_SIZE_SHIFT)
#define MAX_CLUSTER_SIZE_SHIFT25
#define CLUST_FIRST2/* reserved cluster range */
#define CLUST_RSRVD0xfffffff7/* reserved cluster range */
#define INVALID_FAT_ADDRESS0xffffffff
#define ATTR_DIRECTORY0x10/* entry is a directory name */
#define SEFLAG_ALLOCATION_POSSIBLE1
#define SEFLAG_INVALID_FAT_CHAIN2
#define SEFLAG_PSEUDO_ROOTDIR0x80
#define COMPONENT_MAX_CHARS255/* Max # characters in path name single component */
#define ERROR -1
#pragma mark -
#pragma mark Static Data
#pragma mark -
static CICell gCurrentIH = NULL;
static uint8_t gBPSShift = 0;/* log_2(Bytes-Per-Sector) */
static uint8_t gSPCShift = 0;/* log_2(Sectors-Per-Cluster) */
static uint32_t gFATOffset = 0;/* in sectors */
static uint32_t gFATLength = 0;/* in sectors */
static uint32_t gCLOffset = 0;/* in sectors */
static uint32_t gCLCount = 0;/* in clusters */
static uint32_t gRDCl = 0;/* Root Directory Cluster Number */
static uint8_t* gFATCacheBuffer = NULL;
static uint32_t gCachedFATBlockAddress = 0;
static uint16_t* gUPCase = NULL;/* If loaded, should be exactly 2^16 * sizeof(uint16_t) bytes long */
static uint8_t gBPCBShift = 0;/* log_2(Bytes-Per-Cache-Block) */
static char const gExfatID[] = "EXFAT ";
#pragma mark -
#pragma mark Helper Structures
#pragma mark -
struct exfat_dir_iterator
{
uint64_t lsa;/* Current sector address */
uint64_t lsa_end;/* Last sector address + 1 */
uint8_t* buffer;
uint32_t cluster;/* Next cluster number */
uint16_t residue;/* Number of sectors in last cluster */
uint16_t entry_offset;/* Offset of next entry in buffer */
};
struct exfat_inode
{
uint64_t valid_length;/* File/Directory length */
uint32_t first_cluster;/* First cluster number */
uint8_t attributes;/* From direntry_file::attributes (truncated to 8 bits) */
uint8_t stream_extension_flags;/* From direntry_stream_extension::flags */
};
#pragma mark -
#pragma mark exFAT on-disk structures
#pragma mark -
/*
* boot sector of the partition
* http://www.ntfs.com/exfat-boot-sector.htm
*/
struct exfatbootfile {
u_int8_t reserved1[3];/* JumpBoot: 0xEB7690 */
u_int8_t bf_sysid[8];/* FileSystemName: 'EXFAT ' */
u_int8_t reserved2[53];/* MustBeZero */
u_int64_t bf_prtoff;/* PartitionOffset: In sectors; if 0, shall be ignored */
u_int64_t bf_vollen;/* VolumeLength: Size of exFAT volume in sectors */
u_int32_t bf_fatoff;/* FatOffset: In sectors */
u_int32_t bf_fatlen;/* FatLength: In sectors. May exceed the required space in order to align the second FAT */
u_int32_t bf_cloff;/* ClusterHeapOffset: In sectors. */
u_int32_t bf_clcnt;/* ClusterCount: 2^32-11 is the maximum number of clusters could be described. */
u_int32_t bf_rdircl;/* RootDirectoryCluster. */
u_int32_t bf_volsn;/* VolumeSerialNumber. */
u_int16_t bf_fsrev;/* FileSystemRevision: as MAJOR.minor, major revision is high byte, minor is low byte; currently 01.00. */
u_int16_t bf_volflags;/* VolumeFlags. */
u_int8_t bf_bpss;/* BytesPerSectorShift: Power of 2. Minimum 9 (512 bytes per sector), maximum 12 (4096 bytes per sector) */
u_int8_t bf_nfats;/* NumberOfFats: 2 is for TexFAT only */
u_int8_t bf_drvs;/* DriveSelect: Extended INT 13h drive number; typically 0x80 */
u_int8_t reserved3[7];/* Reserved */
u_int8_t bootcode[390];/* BootCode */
u_int16_t bf_bsig;/* BootSignature: 0xAA55 */
uint8_treserved1[3];/* JumpBoot: 0xEB7690 */
uint8_tbf_sysid[8];/* FileSystemName: 'EXFAT ' */
uint8_treserved2[53];/* MustBeZero */
uint64_tbf_prtoff;/* PartitionOffset: In sectors; if 0, shall be ignored */
uint64_tbf_vollen;/* VolumeLength: Size of exFAT volume in sectors */
uint32_tbf_fatoff;/* FatOffset: In sectors */
uint32_tbf_fatlen;/* FatLength: In sectors. May exceed the required space in order to align the second FAT */
uint32_tbf_cloff;/* ClusterHeapOffset: In sectors. */
uint32_tbf_clcnt;/* ClusterCount: 2^32-11 is the maximum number of clusters could be described. */
uint32_tbf_rdircl;/* RootDirectoryCluster. */
uint32_tbf_volsn;/* VolumeSerialNumber. */
uint16_tbf_fsrev;/* FileSystemRevision: as MAJOR.minor, major revision is high byte, minor is low byte; currently 01.00. */
uint16_tbf_volflags;/* VolumeFlags. */
uint8_tbf_bpss;/* BytesPerSectorShift: Power of 2. Minimum 9 (512 bytes per sector), maximum 12 (4096 bytes per sector) */
uint8_tbf_nfats;/* NumberOfFats: 2 is for TexFAT only */
uint8_tbf_drvs;/* DriveSelect: Extended INT 13h drive number; typically 0x80 */
uint8_treserved3[7];/* Reserved */
uint8_tbootcode[390];/* BootCode */
uint16_tbf_bsig;/* BootSignature: 0xAA55 */
};
struct direntry_label {
u_int8_t type;/* EntryType: 0x83 (or 0x03 if label is empty) */
u_int8_t llen;/* CharacterCount: Length in Unicode characters (max 11) */
u_int16_t label[11];/* VolumeLabel: Unicode characters (max 11) */
u_int8_t reserved1[8];/* Reserved */
#define DIRENTRY_TYPE_LABEL((uint8_t) 0x83)
uint8_ttype;/* EntryType: 0x83 (or 0x03 if label is empty) */
uint8_tllen;/* CharacterCount: Length in Unicode characters (max 11) */
#define VOLUME_LABEL_MAX_CHARS11
uint16_tlabel[11];/* VolumeLabel: Unicode characters (max 11) */
uint8_treserved1[8];/* Reserved */
};
#if UNUSED
struct direntry_allocation_bitmap
{
uint8_ttype;/* EntryType: 0x81 (or 0x01 if entry is empty) */
uint8_tbitmap_flags;/* bit 0: 0 1st bitmap, 1 2nd bitmap */
uint8_treserved1[18];/* Reserved */
uint32_tfirst_cluster;/* Cluster address of 1st data block */
uint64_tdata_length;/* Length of the data */
};
struct direntry_upcase_table
{
uint8_ttype;/* EntryType: 0x82 (or 0x02 if entry is empty) */
uint8_treserved1[3];/* Reserved */
uint32_tchecksum;/* Table Checksum */
uint8_treserved2[12];/* Reserved */
uint32_tfirst_cluster;/* Cluster address of 1st data block */
uint64_tdata_length;/* Length of the data */
};
/*
* Skipped:
* Volume GUID direntry 0xA0
* TexFAT Padding direntry 0xA1
* Windows CE Access Control Table 0xE2
*/
#endif /* UNUSED */
struct direntry_file
{
#define DIRENTRY_TYPE_FILE((uint8_t) 0x85)
uint8_ttype;/* EntryType: 0x85 (or 0x05 if entry is empty) */
uint8_tcount2;/* Secondary Count */
uint16_tchecksum;/* Set Checksum */
uint16_tattributes;/* File Attributes, 1 - R, 2 - H, 4 - S, 16 - D, 32 - A */
uint8_treserved1[2];/* Reserved */
uint32_tcreate_time;/* Create Time, DOS Timestamp Format */
uint32_tmod_time;/* Last Modified Time, DOS Timestamp Format */
uint32_taccess_time;/* Last Accessed Time, DOS Timestamp Format */
uint8_tcreate_10ms;/* 10ms increments range 0 - 199 */
uint8_tmod_10ms;/* 10ms increments range 0 - 199 */
uint8_tcreate_tzoff;/* TZ Offset, difference to UTC in 15 min increments */
uint8_tmod_tzoff;/* TZ Offset, difference to UTC in 15 min increments */
uint8_taccess_tzoff;/* TZ Offset, difference to UTC in 15 min increments */
uint8_treserved2[7];/* Reserved */
};
struct direntry_stream_extension
{
#define DIRENTRY_TYPE_ST_EX((uint8_t) 0xC0)
uint8_ttype;/* EntryType: 0xC0 (or 0x40 if entry is empty) */
uint8_tflags;/* bit 0 - Allocation Possible (1 Yes/0 No), bit 1 - No FAT Chain (1 Invalid/0 Valid) */
uint8_treserved1;/* Reserved */
uint8_tname_length;/* Name Length */
uint16_tname_hash;/* Name Hash */
uint8_treserved2[2];/* Reserved */
uint64_tvalid_length;/* Valid Data Length */
uint8_treserved3[4];/* Reserved */
uint32_tfirst_cluster;/* Cluster address of 1st data block */
uint64_tdata_length;/* Length of the data */
};
struct direntry_name_extension
{
#define DIRENTRY_TYPE_NA_EX((uint8_t) 0xC1)
uint8_ttype;/* EntryType: 0xC1 (or 0x41 if entry is empty) */
uint8_treserved1;/* Reserved */
#define LABEL_MAX_CHARS 15
uint16_tlabel[15];/* 15 characters of file name (UTF16LE) */
};
#pragma mark -
#pragma mark FATCache
#pragma mark -
static
int FATCacheInit(void)
{
if (!gFATCacheBuffer)
{
gFATCacheBuffer = (uint8_t*) malloc(MAX_BLOCK_SIZE);
if (!gFATCacheBuffer)
{
return -1;
}
}
gCachedFATBlockAddress = INVALID_FAT_ADDRESS;
return 0;
}
static inline
void FATCacheInvalidate(void)
{
gCachedFATBlockAddress = INVALID_FAT_ADDRESS;
}
static inline
uint16_t CacheBlockSize(void)
{
return (uint16_t) (1 << gBPCBShift);
}
static
int getRange(uint32_t cluster, uint32_t maxContiguousClusters, uint32_t* pNextCluster, uint32_t* pNumContiguousClusters)
{
uint32_t count, lcba;
uint16_t mask;
uint8_t shift;
if (!pNextCluster || !pNumContiguousClusters)
{
return -1;
}
count = 0;
shift = gBPCBShift - 2;
mask = (uint16_t) ((1 << shift) - 1);
while (cluster >= CLUST_FIRST && cluster < CLUST_RSRVD && count < maxContiguousClusters)
{
++count;
lcba = cluster >> shift;
if (lcba != gCachedFATBlockAddress)
{
Seek(gCurrentIH, (((long long) gFATOffset) << gBPSShift) + (((long long) lcba) << gBPCBShift));
Read(gCurrentIH, (long) gFATCacheBuffer, CacheBlockSize());
gCachedFATBlockAddress = lcba;
}
lcba = cluster + 1;
cluster = OSSwapLittleToHostInt32(((uint32_t const*) gFATCacheBuffer)[cluster & mask]);
if (cluster != lcba)
break;
}
*pNextCluster = cluster;
*pNumContiguousClusters = count;
return 0;
}
#pragma mark -
#pragma mark Directory Iterator
#pragma mark -
static
void InitIteratorFromRoot(struct exfat_dir_iterator* pIter)
{
pIter->lsa = 0;
pIter->lsa_end = 0;
pIter->cluster = gRDCl;
pIter->residue = 0;
pIter->entry_offset = CacheBlockSize();
}
static inline
uint64_t RoundUp(uint64_t val, uint8_t shift)
{
return (val + (1 << shift) - 1) >> shift;/* == RoundUpToInt(val/(2^shift)) */
}
static inline
uint16_t Residue(uint64_t val, uint8_t shift)
{
return (-(int16_t) val) & (int16_t) ((1 << shift) - 1);/* == (-val) mod (2^shift) */
}
static inline
uint64_t ClusterToLSA(uint32_t cluster)
{
return (((uint64_t) (cluster - CLUST_FIRST)) << gSPCShift) + gCLOffset;
}
static
void InitIteratorFromInode(struct exfat_dir_iterator* pIter, struct exfat_inode const* pInode)
{
if (pInode->stream_extension_flags & SEFLAG_ALLOCATION_POSSIBLE)
{
uint64_t sector_length = RoundUp(pInode->valid_length, gBPSShift);
if (pInode->stream_extension_flags & SEFLAG_INVALID_FAT_CHAIN)
{
pIter->lsa = ClusterToLSA(pInode->first_cluster);
pIter->lsa_end = pIter->lsa + sector_length;
pIter->cluster = CLUST_RSRVD;
}
else
{
pIter->lsa = 0;
pIter->lsa_end = 0;
pIter->cluster = pInode->first_cluster;
pIter->residue = Residue(sector_length, gSPCShift);
}
}
else
{
pIter->lsa = 0;
pIter->lsa_end = 0;
pIter->cluster = CLUST_RSRVD;
}
pIter->entry_offset = CacheBlockSize();
}
static
uint8_t const* nextDirEntry(struct exfat_dir_iterator* pIter)
{
uint8_t const* ret;
uint16_t toRead;
if (pIter->entry_offset >= CacheBlockSize())
{
if (pIter->lsa == pIter->lsa_end) {
uint32_t next_cluster, contig;
getRange(pIter->cluster, CLUST_RSRVD, &next_cluster, &contig);
if (!contig)
{
return NULL;
}
pIter->lsa = ClusterToLSA(pIter->cluster);
pIter->lsa_end = pIter->lsa + (((uint64_t) contig) << gSPCShift);
if (next_cluster >= CLUST_RSRVD)
{
pIter->lsa_end -= pIter->residue;
}
pIter->cluster = next_cluster;
}
toRead = (uint16_t) (1 << (gBPCBShift - gBPSShift));
if (pIter->lsa + toRead > pIter->lsa_end)
toRead = (uint16_t) (pIter->lsa_end - pIter->lsa);
Seek(gCurrentIH, (long long) (pIter->lsa << gBPSShift));
Read(gCurrentIH, (long) pIter->buffer, ((long) toRead) << gBPSShift);
pIter->lsa += toRead;
pIter->entry_offset = 0;
}
ret = pIter->buffer + pIter->entry_offset;
pIter->entry_offset += sizeof(struct direntry_file);
return ret;
}
#pragma mark -
#pragma mark Path Search
#pragma mark -
static inline
int32_t ToUpper(uint16_t ch)
{
if (gUPCase)
{
return gUPCase[ch];
}
if (ch >= 128)
{
return -1;
}
if ((uint8_t) ch >= 'a' && (uint8_t) ch <= 'z')
{
ch &= ~0x20;
}
return ch;
}
static
int32_t NameHash(uint16_t const* component_utf16le, uint16_t numChars)
{
int32_t ch;
uint16_t hash = 0;
for (; numChars; ++component_utf16le, --numChars)
{
ch = ToUpper(OSSwapLittleToHostInt16(*component_utf16le));
if (ch < 0)
{
return ch;
}
hash = (hash << 15) | (hash >> 1) + (uint8_t) ch;
hash = (hash << 15) | (hash >> 1) + (uint8_t) (ch >> 8);
}
return hash;
}
static
int ComponentToInode(struct exfat_dir_iterator* iterator, uint16_t const* component_utf16le, uint16_t numChars, struct exfat_inode* out_file)
{
union {
uint8_t const* nde;
struct direntry_file const* fe;
struct direntry_stream_extension const* fse;
struct direntry_name_extension const* ne;
} u;
int32_t computed_hash;
uint8_t count2, name_length;
computed_hash = NameHash(component_utf16le, numChars);
while ((u.nde = nextDirEntry(iterator)))
{
if (!*u.nde)
{
break;
}
redo:
if (*u.nde != DIRENTRY_TYPE_FILE)
continue;
count2 = u.fe->count2;
if (count2 < 2)
{
continue;
}
out_file->attributes = (uint8_t) OSSwapLittleToHostInt16(u.fe->attributes);
u.nde = nextDirEntry(iterator);
if (!u.nde || !*u.nde)
break;
if (*u.nde != DIRENTRY_TYPE_ST_EX)
goto redo;
out_file->stream_extension_flags = u.fse->flags;
name_length = u.fse->name_length;
if (name_length != numChars)
continue;
if (computed_hash >= 0 && computed_hash != OSSwapLittleToHostInt16(u.fse->name_hash))
continue;
out_file->valid_length = OSSwapLittleToHostInt64(u.fse->valid_length);
out_file->first_cluster = OSSwapLittleToHostInt32(u.fse->first_cluster);
for (--count2; count2 && name_length; --count2) {
int32_t ch1, ch2;
uint16_t const* q;
uint8_t t;
u.nde = nextDirEntry(iterator);
if (!u.nde || !*u.nde)
goto outta_bad;
if (*u.nde != DIRENTRY_TYPE_NA_EX)
goto redo;
t = name_length > LABEL_MAX_CHARS ? LABEL_MAX_CHARS : name_length;
q = &u.ne->label[0];
for (; t; ++q, ++component_utf16le, --t, --name_length) {
ch1 = ToUpper(OSSwapLittleToHostInt16(*component_utf16le));
ch2 = ToUpper(OSSwapLittleToHostInt16(*q));
if (ch1 != ch2)
goto abort_comparison;
}
}
return 0;
abort_comparison:;
}
outta_bad:
return -1;
}
static
int ExtractDirEntry(struct exfat_dir_iterator* iterator, char** name, long* flags, u_int32_t* time, long* infoValid)
{
union {
uint8_t const* nde;
struct direntry_file const* fe;
struct direntry_stream_extension const* fse;
struct direntry_name_extension const* ne;
} u;
uint8_t count2, name_length, t;
uint16_t component_full[COMPONENT_MAX_CHARS], *cp_ptr;
while ((u.nde = nextDirEntry(iterator))) {
if (!*u.nde)
break;
redo:
if (*u.nde != DIRENTRY_TYPE_FILE)
continue;
count2 = u.fe->count2;
if (count2 < 2)
continue;
if (flags)
*flags = (OSSwapLittleToHostInt16(u.fe->attributes) & ATTR_DIRECTORY) ? kFileTypeDirectory : kFileTypeFlat;
if (time)
*time = OSSwapLittleToHostInt32(u.fe->mod_time);
if (!name)
goto info_valid;
u.nde = nextDirEntry(iterator);
if (!u.nde || !*u.nde)
break;
if (*u.nde != DIRENTRY_TYPE_ST_EX)
goto redo;
name_length = u.fse->name_length;
cp_ptr = &component_full[0];
for (--count2; count2 && name_length; --count2) {
u.nde = nextDirEntry(iterator);
if (!u.nde || !*u.nde)
goto outta_bad;
if (*u.nde != DIRENTRY_TYPE_NA_EX)
goto redo;
t = name_length > LABEL_MAX_CHARS ? LABEL_MAX_CHARS : name_length;
memcpy(cp_ptr, &u.ne->label[0], t * sizeof(uint16_t));
cp_ptr += t;
name_length -= t;
}
/*
* Note: for ASCII can allocate exactly name_length + 1,
* but in case of multibyte characters, allow more space.
*/
*name = (char*) malloc(COMPONENT_MAX_CHARS + 1);
if (*name)
utf_encodestr(&component_full[0], cp_ptr - &component_full[0], (uint8_t*) *name, COMPONENT_MAX_CHARS, OSLittleEndian);
info_valid:
if (infoValid)
*infoValid = 1;
return 0;
}
outta_bad:
return -1;
}
static
int PathToInode(char const* path, struct exfat_inode* out_file, uint8_t* buffer /* size CacheBlockSize() bytes */)
{
struct exfat_dir_iterator iterator;
uint8_t *ptr, *slash, ch;
uint16_t path_utf16le[COMPONENT_MAX_CHARS];
uint16_t numChars;
char have_prev_inode;
InitIteratorFromRoot(&iterator);
iterator.buffer = buffer;
ptr = (uint8_t*) path;/* Note: const_cast */
have_prev_inode = 0;
do {
do {
for (slash = ptr; *slash && *slash != '/'; ++slash);
ch = *slash;
if (slash == ptr) {
if (!ch) {
if (!have_prev_inode) {
/*
* Fill in pseudo-inode for Root Directory
*/
out_file->valid_length = 0;/* Unknown */
out_file->first_cluster = gRDCl;
out_file->attributes = ATTR_DIRECTORY;
out_file->stream_extension_flags = SEFLAG_ALLOCATION_POSSIBLE | SEFLAG_PSEUDO_ROOTDIR;
}
return 0;
}
++ptr;
continue;
}
break;
} while (1);
*slash = 0;
utf_decodestr(ptr, &path_utf16le[0], &numChars, sizeof path_utf16le, OSLittleEndian);
numChars = OSSwapLittleToHostInt16(numChars);
*slash = ch;
ptr = slash + 1;
if (have_prev_inode)
InitIteratorFromInode(&iterator, out_file);
if (ComponentToInode(&iterator, &path_utf16le[0], numChars, out_file) < 0)
break;
if (!ch)/* was last component - done */
return 0;
if (!(out_file->attributes & ATTR_DIRECTORY))/* not a directory and not last component - error */
return -1;
have_prev_inode = 1;
} while (1);
return -1;
}
#pragma mark -
#pragma mark exFAT Implementation
#pragma mark -
void
EXFATFree(CICell ih)
{
if (gCurrentIH == ih) {
gCurrentIH = NULL;
FATCacheInvalidate();
}
free(ih);
}
long
EXFATInitPartition(CICell ih)
{
uint8_t *buffer, bpss, spcs;
struct exfatbootfile const* boot;
if (!ih)
return -1;
if (gCurrentIH == ih)
return FATCacheInit();
buffer = (uint8_t*) malloc(BPS);
if (!buffer)
return -1;
/*
* Read the boot sector of the filesystem, and then check the
* boot signature. If not a boot sector then error out.
*/
Seek(ih, 0);
Read(ih, (long) buffer, BPS);
boot = (struct exfatbootfile const*) buffer;
/* Looking for EXFAT signature. */
if (memcmp((char const*) &boot->bf_sysid[0], EXFAT_BBID, EXFAT_BBIDLEN)) {
free(buffer);
return -1;
}
/*
* Make sure the bytes per sector and sectors per cluster are within reasonable ranges.
*/
bpss = boot->bf_bpss;
if (bpss < MIN_BLOCK_SIZE_SHIFT || bpss > MAX_BLOCK_SIZE_SHIFT) {
free(buffer);
return -1;
}
spcs = boot->bf_spcs;
if (spcs > (MAX_CLUSTER_SIZE_SHIFT - bpss)) {
free(buffer);
return -1;
}
if (FATCacheInit() < 0) {
free(buffer);
return -1;
}
gBPSShift = bpss;
gSPCShift = spcs;
gFATOffset = OSSwapLittleToHostInt32(boot->bf_fatoff);
gFATLength = OSSwapLittleToHostInt32(boot->bf_fatlen);
gCLOffset = OSSwapLittleToHostInt32(boot->bf_cloff);
gCLCount = OSSwapLittleToHostInt32(boot->bf_clcnt);
gRDCl = OSSwapLittleToHostInt32(boot->bf_rdircl);
gBPCBShift = bpss + spcs;
if (gBPCBShift > MAX_BLOCK_SIZE_SHIFT)
gBPCBShift = MAX_BLOCK_SIZE_SHIFT;
gCurrentIH = ih;
free(buffer);
return 0;
}
long
EXFATGetDirEntry(CICell ih, char * dirPath, long long * dirIndex,
char ** name, long * flags, u_int32_t * time,
FinderInfo * finderInfo, long * infoValid)
{
struct exfat_dir_iterator* iterator;
if (!dirPath || !dirIndex)
return -1;
if (EXFATInitPartition(ih) < 0)
return -1;
if (*dirPath == '/')
++dirPath;
iterator = (struct exfat_dir_iterator*) (long) *dirIndex;
if (!iterator) {
struct exfat_inode inode;
uint8_t* buffer;
buffer = (uint8_t*) malloc(CacheBlockSize() + sizeof *iterator);
if (!buffer)
return -1;
iterator = (struct exfat_dir_iterator*) (buffer + CacheBlockSize());
if (PathToInode(dirPath, &inode, buffer) < 0 ||
!(inode.attributes & ATTR_DIRECTORY)) {
free(buffer);
return -1;
}
if (inode.stream_extension_flags & SEFLAG_PSEUDO_ROOTDIR)
InitIteratorFromRoot(iterator);
else
InitIteratorFromInode(iterator, &inode);
iterator->buffer = buffer;
*dirIndex = (long long) (long) iterator;
}
if (ExtractDirEntry(iterator, name, flags, time, infoValid) < 0) {
free(iterator->buffer);
*dirIndex = 0;
return -1;
}
return 0;
}
long
EXFATReadFile(CICell ih, char * filePath, void *base, uint64_t offset, uint64_t length)
{
uint64_t size, toRead, leftToRead;
struct exfat_inode inode;
uint8_t* buffer;
uint32_t cluster;
if (EXFATInitPartition(ih) < 0)
return -1;
if (*filePath == '/')
++filePath;
buffer = (uint8_t*) malloc(CacheBlockSize());
if (!buffer)
return -1;
if (PathToInode(filePath, &inode, buffer) < 0 ||
(inode.attributes & ATTR_DIRECTORY) != 0) {
free(buffer);
return -1;
}
free(buffer);
if (!(inode.stream_extension_flags & SEFLAG_ALLOCATION_POSSIBLE))
return (!offset && !length) ? 0 : -1;
cluster = inode.first_cluster;
size = inode.valid_length;
if (size == offset && !length)
return 0;
if (size <= offset)
return -1;
toRead = size - offset;
if (length && length < toRead)
toRead = length;
if (inode.stream_extension_flags & SEFLAG_INVALID_FAT_CHAIN) {
Seek(ih, (long long) ((ClusterToLSA(cluster) << gBPSShift) + offset));
Read(ih, (long) base, (long) toRead);
return (long) toRead;
}
leftToRead = toRead;
do {
uint64_t chunk, canRead;
uint32_t next_cluster, contig;
getRange(cluster, CLUST_RSRVD, &next_cluster, &contig);
if (!contig)
break;
chunk = ((uint64_t) contig) << (gBPSShift + gSPCShift);
if (offset >= chunk) {
offset -= chunk;
cluster = next_cluster;
continue;
}
canRead = chunk - offset;
if (canRead > leftToRead)
canRead = leftToRead;
Seek(ih, (long long) ((ClusterToLSA(cluster) << gBPSShift) + offset));
Read(ih, (long) base, (long) canRead);
base = ((uint8_t*) base) + canRead;
cluster = next_cluster;
offset = 0;
leftToRead -= canRead;
} while (leftToRead);
return (long) (toRead - leftToRead);
}
long
EXFATGetFileBlock(CICell ih, char *filePath, unsigned long long *firstBlock)
{
uint8_t* buffer;
struct exfat_inode inode;
uint32_t cluster;
if (EXFATInitPartition(ih) < 0)
return -1;
if (*filePath == '/')
++filePath;
buffer = (uint8_t*) malloc(CacheBlockSize());
if (!buffer)
return -1;
if (PathToInode(filePath, &inode, buffer) < 0 ||
(inode.attributes & ATTR_DIRECTORY) != 0 ||
!(inode.stream_extension_flags & SEFLAG_ALLOCATION_POSSIBLE)) {
free(buffer);
return -1;
}
free(buffer);
cluster = inode.first_cluster;
if (cluster < CLUST_FIRST || cluster >= CLUST_RSRVD)
return -1;
*firstBlock = ClusterToLSA(cluster);
return 0;
}
long
EXFATLoadFile(CICell ih, char * filePath)
{
return EXFATReadFile(ih, filePath, (void *)gFSLoadAddress, 0, 0);
}
/**
* Reads volume label into str.
* Reads boot sector, performs dome checking, loads root dir
* Reads boot sector, performs some checking, loads root dir
* and parses volume label.
*/
void
EXFATGetDescription(CICell ih, char *str, long strMaxLen)
{
struct exfatbootfile *boot;
u_int32_t bytesPerSector = 0;
u_int32_t sectorsPerCluster = 0;
uint8_t bpss, spcs;
long long rdirOffset = 0;
char *buf = NULL;
struct direntry_label *dire = NULL;
int loopControl = 0;
DBG("EXFAT: start %x:%x\n", ih->biosdev, ih->part_no);
buf = (char *)malloc(MAX_BLOCK_SIZE);
if (buf == 0)
{
// take our boot structure
boot = (struct exfatbootfile *) buf;
/*
* The first three bytes are an Intel x86 jump instruction. I assume it
* can be the same forms as DOS FAT:
* Make sure the bytes per sector and sectors per cluster are
* powers of two, and within reasonable ranges.
*/
bytesPerSector = 1 << boot->bf_bpss;/* Just one byte; no swapping needed */
DBG("EXFAT: bpss=%d, bytesPerSector=%d\n", boot->bf_bpss, bytesPerSector);
if (boot->bf_bpss < 9 || boot->bf_bpss > 12)
bpss = boot->bf_bpss;/* Just one byte; no swapping needed */
DBG("EXFAT: bpss=%d, bytesPerSector=%d\n", bpss, (1 << bpss));
if (bpss < MIN_BLOCK_SIZE_SHIFT || bpss > MAX_BLOCK_SIZE_SHIFT)
{
DBG("EXFAT: invalid bytes per sector shift(%d)\n", boot->bf_bpss);
DBG("EXFAT: invalid bytes per sector shift(%d)\n", bpss);
goto error;
}
sectorsPerCluster = 1 << boot->bf_spcs;/* Just one byte; no swapping needed */
DBG("EXFAT: spcs=%d, sectorsPerCluster=%d\n", boot->bf_spcs, sectorsPerCluster);
if (boot->bf_spcs > (25 - boot->bf_bpss))
spcs = boot->bf_spcs;/* Just one byte; no swapping needed */
DBG("EXFAT: spcs=%d, sectorsPerCluster=%d\n", spcs, (1 << spcs));
if (spcs > (MAX_CLUSTER_SIZE_SHIFT - bpss))
{
DBG("EXFAT: invalid sectors per cluster shift (%d)\n", boot->bf_spcs);
DBG("EXFAT: invalid sectors per cluster shift (%d)\n", spcs);
goto error;
}
// calculate root dir cluster offset
rdirOffset = boot->bf_cloff + (boot->bf_rdircl - 2) * sectorsPerCluster;
DBG("EXFAT: rdirOffset=%d\n", rdirOffset);
rdirOffset = OSSwapLittleToHostInt32(boot->bf_cloff) + ((long long) (OSSwapLittleToHostInt32(boot->bf_rdircl) - 2) << spcs);
DBG("EXFAT: rdirOffset=%d\n", (int) rdirOffset);
// load MAX_BLOCK_SIZE bytes of root dir
Seek(ih, rdirOffset * bytesPerSector);
Seek(ih, rdirOffset << bpss);
Read(ih, (long)buf, MAX_BLOCK_SIZE);
DBG("buf 0 1 2 = %x %x %x\n", 0x00ff & buf[0], 0x00ff & buf[1], 0x00ff & buf[2]);
*/
loopControl = MAX_BLOCK_SIZE / sizeof(struct direntry_label);
dire = (struct direntry_label *)buf;
while (loopControl && dire->type && dire->type != 0x83)
while (loopControl && dire->type && dire->type != DIRENTRY_TYPE_LABEL)
{
dire++;
loopControl--;
}
if (dire->type == 0x83 && dire->llen > 0 && dire->llen <= 11)
if (dire->type == DIRENTRY_TYPE_LABEL && dire->llen > 0 && dire->llen <= VOLUME_LABEL_MAX_CHARS)
{
utf_encodestr( dire->label, (int)dire->llen, (u_int8_t *)str, strMaxLen, OSLittleEndian );
}
return;
error:
if (buf) free(buf);
DBG("EXFAT: error\n");
PAUSE();
return;
if (buf)
{
free(buf);
}
DBG("EXFAT: error\n");
PAUSE();
return;
}
/**
*/
long EXFATGetUUID(CICell ih, char *uuidStr)
{
struct exfatbootfile *boot;
void *buf = malloc(MAX_BLOCK_SIZE);
if ( !buf )
return -1;
uint32_t volsn;
struct exfatbootfile *boot;
void *buf = malloc(MAX_BLOCK_SIZE);
if ( !buf )
return -1;
/*
* Read the boot sector, check signatures, and do some minimal
* sanity checking. NOTE: the size of the read below is intended
* to be a multiple of all supported block sizes, so we don't
* have to determine or change the device's block size.
*/
Seek(ih, 0);
Read(ih, (long)buf, MAX_BLOCK_SIZE);
/*
* Read the boot sector, check signatures, and do some minimal
* sanity checking. NOTE: the size of the read below is intended
* to be a multiple of all supported block sizes, so we don't
* have to determine or change the device's block size.
*/
Seek(ih, 0);
Read(ih, (long)buf, MAX_BLOCK_SIZE);
boot = (struct exfatbootfile *) buf;
boot = (struct exfatbootfile *) buf;
/*
* Check the "EXFAT " signature.
*/
if (memcmp((const char *)boot->bf_sysid, EXFAT_BBID, EXFAT_BBIDLEN) != 0)
return -1;
/*
* Check the "EXFAT " signature.
*/
if (memcmp((const char *)boot->bf_sysid, EXFAT_BBID, EXFAT_BBIDLEN) != 0)
{
return -1;
}
// Check for non-null volume serial number
if( !boot->bf_volsn )
return -1;
// Check for non-null volume serial number
volsn = OSSwapLittleToHostInt32(boot->bf_volsn);
if( !volsn )
{
return -1;
}
// Use UUID like the one you get on Windows
sprintf(uuidStr, "%04X-%04X", (unsigned short)(boot->bf_volsn >> 16) & 0xFFFF,
(unsigned short)boot->bf_volsn & 0xFFFF);
// Use UUID like the one you get on Windows
sprintf(uuidStr, "%04X-%04X", (unsigned short)(volsn >> 16) & 0xFFFF,
(unsigned short)volsn & 0xFFFF);
DBG("EXFATGetUUID: %x:%x = %s\n", ih->biosdev, ih->part_no, uuidStr);
return 0;
}
DBG("EXFATGetUUID: %x:%x = %s\n", ih->biosdev, ih->part_no, uuidStr);
return 0;
}
/**
* Returns true if given buffer is the boot rec of the EXFAT volume.
*/
bool EXFATProbe(const void * buffer)
{
bool result = false;
bool result = false;
// boot sector structure
const struct exfatbootfile* boot = buffer;
// boot sector structure
const struct exfatbootfile* boot = buffer;
// Looking for EXFAT signature.
if (memcmp((const char *)boot->bf_sysid, EXFAT_BBID, EXFAT_BBIDLEN) == 0)
result = true;
DBG("EXFATProbe: %d\n", result ? 1 : 0);
return result;
// Looking for EXFAT signature.
if (memcmp((const char *)boot->bf_sysid, EXFAT_BBID, EXFAT_BBIDLEN) == 0)
{
result = true;
}
DBG("EXFATProbe: %d\n", result ? 1 : 0);
return result;
}
trunk/i386/libsaio/exfat.h
2323
2424
2525
26
27
2628
2729
30
31
32
33
34
35
36
37
2838
2939
3040
* dmazar, 14/7/2011
* support for EXFAT volume label reading
*
* Zenith432, Nov 30 2014
* support for reading files
*/
extern void EXFATFree(CICell ih);
extern long EXFATInitPartition(CICell ih);
extern long EXFATGetDirEntry(CICell ih, char * dirPath, long long * dirIndex,
char ** name, long * flags, u_int32_t * time,
FinderInfo * finderInfo, long * infoValid);
extern long EXFATReadFile(CICell ih, char * filePath, void *base, uint64_t offset, uint64_t length);
extern long EXFATGetFileBlock(CICell ih, char *filePath, unsigned long long *firstBlock);
extern long EXFATLoadFile(CICell ih, char * filePath);
extern void EXFATGetDescription(CICell ih, char *str, long strMaxLen);
extern bool EXFATProbe (const void *buf);
extern long EXFATGetUUID(CICell ih, char *uuidStr);
trunk/i386/libsaio/fdisk.h
5757
5858
5959
60
6061
6162
6263
#define FDISK_UFS0xa8/* Apple UFS partition */
#define FDISK_HFS0xaf/* Apple HFS partition */
#define FDISK_BOOTER0xab/* Apple booter partition */
#define FDISK_PSEUDO_EXFAT0x107/* Shared with FDISK_NTFS */
/*
* Format of fdisk partion entry (if present).
trunk/i386/libsaio/disk.c
2020
2121
2222
23
24
23
24
2525
2626
2727
2828
2929
30
31
32
30
31
3332
3433
3534
......
3736
3837
3938
40
41
42
39
40
4341
4442
45
46
47
43
44
4845
4946
5047
......
7168
7269
7370
71
7472
7573
7674
......
581579
582580
583581
584
582
585583
586584
587585
588586
589587
590588
591
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
592608
593609
594610
......
12501266
12511267
12521268
1269
1270
1271
1272
1273
12531274
12541275
12551276
......
12781299
12791300
12801301
1281
1302
12821303
1283
1304
12841305
1306
1307
12851308
12861309
12871310
......
15031526
15041527
15051528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
15061542
15071543
15081544
* under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/*
*
*
* Mach Operating System
* Copyright (c) 1990 Carnegie-Mellon University
* Copyright (c) 1989 Carnegie-Mellon University
* All rights reserved. The CMU software License Agreement specifies
* the terms and conditions for use and redistribution.
*/
/*
*
*
* INTEL CORPORATION PROPRIETARY INFORMATION
*
* This software is supplied under the terms of a license agreement or
* nor disclosed except in accordance with the terms of that agreement.
*
* Copyright 1988, 1989 Intel Corporation
*/
/*
*
*
* Copyright 1993 NeXT Computer, Inc.
* All rights reserved.
*/
/*
*
*
* Copyright 2007 VMware Inc.
* "Preboot" ramdisk support added by David Elliott
* GPT support added by David Elliott. Based on IOGUIDPartitionScheme.cpp.
#include "fdisk.h"
#include "hfs.h"
#include "ntfs.h"
#include "exfat.h"
#include "msdos.h"
#include "ext2fs.h"
#include "befs.h"
bvr = NULL;
}
if ( readBootSector( biosdev, blkoff, (void *)0x7e00 ) == 0 )
if ( bvr && readBootSector( biosdev, blkoff, (void *)0x7e00 ) == 0 )
{
bvr->flags |= kBVFlagBootable;
}
}
else if ( readBootSector( biosdev, blkoff, (void *)0x7e00 ) == 0 )
{
bvr->flags |= kBVFlagForeignBoot;
/*
* This is an ugly place to check for exfat/FDisk, but it reads
* the partition boot sector only once.
*/
if (bvr->part_type == FDISK_NTFS && EXFATProbe((void const *)0x7e00))
{
bvr->flags |= kBVFlagNativeBoot | kBVFlagBootable;
bvr->fs_loadfile = EXFATLoadFile;
bvr->fs_readfile = EXFATReadFile;
bvr->fs_getdirentry = EXFATGetDirEntry;
bvr->fs_getfileblock= EXFATGetFileBlock;
bvr->fs_getuuid = EXFATGetUUID;
bvr->description = EXFATGetDescription;
bvr->bv_free = EXFATFree;
}
else
{
bvr->flags |= kBVFlagForeignBoot;
}
}
else
{
result = FDISK_BEFS;
}
else if (EXFATProbe(probeBuffer))
{
result = FDISK_PSEUDO_EXFAT;
}
else if (NTFSProbe(probeBuffer))
{
result = FDISK_NTFS;
}
exit:
if (probeBuffer != NULL) free((void *)probeBuffer);
if (probeBuffer)
{
return result;
free((void *)probeBuffer);
}
return result;
}
//==============================================================================
0, kBIOSDevTypeHardDrive, 0);
break;
case FDISK_PSEUDO_EXFAT:
bvr = newGPTBVRef(biosdev, gptID, gptMap->ent_lba_start, gptMap,
EXFATInitPartition,
EXFATLoadFile,
EXFATReadFile,
EXFATGetDirEntry,
EXFATGetFileBlock,
EXFATGetUUID,
EXFATGetDescription,
EXFATFree,
0, kBIOSDevTypeHardDrive, 0);
break;
default:
bvr = newGPTBVRef(biosdev, gptID, gptMap->ent_lba_start, gptMap,
0, 0, 0, 0, 0, 0, 0,
trunk/CHANGES
1
2
13
24
35
- Zenith432 : Full implementation of exfat support for Chameleon's boot2 stage.
- Micky1979 : Incorporated force umount option -u (boot1-install.c)
- Zenith432 : Replace boot0 with boot0xg. Now boot0xg has all features of previous boot0. ( http://www.insanelymac.com/forum/topic/302938-exfat-volume-boot-record-for-chameleon )
- Zenith432 : Completed patch for ExFAT support ( http://www.insanelymac.com/forum/topic/302938-exfat-volume-boot-record-for-chameleon )
- Zenith432 : add EXFAT boot support by Zenith432

Archive Download the corresponding diff file

Revision: 2516