ReactOS 0.4.17-dev-1190-g4c812f4
btree.c File Reference
#include "ntfs.h"
#include <debug.h>
Include dependency graph for btree.c:

Go to the source code of this file.

Macros

#define NDEBUG
 

Functions

VOID PrintAllVCNs (PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT IndexAllocationContext, ULONG NodeSize)
 
AllocateIndexNode

@implemented

Allocates a new index record in an index allocation.

Parameters
DeviceExtPointer to the target DEVICE_EXTENSION describing the volume the node will be created on.
FileRecordPointer to a copy of the file record containing the index.
IndexBufferSizeSize of an index record for this index, in bytes. Commonly defined as 4096.
IndexAllocationCtxPointer to an NTFS_ATTR_CONTEXT describing the index allocation attribute the node will be assigned to.
IndexAllocationOffsetOffset of the index allocation attribute relative to the file record.
NewVCNPointer to a ULONGLONG which will receive the VCN of the newly-assigned index record
Returns
STATUS_SUCCESS in case of success. STATUS_NOT_IMPLEMENTED if there's no $I30 bitmap attribute in the file record.
Remarks
AllocateIndexNode() doesn't write any data to the index record it creates. Called by UpdateIndexNode(). Don't call PrintAllVCNs() or NtfsDumpFileRecord() after calling AllocateIndexNode() before UpdateIndexNode() finishes. Possible TODO: Create an empty node and write it to the allocated index node, so the index allocation is always valid.
NTSTATUS AllocateIndexNode (PDEVICE_EXTENSION DeviceExt, PFILE_RECORD_HEADER FileRecord, ULONG IndexBufferSize, PNTFS_ATTR_CONTEXT IndexAllocationCtx, ULONG IndexAllocationOffset, PULONGLONG NewVCN)
 
CreateDummyKey

@implemented

Creates the final B_TREE_KEY for a B_TREE_FILENAME_NODE. Also creates the associated index entry.

Parameters
HasChildNodeBOOLEAN to indicate if this key will have a LesserChild.
Returns
The newly-created key.
PB_TREE_KEY CreateDummyKey (BOOLEAN HasChildNode)
 
CreateEmptyBTree

@implemented

Creates an empty B-Tree, which will contain a single root node which will contain a single dummy key.

Parameters
NewTreePointer to a PB_TREE that will receive the pointer of the newly-created B-Tree.
Returns
STATUS_SUCCESS on success. STATUS_INSUFFICIENT_RESOURCES if an allocation fails.
NTSTATUS CreateEmptyBTree (PB_TREE *NewTree)
 
CompareTreeKeys

@implemented

Compare two B_TREE_KEY's to determine their order in the tree.

Parameters
Key1Pointer to a B_TREE_KEY that will be compared.
Key2Pointer to the other B_TREE_KEY that will be compared.
CaseSensitiveBoolean indicating if the function should operate in case-sensitive mode. This will be TRUE if an application created the file with the FILE_FLAG_POSIX_SEMANTICS flag.
Returns
0 if the two keys are equal. < 0 if key1 is less thank key2

‍0 if key1 is greater than key2

Remarks
Any other key is always less than the final (dummy) key in a node. Key1 must not be the dummy node.
LONG CompareTreeKeys (PB_TREE_KEY Key1, PB_TREE_KEY Key2, BOOLEAN CaseSensitive)
 
CountBTreeKeys

@implemented

Counts the number of linked B-Tree keys, starting with FirstKey.

Parameters
FirstKeyPointer to a B_TREE_KEY that will be the first key to be counted.
Returns
The number of keys in a linked-list, including FirstKey and the final dummy key.
ULONG CountBTreeKeys (PB_TREE_KEY FirstKey)
 
PB_TREE_FILENAME_NODE CreateBTreeNodeFromIndexNode (PDEVICE_EXTENSION Vcb, PINDEX_ROOT_ATTRIBUTE IndexRoot, PNTFS_ATTR_CONTEXT IndexAllocationAttributeCtx, PINDEX_ENTRY_ATTRIBUTE NodeEntry)
 
CreateBTreeFromIndex

@implemented

Parse an index and create a B-Tree in memory from it.

Parameters
IndexRootContextPointer to an NTFS_ATTR_CONTEXT that describes the location of the index root attribute.
NewTreePointer to a PB_TREE that will receive the pointer to a newly-created B-Tree.
Returns
STATUS_SUCCESS on success. STATUS_INSUFFICIENT_RESOURCES if an allocation fails.
Remarks
Allocates memory for the entire tree. Caller is responsible for destroying the tree with DestroyBTree().
NTSTATUS CreateBTreeFromIndex (PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER FileRecordWithIndex, PNTFS_ATTR_CONTEXT IndexRootContext, PINDEX_ROOT_ATTRIBUTE IndexRoot, PB_TREE *NewTree)
 
GetSizeOfIndexEntries

@implemented

Sums the size of each index entry in every key in a B-Tree node.

Parameters
NodePointer to a B_TREE_FILENAME_NODE. The size of this node's index entries will be returned.
Returns
The sum of the sizes of every index entry for each key in the B-Tree node.
Remarks
Gets only the size of the index entries; doesn't include the size of any headers that would be added to an index record.
ULONG GetSizeOfIndexEntries (PB_TREE_FILENAME_NODE Node)
 
CreateIndexRootFromBTree

@implemented

Parse a B-Tree in memory and convert it into an index that can be written to disk.

Parameters
DeviceExtPointer to the DEVICE_EXTENSION of the target drive.
TreePointer to a B_TREE that describes the index to be written.
MaxIndexSizeDescribes how large the index can be before it will take too much space in the file record. This is strictly the sum of the sizes of all index entries; it does not include the space required by the index root header (INDEX_ROOT_ATTRIBUTE), since that size will be constant.

After reaching MaxIndexSize, an index can no longer be represented with just an index root attribute, and will require an index allocation and $I30 bitmap (TODO).

Parameters
IndexRootPointer to a PINDEX_ROOT_ATTRIBUTE that will receive a pointer to the newly-created index.
LengthPointer to a ULONG which will receive the length of the new index root.
Returns
STATUS_SUCCESS on success. STATUS_INSUFFICIENT_RESOURCES if an allocation fails. STATUS_NOT_IMPLEMENTED if the new index can't fit within MaxIndexSize.
Remarks
If the function succeeds, it's the caller's responsibility to free IndexRoot with ExFreePoolWithTag().
NTSTATUS CreateIndexRootFromBTree (PDEVICE_EXTENSION DeviceExt, PB_TREE Tree, ULONG MaxIndexSize, PINDEX_ROOT_ATTRIBUTE *IndexRoot, ULONG *Length)
 
NTSTATUS CreateIndexBufferFromBTreeNode (PDEVICE_EXTENSION DeviceExt, PB_TREE_FILENAME_NODE Node, ULONG BufferSize, BOOLEAN HasChildren, PINDEX_BUFFER IndexBuffer)
 
DemoteBTreeRoot

@implemented

Demoting the root means first putting all the keys in the root node into a new node, and making the new node a child of a dummy key. The dummy key then becomes the sole contents of the root node. The B-Tree gets one level deeper. This operation is needed when an index root grows too large for its file record. Demotion is my own term; I might change the name later if I think of something more descriptive or can find an appropriate name for this operation in existing B-Tree literature.

Parameters
TreePointer to the B_TREE whose root is being demoted
Returns
STATUS_SUCCESS on success. STATUS_INSUFFICIENT_RESOURCES if an allocation fails.
NTSTATUS DemoteBTreeRoot (PB_TREE Tree)
 
SetIndexEntryVCN

@implemented

Sets the VCN of a given IndexEntry.

Parameters
IndexEntryPointer to an INDEX_ENTRY_ATTRIBUTE structure that will have its VCN set.
VCNVCN to store in the index entry.
Remarks
The index entry must have enough memory allocated to store the VCN, and must have the NTFS_INDEX_ENTRY_NODE flag set. The VCN of an index entry is stored at the very end of the structure, after the filename attribute. Since the filename attribute can be a variable size, this function makes setting this member easy.
VOID SetIndexEntryVCN (PINDEX_ENTRY_ATTRIBUTE IndexEntry, ULONGLONG VCN)
 
NTSTATUS UpdateIndexAllocation (PDEVICE_EXTENSION DeviceExt, PB_TREE Tree, ULONG IndexBufferSize, PFILE_RECORD_HEADER FileRecord)
 
NTSTATUS UpdateIndexNode (PDEVICE_EXTENSION DeviceExt, PFILE_RECORD_HEADER FileRecord, PB_TREE_FILENAME_NODE Node, ULONG IndexBufferSize, PNTFS_ATTR_CONTEXT IndexAllocationContext, ULONG IndexAllocationOffset)
 
PB_TREE_KEY CreateBTreeKeyFromFilename (ULONGLONG FileReference, PFILENAME_ATTRIBUTE FileNameAttribute)
 
VOID DestroyBTreeKey (PB_TREE_KEY Key)
 
VOID DestroyBTreeNode (PB_TREE_FILENAME_NODE Node)
 
DestroyBTree

@implemented

Destroys a B-Tree.

Parameters
TreePointer to the B_TREE which will be destroyed.
Remarks
Destroys every bit of data stored in the tree.
VOID DestroyBTree (PB_TREE Tree)
 
VOID DumpBTreeKey (PB_TREE Tree, PB_TREE_KEY Key, ULONG Number, ULONG Depth)
 
VOID DumpBTreeNode (PB_TREE Tree, PB_TREE_FILENAME_NODE Node, ULONG Number, ULONG Depth)
 
DumpBTree

@implemented

Displays a B-Tree.

Parameters
TreePointer to the B_TREE which will be displayed.
Remarks
Displays a diagnostic summary of a B_TREE.
VOID DumpBTree (PB_TREE Tree)
 
ULONGLONG GetAllocationOffsetFromVCN (PDEVICE_EXTENSION DeviceExt, ULONG IndexBufferSize, ULONGLONG Vcn)
 
ULONGLONG GetIndexEntryVCN (PINDEX_ENTRY_ATTRIBUTE IndexEntry)
 
NtfsInsertKey

@implemented

Inserts a FILENAME_ATTRIBUTE into a B-Tree node.

Parameters
TreePointer to the B_TREE the key (filename attribute) is being inserted into.
FileReferenceReference number to the file being added. This will be a combination of the MFT index and update sequence number.
FileNameAttributePointer to a FILENAME_ATTRIBUTE which is the data for the key that will be added to the tree. A copy will be made.
NodePointer to a B_TREE_FILENAME_NODE into which a new key will be inserted, in order.
CaseSensitiveBoolean indicating if the function should operate in case-sensitive mode. This will be TRUE if an application created the file with the FILE_FLAG_POSIX_SEMANTICS flag.
MaxIndexRootSizeThe maximum size, in bytes, of node entries that can be stored in the index root before it will grow too large for the file record. This number is just the size of the entries, without any headers for the attribute or index root.
IndexRecordSizeThe size, in bytes, of an index record for this index. AKA an index buffer. Usually set to 4096.
MedianKeyPointer to a PB_TREE_KEY that will receive a pointer to the median key, should the node grow too large and need to be split. Will be set to NULL if the node isn't split.
NewRightHandSiblingPointer to a PB_TREE_FILENAME_NODE that will receive a pointer to a newly-created right-hand sibling node, should the node grow too large and need to be split. Will be set to NULL if the node isn't split.
Remarks
A node is always sorted, with the least comparable filename stored first and a dummy key to mark the end.
NTSTATUS NtfsInsertKey (PB_TREE Tree, ULONGLONG FileReference, PFILENAME_ATTRIBUTE FileNameAttribute, PB_TREE_FILENAME_NODE Node, BOOLEAN CaseSensitive, ULONG MaxIndexRootSize, ULONG IndexRecordSize, PB_TREE_KEY *MedianKey, PB_TREE_FILENAME_NODE *NewRightHandSibling)
 
FindLargestKeyInSubtree

@implemented

Finds the greatest key in the subtree rooted at Node, along with the node holding it and the key that precedes it in that node's list.

Parameters
NodeRoot of the subtree to search.
OwnerNodeReceives the node containing the greatest key, or NULL if the subtree holds no filenames.
PreviousKeyReceives the key before the greatest key in OwnerNode's list, or NULL if it's the first.
LargestKeyReceives the greatest key, or NULL if the subtree holds no filenames.
Remarks
An end marker's child holds everything sorting after the last real key, so the greatest key of a subtree is the last real key of the rightmost node reachable through end markers.
static VOID FindLargestKeyInSubtree (PB_TREE_FILENAME_NODE Node, PB_TREE_FILENAME_NODE *OwnerNode, PB_TREE_KEY *PreviousKey, PB_TREE_KEY *LargestKey)
 
ReplaceKeyIndexEntry

@implemented

Replaces the index entry of Key with a copy of Source's, leaving Key's child pointer alone.

Parameters
KeyKey whose index entry will be replaced. It keeps whatever child it had.
SourceKey to copy the file reference and $FILE_NAME from.
Returns
STATUS_SUCCESS on success, STATUS_INSUFFICIENT_RESOURCES if the allocation fails.
Remarks
Key always has a child here, so the replacement is built with room for the VCN and with NTFS_INDEX_ENTRY_NODE set. UpdateIndexAllocation() only grows an entry to make room for a VCN, never the reverse.
static NTSTATUS ReplaceKeyIndexEntry (PB_TREE_KEY Key, PB_TREE_KEY Source)
 
