-
Notifications
You must be signed in to change notification settings - Fork 6
/
ntfs.h
2346 lines (1805 loc) · 75.4 KB
/
ntfs.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
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
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#pragma once
/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
Ntfs.h
Current Version Numbers:
Major.Minor Version: 1.2
Abstract:
This module defines the on-disk structure of the Ntfs file system.
An Ntfs volume consists of sectors of data allocated on a granularity
called a cluster. The cluster factor is the number of sectors per
cluster. Valid cluster factors are 1, 2, 4, 8, etc.
The Ntfs volume starts with a boot sector at LBN=0, and a duplicate
boot sector at LBN=(number of sectors on the partition div 2). So
a disk with N sectors start with two boot sectors as illustrated.
0 ... N/2 ... N
+-----------+-------+------------+-------+------------+
|BootSector | ... | BootSector | ... | |
+-----------+-------+------------+-------+------------+
The boot sector gives you the standard Bios Parameter Block, and
tells you how many sectors are in the volume, and gives you the starting
LCNs of the master file table (mft) and the duplicate master file table
(mft2).
The master file table contains the file record segments for all of
the volume. The first 16 or so file record segments are reserved for
special files. Mft2 only mirrors the first three record segments.
0 1 2 3 4 5 6 7 8 9 ...
+---+---+---+---+---+---+---+---+---+---+-----+
| M | M | L | V | A | R | B | B | B | Q | |
| f | f | o | o | t | o | i | o | a | u | |
| t | t | g | l | t | o | t | o | d | o | |
| | 2 | F | D | r | t | M | t | C | t | ... |
| | | i | a | D | D | a | | l | a | |
| | | l | s | e | i | p | | u | | |
| | | e | d | f | r | | | s | | |
+---+---+---+---+---+---+---+---+---+---+-----+
Each file record segment starts with a file record segment header, and
is followed by one or more attributes. Each attribute starts with an
attribute record header. The attribute record denotes the attribute type,
optional name, and value for the attribute. If the attribute is resident
the value is contained in the file record and immediately follows the
attribute record header. If the attribute is non-resident the value
value is off in some other sectors on the disk.
+---------+-----------------+-------+
| File | Attrib : Name | |
| Record | Record : and/or | ... |
| Segment | Header : Attrib | |
| Header | : Data | |
+---------+-----------------+-------+
Now if we run out of space for storing attributes in the file record
segment we allocate additional file record segments and insert in the
first (or base) file record segment an attribute called the Attribute
List. The attribute list indicates for every attribute associated
with the file where the attribute can be found. This includes
those in the base file record.
The value part of the attribute we're calling the attribute list is
a list of sorted attribute list entries. Though illustrated
here as resident the attribute list can be nonresident.
+---------+---------------------------+-----------------+-------+
| File | Attrib : Attrib : | Attrib : Name | |
| Record | Record : List : ... | Record : and/or | ... |
| Segment | Header : Entry : | Header : Attrib | |
| Header | : : | : Data | |
+---------+---------------------------+-----------------+-------+
|
V
+---------+-----------------+-------+
| File | Attrib : Name | |
| Record | Record : and/or | ... |
| Segment | Header : Attrib | |
| Header | : Data | |
+---------+-----------------+-------+
This file defines all of the above structures and also lays out the
structures for some predefined attributes values (e.g., standard
information, etc).
Attributes are stored in ascending order in the file record and
attribute list. The sorting is done by first sorting according to
attribute type code, then attribute name, and lastly attribute value.
NTFS guarantees that if two attributes of the same type code and
name exist on a file then they must have different values, and the
values must be resident.
The indexing attributes are the last interesting attribute. The data
component of an index root attribute must always be resident and contains
an index header followed by a list of index list entries.
+--------+------------------------+
| Attrib | : Index : |
| Record | Index : List : ... |
| Header | Header : Entry : |
| | : : |
+--------+------------------------+
Each index list entry contains the key for the index and a reference
to the file record segment for the index. If ever we need to spill
out of the file record segment we allocate an additional cluster from
the disk (not file record segments). The storage for the additional
clusters is the data part of an index allocation attribute. So what
we wind up with is an index allocation attribute (non-resident) consisting
of Index Allocation Buffers which are referenced by the b-tree used in in
the index.
+--------+------------------------+-----+---------------------+
| Attrib | Index : Index : | | Attrib : Index |
| Record | List : List : ... | ... | Record : Allocation |
| Header | Header : Entry : | | Header : |
| | : : | | : |
+--------+------------------------+-----+---------------------+
|
| (VCN within index allocation)
V
+------------+------------------------------+
| Index | : Index : Index : |
| Allocation | Index : List : List : ... |
| Buffer | Header: Entry : Entry : |
| | : : : |
+------------+------------------------------+
Resident attributes are straight forward. Non-resident attributes require
a little more work. If the attribute is non-resident then following
the attribute record header is a list of retrieval information giving a
VCN to LCN mapping for the attribute. In the figure above the
Index allocation attribute is a a non-resident attribute
+---------+----------------------+-------+
| File | Attrib : Retrieval | |
| Record | Record : Information | ... |
| Segment | Header : | |
| Header | : | |
+---------+----------------------+-------+
If the retrieval information does not fit in the base file segment then
it can be stored in an external file record segment all by itself, and
if in the still doesn't fit in one external file record segment then
there is a provision in the attribute list to contain multiple
entries for an attribute that needs additional retrieval information.
Author:
Brian Andrew [BrianAn] 21-May-1991
David Goebel [DavidGoe]
Gary Kimura [GaryKi]
Tom Miller [TomM]
Revision History:
IMPORTANT NOTE:
The NTFS on-disk structure must guarantee natural alignment of all
arithmetic quantities on disk up to and including quad-word (64-bit)
numbers. Therefore, all attribute records are quad-word aligned, etc.
--*/
#include "windows.h"
#ifndef _NTFS_
#define _NTFS_
#pragma pack(4)
//
// The fundamental unit of allocation on an Ntfs volume is the
// cluster. Format guarantees that the cluster size is an integral
// power of two times the physical sector size of the device. Ntfs
// reserves 64-bits to describe a cluster, in order to support
// large disks. The LCN represents a physical cluster number on
// the disk, and the VCN represents a virtual cluster number within
// an attribute.
//
typedef LONGLONG LCN;
typedef LCN *PLCN;
typedef LONGLONG VCN;
typedef VCN *PVCN;
typedef LONGLONG LBO;
typedef LBO *PLBO;
typedef LONGLONG VBO;
typedef VBO *PVBO;
//
// Temporary definitions ****
//
typedef ULONG COLLATION_RULE;
typedef ULONG DISPLAY_RULE;
//
// The compression chunk size is constant for now, at 4KB.
//
#define NTFS_CHUNK_SIZE (0x1000)
#define NTFS_CHUNK_SHIFT (12)
//
// This number is actually the log of the number of clusters per compression
// unit to be stored in a nonresident attribute record header.
//
#define NTFS_CLUSTERS_PER_COMPRESSION (4)
//
// Collation Rules
//
//
// For binary collation, values are collated by a binary compare of
// their bytes, with the first byte being most significant.
//
#define COLLATION_BINARY (0)
//
// For collation of Ntfs file names, file names are collated as
// Unicode strings. See below.
//
#define COLLATION_FILE_NAME (1)
//
// For collation of Unicode strings, the strings are collated by
// their binary Unicode value, with the exception that for
// characters which may be upcased, the lower case value for that
// character collates immediately after the upcased value.
//
#define COLLATION_UNICODE_STRING (2)
//
// Total number of collation rules
//
#define COLLATION_NUMBER_RULES (3)
//
// The following macros are used to set and query with respect to
// the update sequence arrays.
//
#define UpdateSequenceStructureSize(MSH) \
(((((PMULTI_SECTOR_HEADER)(MSH))->UpdateSequenceArraySize - 1) * SEQUENCE_NUMBER_STRIDE))
#define UpdateSequenceArraySize(STRUCT_SIZE) (((STRUCT_SIZE) / SEQUENCE_NUMBER_STRIDE + 1))
//
// The MFT Segment Reference is an address in the MFT tagged with
// a circularly reused sequence number set at the time that the MFT
// Segment Reference was valid. Note that this format limits the
// size of the Master File Table to 2**48 segments. So, for
// example, with a 1KB segment size the maximum size of the master
// file would be 2**58 bytes, or 2**28 gigabytes.
//
typedef struct _MFT_SEGMENT_REFERENCE
{
//
// First a 48 bit segment number.
//
ULONG SegmentNumberLowPart; // offset = 0x000
USHORT SegmentNumberHighPart; // offset = 0x004
//
// Now a 16 bit nonzero sequence number. A value of 0 is
// reserved to allow the possibility of a routine accepting
// 0 as a sign that the sequence number check should be
// repressed.
//
USHORT SequenceNumber; // offset = 0x006
} MFT_SEGMENT_REFERENCE, *PMFT_SEGMENT_REFERENCE; // sizeof = 0x008
//
// A file reference in NTFS is simply the MFT Segment Reference of
// the Base file record.
//
typedef MFT_SEGMENT_REFERENCE FILE_REFERENCE, *PFILE_REFERENCE;
//
// While the format allows 48 bits worth of segment number, the current
// implementation restricts this to 32 bits. Using NtfsUnsafeSegmentNumber
// results in a performance win. When the implementation changes, the
// unsafe segment numbers must be cleaned up. NtfsFullSegmentNumber is
// used in a few spots to guarantee integrity of the disk.
//
#define NtfsSegmentNumber(fr) NtfsUnsafeSegmentNumber(fr)
#define NtfsFullSegmentNumber(fr) ((*(ULONGLONG UNALIGNED *)(fr)) & 0xFFFFFFFFFFFF)
#define NtfsUnsafeSegmentNumber(fr) ((fr)->SegmentNumberLowPart)
#define NtfsSetSegmentNumber(fr, high, low) ((fr)->SegmentNumberHighPart = (high), (fr)->SegmentNumberLowPart = (low))
#define NtfsEqualMftRef(X, Y) (NtfsSegmentNumber(X) == NtfsSegmentNumber(Y))
#define NtfsLtrMftRef(X, Y) (NtfsSegmentNumber(X) < NtfsSegmentNumber(Y))
#define NtfsGtrMftRef(X, Y) (NtfsSegmentNumber(X) > NtfsSegmentNumber(Y))
#define NtfsLeqMftRef(X, Y) (NtfsSegmentNumber(X) <= NtfsSegmentNumber(Y))
#define NtfsGeqMftRef(X, Y) (NtfsSegmentNumber(X) >= NtfsSegmentNumber(Y))
//
// System File Numbers. The following file numbers are a fixed
// part of the volume number. For the system files, the
// SequenceNumber is always equal to the File Number. So to form a
// File Reference for a given System File, set LowPart and
// SequenceNumber to the File Number, and set HighPart to 0. Any
// unused file numbers prior to FIRST_USER_FILE_NUMBER should not
// be used. They are reserved to allow the potential for easy
// upgrade of existing volumes from future versions of the file
// system.
//
// Each definition below is followed by a comment containing
// the file name for the file:
//
// Number Name
// ------ ----
#define MASTER_FILE_TABLE_NUMBER (0) // $Mft
#define MASTER_FILE_TABLE2_NUMBER (1) // $MftMirr
#define LOG_FILE_NUMBER (2) // $LogFile
#define VOLUME_DASD_NUMBER (3) // $Volume
#define ATTRIBUTE_DEF_TABLE_NUMBER (4) // $AttrDef
#define ROOT_FILE_NAME_INDEX_NUMBER (5) // .
#define BIT_MAP_FILE_NUMBER (6) // $BitMap
#define BOOT_FILE_NUMBER (7) // $Boot
#define BAD_CLUSTER_FILE_NUMBER (8) // $BadClus
#define QUOTA_TABLE_NUMBER (9) // $Quota
#define UPCASE_TABLE_NUMBER (10) // $UpCase
#define CAIRO_NUMBER (11) // $Cairo
#define FIRST_USER_FILE_NUMBER (16)
//
// The number of bits to extend the Mft and bitmap. We round these up to a
// cluster boundary for a large cluster volume
//
#define BITMAP_EXTEND_GRANULARITY (64)
#define MFT_HOLE_GRANULARITY (32)
#define MFT_EXTEND_GRANULARITY (16)
//
// The shift values for determining the threshold for the Mft defragging.
//
#define MFT_DEFRAG_UPPER_THRESHOLD (3) // Defrag if 1/8 of free space
#define MFT_DEFRAG_LOWER_THRESHOLD (4) // Stop at 1/16 of free space
//
// Attribute Type Code. Attribute Types also have a Unicode Name,
// and the correspondence between the Unicode Name and the
// Attribute Type Code is stored in the Attribute Definition File.
//
typedef ULONG ATTRIBUTE_TYPE_CODE;
typedef ATTRIBUTE_TYPE_CODE *PATTRIBUTE_TYPE_CODE;
//
// System-defined Attribute Type Codes. For the System-defined
// attributes, the Unicode Name is exactly equal to the name of the
// following symbols. For this reason, all of the system-defined
// attribute names start with "$", to always distinguish them when
// attribute names are listed, and to reserve a namespace for
// attributes defined in the future. I.e., a User-Defined
// attribute name will never collide with a current or future
// system-defined attribute name if it does not start with "$".
// User attribute numbers should not start until
// $FIRST_USER_DEFINED_ATTRIBUTE, too allow the potential for
// upgrading existing volumes with new user-defined attributes in
// future versions of NTFS. The tagged attribute list is
// terminated with a lone-standing 0 ($END) - the rest of the
// attribute record does not exist.
//
// The type code value of 0 is reserved for convenience of the
// implementation.
//
#define $UNUSED (0X0)
#define $STANDARD_INFORMATION (0x10)
#define $ATTRIBUTE_LIST (0x20)
#define $FILE_NAME (0x30)
#define $OBJECT_ID (0x40)
#define $SECURITY_DESCRIPTOR (0x50)
#define $VOLUME_NAME (0x60)
#define $VOLUME_INFORMATION (0x70)
#define $DATA (0x80)
#define $INDEX_ROOT (0x90)
#define $INDEX_ALLOCATION (0xA0)
#define $BITMAP (0xB0)
#define $SYMBOLIC_LINK (0xC0)
#define $EA_INFORMATION (0xD0)
#define $EA (0xE0)
#ifdef _CAIRO_
#define $PROPERTY_SET (0xF0)
#endif // _CAIRO_
#define $FIRST_USER_DEFINED_ATTRIBUTE (0x100)
#define $END (0xFFFFFFFF)
//
// The boot sector is duplicated on the partition. The first copy
// is on the first physical sector (LBN == 0) of the partition, and
// the second copy is at <number sectors on partition> / 2. If the
// first copy can not be read when trying to mount the disk, the
// second copy may be read and has the identical contents. Format
// must figure out which cluster the second boot record belongs in,
// and it must zero all of the other sectors that happen to be in
// the same cluster. The boot file minimally contains with two
// clusters, which are the two clusters which contain the copies of
// the boot record. If format knows that some system likes to put
// code somewhere, then it should also align this requirement to
// even clusters, and add that to the boot file as well.
//
// Part of the sector contains a BIOS Parameter Block. The BIOS in
// the sector is packed (i.e., unaligned) so we'll supply an
// unpacking macro to translate a packed BIOS into its unpacked
// equivalent. The unpacked BIOS structure is already defined in
// ntioapi.h so we only need to define the packed BIOS.
//
#pragma pack(1) // while parse DBR,must indicate the pack(1)
//
// Define the Packed and Unpacked BIOS Parameter Block
//
typedef struct _PACKED_BIOS_PARAMETER_BLOCK
{
UCHAR BytesPerSector[2]; // offset = 0x000
UCHAR SectorsPerCluster[1]; // offset = 0x002
UCHAR ReservedSectors[2]; // offset = 0x003 (zero)
UCHAR Fats[1]; // offset = 0x005 (zero)
UCHAR RootEntries[2]; // offset = 0x006 (zero)
UCHAR Sectors[2]; // offset = 0x008 (zero)
UCHAR Media[1]; // offset = 0x00A
UCHAR SectorsPerFat[2]; // offset = 0x00B (zero)
UCHAR SectorsPerTrack[2]; // offset = 0x00D
UCHAR Heads[2]; // offset = 0x00F
UCHAR HiddenSectors[4]; // offset = 0x011 (zero)
UCHAR LargeSectors[4]; // offset = 0x015 (zero)
} PACKED_BIOS_PARAMETER_BLOCK; // sizeof = 0x019
typedef PACKED_BIOS_PARAMETER_BLOCK *PPACKED_BIOS_PARAMETER_BLOCK;
typedef struct BIOS_PARAMETER_BLOCK
{
USHORT BytesPerSector;
UCHAR SectorsPerCluster;
USHORT ReservedSectors;
UCHAR Fats; // Number of FATs : always 0
USHORT RootEntries; // Root directory entries : always 0
USHORT Sectors; // Total logical sectors : always 0
UCHAR Media; // Media descriptor
USHORT SectorsPerFat;
USHORT SectorsPerTrack;
USHORT Heads; // Number of heads
ULONG HiddenSectors; // ?????????????????????
ULONG LargeSectors; // Large total logical sectors
} BIOS_PARAMETER_BLOCK;
typedef BIOS_PARAMETER_BLOCK *PBIOS_PARAMETER_BLOCK;
//
// This macro takes a Packed BIOS and fills in its Unpacked
// equivalent
//
#define NtfsUnpackBios(Bios, Pbios) \
{ \
CopyUchar2(&((Bios)->BytesPerSector), &(Pbios)->BytesPerSector); \
CopyUchar1(&((Bios)->SectorsPerCluster), &(Pbios)->SectorsPerCluster); \
CopyUchar2(&((Bios)->ReservedSectors), &(Pbios)->ReservedSectors); \
CopyUchar1(&((Bios)->Fats), &(Pbios)->Fats); \
CopyUchar2(&((Bios)->RootEntries), &(Pbios)->RootEntries); \
CopyUchar2(&((Bios)->Sectors), &(Pbios)->Sectors); \
CopyUchar1(&((Bios)->Media), &(Pbios)->Media); \
CopyUchar2(&((Bios)->SectorsPerFat), &(Pbios)->SectorsPerFat); \
CopyUchar2(&((Bios)->SectorsPerTrack), &(Pbios)->SectorsPerTrack); \
CopyUchar2(&((Bios)->Heads), &(Pbios)->Heads); \
CopyUchar4(&((Bios)->HiddenSectors), &(Pbios)->HiddenSectors); \
CopyUchar4(&((Bios)->LargeSectors), &(Pbios)->LargeSectors); \
}
//
// Define the boot sector. Note that MFT2 is exactly three file
// record segments long, and it mirrors the first three file record
// segments from the MFT, which are MFT, MFT2 and the Log File.
//
// The Oem field contains the ASCII characters "NTFS ".
//
// The Checksum field is a simple additive checksum of all of the
// ULONGs which precede the Checksum ULONG. The rest of the sector
// is not included in this Checksum.
//
typedef struct _PACKED_BOOT_SECTOR
{
UCHAR Jump[3]; // offset = 0x000
UCHAR Oem[8]; // offset = 0x003
BIOS_PARAMETER_BLOCK PackedBpb; // offset = 0x00B
UCHAR Unused[4]; // offset = 0x024
LONGLONG NumberSectors; // offset = 0x028
LCN MftStartLcn; // offset = 0x030
LCN Mft2StartLcn; // offset = 0x038
UCHAR ClustersPerFileRecordSegment; // offset = 0x040
UCHAR Reserved0[3];
UCHAR DefaultClustersPerIndexAllocationBuffer; // offset = 0x044
UCHAR Reserved1[3];
LONGLONG SerialNumber; // offset = 0x048
ULONG Checksum; // offset = 0x050
UCHAR BootStrap[0x1aa]; // offset = 0x054
WORD signature;
} PACKED_BOOT_SECTOR; // sizeof = 0x200
typedef PACKED_BOOT_SECTOR *PPACKED_BOOT_SECTOR;
//
// This structure must be allocated at the start of the structure being
// protected.
//
typedef struct _MULTI_SECTOR_HEADER
{
UCHAR Signature[4]; // Space for a four-character signature
//
// Offset to Update Sequence Array, from start of structure. The Update
// Sequence Array must end before the last USHORT in the first "sector"
// of size SEQUENCE_NUMBER_STRIDE. (I.e., with the current constants,
// the sum of the next two fields must be <= 510.)
//
USHORT UpdateSequenceArrayOffset;
//
// Size of Update Sequence Array (from above formula)
//
USHORT UpdateSequenceArraySize;
} MULTI_SECTOR_HEADER, *PMULTI_SECTOR_HEADER;
//
// The following structure is used to identify a log record by a log
// sequence number.
//
typedef LARGE_INTEGER LSN, *PLSN;
typedef USHORT UPDATE_SEQUENCE_NUMBER, *PUPDATE_SEQUENCE_NUMBER;
//
// This array must be present at the offset described above.
//
typedef UPDATE_SEQUENCE_NUMBER UPDATE_SEQUENCE_ARRAY[1];
//
// File Record Segment. This is the header that begins every File
// Record Segment in the Master File Table.
//
typedef struct _FILE_RECORD_SEGMENT_HEADER
{
//
// Multi-Sector Header as defined by the Cache Manager. This
// structure will always contain the signature "FILE" and a
// description of the location and size of the Update Sequence
// Array.
//
MULTI_SECTOR_HEADER MultiSectorHeader; // offset = 0x000
//
// Log File Sequence Number of last logged update to this File
// Record Segment.
//
LSN Lsn; // offset = 0x008
//
// Sequence Number. This is incremented each time that a File
// Record segment is freed, and 0 is not used. The
// SequenceNumber field of a File Reference must match the
// contents of this field, or else the File Reference is
// incorrect (presumably stale).
//
USHORT SequenceNumber; // offset = 0x010
//
// This is the count of the number of references which exist
// for this segment, from an INDEX_xxx attribute. In File
// Records Segments other than the Base File Record Segment,
// this field is 0.
//
USHORT ReferenceCount; // offset = 0x012
//
// Offset to the first Attribute record in bytes.
//
USHORT FirstAttributeOffset; // offset = 0x014
//
// FILE_xxx flags.
//
USHORT Flags; // offset = 0x016
//
// First free byte available for attribute storage, from start
// of this header. This value should always be aligned to a
// quad-word boundary, since attributes are quad-word aligned.
//
ULONG FirstFreeByte; // offset = x0018
//
// Total bytes available in this file record segment, from the
// start of this header. This is essentially the file record
// segment size.
//
ULONG BytesAvailable; // offset = 0x01C
//
// This is a File Reference to the Base file record segment for
// this file. If this is the Base, then the value of this
// field is all 0's.
//
FILE_REFERENCE BaseFileRecordSegment; // offset = 0x020
//
// This is the attribute instance number to be used when
// creating an attribute. It is zeroed when the base file
// record is created, and captured for each new attribute as it
// is created and incremented afterwards for the next
// attribute. Instance numbering must also occur for the
// initial attributes. Zero is a valid attribute instance
// number, and typically used for standard information.
//
USHORT NextAttributeInstance; // offset = 0x028
//
// Update Sequence Array to protect multi-sector transfers of
// the File Record Segment. Accesses to already initialized
// File Record Segments should go through the offset above, for
// upwards compatibility.
//
UPDATE_SEQUENCE_ARRAY UpdateArrayForCreateOnly; // offset = 0x02A
} FILE_RECORD_SEGMENT_HEADER;
typedef FILE_RECORD_SEGMENT_HEADER *PFILE_RECORD_SEGMENT_HEADER;
//
// FILE_xxx flags.
//
#define FILE_RECORD_SEGMENT_IN_USE (0x0001)
#define FILE_FILE_NAME_INDEX_PRESENT (0x0002)
//
// Define a macro to determine the maximum space available for a
// single attribute. For example, this is required when a
// nonresident attribute has to split into multiple file records -
// we need to know how much we can squeeze into a single file
// record. If this macro has any inaccurracy, it must be in the
// direction of returning a slightly smaller number than actually
// required.
//
// ULONG
// NtfsMaximumAttributeSize (
// IN ULONG FileRecordSegmentSize
// );
//
#define NtfsMaximumAttributeSize(FRSS) \
((FRSS)-QuadAlign(sizeof(FILE_RECORD_SEGMENT_HEADER)) - \
QuadAlign((((FRSS) / SEQUENCE_NUMBER_STRIDE) * sizeof(UPDATE_SEQUENCE_NUMBER))) - \
QuadAlign(sizeof(ATTRIBUTE_TYPE_CODE)))
//
// Attribute Record. Logically an attribute has a type, an
// optional name, and a value, however the storage details make it
// a little more complicated. For starters, an attribute's value
// may either be resident in the file record segment itself, on
// nonresident in a separate data stream. If it is nonresident, it
// may actually exist multiple times in multiple file record
// segments to describe different ranges of VCNs.
//
// Attribute Records are always aligned on a quad word (64-bit)
// boundary.
//
typedef struct _ATTRIBUTE_RECORD_HEADER
{
//
// Attribute Type Code.
//
ATTRIBUTE_TYPE_CODE TypeCode; // offset = 0x000
//
// Length of this Attribute Record in bytes. The length is
// always rounded to a quad word boundary, if necessary. Also
// the length only reflects the size necessary to store the
// given record variant.
//
ULONG RecordLength; // offset = 0x004
//
// Attribute Form Code (see below)
//
UCHAR FormCode; // offset = 0x008
//
// Length of the optional attribute name in characters, or 0 if
// there is none.
//
UCHAR NameLength; // offset = 0x009
//
// Offset to the attribute name from start of attribute record,
// in bytes, if it exists. This field is undefined if
// NameLength is 0.
//
USHORT NameOffset; // offset = 0x00A
//
// ATTRIBUTE_xxx flags.
//
USHORT Flags; // offset = 0x00C
//
// The file-record-unique attribute instance number for this
// attribute.
//
USHORT Instance; // offset = 0x00E
//
// The following union handles the cases distinguished by the
// Form Code.
//
union
{
//
// Resident Form. Attribute resides in file record segment.
//
struct
{
//
// Length of attribute value in bytes.
//
ULONG ValueLength; // offset = 0x010
//
// Offset to value from start of attribute record, in
// bytes.
//
USHORT ValueOffset; // offset = 0x014
//
// RESIDENT_FORM_xxx Flags.
//
UCHAR ResidentFlags; // offset = 0x016
//
// Reserved.
//
UCHAR Reserved; // offset = 0x017
} Resident;
//
// Nonresident Form. Attribute resides in separate stream.
//
struct
{
//
// Lowest VCN covered by this attribute record.
//
VCN LowestVcn; // offset = 0x010
//
// Highest VCN covered by this attribute record.
//
VCN HighestVcn; // offset = 0x018
//
// Offset to the Mapping Pairs Array (defined below),
// in bytes, from the start of the attribute record.
//
USHORT MappingPairsOffset; // offset = 0x020
//
// Unit of Compression size for this stream, expressed
// as a log of the cluster size.
//
// 0 means file is not compressed
// 1, 2, 3, and 4 are potentially legal values if the
// stream is compressed, however the implementation
// may only choose to use 4, or possibly 3. Note
// that 4 means cluster size time 16. If convenient
// the implementation may wish to accept a
// reasonable range of legal values here (1-5?),
// even if the implementation only generates
// a smaller set of values itself.
//
UCHAR CompressionUnit; // offset = 0x022
//
// Reserved to get to quad word boundary.
//
UCHAR Reserved[5]; // offset = 0x023
//
// Allocated Length of the file in bytes. This is
// obviously an even multiple of the cluster size.
// (Not present if LowestVcn != 0.)
//
LONGLONG AllocatedLength; // offset = 0x028
//
// File Size in bytes (highest byte which may be read +
// 1). (Not present if LowestVcn != 0.)
//
LONGLONG FileSize; // offset = 0x030
//
// Valid Data Length (highest initialized byte + 1).
// This field must also be rounded to a cluster
// boundary, and the data must always be initialized to
// a cluster boundary. (Not present if LowestVcn != 0.)
//
LONGLONG ValidDataLength; // offset = 0x038
//
// Totally allocated. This field is only present for the first
// file record of a compressed stream. It represents the sum of
// the allocated clusters for a file.
//
LONGLONG TotalAllocated; // offset = 0x040
//
//
// Mapping Pairs Array, starting at the offset stored
// above.
//
// The Mapping Pairs Array is stored in a compressed
// form, and assumes that this information is
// decompressed and cached by the system. The reason
// for compressing this information is clear, it is
// done in the hopes that all of the retrieval
// information always fits in a single file record
// segment.
//
// Logically, the MappingPairs Array stores a series of
// NextVcn/CurrentLcn pairs. So, for example, given
// that we know the first Vcn (from LowestVcn above),
// the first Mapping Pair tells us what the next Vcn is
// (for the next Mapping Pair), and what Lcn the
// current Vcn is mapped to, or 0 if the Current Vcn is
// not allocated. (This is exactly the FsRtl MCB
// structure).
//
// For example, if a file has a single run of 8
// clusters, starting at Lcn 128, and the file starts
// at LowestVcn=0, then the Mapping Pairs array has
// just one entry, which is:
//
// NextVcn = 8
// CurrentLcn = 128
//
// The compression is implemented with the following
// algorithm. Assume that you initialize two "working"
// variables as follows:
//
// NextVcn = LowestVcn (from above)
// CurrentLcn = 0
//
// The MappingPairs array is byte stream, which simply
// store the changes to the working variables above,
// when processed sequentially. The byte stream is to
// be interpreted as a zero-terminated stream of
// triples, as follows:
//
// count byte = v + (l * 16)
//
// where v = number of changed low-order Vcn bytes
// l = number of changed low-order Lcn bytes
//
// v Vcn change bytes
// l Lcn change bytes
//
// The byte stream terminates when a count byte of 0 is
// encountered.
//
// The decompression algorithm goes as follows,
// assuming that Attribute is a pointer to the
// attribute record.
//
// 1. Initialize:
// NextVcn = Attribute->LowestVcn;
// CurrentLcn = 0;
//
// 2. Initialize byte stream pointer to: (PCHAR)Attribute +
// Attribute->AttributeForm->Nonresident->MappingPairsOffset
//
// 3. CurrentVcn = NextVcn;
//
// 4. Read next byte from stream. If it is 0, then
// break, else extract v and l (see above).
//
// 5. Interpret the next v bytes as a signed quantity,
// with the low-order byte coming first. Unpack it
// sign-extended into 64 bits and add it to NextVcn.
// (It can really only be positive, but the Lcn