RemoveKeyFromNode

@implemented

Removes a key that has already been located from the node that holds it.

Parameters
NodeNode containing Key.
PreviousKeyThe key before Key in Node's list, or NULL if Key is the first key.
KeyThe key to remove.
Returns
STATUS_SUCCESS on success, STATUS_INSUFFICIENT_RESOURCES if an allocation fails.
static NTSTATUS RemoveKeyFromNode (PB_TREE_FILENAME_NODE Node, PB_TREE_KEY PreviousKey, PB_TREE_KEY Key)
 
NtfsRemoveKey

@implemented

Removes the key matching a given filename from a B-Tree.

Parameters
NodeNode to search. Callers start at the root node of the tree.
SearchKeyKey describing the filename to remove. Only its $FILE_NAME is used, for comparison.
CaseSensitiveBoolean indicating if the function should operate in case-sensitive mode. This will be TRUE if an application created the file with the FILE_FLAG_POSIX_SEMANTICS flag.
Returns
STATUS_SUCCESS if the key was found and removed. STATUS_OBJECT_NAME_NOT_FOUND if no key in the tree matches. STATUS_INSUFFICIENT_RESOURCES if an allocation fails.
Remarks
A node left holding only its end marker is left in place. Readers descend into it and come back empty, whereas collapsing it would free an index buffer a parent VCN still points at.
NTSTATUS NtfsRemoveKey (PB_TREE_FILENAME_NODE Node, PB_TREE_KEY SearchKey, BOOLEAN CaseSensitive)
 
SplitBTreeNode

@implemented

Splits a B-Tree node that has grown too large. Finds the median key and sets up a right-hand-sibling node to contain the keys to the right of the median key.

Parameters
TreePointer to the B_TREE which contains the node being split
NodePointer to the B_TREE_FILENAME_NODE that needs to be split
MedianKeyPointer a PB_TREE_KEY that will receive the pointer to the key in the middle of the node being split
NewRightHandSiblingPointer to a PB_TREE_FILENAME_NODE that will receive a pointer to a newly-created B_TREE_FILENAME_NODE containing the keys to the right of MedianKey.
CaseSensitiveBoolean indicating if the function should operate in case-sensitive mode. This will be TRUE if an application created the file with the FILE_FLAG_POSIX_SEMANTICS flag.
Returns
STATUS_SUCCESS on success. STATUS_INSUFFICIENT_RESOURCES if an allocation fails.
Remarks
It's the responsibility of the caller to insert the new median key into the parent node, as well as making the NewRightHandSibling the lesser child of the node that is currently Node's parent.
NTSTATUS SplitBTreeNode (PB_TREE Tree, PB_TREE_FILENAME_NODE Node, PB_TREE_KEY *MedianKey, PB_TREE_FILENAME_NODE *NewRightHandSibling, BOOLEAN CaseSensitive)
 

Macro Definition Documentation

◆ NDEBUG

#define NDEBUG

Definition at line 30 of file btree.c.

Function Documentation

◆ AllocateIndexNode()

NTSTATUS AllocateIndexNode ( PDEVICE_EXTENSION  DeviceExt,
PFILE_RECORD_HEADER  FileRecord,
ULONG  IndexBufferSize,
PNTFS_ATTR_CONTEXT  IndexAllocationCtx,
ULONG  IndexAllocationOffset,
PULONGLONG  NewVCN 
)

Definition at line 117 of file btree.c.

123{
125 PNTFS_ATTR_CONTEXT BitmapCtx;
126 ULONGLONG IndexAllocationLength, BitmapLength;
127 ULONG BitmapOffset;
128 ULONGLONG NextNodeNumber;
129 PCHAR *BitmapMem;
130 ULONG *BitmapPtr;
133 ULONG BytesNeeded;
135
136 DPRINT("AllocateIndexNode(%p, %p, %lu, %p, %lu, %p) called.\n", DeviceExt,
137 FileRecord,
138 IndexBufferSize,
139 IndexAllocationCtx,
140 IndexAllocationOffset,
141 NewVCN);
142
143 // Get the length of the attribute allocation
144 IndexAllocationLength = AttributeDataLength(IndexAllocationCtx->pRecord);
145
146 // Find the bitmap attribute for the index
147 Status = FindAttribute(DeviceExt,
148 FileRecord,
150 L"$I30",
151 4,
152 &BitmapCtx,
153 &BitmapOffset);
154 if (!NT_SUCCESS(Status))
155 {
156 DPRINT1("FIXME: Need to add bitmap attribute!\n");
158 }
159
160 // Get the length of the bitmap attribute
161 BitmapLength = AttributeDataLength(BitmapCtx->pRecord);
162
163 NextNodeNumber = IndexAllocationLength / DeviceExt->NtfsInfo.BytesPerIndexRecord;
164
165 // TODO: Find unused allocation in bitmap and use that space first
166
167 // Add another bit to bitmap
168
169 // See how many bytes we need to store the amount of bits we'll have
170 BytesNeeded = NextNodeNumber / 8;
171 BytesNeeded++;
172
173 // Windows seems to allocate the bitmap in 8-byte chunks to keep any bytes from being wasted on padding
174 BytesNeeded = ALIGN_UP(BytesNeeded, ATTR_RECORD_ALIGNMENT);
175
176 // Allocate memory for the bitmap, including some padding; RtlInitializeBitmap() wants a pointer
177 // that's ULONG-aligned, and it wants the size of the memory allocated for it to be a ULONG-multiple.
178 BitmapMem = ExAllocatePoolWithTag(NonPagedPool, BytesNeeded + sizeof(ULONG), TAG_NTFS);
179 if (!BitmapMem)
180 {
181 DPRINT1("Error: failed to allocate bitmap!");
182 ReleaseAttributeContext(BitmapCtx);
184 }
185 // RtlInitializeBitmap() wants a pointer that's ULONG-aligned.
186 BitmapPtr = (PULONG)ALIGN_UP_BY((ULONG_PTR)BitmapMem, sizeof(ULONG));
187
188 RtlZeroMemory(BitmapPtr, BytesNeeded);
189
190 // Read the existing bitmap data
191 Status = ReadAttribute(DeviceExt, BitmapCtx, 0, (PCHAR)BitmapPtr, BitmapLength);
192
193 // Initialize bitmap. It has to describe the node we are about to add, so
194 // it needs NextNodeNumber + 1 bits, not NextNodeNumber.
195 RtlInitializeBitMap(&Bitmap, BitmapPtr, NextNodeNumber + 1);
196
197 // Do we need to enlarge the bitmap?
198 if (BytesNeeded > BitmapLength)
199 {
200 // TODO: handle synchronization issues that could occur from changing the directory's file record
201 // Change bitmap size
202 DataSize.QuadPart = BytesNeeded;
203 if (BitmapCtx->pRecord->IsNonResident)
204 {
206 BitmapCtx,
207 BitmapOffset,
208 FileRecord,
209 &DataSize);
210 }
211 else
212 {
214 BitmapCtx,
215 BitmapOffset,
216 FileRecord,
217 &DataSize);
218 }
219 if (!NT_SUCCESS(Status))
220 {
221 DPRINT1("ERROR: Failed to set length of bitmap attribute!\n");
222 ReleaseAttributeContext(BitmapCtx);
223 return Status;
224 }
225 }
226
227 // Enlarge Index Allocation attribute
228 DataSize.QuadPart = IndexAllocationLength + IndexBufferSize;
230 IndexAllocationCtx,
231 IndexAllocationOffset,
232 FileRecord,
233 &DataSize);
234 if (!NT_SUCCESS(Status))
235 {
236 DPRINT1("ERROR: Failed to set length of index allocation!\n");
237 ReleaseAttributeContext(BitmapCtx);
238 return Status;
239 }
240
241 // Update file record on disk
242 Status = UpdateFileRecord(DeviceExt, IndexAllocationCtx->FileMFTIndex, FileRecord);
243 if (!NT_SUCCESS(Status))
244 {
245 DPRINT1("ERROR: Failed to update file record!\n");
246 ReleaseAttributeContext(BitmapCtx);
247 return Status;
248 }
249
250 // Set the bit for the new index record
251 RtlSetBits(&Bitmap, NextNodeNumber, 1);
252
253 // Write the new bitmap attribute
254 Status = WriteAttribute(DeviceExt,
255 BitmapCtx,
256 0,
257 (const PUCHAR)BitmapPtr,
258 BytesNeeded,
260 FileRecord);
261 if (!NT_SUCCESS(Status))
262 {
263 DPRINT1("ERROR: Unable to write to $I30 bitmap attribute!\n");
264 }
265
266 // Calculate VCN of new node number. An index record smaller than a cluster is addressed
267 // in sectors; dividing by the cluster size would give every node a VCN of zero.
268 if (IndexBufferSize < DeviceExt->NtfsInfo.BytesPerCluster)
269 *NewVCN = NextNodeNumber * (IndexBufferSize / DeviceExt->NtfsInfo.BytesPerSector);
270 else
271 *NewVCN = NextNodeNumber * (IndexBufferSize / DeviceExt->NtfsInfo.BytesPerCluster);
272
273 DPRINT("New VCN: %I64u\n", *NewVCN);
274
275 ExFreePoolWithTag(BitmapMem, TAG_NTFS);
276 ReleaseAttributeContext(BitmapCtx);
277
278 return Status;
279}
#define ALIGN_UP_BY(size, align)
LONG NTSTATUS
Definition: precomp.h:26
#define DPRINT1
Definition: precomp.h:8
#define STATUS_NOT_IMPLEMENTED
Definition: d3dkmdt.h:42
#define RtlInitializeBitMap
Definition: dbgbitmap.h:326
#define RtlSetBits
Definition: dbgbitmap.h:345
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:33
#define L(x)
Definition: resources.c:13
@ AttributeBitmap
Definition: ntfs.h:198
#define TAG_NTFS
Definition: ntfs.h:12
#define ATTR_RECORD_ALIGNMENT
Definition: ntfs.h:351
#define ExAllocatePoolWithTag(hernya, size, tag)
Definition: env_spec_w32.h:350
#define NonPagedPool
Definition: env_spec_w32.h:307
Status
Definition: gdiplustypes.h:24
NTSTATUS UpdateFileRecord(PDEVICE_EXTENSION Vcb, ULONGLONG MftIndex, PFILE_RECORD_HEADER FileRecord)
Definition: mft.c:2391
VOID ReleaseAttributeContext(PNTFS_ATTR_CONTEXT Context)
Definition: mft.c:104
ULONGLONG AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord)
Definition: mft.c:259
NTSTATUS FindAttribute(PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER MftRecord, ULONG Type, PCWSTR Name, ULONG NameLength, PNTFS_ATTR_CONTEXT *AttrCtx, PULONG Offset)
Definition: mft.c:131
ULONG ReadAttribute(PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset, PCHAR Buffer, ULONG Length)
Definition: mft.c:1625
NTSTATUS WriteAttribute(PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset, const PUCHAR Buffer, ULONG Length, PULONG RealLengthWritten, PFILE_RECORD_HEADER FileRecord)
Definition: mft.c:1829
NTSTATUS SetNonResidentAttributeDataLength(PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT AttrContext, ULONG AttrOffset, PFILE_RECORD_HEADER FileRecord, PLARGE_INTEGER DataSize)
Definition: mft.c:1065
NTSTATUS SetResidentAttributeDataLength(PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT AttrContext, ULONG AttrOffset, PFILE_RECORD_HEADER FileRecord, PLARGE_INTEGER DataSize)
Definition: mft.c:1148
#define ExFreePoolWithTag(_P, _T)
Definition: module.h:1109
_In_ NDIS_STATUS _In_ ULONG _In_ USHORT _In_opt_ PVOID _In_ ULONG DataSize
Definition: ndis.h:4755
#define DPRINT
Definition: sndvol32.h:73
uint32_t * PULONG
Definition: typedefs.h:59
uint64_t ULONGLONG
Definition: typedefs.h:67
#define RtlZeroMemory(Destination, Length)
Definition: typedefs.h:262
uint32_t ULONG_PTR
Definition: typedefs.h:65
unsigned char * PUCHAR
Definition: typedefs.h:53
uint32_t ULONG
Definition: typedefs.h:59
char * PCHAR
Definition: typedefs.h:51
#define STATUS_INSUFFICIENT_RESOURCES
Definition: udferr_usr.h:158
#define ALIGN_UP(size, type)
Definition: umtypes.h:91
_Must_inspect_result_ _In_ WDFIOTARGET _In_opt_ WDFREQUEST _In_opt_ PWDF_MEMORY_DESCRIPTOR _In_opt_ PLONGLONG _In_opt_ PWDF_REQUEST_SEND_OPTIONS _Out_opt_ PULONG_PTR BytesWritten
Definition: wdfiotarget.h:960

Referenced by UpdateIndexNode().

◆ CompareTreeKeys()

LONG CompareTreeKeys ( PB_TREE_KEY  Key1,
PB_TREE_KEY  Key2,
BOOLEAN  CaseSensitive 
)

Definition at line 422 of file btree.c.

423{
424 UNICODE_STRING Key1Name, Key2Name;
425 LONG Comparison;
426
427 // Key1 must not be the final key (AKA the dummy key)
429
430 // If Key2 is the "dummy key", key 1 will always come first
431 if (Key2->NextKey == NULL)
432 return -1;
433
434 Key1Name.Buffer = Key1->IndexEntry->FileName.Name;
435 Key1Name.Length = Key1Name.MaximumLength
436 = Key1->IndexEntry->FileName.NameLength * sizeof(WCHAR);
437
438 Key2Name.Buffer = Key2->IndexEntry->FileName.Name;
439 Key2Name.Length = Key2Name.MaximumLength
440 = Key2->IndexEntry->FileName.NameLength * sizeof(WCHAR);
441
442 // Are the two keys the same length?
443 if (Key1Name.Length == Key2Name.Length)
444 return RtlCompareUnicodeString(&Key1Name, &Key2Name, !CaseSensitive);
445
446 // Is Key1 shorter?
447 if (Key1Name.Length < Key2Name.Length)
448 {
449 // Truncate KeyName2 to be the same length as KeyName1
450 Key2Name.Length = Key1Name.Length;
451
452 // Compare the names of the same length
453 Comparison = RtlCompareUnicodeString(&Key1Name, &Key2Name, !CaseSensitive);
454
455 // If the truncated names are the same length, the shorter one comes first
456 if (Comparison == 0)
457 return -1;
458 }
459 else
460 {
461 // Key2 is shorter
462 // Truncate KeyName1 to be the same length as KeyName2
463 Key1Name.Length = Key2Name.Length;
464
465 // Compare the names of the same length
466 Comparison = RtlCompareUnicodeString(&Key1Name, &Key2Name, !CaseSensitive);
467
468 // If the truncated names are the same length, the shorter one comes first
469 if (Comparison == 0)
470 return 1;
471 }
472
473 return Comparison;
474}
#define NTFS_INDEX_ENTRY_END
Definition: ntfs.h:61
#define NULL
Definition: types.h:112
ULONG RtlCompareUnicodeString(PUNICODE_STRING s1, PUNICODE_STRING s2, BOOLEAN UpCase)
Definition: string_lib.cpp:31
#define ASSERT(a)
Definition: mode.c:44
short WCHAR
Definition: pedump.c:58
long LONG
Definition: pedump.c:60
UCHAR NameLength
Definition: ntfs.h:408
WCHAR Name[1]
Definition: ntfs.h:410
FILENAME_ATTRIBUTE FileName
Definition: ntfs.h:458
USHORT Flags
Definition: ntfs.h:456
struct _B_TREE_KEY * NextKey
Definition: ntfs.h:467
PINDEX_ENTRY_ATTRIBUTE IndexEntry
Definition: ntfs.h:469
USHORT MaximumLength
Definition: env_spec_w32.h:370

Referenced by NtfsInsertKey(), and NtfsRemoveKey().

◆ CountBTreeKeys()

ULONG CountBTreeKeys ( PB_TREE_KEY  FirstKey)

Definition at line 489 of file btree.c.

490{
491 ULONG Count = 0;
492 PB_TREE_KEY Current = FirstKey;
493
494 while (Current != NULL)
495 {
496 Count++;
497 Current = Current->NextKey;
498 }
499
500 return Count;
501}
int Count
Definition: noreturn.cpp:7

Referenced by SplitBTreeNode().

◆ CreateBTreeFromIndex()

NTSTATUS CreateBTreeFromIndex ( PDEVICE_EXTENSION  Vcb,
PFILE_RECORD_HEADER  FileRecordWithIndex,
PNTFS_ATTR_CONTEXT  IndexRootContext,
PINDEX_ROOT_ATTRIBUTE  IndexRoot,
PB_TREE *  NewTree 
)

Definition at line 687 of file btree.c.

693{
694 PINDEX_ENTRY_ATTRIBUTE CurrentNodeEntry;
698 ULONG CurrentOffset = IndexRoot->Header.FirstEntryOffset;
699 PNTFS_ATTR_CONTEXT IndexAllocationContext = NULL;
701
702 DPRINT("CreateBTreeFromIndex(%p, %p)\n", IndexRoot, NewTree);
703
704 if (!Tree || !RootNode || !CurrentKey)
705 {
706 DPRINT1("Couldn't allocate enough memory for B-Tree!\n");
707 if (Tree)
709 if (CurrentKey)
710 ExFreePoolWithTag(CurrentKey, TAG_NTFS);
711 if (RootNode)
714 }
715
716 RtlZeroMemory(Tree, sizeof(B_TREE));
718 RtlZeroMemory(CurrentKey, sizeof(B_TREE_KEY));
719
720 // See if the file record has an attribute allocation
722 FileRecordWithIndex,
724 L"$I30",
725 4,
726 &IndexAllocationContext,
727 NULL);
728 if (!NT_SUCCESS(Status))
729 IndexAllocationContext = NULL;
730
731 // Setup the Tree
732 RootNode->FirstKey = CurrentKey;
733 Tree->RootNode = RootNode;
734
735 // Make sure we won't try reading past the attribute-end
736 if (FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE, Header) + IndexRoot->Header.TotalSizeOfEntries > IndexRootContext->pRecord->Resident.ValueLength)
737 {
738 DPRINT1("Filesystem corruption detected!\n");
741 goto Cleanup;
742 }
743
744 // Start at the first node entry
745 CurrentNodeEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)IndexRoot
747 + IndexRoot->Header.FirstEntryOffset);
748
749 // Create a key for each entry in the node
750 while (CurrentOffset < IndexRoot->Header.TotalSizeOfEntries)
751 {
752 // Allocate memory for the current entry
753 CurrentKey->IndexEntry = ExAllocatePoolWithTag(NonPagedPool, CurrentNodeEntry->Length, TAG_NTFS);
754 if (!CurrentKey->IndexEntry)
755 {
756 DPRINT1("ERROR: Couldn't allocate memory for next key!\n");
759 goto Cleanup;
760 }
761
762 RootNode->KeyCount++;
763
764 // If this isn't the last entry
765 if (!(CurrentNodeEntry->Flags & NTFS_INDEX_ENTRY_END))
766 {
767 // Create the next key
769 if (!NextKey)
770 {
771 DPRINT1("ERROR: Couldn't allocate memory for next key!\n");
774 goto Cleanup;
775 }
776
777 RtlZeroMemory(NextKey, sizeof(B_TREE_KEY));
778
779 // Add NextKey to the end of the list
780 CurrentKey->NextKey = NextKey;
781
782 // Copy the current entry to its key
783 RtlCopyMemory(CurrentKey->IndexEntry, CurrentNodeEntry, CurrentNodeEntry->Length);
784
785 // Does this key have a sub-node?
786 if (CurrentKey->IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE)
787 {
788 // Create the child node
790 IndexRoot,
791 IndexAllocationContext,
792 CurrentKey->IndexEntry);
793 if (!CurrentKey->LesserChild)
794 {
795 DPRINT1("ERROR: Couldn't create child node!\n");
798 goto Cleanup;
799 }
800 }
801
802 // Advance to the next entry
803 CurrentOffset += CurrentNodeEntry->Length;
804 CurrentNodeEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)CurrentNodeEntry + CurrentNodeEntry->Length);
805 CurrentKey = NextKey;
806 }
807 else
808 {
809 // Copy the final entry to its key
810 RtlCopyMemory(CurrentKey->IndexEntry, CurrentNodeEntry, CurrentNodeEntry->Length);
811 CurrentKey->NextKey = NULL;
812
813 // Does this key have a sub-node?
814 if (CurrentKey->IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE)
815 {
816 // Create the child node
818 IndexRoot,
819 IndexAllocationContext,
820 CurrentKey->IndexEntry);
821 if (!CurrentKey->LesserChild)
822 {
823 DPRINT1("ERROR: Couldn't create child node!\n");
826 goto Cleanup;
827 }
828 }
829
830 break;
831 }
832 }
833
834 *NewTree = Tree;
836
837Cleanup:
838 if (IndexAllocationContext)
839 ReleaseAttributeContext(IndexAllocationContext);
840
841 return Status;
842}
CRegistryTree Tree
#define NTFS_INDEX_ENTRY_NODE
Definition: ntfs.h:60
PB_TREE_FILENAME_NODE CreateBTreeNodeFromIndexNode(PDEVICE_EXTENSION Vcb, PINDEX_ROOT_ATTRIBUTE IndexRoot, PNTFS_ATTR_CONTEXT IndexAllocationAttributeCtx, PINDEX_ENTRY_ATTRIBUTE NodeEntry)
Definition: btree.c:504
VOID DestroyBTree(PB_TREE Tree)
Definition: btree.c:1549
Definition: Header.h:9
static const WCHAR Cleanup[]
Definition: register.c:80
struct INDEX_ENTRY_ATTRIBUTE * PINDEX_ENTRY_ATTRIBUTE
@ AttributeIndexAllocation
Definition: ntfs.h:197
PCONFIGURATION_COMPONENT_DATA RootNode
Definition: macharm.c:19
#define Vcb
Definition: cdprocs.h:1415
#define STATUS_SUCCESS
Definition: shellext.h:65
Definition: ntfs.h:485
Definition: ntfs.h:440
USHORT Length
Definition: ntfs.h:454
ULONG FirstEntryOffset
Definition: ntfs.h:415
ULONG TotalSizeOfEntries
Definition: ntfs.h:416
INDEX_HEADER_ATTRIBUTE Header
Definition: ntfs.h:429
B_TREE_FILENAME_NODE * LesserChild
Definition: ntfs.h:468
#define FIELD_OFFSET(t, f)
Definition: typedefs.h:255
#define RtlCopyMemory(Destination, Source, Length)
Definition: typedefs.h:263
#define STATUS_FILE_CORRUPT_ERROR
Definition: udferr_usr.h:168

Referenced by AddFilenameToDirectoryWorker(), and RemoveFilenameFromDirectoryWorker().

◆ CreateBTreeKeyFromFilename()

PB_TREE_KEY CreateBTreeKeyFromFilename ( ULONGLONG  FileReference,
PFILENAME_ATTRIBUTE  FileNameAttribute 
)

Definition at line 1466 of file btree.c.

1467{
1468 PB_TREE_KEY NewKey;
1469 ULONG AttributeSize = GetFileNameAttributeLength(FileNameAttribute);
1471
1472 // Create a new Index Entry for the file
1474 if (!NewEntry)
1475 {
1476 DPRINT1("ERROR: Failed to allocate memory for Index Entry!\n");
1477 return NULL;
1478 }
1479
1480 // Setup the Index Entry
1481 RtlZeroMemory(NewEntry, EntrySize);
1482 NewEntry->Data.Directory.IndexedFile = FileReference;
1483 NewEntry->Length = EntrySize;
1484 NewEntry->KeyLength = AttributeSize;
1485
1486 // Copy the FileNameAttribute
1487 RtlCopyMemory(&NewEntry->FileName, FileNameAttribute, AttributeSize);
1488
1489 // Setup the New Key
1491 if (!NewKey)
1492 {
1493 DPRINT1("ERROR: Failed to allocate memory for new key!\n");
1494 ExFreePoolWithTag(NewEntry, TAG_NTFS);
1495 return NULL;
1496 }
1497
1498 /* DestroyBTreeKey() follows LesserChild, it can't be left to pool garbage */
1499 RtlZeroMemory(NewKey, sizeof(*NewKey));
1500 NewKey->IndexEntry = NewEntry;
1501
1502 return NewKey;
1503}
ULONG GetFileNameAttributeLength(PFILENAME_ATTRIBUTE FileNameAttribute)
Definition: attrib.c:2008
union INDEX_ENTRY_ATTRIBUTE::@970 Data
struct INDEX_ENTRY_ATTRIBUTE::@970::@971 Directory
USHORT KeyLength
Definition: ntfs.h:455
_In_ UCHAR EntrySize
Definition: iofuncs.h:642

Referenced by NtfsInsertKey(), and RemoveFilenameFromDirectoryWorker().

◆ CreateBTreeNodeFromIndexNode()

PB_TREE_FILENAME_NODE CreateBTreeNodeFromIndexNode ( PDEVICE_EXTENSION  Vcb,
PINDEX_ROOT_ATTRIBUTE  IndexRoot,
PNTFS_ATTR_CONTEXT  IndexAllocationAttributeCtx,
PINDEX_ENTRY_ATTRIBUTE  NodeEntry 
)

Definition at line 504 of file btree.c.

508{
509 PB_TREE_FILENAME_NODE NewNode;
510 PINDEX_ENTRY_ATTRIBUTE CurrentNodeEntry;
511 PINDEX_ENTRY_ATTRIBUTE FirstNodeEntry;
512 ULONG CurrentEntryOffset = 0;
513 PINDEX_BUFFER NodeBuffer;
514 ULONG IndexBufferSize = Vcb->NtfsInfo.BytesPerIndexRecord;
515 PULONGLONG VCN;
516 PB_TREE_KEY CurrentKey;
518 ULONGLONG IndexNodeOffset;
520
521 if (IndexAllocationAttributeCtx == NULL)
522 {
523 DPRINT1("ERROR: Couldn't find index allocation attribute even though there should be one!\n");
524 return NULL;
525 }
526
527 // Get the node number from the end of the node entry
528 VCN = (PULONGLONG)((ULONG_PTR)NodeEntry + NodeEntry->Length - sizeof(ULONGLONG));
529
530 // Create the new tree node
532 if (!NewNode)
533 {
534 DPRINT1("ERROR: Couldn't allocate memory for new filename node.\n");
535 return NULL;
536 }
537 RtlZeroMemory(NewNode, sizeof(B_TREE_FILENAME_NODE));
538
539 // Create the first key
541 if (!CurrentKey)
542 {
543 DPRINT1("ERROR: Failed to allocate memory for key!\n");
544 ExFreePoolWithTag(NewNode, TAG_NTFS);
545 return NULL;
546 }
547 RtlZeroMemory(CurrentKey, sizeof(B_TREE_KEY));
548 NewNode->FirstKey = CurrentKey;
549
550 // Allocate memory for the node buffer
551 NodeBuffer = ExAllocatePoolWithTag(NonPagedPool, IndexBufferSize, TAG_NTFS);
552 if (!NodeBuffer)
553 {
554 DPRINT1("ERROR: Couldn't allocate memory for node buffer!\n");
555 ExFreePoolWithTag(CurrentKey, TAG_NTFS);
556 ExFreePoolWithTag(NewNode, TAG_NTFS);
557 return NULL;
558 }
559
560 // Calculate offset into index allocation
561 IndexNodeOffset = GetAllocationOffsetFromVCN(Vcb, IndexBufferSize, *VCN);
562
563 // TODO: Confirm index bitmap has this node marked as in-use
564
565 // Read the node
567 IndexAllocationAttributeCtx,
568 IndexNodeOffset,
569 (PCHAR)NodeBuffer,
570 IndexBufferSize);
571
572 ASSERT(BytesRead == IndexBufferSize);
573 NT_ASSERT(NodeBuffer->Ntfs.Type == NRH_INDX_TYPE);
574 NT_ASSERT(NodeBuffer->VCN == *VCN);
575
576 // Apply the fixup array to the node buffer
577 Status = FixupUpdateSequenceArray(Vcb, &NodeBuffer->Ntfs);
578 if (!NT_SUCCESS(Status))
579 {
580 DPRINT1("ERROR: Couldn't apply fixup array to index node buffer!\n");
581 ExFreePoolWithTag(NodeBuffer, TAG_NTFS);
582 ExFreePoolWithTag(CurrentKey, TAG_NTFS);
583 ExFreePoolWithTag(NewNode, TAG_NTFS);
584 return NULL;
585 }
586
587 // Walk through the index and create keys for all the entries
588 FirstNodeEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)(&NodeBuffer->Header)
589 + NodeBuffer->Header.FirstEntryOffset);
590 CurrentNodeEntry = FirstNodeEntry;
591 while (CurrentEntryOffset < NodeBuffer->Header.TotalSizeOfEntries)
592 {
593 // Allocate memory for the current entry
594 CurrentKey->IndexEntry = ExAllocatePoolWithTag(NonPagedPool, CurrentNodeEntry->Length, TAG_NTFS);
595 if (!CurrentKey->IndexEntry)
596 {
597 DPRINT1("ERROR: Couldn't allocate memory for next key!\n");
598 DestroyBTreeNode(NewNode);
599 ExFreePoolWithTag(NodeBuffer, TAG_NTFS);
600 return NULL;
601 }
602
603 NewNode->KeyCount++;
604
605 // If this isn't the last entry
606 if (!(CurrentNodeEntry->Flags & NTFS_INDEX_ENTRY_END))
607 {
608 // Create the next key
610 if (!NextKey)
611 {
612 DPRINT1("ERROR: Couldn't allocate memory for next key!\n");
613 DestroyBTreeNode(NewNode);
614 ExFreePoolWithTag(NodeBuffer, TAG_NTFS);
615 return NULL;
616 }
617 RtlZeroMemory(NextKey, sizeof(B_TREE_KEY));
618
619 // Add NextKey to the end of the list
620 CurrentKey->NextKey = NextKey;
621
622 // Copy the current entry to its key
623 RtlCopyMemory(CurrentKey->IndexEntry, CurrentNodeEntry, CurrentNodeEntry->Length);
624
625 // See if the current key has a sub-node
626 if (CurrentKey->IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE)
627 {
629 IndexRoot,
630 IndexAllocationAttributeCtx,
631 CurrentKey->IndexEntry);
632 }
633
634 CurrentKey = NextKey;
635 }
636 else
637 {
638 // Copy the final entry to its key
639 RtlCopyMemory(CurrentKey->IndexEntry, CurrentNodeEntry, CurrentNodeEntry->Length);
640 CurrentKey->NextKey = NULL;
641
642 // See if the current key has a sub-node
643 if (CurrentKey->IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE)
644 {
646 IndexRoot,
647 IndexAllocationAttributeCtx,
648 CurrentKey->IndexEntry);
649 }
650
651 break;
652 }
653
654 // Advance to the next entry
655 CurrentEntryOffset += CurrentNodeEntry->Length;
656 CurrentNodeEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)CurrentNodeEntry + CurrentNodeEntry->Length);
657 }
658
659 NewNode->VCN = *VCN;
660 NewNode->HasValidVCN = TRUE;
661
662 ExFreePoolWithTag(NodeBuffer, TAG_NTFS);
663
664 return NewNode;
665}
VOID DestroyBTreeNode(PB_TREE_FILENAME_NODE Node)
Definition: btree.c:1518
ULONGLONG GetAllocationOffsetFromVCN(PDEVICE_EXTENSION DeviceExt, ULONG IndexBufferSize, ULONGLONG Vcn)
Definition: btree.c:1637
#define TRUE
Definition: types.h:120
#define NRH_INDX_TYPE
Definition: ntfs.h:278
NTSTATUS FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb, PNTFS_RECORD_HEADER Record)
Definition: mft.c:2425
__GNU_EXTENSION typedef unsigned __int64 * PULONGLONG
Definition: ntbasedef.h:395
INDEX_HEADER_ATTRIBUTE Header
Definition: ntfs.h:436
ULONGLONG VCN
Definition: ntfs.h:435
NTFS_RECORD_HEADER Ntfs
Definition: ntfs.h:434
ULONGLONG VCN
Definition: ntfs.h:480
BOOLEAN HasValidVCN
Definition: ntfs.h:478
PB_TREE_KEY FirstKey
Definition: ntfs.h:481
_Must_inspect_result_ _In_ WDFIOTARGET _In_opt_ WDFREQUEST _In_opt_ PWDF_MEMORY_DESCRIPTOR _In_opt_ PLONGLONG _In_opt_ PWDF_REQUEST_SEND_OPTIONS _Out_opt_ PULONG_PTR BytesRead
Definition: wdfiotarget.h:870
#define NT_ASSERT
Definition: rtlfuncs.h:3334

Referenced by CreateBTreeFromIndex(), and CreateBTreeNodeFromIndexNode().

◆ CreateDummyKey()

PB_TREE_KEY CreateDummyKey ( BOOLEAN  HasChildNode)

Definition at line 294 of file btree.c.

295{
296 PINDEX_ENTRY_ATTRIBUTE NewIndexEntry;
297 PB_TREE_KEY NewDummyKey;
298
299 // Calculate max size of a dummy key
301 EntrySize += sizeof(ULONGLONG); // for VCN
302
303 // Create the index entry for the key
305 if (!NewIndexEntry)
306 {
307 DPRINT1("Couldn't allocate memory for dummy key index entry!\n");
308 return NULL;
309 }
310
311 RtlZeroMemory(NewIndexEntry, EntrySize);
312
313 if (HasChildNode)
314 {
316 }
317 else
318 {
319 NewIndexEntry->Flags = NTFS_INDEX_ENTRY_END;
320 EntrySize -= sizeof(ULONGLONG); // no VCN
321 }
322
323 NewIndexEntry->Length = EntrySize;
324
325 // Create the key
326 NewDummyKey = ExAllocatePoolWithTag(NonPagedPool, sizeof(B_TREE_KEY), TAG_NTFS);
327 if (!NewDummyKey)
328 {
329 DPRINT1("Unable to allocate dummy key!\n");
330 ExFreePoolWithTag(NewIndexEntry, TAG_NTFS);
331 return NULL;
332 }
333 RtlZeroMemory(NewDummyKey, sizeof(B_TREE_KEY));
334
335 NewDummyKey->IndexEntry = NewIndexEntry;
336
337 return NewDummyKey;
338}

Referenced by CreateEmptyBTree(), DemoteBTreeRoot(), and SplitBTreeNode().

◆ CreateEmptyBTree()

NTSTATUS CreateEmptyBTree ( PB_TREE *  NewTree)

Definition at line 353 of file btree.c.

354{
357 PB_TREE_KEY DummyKey;
358
359 DPRINT("CreateEmptyBTree(%p) called\n", NewTree);
360
361 if (!Tree || !RootNode)
362 {
363 DPRINT1("Couldn't allocate enough memory for B-Tree!\n");
364 if (Tree)
366 if (RootNode)
369 }
370
371 // Create the dummy key
372 DummyKey = CreateDummyKey(FALSE);
373 if (!DummyKey)
374 {
375 DPRINT1("ERROR: Failed to create dummy key!\n");
379 }
380
381 RtlZeroMemory(Tree, sizeof(B_TREE));
383
384 // Setup the Tree
385 RootNode->FirstKey = DummyKey;
386 RootNode->KeyCount = 1;
387 RootNode->DiskNeedsUpdating = TRUE;
388 Tree->RootNode = RootNode;
389
390 *NewTree = Tree;
391
392 // Memory will be freed when DestroyBTree() is called
393
394 return STATUS_SUCCESS;
395}
PB_TREE_KEY CreateDummyKey(BOOLEAN HasChildNode)
Definition: btree.c:294
#define FALSE
Definition: types.h:117

Referenced by NtfsCreateDirectory().

◆ CreateIndexBufferFromBTreeNode()

NTSTATUS CreateIndexBufferFromBTreeNode ( PDEVICE_EXTENSION  DeviceExt,
PB_TREE_FILENAME_NODE  Node,
ULONG  BufferSize,
BOOLEAN  HasChildren,
PINDEX_BUFFER  IndexBuffer 
)

Definition at line 1006 of file btree.c.

1011{
1012 ULONG i;
1013 PB_TREE_KEY CurrentKey;
1014 PINDEX_ENTRY_ATTRIBUTE CurrentNodeEntry;
1016
1017 // TODO: Fix magic, do math
1018 RtlZeroMemory(IndexBuffer, BufferSize);
1019 IndexBuffer->Ntfs.Type = NRH_INDX_TYPE;
1020 IndexBuffer->Ntfs.UsaOffset = 0x28;
1021 IndexBuffer->Ntfs.UsaCount = 9;
1022
1023 // TODO: Check bitmap for VCN
1024 ASSERT(Node->HasValidVCN);
1025 IndexBuffer->VCN = Node->VCN;
1026
1027 // Windows seems to alternate between using 0x28 and 0x40 for the first entry offset of each index buffer.
1028 // Interestingly, neither Windows nor chkdsk seem to mind if we just use 0x28 for every index record.
1029 IndexBuffer->Header.FirstEntryOffset = 0x28;
1031
1032 // Start summing the total size of this node's entries
1033 IndexBuffer->Header.TotalSizeOfEntries = IndexBuffer->Header.FirstEntryOffset;
1034
1035 CurrentKey = Node->FirstKey;
1036 CurrentNodeEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&(IndexBuffer->Header)
1037 + IndexBuffer->Header.FirstEntryOffset);
1038 for (i = 0; i < Node->KeyCount; i++)
1039 {
1040 // Would adding the current entry to the index increase the node size beyond the allocation size?
1042 + IndexBuffer->Header.TotalSizeOfEntries
1043 + CurrentNodeEntry->Length;
1044 if (IndexSize > BufferSize)
1045 {
1046 DPRINT1("TODO: Adding file would require creating a new node!\n");
1048 }
1049
1050 ASSERT(CurrentKey->IndexEntry->Length != 0);
1051
1052 // Copy the index entry
1053 RtlCopyMemory(CurrentNodeEntry, CurrentKey->IndexEntry, CurrentKey->IndexEntry->Length);
1054
1055 DPRINT("Index Node Entry Stream Length: %u\nIndex Node Entry Length: %u\n",
1056 CurrentNodeEntry->KeyLength,
1057 CurrentNodeEntry->Length);
1058
1059 // Add Length of Current Entry to Total Size of Entries
1060 IndexBuffer->Header.TotalSizeOfEntries += CurrentNodeEntry->Length;
1061
1062 // Check for child nodes
1063 if (HasChildren)
1064 IndexBuffer->Header.Flags = INDEX_NODE_LARGE;
1065
1066 // Go to the next node entry
1067 CurrentNodeEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)CurrentNodeEntry + CurrentNodeEntry->Length);
1068 CurrentKey = CurrentKey->NextKey;
1069 }
1070
1071 Status = AddFixupArray(DeviceExt, &IndexBuffer->Ntfs);
1072
1073 return Status;
1074}
_In_ ULONG BufferSize
Definition: dispmprt.h:490
#define INDEX_NODE_LARGE
Definition: ntfs.h:239
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
NTSTATUS AddFixupArray(PDEVICE_EXTENSION Vcb, PNTFS_RECORD_HEADER Record)
Definition: mft.c:3575
USHORT UsaCount
Definition: ntfs.h:272
USHORT UsaOffset
Definition: ntfs.h:271
Definition: dlist.c:348

Referenced by UpdateIndexNode().

◆ CreateIndexRootFromBTree()

NTSTATUS CreateIndexRootFromBTree ( PDEVICE_EXTENSION  DeviceExt,
PB_TREE  Tree,
ULONG  MaxIndexSize,
PINDEX_ROOT_ATTRIBUTE *  IndexRoot,
ULONG *  Length 
)

Definition at line 915 of file btree.c.

920{
921 ULONG i;
922 PB_TREE_KEY CurrentKey;
923 PINDEX_ENTRY_ATTRIBUTE CurrentNodeEntry;
925 DeviceExt->NtfsInfo.BytesPerFileRecord,
926 TAG_NTFS);
927
928 DPRINT("CreateIndexRootFromBTree(%p, %p, 0x%lx, %p, %p)\n", DeviceExt, Tree, MaxIndexSize, IndexRoot, Length);
929
930#ifndef NDEBUG
932#endif
933
934 if (!NewIndexRoot)
935 {
936 DPRINT1("Failed to allocate memory for Index Root!\n");
938 }
939
940 // Setup the new index root
941 RtlZeroMemory(NewIndexRoot, DeviceExt->NtfsInfo.BytesPerFileRecord);
942
943 NewIndexRoot->AttributeType = AttributeFileName;
944 NewIndexRoot->CollationRule = COLLATION_FILE_NAME;
945 NewIndexRoot->SizeOfEntry = DeviceExt->NtfsInfo.BytesPerIndexRecord;
946 // If Bytes per index record is less than cluster size, clusters per index record becomes sectors per index
947 if (NewIndexRoot->SizeOfEntry < DeviceExt->NtfsInfo.BytesPerCluster)
948 NewIndexRoot->ClustersPerIndexRecord = NewIndexRoot->SizeOfEntry / DeviceExt->NtfsInfo.BytesPerSector;
949 else
950 NewIndexRoot->ClustersPerIndexRecord = NewIndexRoot->SizeOfEntry / DeviceExt->NtfsInfo.BytesPerCluster;
951
952 // Setup the Index node header
953 NewIndexRoot->Header.FirstEntryOffset = sizeof(INDEX_HEADER_ATTRIBUTE);
954 NewIndexRoot->Header.Flags = INDEX_ROOT_SMALL;
955
956 // Start summing the total size of this node's entries
957 NewIndexRoot->Header.TotalSizeOfEntries = NewIndexRoot->Header.FirstEntryOffset;
958
959 // Setup each Node Entry
960 CurrentKey = Tree->RootNode->FirstKey;
961 CurrentNodeEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)NewIndexRoot
963 + NewIndexRoot->Header.FirstEntryOffset);
964 for (i = 0; i < Tree->RootNode->KeyCount; i++)
965 {
966 // Would adding the current entry to the index increase the index size beyond the limit we've set?
967 ULONG IndexSize = NewIndexRoot->Header.TotalSizeOfEntries - NewIndexRoot->Header.FirstEntryOffset + CurrentKey->IndexEntry->Length;
968 if (IndexSize > MaxIndexSize)
969 {
970 DPRINT1("TODO: Adding file would require creating an attribute list!\n");
971 ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
973 }
974
975 ASSERT(CurrentKey->IndexEntry->Length != 0);
976
977 // Copy the index entry
978 RtlCopyMemory(CurrentNodeEntry, CurrentKey->IndexEntry, CurrentKey->IndexEntry->Length);
979
980 DPRINT("Index Node Entry Stream Length: %u\nIndex Node Entry Length: %u\n",
981 CurrentNodeEntry->KeyLength,
982 CurrentNodeEntry->Length);
983
984 // Does the current key have any sub-nodes?
985 if (CurrentKey->LesserChild)
986 NewIndexRoot->Header.Flags = INDEX_ROOT_LARGE;
987
988 // Add Length of Current Entry to Total Size of Entries
989 NewIndexRoot->Header.TotalSizeOfEntries += CurrentKey->IndexEntry->Length;
990
991 // Go to the next node entry
992 CurrentNodeEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)CurrentNodeEntry + CurrentNodeEntry->Length);
993
994 CurrentKey = CurrentKey->NextKey;
995 }
996
997 NewIndexRoot->Header.AllocatedSize = NewIndexRoot->Header.TotalSizeOfEntries;
998
999 *IndexRoot = NewIndexRoot;
1001
1002 return STATUS_SUCCESS;
1003}
VOID DumpBTree(PB_TREE Tree)
Definition: btree.c:1629
#define INDEX_ROOT_SMALL
Definition: ntfs.h:235
#define COLLATION_FILE_NAME
Definition: ntfs.h:228
#define INDEX_ROOT_LARGE
Definition: ntfs.h:236
@ AttributeFileName
Definition: ntfs.h:190
_In_ ULONG _In_ ULONG _In_ ULONG Length
Definition: ntddpcm.h:102
ULONG AttributeType
Definition: ntfs.h:424
ULONG CollationRule
Definition: ntfs.h:425
UCHAR ClustersPerIndexRecord
Definition: ntfs.h:427
ULONG SizeOfEntry
Definition: ntfs.h:426

Referenced by AddFilenameToDirectoryWorker(), NtfsCreateDirectory(), and RemoveFilenameFromDirectoryWorker().

◆ DemoteBTreeRoot()

NTSTATUS DemoteBTreeRoot ( PB_TREE  Tree)

Definition at line 1094 of file btree.c.

1095{
1096 PB_TREE_FILENAME_NODE NewSubNode, NewIndexRoot;
1097 PB_TREE_KEY DummyKey;
1098
1099 DPRINT("Collapsing Index Root into sub-node.\n");
1100
1101#ifndef NDEBUG
1102 DumpBTree(Tree);
1103#endif
1104
1105 // Create a new node that will hold the keys currently in index root
1107 if (!NewSubNode)
1108 {
1109 DPRINT1("ERROR: Couldn't allocate memory for new sub-node.\n");
1111 }
1112 RtlZeroMemory(NewSubNode, sizeof(B_TREE_FILENAME_NODE));
1113
1114 // Copy the applicable data from the old index root node
1115 NewSubNode->KeyCount = Tree->RootNode->KeyCount;
1116 NewSubNode->FirstKey = Tree->RootNode->FirstKey;
1117 NewSubNode->DiskNeedsUpdating = TRUE;
1118
1119 // Create a new dummy key, and make the new node it's child
1120 DummyKey = CreateDummyKey(TRUE);
1121 if (!DummyKey)
1122 {
1123 DPRINT1("ERROR: Couldn't allocate memory for new root node.\n");
1124 ExFreePoolWithTag(NewSubNode, TAG_NTFS);
1126 }
1127
1128 // Make the new node a child of the dummy key
1129 DummyKey->LesserChild = NewSubNode;
1130
1131 // Create a new index root node
1133 if (!NewIndexRoot)
1134 {
1135 DPRINT1("ERROR: Couldn't allocate memory for new index root.\n");
1136 ExFreePoolWithTag(NewSubNode, TAG_NTFS);
1137 ExFreePoolWithTag(DummyKey, TAG_NTFS);
1139 }
1140 RtlZeroMemory(NewIndexRoot, sizeof(B_TREE_FILENAME_NODE));
1141
1142 NewIndexRoot->DiskNeedsUpdating = TRUE;
1143
1144 // Insert the dummy key into the new node
1145 NewIndexRoot->FirstKey = DummyKey;
1146 NewIndexRoot->KeyCount = 1;
1147 NewIndexRoot->DiskNeedsUpdating = TRUE;
1148
1149 // Make the new node the Tree's root node
1150 Tree->RootNode = NewIndexRoot;
1151
1152#ifndef NDEBUG
1153 DumpBTree(Tree);
1154#endif
1155
1156 return STATUS_SUCCESS;
1157}
BOOLEAN DiskNeedsUpdating
Definition: ntfs.h:479

Referenced by AddFilenameToDirectoryWorker().

◆ DestroyBTree()

VOID DestroyBTree ( PB_TREE  Tree)

◆ DestroyBTreeKey()

VOID DestroyBTreeKey ( PB_TREE_KEY  Key)

Definition at line 1506 of file btree.c.

1507{
1508 if (Key->IndexEntry)
1509 ExFreePoolWithTag(Key->IndexEntry, TAG_NTFS);
1510
1511 if (Key->LesserChild)
1512 DestroyBTreeNode(Key->LesserChild);
1513
1515}

Referenced by DestroyBTreeNode(), NtfsInsertKey(), RemoveFilenameFromDirectoryWorker(), and RemoveKeyFromNode().

◆ DestroyBTreeNode()

VOID DestroyBTreeNode ( PB_TREE_FILENAME_NODE  Node)

Definition at line 1518 of file btree.c.

1519{
1520 PB_TREE_KEY NextKey;
1521 PB_TREE_KEY CurrentKey = Node->FirstKey;
1522 ULONG i;
1523 for (i = 0; i < Node->KeyCount; i++)
1524 {
1525 NT_ASSERT(CurrentKey);
1526 NextKey = CurrentKey->NextKey;
1527 DestroyBTreeKey(CurrentKey);
1528 CurrentKey = NextKey;
1529 }
1530
1531 NT_ASSERT(NextKey == NULL);
1532
1534}
VOID DestroyBTreeKey(PB_TREE_KEY Key)
Definition: btree.c:1506

Referenced by CreateBTreeNodeFromIndexNode(), DestroyBTree(), and DestroyBTreeKey().

◆ DumpBTree()

VOID DumpBTree ( PB_TREE  Tree)

Definition at line 1629 of file btree.c.

1630{
1631 DbgPrint("B_TREE @ %p\n", Tree);
1632 DumpBTreeNode(Tree, Tree->RootNode, 0, 0);
1633}
VOID DumpBTreeNode(PB_TREE Tree, PB_TREE_FILENAME_NODE Node, ULONG Number, ULONG Depth)
Definition: btree.c:1590
#define DbgPrint
Definition: hal.h:12

Referenced by AddFilenameToDirectoryWorker(), CreateIndexRootFromBTree(), DemoteBTreeRoot(), NtfsInsertKey(), and UpdateIndexAllocation().

◆ DumpBTreeKey()

VOID DumpBTreeKey ( PB_TREE  Tree,
PB_TREE_KEY  Key,
ULONG  Number,
ULONG  Depth 
)

Definition at line 1556 of file btree.c.

1557{
1558 ULONG i;
1559 for (i = 0; i < Depth; i++)
1560 DbgPrint(" ");
1561 DbgPrint(" Key #%d", Number);
1562
1563 if (!(Key->IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
1564 {
1566 FileName.Length = Key->IndexEntry->FileName.NameLength * sizeof(WCHAR);
1567 FileName.MaximumLength = FileName.Length;
1568 FileName.Buffer = Key->IndexEntry->FileName.Name;
1569 DbgPrint(" '%wZ'\n", &FileName);
1570 }
1571 else
1572 {
1573 DbgPrint(" (Dummy Key)\n");
1574 }
1575
1576 // Is there a child node?
1577 if (Key->IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE)
1578 {
1579 if (Key->LesserChild)
1580 DumpBTreeNode(Tree, Key->LesserChild, Number, Depth + 1);
1581 else
1582 {
1583 // This will be an assert once nodes with arbitrary depth are debugged
1584 DPRINT1("DRIVER ERROR: No Key->LesserChild despite Key->IndexEntry->Flags indicating this is a node!\n");
1585 }
1586 }
1587}
UINT Depth
Definition: d3d12.idl:4644
struct _FileName FileName
Definition: fatprocs.h:897
_In_ ULONG Number
Definition: haltypes.h:1940

Referenced by DumpBTreeNode().

◆ DumpBTreeNode()

VOID DumpBTreeNode ( PB_TREE  Tree,
PB_TREE_FILENAME_NODE  Node,
ULONG  Number,
ULONG  Depth 
)

Definition at line 1590 of file btree.c.

1594{
1595 PB_TREE_KEY CurrentKey;
1596 ULONG i;
1597 for (i = 0; i < Depth; i++)
1598 DbgPrint(" ");
1599 DbgPrint("Node #%d, Depth %d, has %d key%s", Number, Depth, Node->KeyCount, Node->KeyCount == 1 ? "" : "s");
1600
1601 if (Node->HasValidVCN)
1602 DbgPrint(" VCN: %I64u\n", Node->VCN);
1603 else if (Tree->RootNode == Node)
1604 DbgPrint(" Index Root");
1605 else
1606 DbgPrint(" NOT ASSIGNED VCN YET\n");
1607
1608 CurrentKey = Node->FirstKey;
1609 for (i = 0; i < Node->KeyCount; i++)
1610 {
1611 DumpBTreeKey(Tree, CurrentKey, i, Depth);
1612 CurrentKey = CurrentKey->NextKey;
1613 }
1614}
VOID DumpBTreeKey(PB_TREE Tree, PB_TREE_KEY Key, ULONG Number, ULONG Depth)
Definition: btree.c:1556

Referenced by DumpBTree(), DumpBTreeKey(), and SplitBTreeNode().

◆ FindLargestKeyInSubtree()

static VOID FindLargestKeyInSubtree ( PB_TREE_FILENAME_NODE  Node,
PB_TREE_FILENAME_NODE *  OwnerNode,
PB_TREE_KEY *  PreviousKey,
PB_TREE_KEY *  LargestKey 
)
static

Definition at line 1887 of file btree.c.

1891{
1892 PB_TREE_KEY CurrentKey = Node->FirstKey;
1893 PB_TREE_KEY Previous = NULL;
1894 PB_TREE_KEY BeforePrevious = NULL;
1895
1896 *OwnerNode = NULL;
1897 *PreviousKey = NULL;
1898 *LargestKey = NULL;
1899
1900 while (CurrentKey != NULL &&
1902 {
1903 BeforePrevious = Previous;
1904 Previous = CurrentKey;
1905 CurrentKey = CurrentKey->NextKey;
1906 }
1907
1908 // Anything greater than the last real key lives beneath the end marker
1909 if (CurrentKey != NULL && CurrentKey->LesserChild != NULL)
1910 {
1911 FindLargestKeyInSubtree(CurrentKey->LesserChild, OwnerNode, PreviousKey, LargestKey);
1912 if (*LargestKey != NULL)
1913 return;
1914 }
1915
1916 // Nothing but an end marker: this subtree holds no filenames
1917 if (Previous == NULL)
1918 return;
1919
1920 *OwnerNode = Node;
1921 *PreviousKey = BeforePrevious;
1922 *LargestKey = Previous;
1923}
static VOID FindLargestKeyInSubtree(PB_TREE_FILENAME_NODE Node, PB_TREE_FILENAME_NODE *OwnerNode, PB_TREE_KEY *PreviousKey, PB_TREE_KEY *LargestKey)
Definition: btree.c:1887
union node Node
Definition: types.h:1255
#define BooleanFlagOn(F, SF)
Definition: ext2fs.h:183

Referenced by FindLargestKeyInSubtree(), and RemoveKeyFromNode().

◆ GetAllocationOffsetFromVCN()

ULONGLONG GetAllocationOffsetFromVCN ( PDEVICE_EXTENSION  DeviceExt,
ULONG  IndexBufferSize,
ULONGLONG  Vcn 
)

Definition at line 1637 of file btree.c.

1640{
1641 if (IndexBufferSize < DeviceExt->NtfsInfo.BytesPerCluster)
1642 return Vcn * DeviceExt->NtfsInfo.BytesPerSector;
1643
1644 return Vcn * DeviceExt->NtfsInfo.BytesPerCluster;
1645}

Referenced by BrowseSubNodeIndexEntries(), CreateBTreeNodeFromIndexNode(), NtfsIndexNodeIsEmpty(), and UpdateIndexNode().

◆ GetIndexEntryVCN()

ULONGLONG GetIndexEntryVCN ( PINDEX_ENTRY_ATTRIBUTE  IndexEntry)

Definition at line 1648 of file btree.c.

1649{
1650 PULONGLONG Destination = (PULONGLONG)((ULONG_PTR)IndexEntry + IndexEntry->Length - sizeof(ULONGLONG));
1651
1652 ASSERT(IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE);
1653
1654 return *Destination;
1655}
_In_ PUNICODE_STRING _Inout_ PUNICODE_STRING Destination
Definition: rtlfuncs.h:3051

Referenced by BrowseIndexEntries(), BrowseSubNodeIndexEntries(), NtfsIndexEntriesAreEmpty(), and SplitBTreeNode().

◆ GetSizeOfIndexEntries()

ULONG GetSizeOfIndexEntries ( PB_TREE_FILENAME_NODE  Node)

Definition at line 860 of file btree.c.

861{
862 // Start summing the total size of this node's entries
863 ULONG NodeSize = 0;
864
865 // Walk through the list of Node Entries
866 PB_TREE_KEY CurrentKey = Node->FirstKey;
867 ULONG i;
868 for (i = 0; i < Node->KeyCount; i++)
869 {
870 ASSERT(CurrentKey->IndexEntry->Length != 0);
871
872 // Add the length of the current node
873 NodeSize += CurrentKey->IndexEntry->Length;
874 CurrentKey = CurrentKey->NextKey;
875 }
876
877 return NodeSize;
878}

Referenced by AddFilenameToDirectoryWorker(), and NtfsInsertKey().

◆ NtfsInsertKey()

NTSTATUS NtfsInsertKey ( PB_TREE  Tree,
ULONGLONG  FileReference,
PFILENAME_ATTRIBUTE  FileNameAttribute,
PB_TREE_FILENAME_NODE  Node,
BOOLEAN  CaseSensitive,
ULONG  MaxIndexRootSize,
ULONG  IndexRecordSize,
PB_TREE_KEY *  MedianKey,
PB_TREE_FILENAME_NODE *  NewRightHandSibling 
)

Definition at line 1698 of file btree.c.

1707{
1708 PB_TREE_KEY NewKey, CurrentKey, PreviousKey;
1710 ULONG NodeSize;
1711 ULONG AllocatedNodeSize;
1712 ULONG MaxNodeSizeWithoutHeader;
1713 ULONG i;
1714
1715 *MedianKey = NULL;
1716 *NewRightHandSibling = NULL;
1717
1718 DPRINT("NtfsInsertKey(%p, 0x%I64x, %p, %p, %s, %lu, %lu, %p, %p)\n",
1719 Tree,
1720 FileReference,
1721 FileNameAttribute,
1722 Node,
1723 CaseSensitive ? "TRUE" : "FALSE",
1724 MaxIndexRootSize,
1725 IndexRecordSize,
1726 MedianKey,
1727 NewRightHandSibling);
1728
1729 // Create the key for the filename attribute
1730 NewKey = CreateBTreeKeyFromFilename(FileReference, FileNameAttribute);
1731 if (!NewKey)
1733
1734 // Find where to insert the key
1735 CurrentKey = Node->FirstKey;
1736 PreviousKey = NULL;
1737 for (i = 0; i < Node->KeyCount; i++)
1738 {
1739 // Should the New Key go before the current key?
1740 LONG Comparison = CompareTreeKeys(NewKey, CurrentKey, CaseSensitive);
1741
1742 /* The name is already in this index. Inserting it again would leave
1743 * two entries the lookup code can never tell apart, so refuse. */
1744 if (Comparison == 0)
1745 {
1746 DPRINT1("Refusing duplicate index entry '%.*S' (existing file reference 0x%I64x)\n",
1748 NewKey->IndexEntry->FileName.Name,
1749 CurrentKey->IndexEntry->Data.Directory.IndexedFile);
1750
1751 DestroyBTreeKey(NewKey);
1752
1754 }
1755
1756 // Is NewKey < CurrentKey?
1757 if (Comparison < 0)
1758 {
1759 // Does CurrentKey have a sub-node?
1760 if (CurrentKey->LesserChild)
1761 {
1762 PB_TREE_KEY NewLeftKey;
1763 PB_TREE_FILENAME_NODE NewChild;
1764
1765 // Insert the key into the child node
1767 FileReference,
1768 FileNameAttribute,
1769 CurrentKey->LesserChild,
1770 CaseSensitive,
1771 MaxIndexRootSize,
1772 IndexRecordSize,
1773 &NewLeftKey,
1774 &NewChild);
1775 if (!NT_SUCCESS(Status))
1776 {
1777 DPRINT1("ERROR: Failed to insert key.\n");
1778 ExFreePoolWithTag(NewKey, TAG_NTFS);
1779 return Status;
1780 }
1781
1782 // Did the child node get split?
1783 if (NewLeftKey)
1784 {
1785 ASSERT(NewChild != NULL);
1786
1787 // Insert the new left key to the left of the current key
1788 NewLeftKey->NextKey = CurrentKey;
1789
1790 // Is CurrentKey the first key?
1791 if (!PreviousKey)
1792 Node->FirstKey = NewLeftKey;
1793 else
1794 PreviousKey->NextKey = NewLeftKey;
1795
1796 // CurrentKey->LesserChild will be the right-hand sibling
1797 CurrentKey->LesserChild = NewChild;
1798
1799 Node->KeyCount++;
1800 Node->DiskNeedsUpdating = TRUE;
1801
1802#ifndef NDEBUG
1803 DumpBTree(Tree);
1804#endif
1805 }
1806 }
1807 else
1808 {
1809 // Insert New Key before Current Key
1810 NewKey->NextKey = CurrentKey;
1811
1812 // Increase KeyCount and mark node as dirty
1813 Node->KeyCount++;
1814 Node->DiskNeedsUpdating = TRUE;
1815
1816 // was CurrentKey the first key?
1817 if (CurrentKey == Node->FirstKey)
1818 Node->FirstKey = NewKey;
1819 else
1820 PreviousKey->NextKey = NewKey;
1821 }
1822 break;
1823 }
1824
1825 PreviousKey = CurrentKey;
1826 CurrentKey = CurrentKey->NextKey;
1827 }
1828
1829 // Determine how much space the index entries will need
1830 NodeSize = GetSizeOfIndexEntries(Node);
1831
1832 // Is Node not the root node?
1833 if (Node != Tree->RootNode)
1834 {
1835 // Calculate maximum size of index entries without any headers
1836 AllocatedNodeSize = IndexRecordSize - FIELD_OFFSET(INDEX_BUFFER, Header);
1837
1838 // TODO: Replace magic with math
1839 MaxNodeSizeWithoutHeader = AllocatedNodeSize - 0x28;
1840
1841 // Has the node grown larger than its allocated size?
1842 if (NodeSize > MaxNodeSizeWithoutHeader)
1843 {
1845
1846 Status = SplitBTreeNode(Tree, Node, MedianKey, NewRightHandSibling, CaseSensitive);
1847 if (!NT_SUCCESS(Status))
1848 {
1849 DPRINT1("ERROR: Failed to split B-Tree node!\n");
1850 return Status;
1851 }
1852
1853 return Status;
1854 }
1855 }
1856
1857 // NewEntry and NewKey will be destroyed later by DestroyBTree()
1858
1859 return Status;
1860}
LONG CompareTreeKeys(PB_TREE_KEY Key1, PB_TREE_KEY Key2, BOOLEAN CaseSensitive)
Definition: btree.c:422
NTSTATUS SplitBTreeNode(PB_TREE Tree, PB_TREE_FILENAME_NODE Node, PB_TREE_KEY *MedianKey, PB_TREE_FILENAME_NODE *NewRightHandSibling, BOOLEAN CaseSensitive)
Definition: btree.c:2138
NTSTATUS NtfsInsertKey(PB_TREE Tree, ULONGLONG FileReference, PFILENAME_ATTRIBUTE FileNameAttribute, PB_TREE_FILENAME_NODE Node, BOOLEAN CaseSensitive, ULONG MaxIndexRootSize, ULONG IndexRecordSize, PB_TREE_KEY *MedianKey, PB_TREE_FILENAME_NODE *NewRightHandSibling)
Definition: btree.c:1698
ULONG GetSizeOfIndexEntries(PB_TREE_FILENAME_NODE Node)
Definition: btree.c:860
PB_TREE_KEY CreateBTreeKeyFromFilename(ULONGLONG FileReference, PFILENAME_ATTRIBUTE FileNameAttribute)
Definition: btree.c:1466
#define STATUS_OBJECT_NAME_COLLISION
Definition: udferr_usr.h:150

Referenced by AddFilenameToDirectoryWorker(), and NtfsInsertKey().

◆ NtfsRemoveKey()

NTSTATUS NtfsRemoveKey ( PB_TREE_FILENAME_NODE  Node,
PB_TREE_KEY  SearchKey,
BOOLEAN  CaseSensitive 
)

Definition at line 2067 of file btree.c.

2070{
2071 PB_TREE_KEY PreviousKey = NULL;
2072 PB_TREE_KEY CurrentKey = Node->FirstKey;
2073 ULONG i;
2074
2075 for (i = 0; i < Node->KeyCount; i++)
2076 {
2077 LONG Comparison;
2078
2079 if (CurrentKey == NULL)
2080 break;
2081
2083 break;
2084
2085 Comparison = CompareTreeKeys(SearchKey, CurrentKey, CaseSensitive);
2086
2087 if (Comparison == 0)
2088 return RemoveKeyFromNode(Node, PreviousKey, CurrentKey);
2089
2090 // Everything that sorts before CurrentKey is beneath it
2091 if (Comparison < 0)
2092 break;
2093
2094 PreviousKey = CurrentKey;
2095 CurrentKey = CurrentKey->NextKey;
2096 }
2097
2098 if (CurrentKey != NULL && CurrentKey->LesserChild != NULL)
2099 return NtfsRemoveKey(CurrentKey->LesserChild, SearchKey, CaseSensitive);
2100
2102}
NTSTATUS NtfsRemoveKey(PB_TREE_FILENAME_NODE Node, PB_TREE_KEY SearchKey, BOOLEAN CaseSensitive)
Definition: btree.c:2067
static NTSTATUS RemoveKeyFromNode(PB_TREE_FILENAME_NODE Node, PB_TREE_KEY PreviousKey, PB_TREE_KEY Key)
Definition: btree.c:1995
#define STATUS_OBJECT_NAME_NOT_FOUND
Definition: udferr_usr.h:149

Referenced by NtfsRemoveKey(), and RemoveFilenameFromDirectoryWorker().

◆ PrintAllVCNs()

VOID PrintAllVCNs ( PDEVICE_EXTENSION  Vcb,
PNTFS_ATTR_CONTEXT  IndexAllocationContext,
ULONG  NodeSize 
)

Definition at line 38 of file btree.c.

41{
42 ULONGLONG CurrentOffset = 0;
43 PINDEX_BUFFER CurrentNode, Buffer;
44 ULONGLONG BufferSize = AttributeDataLength(IndexAllocationContext->pRecord);
47 int Count = 0;
48
49 if (BufferSize == 0)
50 {
51 DPRINT1("Index Allocation is empty.\n");
52 return;
53 }
54
56
57 BytesRead = ReadAttribute(Vcb, IndexAllocationContext, 0, (PCHAR)Buffer, BufferSize);
58
60
61 CurrentNode = Buffer;
62
63 // loop through all the nodes
64 for (i = 0; i < BufferSize; i += NodeSize)
65 {
67 if (!NT_SUCCESS(Status))
68 {
69 DPRINT1("ERROR: Fixing fixup failed!\n");
70 continue;
71 }
72
73 DPRINT("Node #%d, VCN: %I64u\n", Count, CurrentNode->VCN);
74
75 CurrentNode = (PINDEX_BUFFER)((ULONG_PTR)CurrentNode + NodeSize);
76 CurrentOffset += NodeSize;
77 Count++;
78 }
79
81}
Definition: bufpool.h:45
#define BufferSize
Definition: mmc.h:75
struct INDEX_BUFFER * PINDEX_BUFFER

Referenced by UpdateIndexAllocation().

◆ RemoveKeyFromNode()

static NTSTATUS RemoveKeyFromNode ( PB_TREE_FILENAME_NODE  Node,
PB_TREE_KEY  PreviousKey,
PB_TREE_KEY  Key 
)
static

Definition at line 1995 of file btree.c.

1998{
2000
2001 /* Either way this node's keys change. Only dirty nodes are written back,
2002 * so without this a removal from a sub-node never reaches the disk. */
2003 Node->DiskNeedsUpdating = TRUE;
2004
2005 if (Key->LesserChild != NULL)
2006 {
2007 PB_TREE_FILENAME_NODE OwnerNode;
2008 PB_TREE_KEY PreviousLargest;
2009 PB_TREE_KEY LargestKey;
2010
2011 FindLargestKeyInSubtree(Key->LesserChild, &OwnerNode, &PreviousLargest, &LargestKey);
2012
2013 if (LargestKey != NULL)
2014 {
2015 // Unlinking an interior key would orphan its child, so promote the greatest key
2016 // beneath it and remove that one instead
2017 Status = ReplaceKeyIndexEntry(Key, LargestKey);
2018 if (!NT_SUCCESS(Status))
2019 return Status;
2020
2021 return RemoveKeyFromNode(OwnerNode, PreviousLargest, LargestKey);
2022 }
2023
2024 // The child holds no filenames, so there is nothing to promote; DestroyBTreeKey()
2025 // frees the empty child along with the key
2026 }
2027
2028 if (PreviousKey == NULL)
2029 Node->FirstKey = Key->NextKey;
2030 else
2031 PreviousKey->NextKey = Key->NextKey;
2032
2033 Node->KeyCount--;
2034
2035 Key->NextKey = NULL;
2037
2038 return STATUS_SUCCESS;
2039}
static NTSTATUS ReplaceKeyIndexEntry(PB_TREE_KEY Key, PB_TREE_KEY Source)
Definition: btree.c:1947

Referenced by NtfsRemoveKey(), and RemoveKeyFromNode().

◆ ReplaceKeyIndexEntry()

static NTSTATUS ReplaceKeyIndexEntry ( PB_TREE_KEY  Key,
PB_TREE_KEY  Source 
)
static

Definition at line 1947 of file btree.c.

1948{
1949 PINDEX_ENTRY_ATTRIBUTE NewEntry;
1950 ULONG AttributeSize = GetFileNameAttributeLength(&Source->IndexEntry->FileName);
1952
1953 EntrySize += sizeof(ULONGLONG); // for the VCN of the child Key retains
1954
1956 if (!NewEntry)
1957 {
1958 DPRINT1("ERROR: Failed to allocate memory for replacement index entry!\n");
1960 }
1961
1962 RtlZeroMemory(NewEntry, EntrySize);
1963 NewEntry->Data.Directory.IndexedFile = Source->IndexEntry->Data.Directory.IndexedFile;
1964 NewEntry->Length = EntrySize;
1965 NewEntry->KeyLength = AttributeSize;
1966 NewEntry->Flags = NTFS_INDEX_ENTRY_NODE;
1967 RtlCopyMemory(&NewEntry->FileName, &Source->IndexEntry->FileName, AttributeSize);
1968
1969 ExFreePoolWithTag(Key->IndexEntry, TAG_NTFS);
1970 Key->IndexEntry = NewEntry;
1971
1972 return STATUS_SUCCESS;
1973}
_In_ UINT _In_ UINT _In_ PNDIS_PACKET Source
Definition: ndis.h:3169

Referenced by RemoveKeyFromNode().

◆ SetIndexEntryVCN()

VOID SetIndexEntryVCN ( PINDEX_ENTRY_ATTRIBUTE  IndexEntry,
ULONGLONG  VCN 
)

Definition at line 1177 of file btree.c.

1178{
1179 PULONGLONG Destination = (PULONGLONG)((ULONG_PTR)IndexEntry + IndexEntry->Length - sizeof(ULONGLONG));
1180
1181 ASSERT(IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE);
1182
1183 *Destination = VCN;
1184}

Referenced by SplitBTreeNode(), UpdateIndexAllocation(), and UpdateIndexNode().

◆ SplitBTreeNode()

NTSTATUS SplitBTreeNode ( PB_TREE  Tree,
PB_TREE_FILENAME_NODE  Node,
PB_TREE_KEY *  MedianKey,
PB_TREE_FILENAME_NODE *  NewRightHandSibling,
BOOLEAN  CaseSensitive 
)

Definition at line 2138 of file btree.c.

2143{
2144 ULONG MedianKeyIndex;
2145 PB_TREE_KEY LastKeyBeforeMedian, FirstKeyAfterMedian;
2146 ULONG KeyCount;
2147 ULONG HalfSize;
2148 ULONG SizeSum;
2149 ULONG i;
2150
2151 DPRINT("SplitBTreeNode(%p, %p, %p, %p, %s) called\n",
2152 Tree,
2153 Node,
2154 MedianKey,
2155 NewRightHandSibling,
2156 CaseSensitive ? "TRUE" : "FALSE");
2157
2158#ifndef NDEBUG
2159 DumpBTreeNode(Tree, Node, 0, 0);
2160#endif
2161
2162 // Create the right hand sibling
2163 *NewRightHandSibling = ExAllocatePoolWithTag(NonPagedPool, sizeof(B_TREE_FILENAME_NODE), TAG_NTFS);
2164 if (*NewRightHandSibling == NULL)
2165 {
2166 DPRINT1("Error: Failed to allocate memory for right hand sibling!\n");
2168 }
2169 RtlZeroMemory(*NewRightHandSibling, sizeof(B_TREE_FILENAME_NODE));
2170 (*NewRightHandSibling)->DiskNeedsUpdating = TRUE;
2171
2172
2173 // Find the last key before the median
2174
2175 // This is roughly how NTFS-3G calculates median, and it's not congruent with what Windows does:
2176 /*
2177 // find the median key index
2178 MedianKeyIndex = (Node->KeyCount + 1) / 2;
2179 MedianKeyIndex--;
2180
2181 LastKeyBeforeMedian = Node->FirstKey;
2182 for (i = 0; i < MedianKeyIndex - 1; i++)
2183 LastKeyBeforeMedian = LastKeyBeforeMedian->NextKey;*/
2184
2185 // The method we'll use is a little bit closer to how Windows determines the median but it's not identical.
2186 // What Windows does is actually more complicated than this, I think because Windows allocates more slack space to Odd-numbered
2187 // Index Records, leaving less room for index entries in these records (I haven't discovered why this is done).
2188 // (Neither Windows nor chkdsk complain if we choose a different median than Windows would have chosen, as our median will be in the ballpark)
2189
2190 // Use size to locate the median key / index
2191 LastKeyBeforeMedian = Node->FirstKey;
2192 MedianKeyIndex = 0;
2193 HalfSize = 2016; // half the allocated size after subtracting the first index entry offset (TODO: MATH)
2194 SizeSum = 0;
2195 for (i = 0; i < Node->KeyCount; i++)
2196 {
2197 SizeSum += LastKeyBeforeMedian->IndexEntry->Length;
2198
2199 if (SizeSum > HalfSize)
2200 break;
2201
2202 MedianKeyIndex++;
2203 LastKeyBeforeMedian = LastKeyBeforeMedian->NextKey;
2204 }
2205
2206 // Now we can get the median key and the key that follows it
2207 *MedianKey = LastKeyBeforeMedian->NextKey;
2208 FirstKeyAfterMedian = (*MedianKey)->NextKey;
2209
2210 DPRINT("%lu keys, %lu median\n", Node->KeyCount, MedianKeyIndex);
2211 DPRINT("\t\tMedian: %.*S\n", (*MedianKey)->IndexEntry->FileName.NameLength, (*MedianKey)->IndexEntry->FileName.Name);
2212
2213 // "Node" will be the left hand sibling after the split, containing all keys prior to the median key
2214
2215 // We need to create a dummy pointer at the end of the LHS. The dummy's child will be the median's child.
2216 LastKeyBeforeMedian->NextKey = CreateDummyKey(BooleanFlagOn((*MedianKey)->IndexEntry->Flags, NTFS_INDEX_ENTRY_NODE));
2217 if (LastKeyBeforeMedian->NextKey == NULL)
2218 {
2219 DPRINT1("Error: Couldn't allocate dummy key!\n");
2220 LastKeyBeforeMedian->NextKey = *MedianKey;
2221 ExFreePoolWithTag(*NewRightHandSibling, TAG_NTFS);
2223 }
2224
2225 // Did the median key have a child node?
2226 if ((*MedianKey)->IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE)
2227 {
2228 // Set the child of the new dummy key
2229 LastKeyBeforeMedian->NextKey->LesserChild = (*MedianKey)->LesserChild;
2230
2231 // Give the dummy key's index entry the same sub-node VCN the median
2232 SetIndexEntryVCN(LastKeyBeforeMedian->NextKey->IndexEntry, GetIndexEntryVCN((*MedianKey)->IndexEntry));
2233 }
2234 else
2235 {
2236 // Median key didn't have a child node, but it will. Create a new index entry large enough to store a VCN.
2238 (*MedianKey)->IndexEntry->Length + sizeof(ULONGLONG),
2239 TAG_NTFS);
2240 if (!NewIndexEntry)
2241 {
2242 DPRINT1("Unable to allocate memory for new index entry!\n");
2243 LastKeyBeforeMedian->NextKey = *MedianKey;
2244 ExFreePoolWithTag(*NewRightHandSibling, TAG_NTFS);
2246 }
2247
2248 // Copy the old index entry to the new one
2249 RtlCopyMemory(NewIndexEntry, (*MedianKey)->IndexEntry, (*MedianKey)->IndexEntry->Length);
2250
2251 // Use the new index entry after freeing the old one
2252 ExFreePoolWithTag((*MedianKey)->IndexEntry, TAG_NTFS);
2253 (*MedianKey)->IndexEntry = NewIndexEntry;
2254
2255 // Update the length for the VCN
2256 (*MedianKey)->IndexEntry->Length += sizeof(ULONGLONG);
2257
2258 // Set the node flag
2259 (*MedianKey)->IndexEntry->Flags |= NTFS_INDEX_ENTRY_NODE;
2260 }
2261
2262 // "Node" will become the child of the median key
2263 (*MedianKey)->LesserChild = Node;
2264 SetIndexEntryVCN((*MedianKey)->IndexEntry, Node->VCN);
2265
2266 // Update Node's KeyCount (remember to add 1 for the new dummy key)
2267 Node->KeyCount = MedianKeyIndex + 2;
2268
2269 KeyCount = CountBTreeKeys(Node->FirstKey);
2270 ASSERT(Node->KeyCount == KeyCount);
2271
2272 // everything to the right of MedianKey becomes the right hand sibling of Node
2273 (*NewRightHandSibling)->FirstKey = FirstKeyAfterMedian;
2274 (*NewRightHandSibling)->KeyCount = CountBTreeKeys(FirstKeyAfterMedian);
2275
2276#ifndef NDEBUG
2277 DPRINT1("Left-hand node after split:\n");
2278 DumpBTreeNode(Tree, Node, 0, 0);
2279
2280 DPRINT1("Right-hand sibling node after split:\n");
2281 DumpBTreeNode(Tree, *NewRightHandSibling, 0, 0);
2282#endif
2283
2284 return STATUS_SUCCESS;
2285}
ULONG CountBTreeKeys(PB_TREE_KEY FirstKey)
Definition: btree.c:489
ULONGLONG GetIndexEntryVCN(PINDEX_ENTRY_ATTRIBUTE IndexEntry)
Definition: btree.c:1648
VOID SetIndexEntryVCN(PINDEX_ENTRY_ATTRIBUTE IndexEntry, ULONGLONG VCN)
Definition: btree.c:1177

Referenced by NtfsInsertKey().

◆ UpdateIndexAllocation()

NTSTATUS UpdateIndexAllocation ( PDEVICE_EXTENSION  DeviceExt,
PB_TREE  Tree,
ULONG  IndexBufferSize,
PFILE_RECORD_HEADER  FileRecord 
)

Definition at line 1187 of file btree.c.

1191{
1192 // Find the index allocation and bitmap
1193 PNTFS_ATTR_CONTEXT IndexAllocationContext;
1194 PB_TREE_KEY CurrentKey;
1196 BOOLEAN HasIndexAllocation = FALSE;
1197 ULONG i;
1198 ULONG IndexAllocationOffset;
1199
1200 DPRINT("UpdateIndexAllocation() called.\n");
1201
1202 Status = FindAttribute(DeviceExt, FileRecord, AttributeIndexAllocation, L"$I30", 4, &IndexAllocationContext, &IndexAllocationOffset);
1203 if (NT_SUCCESS(Status))
1204 {
1205 HasIndexAllocation = TRUE;
1206
1207#ifndef NDEBUG
1208 PrintAllVCNs(DeviceExt,
1209 IndexAllocationContext,
1210 IndexBufferSize);
1211#endif
1212 }
1213 // Walk through the root node and update all the sub-nodes
1214 CurrentKey = Tree->RootNode->FirstKey;
1215 for (i = 0; i < Tree->RootNode->KeyCount; i++)
1216 {
1217 if (CurrentKey->LesserChild)
1218 {
1219 if (!HasIndexAllocation)
1220 {
1221 // We need to add an index allocation to the file record
1222 PNTFS_ATTR_RECORD EndMarker = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->BytesInUse - (sizeof(ULONG) * 2));
1223 DPRINT("Adding index allocation...\n");
1224
1225 // Add index allocation to the very end of the file record
1226 Status = AddIndexAllocation(DeviceExt,
1227 FileRecord,
1228 EndMarker,
1229 L"$I30",
1230 4);
1231 if (!NT_SUCCESS(Status))
1232 {
1233 DPRINT1("ERROR: Failed to add index allocation!\n");
1234 return Status;
1235 }
1236
1237 // Find the new attribute
1238 Status = FindAttribute(DeviceExt, FileRecord, AttributeIndexAllocation, L"$I30", 4, &IndexAllocationContext, &IndexAllocationOffset);
1239 if (!NT_SUCCESS(Status))
1240 {
1241 DPRINT1("ERROR: Couldn't find newly-created index allocation!\n");
1242 return Status;
1243 }
1244
1245 // Advance end marker
1246 EndMarker = (PNTFS_ATTR_RECORD)((ULONG_PTR)EndMarker + EndMarker->Length);
1247
1248 // Add index bitmap to the very end of the file record
1249 Status = AddBitmap(DeviceExt,
1250 FileRecord,
1251 EndMarker,
1252 L"$I30",
1253 4);
1254 if (!NT_SUCCESS(Status))
1255 {
1256 DPRINT1("ERROR: Failed to add index bitmap!\n");
1257 ReleaseAttributeContext(IndexAllocationContext);
1258 return Status;
1259 }
1260
1261 HasIndexAllocation = TRUE;
1262 }
1263
1264 // Is the Index Entry large enough to store the VCN?
1266 {
1267 // Allocate memory for the larger index entry
1269 CurrentKey->IndexEntry->Length + sizeof(ULONGLONG),
1270 TAG_NTFS);
1271 if (!NewEntry)
1272 {
1273 DPRINT1("ERROR: Unable to allocate memory for new index entry!\n");
1274 if (HasIndexAllocation)
1275 ReleaseAttributeContext(IndexAllocationContext);
1277 }
1278
1279 // Copy the old entry to the new one
1280 RtlCopyMemory(NewEntry, CurrentKey->IndexEntry, CurrentKey->IndexEntry->Length);
1281
1282 NewEntry->Length += sizeof(ULONGLONG);
1283
1284 // Free the old memory
1286
1287 CurrentKey->IndexEntry = NewEntry;
1288 CurrentKey->IndexEntry->Flags |= NTFS_INDEX_ENTRY_NODE;
1289 }
1290
1291 // Update the sub-node
1292 Status = UpdateIndexNode(DeviceExt, FileRecord, CurrentKey->LesserChild, IndexBufferSize, IndexAllocationContext, IndexAllocationOffset);
1293 if (!NT_SUCCESS(Status))
1294 {
1295 DPRINT1("ERROR: Failed to update index node!\n");
1296 ReleaseAttributeContext(IndexAllocationContext);
1297 return Status;
1298 }
1299
1300 // Update the VCN stored in the index entry of CurrentKey
1301 SetIndexEntryVCN(CurrentKey->IndexEntry, CurrentKey->LesserChild->VCN);
1302 }
1303 CurrentKey = CurrentKey->NextKey;
1304 }
1305
1306#ifndef NDEBUG
1307 DumpBTree(Tree);
1308#endif
1309
1310 if (HasIndexAllocation)
1311 {
1312#ifndef NDEBUG
1313 PrintAllVCNs(DeviceExt,
1314 IndexAllocationContext,
1315 IndexBufferSize);
1316#endif
1317 ReleaseAttributeContext(IndexAllocationContext);
1318 }
1319
1320 return STATUS_SUCCESS;
1321}
unsigned char BOOLEAN
Definition: actypes.h:127
struct NTFS_ATTR_RECORD * PNTFS_ATTR_RECORD
VOID PrintAllVCNs(PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT IndexAllocationContext, ULONG NodeSize)
Definition: btree.c:38
NTSTATUS UpdateIndexNode(PDEVICE_EXTENSION DeviceExt, PFILE_RECORD_HEADER FileRecord, PB_TREE_FILENAME_NODE Node, ULONG IndexBufferSize, PNTFS_ATTR_CONTEXT IndexAllocationContext, ULONG IndexAllocationOffset)
Definition: btree.c:1324
NTSTATUS AddBitmap(PNTFS_VCB Vcb, PFILE_RECORD_HEADER FileRecord, PNTFS_ATTR_RECORD AttributeAddress, PCWSTR Name, USHORT NameLength)
Definition: attrib.c:76
NTSTATUS AddIndexAllocation(PNTFS_VCB Vcb, PFILE_RECORD_HEADER FileRecord, PNTFS_ATTR_RECORD AttributeAddress, PCWSTR Name, USHORT NameLength)
Definition: attrib.c:380
ULONG Length
Definition: ntfs.h:135
ULONG BytesInUse
Definition: ntfs.h:288

Referenced by AddFilenameToDirectoryWorker(), and RemoveFilenameFromDirectoryWorker().

◆ UpdateIndexNode()

NTSTATUS UpdateIndexNode ( PDEVICE_EXTENSION  DeviceExt,
PFILE_RECORD_HEADER  FileRecord,
PB_TREE_FILENAME_NODE  Node,
ULONG  IndexBufferSize,
PNTFS_ATTR_CONTEXT  IndexAllocationContext,
ULONG  IndexAllocationOffset 
)

Definition at line 1324 of file btree.c.

1330{
1331 ULONG i;
1332 PB_TREE_KEY CurrentKey = Node->FirstKey;
1333 BOOLEAN HasChildren = FALSE;
1335
1336
1337 DPRINT("UpdateIndexNode(%p, %p, %p, %lu, %p, %lu) called for index node with VCN %I64u\n",
1338 DeviceExt,
1339 FileRecord,
1340 Node,
1341 IndexBufferSize,
1342 IndexAllocationContext,
1343 IndexAllocationOffset,
1344 Node->VCN);
1345
1346 // Walk through the node and look for children to update
1347 for (i = 0; i < Node->KeyCount; i++)
1348 {
1349 ASSERT(CurrentKey);
1350
1351 // If there's a child node
1352 if (CurrentKey->LesserChild)
1353 {
1354 HasChildren = TRUE;
1355
1356 // Update the child node on disk
1357 Status = UpdateIndexNode(DeviceExt, FileRecord, CurrentKey->LesserChild, IndexBufferSize, IndexAllocationContext, IndexAllocationOffset);
1358 if (!NT_SUCCESS(Status))
1359 {
1360 DPRINT1("ERROR: Failed to update child node!\n");
1361 return Status;
1362 }
1363
1364 // Is the Index Entry large enough to store the VCN?
1366 {
1367 // Allocate memory for the larger index entry
1369 CurrentKey->IndexEntry->Length + sizeof(ULONGLONG),
1370 TAG_NTFS);
1371 if (!NewEntry)
1372 {
1373 DPRINT1("ERROR: Unable to allocate memory for new index entry!\n");
1375 }
1376
1377 // Copy the old entry to the new one
1378 RtlCopyMemory(NewEntry, CurrentKey->IndexEntry, CurrentKey->IndexEntry->Length);
1379
1380 NewEntry->Length += sizeof(ULONGLONG);
1381
1382 // Free the old memory
1384
1385 CurrentKey->IndexEntry = NewEntry;
1386 }
1387
1388 // Update the VCN stored in the index entry of CurrentKey
1389 SetIndexEntryVCN(CurrentKey->IndexEntry, CurrentKey->LesserChild->VCN);
1390
1391 CurrentKey->IndexEntry->Flags |= NTFS_INDEX_ENTRY_NODE;
1392 }
1393
1394 CurrentKey = CurrentKey->NextKey;
1395 }
1396
1397
1398 // Do we need to write this node to disk?
1399 if (Node->DiskNeedsUpdating)
1400 {
1401 ULONGLONG NodeOffset;
1402 ULONG LengthWritten;
1403 PINDEX_BUFFER IndexBuffer;
1404
1405 // Does the node need to be assigned a VCN?
1406 if (!Node->HasValidVCN)
1407 {
1408 // Allocate the node
1409 Status = AllocateIndexNode(DeviceExt,
1410 FileRecord,
1411 IndexBufferSize,
1412 IndexAllocationContext,
1413 IndexAllocationOffset,
1414 &Node->VCN);
1415 if (!NT_SUCCESS(Status))
1416 {
1417 DPRINT1("ERROR: Failed to allocate index record in index allocation!\n");
1418 return Status;
1419 }
1420
1421 Node->HasValidVCN = TRUE;
1422 }
1423
1424 // Allocate memory for an index buffer
1425 IndexBuffer = ExAllocatePoolWithTag(NonPagedPool, IndexBufferSize, TAG_NTFS);
1426 if (!IndexBuffer)
1427 {
1428 DPRINT1("ERROR: Failed to allocate %lu bytes for index buffer!\n", IndexBufferSize);
1430 }
1431
1432 // Create the index buffer we'll be writing to disk to represent this node
1433 Status = CreateIndexBufferFromBTreeNode(DeviceExt, Node, IndexBufferSize, HasChildren, IndexBuffer);
1434 if (!NT_SUCCESS(Status))
1435 {
1436 DPRINT1("ERROR: Failed to create index buffer from node!\n");
1437 ExFreePoolWithTag(IndexBuffer, TAG_NTFS);
1438 return Status;
1439 }
1440
1441 // Get Offset of index buffer in index allocation
1442 NodeOffset = GetAllocationOffsetFromVCN(DeviceExt, IndexBufferSize, Node->VCN);
1443
1444 // Write the buffer to the index allocation
1445 Status = WriteAttribute(DeviceExt, IndexAllocationContext, NodeOffset, (const PUCHAR)IndexBuffer, IndexBufferSize, &LengthWritten, FileRecord);
1446 if (!NT_SUCCESS(Status) || LengthWritten != IndexBufferSize)
1447 {
1448 DPRINT1("ERROR: Failed to update index allocation!\n");
1449 ExFreePoolWithTag(IndexBuffer, TAG_NTFS);
1450 if (!NT_SUCCESS(Status))
1451 return Status;
1452 else
1453 return STATUS_END_OF_FILE;
1454 }
1455
1456 Node->DiskNeedsUpdating = FALSE;
1457
1458 // Free the index buffer
1459 ExFreePoolWithTag(IndexBuffer, TAG_NTFS);
1460 }
1461
1462 return STATUS_SUCCESS;
1463}
NTSTATUS AllocateIndexNode(PDEVICE_EXTENSION DeviceExt, PFILE_RECORD_HEADER FileRecord, ULONG IndexBufferSize, PNTFS_ATTR_CONTEXT IndexAllocationCtx, ULONG IndexAllocationOffset, PULONGLONG NewVCN)
Definition: btree.c:117
NTSTATUS CreateIndexBufferFromBTreeNode(PDEVICE_EXTENSION DeviceExt, PB_TREE_FILENAME_NODE Node, ULONG BufferSize, BOOLEAN HasChildren, PINDEX_BUFFER IndexBuffer)
Definition: btree.c:1006
#define STATUS_END_OF_FILE
Definition: shellext.h:67

Referenced by UpdateIndexAllocation(), and UpdateIndexNode().