ReactOS 0.4.17-dev-1137-g7772194
btree.c
Go to the documentation of this file.
1/*
2* ReactOS kernel
3* Copyright (C) 2002, 2017 ReactOS Team
4*
5* This program is free software; you can redistribute it and/or modify
6* it under the terms of the GNU General Public License as published by
7* the Free Software Foundation; either version 2 of the License, or
8* (at your option) any later version.
9*
10* This program is distributed in the hope that it will be useful,
11* but WITHOUT ANY WARRANTY; without even the implied warranty of
12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13* GNU General Public License for more details.
14*
15* You should have received a copy of the GNU General Public License
16* along with this program; if not, write to the Free Software
17* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
18*
19* COPYRIGHT: See COPYING in the top level directory
20* PROJECT: ReactOS kernel
21* FILE: drivers/filesystem/ntfs/btree.c
22* PURPOSE: NTFS filesystem driver
23* PROGRAMMERS: Trevor Thompson
24*/
25
26/* INCLUDES *****************************************************************/
27
28#include "ntfs.h"
29
30#define NDEBUG
31#include <debug.h>
32
33/* FUNCTIONS ****************************************************************/
34
35// TEMP FUNCTION for diagnostic purposes.
36// Prints VCN of every node in an index allocation
37VOID
39 PNTFS_ATTR_CONTEXT IndexAllocationContext,
40 ULONG NodeSize)
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}
82
118 PFILE_RECORD_HEADER FileRecord,
119 ULONG IndexBufferSize,
120 PNTFS_ATTR_CONTEXT IndexAllocationCtx,
121 ULONG IndexAllocationOffset,
122 PULONGLONG NewVCN)
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}
280
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}
339
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}
396
421LONG
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}
475
488ULONG
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}
502
505 PINDEX_ROOT_ATTRIBUTE IndexRoot,
506 PNTFS_ATTR_CONTEXT IndexAllocationAttributeCtx,
507 PINDEX_ENTRY_ATTRIBUTE NodeEntry)
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}
666
688 PFILE_RECORD_HEADER FileRecordWithIndex,
689 /*PCWSTR IndexName,*/
690 PNTFS_ATTR_CONTEXT IndexRootContext,
691 PINDEX_ROOT_ATTRIBUTE IndexRoot,
692 PB_TREE *NewTree)
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}
843
859ULONG
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}
879
917 ULONG MaxIndexSize,
918 PINDEX_ROOT_ATTRIBUTE *IndexRoot,
919 ULONG *Length)
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}
1004
1009 BOOLEAN HasChildren,
1010 PINDEX_BUFFER IndexBuffer)
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}
1075
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}
1158
1176VOID
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}
1185
1188 PB_TREE Tree,
1189 ULONG IndexBufferSize,
1190 PFILE_RECORD_HEADER FileRecord)
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}
1322
1325 PFILE_RECORD_HEADER FileRecord,
1327 ULONG IndexBufferSize,
1328 PNTFS_ATTR_CONTEXT IndexAllocationContext,
1329 ULONG IndexAllocationOffset)
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}
1464
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 NewKey->IndexEntry = NewEntry;
1498 NewKey->NextKey = NULL;
1499
1500 return NewKey;
1501}
1502
1503VOID
1505{
1506 if (Key->IndexEntry)
1507 ExFreePoolWithTag(Key->IndexEntry, TAG_NTFS);
1508
1509 if (Key->LesserChild)
1510 DestroyBTreeNode(Key->LesserChild);
1511
1513}
1514
1515VOID
1517{
1518 PB_TREE_KEY NextKey;
1519 PB_TREE_KEY CurrentKey = Node->FirstKey;
1520 ULONG i;
1521 for (i = 0; i < Node->KeyCount; i++)
1522 {
1523 NT_ASSERT(CurrentKey);
1524 NextKey = CurrentKey->NextKey;
1525 DestroyBTreeKey(CurrentKey);
1526 CurrentKey = NextKey;
1527 }
1528
1529 NT_ASSERT(NextKey == NULL);
1530
1532}
1533
1546VOID
1548{
1549 DestroyBTreeNode(Tree->RootNode);
1551}
1552
1553VOID
1555{
1556 ULONG i;
1557 for (i = 0; i < Depth; i++)
1558 DbgPrint(" ");
1559 DbgPrint(" Key #%d", Number);
1560
1561 if (!(Key->IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
1562 {
1564 FileName.Length = Key->IndexEntry->FileName.NameLength * sizeof(WCHAR);
1565 FileName.MaximumLength = FileName.Length;
1566 FileName.Buffer = Key->IndexEntry->FileName.Name;
1567 DbgPrint(" '%wZ'\n", &FileName);
1568 }
1569 else
1570 {
1571 DbgPrint(" (Dummy Key)\n");
1572 }
1573
1574 // Is there a child node?
1575 if (Key->IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE)
1576 {
1577 if (Key->LesserChild)
1578 DumpBTreeNode(Tree, Key->LesserChild, Number, Depth + 1);
1579 else
1580 {
1581 // This will be an assert once nodes with arbitrary depth are debugged
1582 DPRINT1("DRIVER ERROR: No Key->LesserChild despite Key->IndexEntry->Flags indicating this is a node!\n");
1583 }
1584 }
1585}
1586
1587VOID
1590 ULONG Number,
1591 ULONG Depth)
1592{
1593 PB_TREE_KEY CurrentKey;
1594 ULONG i;
1595 for (i = 0; i < Depth; i++)
1596 DbgPrint(" ");
1597 DbgPrint("Node #%d, Depth %d, has %d key%s", Number, Depth, Node->KeyCount, Node->KeyCount == 1 ? "" : "s");
1598
1599 if (Node->HasValidVCN)
1600 DbgPrint(" VCN: %I64u\n", Node->VCN);
1601 else if (Tree->RootNode == Node)
1602 DbgPrint(" Index Root");
1603 else
1604 DbgPrint(" NOT ASSIGNED VCN YET\n");
1605
1606 CurrentKey = Node->FirstKey;
1607 for (i = 0; i < Node->KeyCount; i++)
1608 {
1609 DumpBTreeKey(Tree, CurrentKey, i, Depth);
1610 CurrentKey = CurrentKey->NextKey;
1611 }
1612}
1613
1626VOID
1628{
1629 DbgPrint("B_TREE @ %p\n", Tree);
1630 DumpBTreeNode(Tree, Tree->RootNode, 0, 0);
1631}
1632
1633// Calculates start of Index Buffer relative to the index allocation, given the node's VCN
1636 ULONG IndexBufferSize,
1637 ULONGLONG Vcn)
1638{
1639 if (IndexBufferSize < DeviceExt->NtfsInfo.BytesPerCluster)
1640 return Vcn * DeviceExt->NtfsInfo.BytesPerSector;
1641
1642 return Vcn * DeviceExt->NtfsInfo.BytesPerCluster;
1643}
1644
1647{
1648 PULONGLONG Destination = (PULONGLONG)((ULONG_PTR)IndexEntry + IndexEntry->Length - sizeof(ULONGLONG));
1649
1650 ASSERT(IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE);
1651
1652 return *Destination;
1653}
1654
1697 ULONGLONG FileReference,
1698 PFILENAME_ATTRIBUTE FileNameAttribute,
1700 BOOLEAN CaseSensitive,
1701 ULONG MaxIndexRootSize,
1702 ULONG IndexRecordSize,
1703 PB_TREE_KEY *MedianKey,
1704 PB_TREE_FILENAME_NODE *NewRightHandSibling)
1705{
1706 PB_TREE_KEY NewKey, CurrentKey, PreviousKey;
1708 ULONG NodeSize;
1709 ULONG AllocatedNodeSize;
1710 ULONG MaxNodeSizeWithoutHeader;
1711 ULONG i;
1712
1713 *MedianKey = NULL;
1714 *NewRightHandSibling = NULL;
1715
1716 DPRINT("NtfsInsertKey(%p, 0x%I64x, %p, %p, %s, %lu, %lu, %p, %p)\n",
1717 Tree,
1718 FileReference,
1719 FileNameAttribute,
1720 Node,
1721 CaseSensitive ? "TRUE" : "FALSE",
1722 MaxIndexRootSize,
1723 IndexRecordSize,
1724 MedianKey,
1725 NewRightHandSibling);
1726
1727 // Create the key for the filename attribute
1728 NewKey = CreateBTreeKeyFromFilename(FileReference, FileNameAttribute);
1729 if (!NewKey)
1731
1732 // Find where to insert the key
1733 CurrentKey = Node->FirstKey;
1734 PreviousKey = NULL;
1735 for (i = 0; i < Node->KeyCount; i++)
1736 {
1737 // Should the New Key go before the current key?
1738 LONG Comparison = CompareTreeKeys(NewKey, CurrentKey, CaseSensitive);
1739
1740 /* The name is already in this index. Inserting it again would leave
1741 * two entries the lookup code can never tell apart, so refuse. */
1742 if (Comparison == 0)
1743 {
1744 DPRINT1("Refusing duplicate index entry '%.*S' (existing file reference 0x%I64x)\n",
1746 NewKey->IndexEntry->FileName.Name,
1747 CurrentKey->IndexEntry->Data.Directory.IndexedFile);
1748
1749 DestroyBTreeKey(NewKey);
1750
1752 }
1753
1754 // Is NewKey < CurrentKey?
1755 if (Comparison < 0)
1756 {
1757 // Does CurrentKey have a sub-node?
1758 if (CurrentKey->LesserChild)
1759 {
1760 PB_TREE_KEY NewLeftKey;
1761 PB_TREE_FILENAME_NODE NewChild;
1762
1763 // Insert the key into the child node
1765 FileReference,
1766 FileNameAttribute,
1767 CurrentKey->LesserChild,
1768 CaseSensitive,
1769 MaxIndexRootSize,
1770 IndexRecordSize,
1771 &NewLeftKey,
1772 &NewChild);
1773 if (!NT_SUCCESS(Status))
1774 {
1775 DPRINT1("ERROR: Failed to insert key.\n");
1776 ExFreePoolWithTag(NewKey, TAG_NTFS);
1777 return Status;
1778 }
1779
1780 // Did the child node get split?
1781 if (NewLeftKey)
1782 {
1783 ASSERT(NewChild != NULL);
1784
1785 // Insert the new left key to the left of the current key
1786 NewLeftKey->NextKey = CurrentKey;
1787
1788 // Is CurrentKey the first key?
1789 if (!PreviousKey)
1790 Node->FirstKey = NewLeftKey;
1791 else
1792 PreviousKey->NextKey = NewLeftKey;
1793
1794 // CurrentKey->LesserChild will be the right-hand sibling
1795 CurrentKey->LesserChild = NewChild;
1796
1797 Node->KeyCount++;
1798 Node->DiskNeedsUpdating = TRUE;
1799
1800#ifndef NDEBUG
1801 DumpBTree(Tree);
1802#endif
1803 }
1804 }
1805 else
1806 {
1807 // Insert New Key before Current Key
1808 NewKey->NextKey = CurrentKey;
1809
1810 // Increase KeyCount and mark node as dirty
1811 Node->KeyCount++;
1812 Node->DiskNeedsUpdating = TRUE;
1813
1814 // was CurrentKey the first key?
1815 if (CurrentKey == Node->FirstKey)
1816 Node->FirstKey = NewKey;
1817 else
1818 PreviousKey->NextKey = NewKey;
1819 }
1820 break;
1821 }
1822
1823 PreviousKey = CurrentKey;
1824 CurrentKey = CurrentKey->NextKey;
1825 }
1826
1827 // Determine how much space the index entries will need
1828 NodeSize = GetSizeOfIndexEntries(Node);
1829
1830 // Is Node not the root node?
1831 if (Node != Tree->RootNode)
1832 {
1833 // Calculate maximum size of index entries without any headers
1834 AllocatedNodeSize = IndexRecordSize - FIELD_OFFSET(INDEX_BUFFER, Header);
1835
1836 // TODO: Replace magic with math
1837 MaxNodeSizeWithoutHeader = AllocatedNodeSize - 0x28;
1838
1839 // Has the node grown larger than its allocated size?
1840 if (NodeSize > MaxNodeSizeWithoutHeader)
1841 {
1843
1844 Status = SplitBTreeNode(Tree, Node, MedianKey, NewRightHandSibling, CaseSensitive);
1845 if (!NT_SUCCESS(Status))
1846 {
1847 DPRINT1("ERROR: Failed to split B-Tree node!\n");
1848 return Status;
1849 }
1850
1851 return Status;
1852 }
1853 }
1854
1855 // NewEntry and NewKey will be destroyed later by DestroyBTree()
1856
1857 return Status;
1858}
1859
1883static
1884VOID
1886 PB_TREE_FILENAME_NODE *OwnerNode,
1887 PB_TREE_KEY *PreviousKey,
1888 PB_TREE_KEY *LargestKey)
1889{
1890 PB_TREE_KEY CurrentKey = Node->FirstKey;
1891 PB_TREE_KEY Previous = NULL;
1892 PB_TREE_KEY BeforePrevious = NULL;
1893
1894 *OwnerNode = NULL;
1895 *PreviousKey = NULL;
1896 *LargestKey = NULL;
1897
1898 while (CurrentKey != NULL &&
1900 {
1901 BeforePrevious = Previous;
1902 Previous = CurrentKey;
1903 CurrentKey = CurrentKey->NextKey;
1904 }
1905
1906 // Anything greater than the last real key lives beneath the end marker
1907 if (CurrentKey != NULL && CurrentKey->LesserChild != NULL)
1908 {
1909 FindLargestKeyInSubtree(CurrentKey->LesserChild, OwnerNode, PreviousKey, LargestKey);
1910 if (*LargestKey != NULL)
1911 return;
1912 }
1913
1914 // Nothing but an end marker: this subtree holds no filenames
1915 if (Previous == NULL)
1916 return;
1917
1918 *OwnerNode = Node;
1919 *PreviousKey = BeforePrevious;
1920 *LargestKey = Previous;
1921}
1922
1943static
1946{
1947 PINDEX_ENTRY_ATTRIBUTE NewEntry;
1948 ULONG AttributeSize = GetFileNameAttributeLength(&Source->IndexEntry->FileName);
1950
1951 EntrySize += sizeof(ULONGLONG); // for the VCN of the child Key retains
1952
1954 if (!NewEntry)
1955 {
1956 DPRINT1("ERROR: Failed to allocate memory for replacement index entry!\n");
1958 }
1959
1960 RtlZeroMemory(NewEntry, EntrySize);
1961 NewEntry->Data.Directory.IndexedFile = Source->IndexEntry->Data.Directory.IndexedFile;
1962 NewEntry->Length = EntrySize;
1963 NewEntry->KeyLength = AttributeSize;
1964 NewEntry->Flags = NTFS_INDEX_ENTRY_NODE;
1965 RtlCopyMemory(&NewEntry->FileName, &Source->IndexEntry->FileName, AttributeSize);
1966
1967 ExFreePoolWithTag(Key->IndexEntry, TAG_NTFS);
1968 Key->IndexEntry = NewEntry;
1969
1970 return STATUS_SUCCESS;
1971}
1972
1991static
1994 PB_TREE_KEY PreviousKey,
1996{
1998
1999 /* Either way this node's keys change. Only dirty nodes are written back,
2000 * so without this a removal from a sub-node never reaches the disk. */
2001 Node->DiskNeedsUpdating = TRUE;
2002
2003 if (Key->LesserChild != NULL)
2004 {
2005 PB_TREE_FILENAME_NODE OwnerNode;
2006 PB_TREE_KEY PreviousLargest;
2007 PB_TREE_KEY LargestKey;
2008
2009 FindLargestKeyInSubtree(Key->LesserChild, &OwnerNode, &PreviousLargest, &LargestKey);
2010
2011 if (LargestKey != NULL)
2012 {
2013 // Unlinking an interior key would orphan its child, so promote the greatest key
2014 // beneath it and remove that one instead
2015 Status = ReplaceKeyIndexEntry(Key, LargestKey);
2016 if (!NT_SUCCESS(Status))
2017 return Status;
2018
2019 return RemoveKeyFromNode(OwnerNode, PreviousLargest, LargestKey);
2020 }
2021
2022 // The child holds no filenames, so there is nothing to promote; DestroyBTreeKey()
2023 // frees the empty child along with the key
2024 }
2025
2026 if (PreviousKey == NULL)
2027 Node->FirstKey = Key->NextKey;
2028 else
2029 PreviousKey->NextKey = Key->NextKey;
2030
2031 Node->KeyCount--;
2032
2033 Key->NextKey = NULL;
2035
2036 return STATUS_SUCCESS;
2037}
2038
2066 PB_TREE_KEY SearchKey,
2067 BOOLEAN CaseSensitive)
2068{
2069 PB_TREE_KEY PreviousKey = NULL;
2070 PB_TREE_KEY CurrentKey = Node->FirstKey;
2071 ULONG i;
2072
2073 for (i = 0; i < Node->KeyCount; i++)
2074 {
2075 LONG Comparison;
2076
2077 if (CurrentKey == NULL)
2078 break;
2079
2081 break;
2082
2083 Comparison = CompareTreeKeys(SearchKey, CurrentKey, CaseSensitive);
2084
2085 if (Comparison == 0)
2086 return RemoveKeyFromNode(Node, PreviousKey, CurrentKey);
2087
2088 // Everything that sorts before CurrentKey is beneath it
2089 if (Comparison < 0)
2090 break;
2091
2092 PreviousKey = CurrentKey;
2093 CurrentKey = CurrentKey->NextKey;
2094 }
2095
2096 if (CurrentKey != NULL && CurrentKey->LesserChild != NULL)
2097 return NtfsRemoveKey(CurrentKey->LesserChild, SearchKey, CaseSensitive);
2098
2100}
2101
2102
2138 PB_TREE_KEY *MedianKey,
2139 PB_TREE_FILENAME_NODE *NewRightHandSibling,
2140 BOOLEAN CaseSensitive)
2141{
2142 ULONG MedianKeyIndex;
2143 PB_TREE_KEY LastKeyBeforeMedian, FirstKeyAfterMedian;
2144 ULONG KeyCount;
2145 ULONG HalfSize;
2146 ULONG SizeSum;
2147 ULONG i;
2148
2149 DPRINT("SplitBTreeNode(%p, %p, %p, %p, %s) called\n",
2150 Tree,
2151 Node,
2152 MedianKey,
2153 NewRightHandSibling,
2154 CaseSensitive ? "TRUE" : "FALSE");
2155
2156#ifndef NDEBUG
2157 DumpBTreeNode(Tree, Node, 0, 0);
2158#endif
2159
2160 // Create the right hand sibling
2161 *NewRightHandSibling = ExAllocatePoolWithTag(NonPagedPool, sizeof(B_TREE_FILENAME_NODE), TAG_NTFS);
2162 if (*NewRightHandSibling == NULL)
2163 {
2164 DPRINT1("Error: Failed to allocate memory for right hand sibling!\n");
2166 }
2167 RtlZeroMemory(*NewRightHandSibling, sizeof(B_TREE_FILENAME_NODE));
2168 (*NewRightHandSibling)->DiskNeedsUpdating = TRUE;
2169
2170
2171 // Find the last key before the median
2172
2173 // This is roughly how NTFS-3G calculates median, and it's not congruent with what Windows does:
2174 /*
2175 // find the median key index
2176 MedianKeyIndex = (Node->KeyCount + 1) / 2;
2177 MedianKeyIndex--;
2178
2179 LastKeyBeforeMedian = Node->FirstKey;
2180 for (i = 0; i < MedianKeyIndex - 1; i++)
2181 LastKeyBeforeMedian = LastKeyBeforeMedian->NextKey;*/
2182
2183 // The method we'll use is a little bit closer to how Windows determines the median but it's not identical.
2184 // What Windows does is actually more complicated than this, I think because Windows allocates more slack space to Odd-numbered
2185 // Index Records, leaving less room for index entries in these records (I haven't discovered why this is done).
2186 // (Neither Windows nor chkdsk complain if we choose a different median than Windows would have chosen, as our median will be in the ballpark)
2187
2188 // Use size to locate the median key / index
2189 LastKeyBeforeMedian = Node->FirstKey;
2190 MedianKeyIndex = 0;
2191 HalfSize = 2016; // half the allocated size after subtracting the first index entry offset (TODO: MATH)
2192 SizeSum = 0;
2193 for (i = 0; i < Node->KeyCount; i++)
2194 {
2195 SizeSum += LastKeyBeforeMedian->IndexEntry->Length;
2196
2197 if (SizeSum > HalfSize)
2198 break;
2199
2200 MedianKeyIndex++;
2201 LastKeyBeforeMedian = LastKeyBeforeMedian->NextKey;
2202 }
2203
2204 // Now we can get the median key and the key that follows it
2205 *MedianKey = LastKeyBeforeMedian->NextKey;
2206 FirstKeyAfterMedian = (*MedianKey)->NextKey;
2207
2208 DPRINT("%lu keys, %lu median\n", Node->KeyCount, MedianKeyIndex);
2209 DPRINT("\t\tMedian: %.*S\n", (*MedianKey)->IndexEntry->FileName.NameLength, (*MedianKey)->IndexEntry->FileName.Name);
2210
2211 // "Node" will be the left hand sibling after the split, containing all keys prior to the median key
2212
2213 // We need to create a dummy pointer at the end of the LHS. The dummy's child will be the median's child.
2214 LastKeyBeforeMedian->NextKey = CreateDummyKey(BooleanFlagOn((*MedianKey)->IndexEntry->Flags, NTFS_INDEX_ENTRY_NODE));
2215 if (LastKeyBeforeMedian->NextKey == NULL)
2216 {
2217 DPRINT1("Error: Couldn't allocate dummy key!\n");
2218 LastKeyBeforeMedian->NextKey = *MedianKey;
2219 ExFreePoolWithTag(*NewRightHandSibling, TAG_NTFS);
2221 }
2222
2223 // Did the median key have a child node?
2224 if ((*MedianKey)->IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE)
2225 {
2226 // Set the child of the new dummy key
2227 LastKeyBeforeMedian->NextKey->LesserChild = (*MedianKey)->LesserChild;
2228
2229 // Give the dummy key's index entry the same sub-node VCN the median
2230 SetIndexEntryVCN(LastKeyBeforeMedian->NextKey->IndexEntry, GetIndexEntryVCN((*MedianKey)->IndexEntry));
2231 }
2232 else
2233 {
2234 // Median key didn't have a child node, but it will. Create a new index entry large enough to store a VCN.
2236 (*MedianKey)->IndexEntry->Length + sizeof(ULONGLONG),
2237 TAG_NTFS);
2238 if (!NewIndexEntry)
2239 {
2240 DPRINT1("Unable to allocate memory for new index entry!\n");
2241 LastKeyBeforeMedian->NextKey = *MedianKey;
2242 ExFreePoolWithTag(*NewRightHandSibling, TAG_NTFS);
2244 }
2245
2246 // Copy the old index entry to the new one
2247 RtlCopyMemory(NewIndexEntry, (*MedianKey)->IndexEntry, (*MedianKey)->IndexEntry->Length);
2248
2249 // Use the new index entry after freeing the old one
2250 ExFreePoolWithTag((*MedianKey)->IndexEntry, TAG_NTFS);
2251 (*MedianKey)->IndexEntry = NewIndexEntry;
2252
2253 // Update the length for the VCN
2254 (*MedianKey)->IndexEntry->Length += sizeof(ULONGLONG);
2255
2256 // Set the node flag
2257 (*MedianKey)->IndexEntry->Flags |= NTFS_INDEX_ENTRY_NODE;
2258 }
2259
2260 // "Node" will become the child of the median key
2261 (*MedianKey)->LesserChild = Node;
2262 SetIndexEntryVCN((*MedianKey)->IndexEntry, Node->VCN);
2263
2264 // Update Node's KeyCount (remember to add 1 for the new dummy key)
2265 Node->KeyCount = MedianKeyIndex + 2;
2266
2267 KeyCount = CountBTreeKeys(Node->FirstKey);
2268 ASSERT(Node->KeyCount == KeyCount);
2269
2270 // everything to the right of MedianKey becomes the right hand sibling of Node
2271 (*NewRightHandSibling)->FirstKey = FirstKeyAfterMedian;
2272 (*NewRightHandSibling)->KeyCount = CountBTreeKeys(FirstKeyAfterMedian);
2273
2274#ifndef NDEBUG
2275 DPRINT1("Left-hand node after split:\n");
2276 DumpBTreeNode(Tree, Node, 0, 0);
2277
2278 DPRINT1("Right-hand sibling node after split:\n");
2279 DumpBTreeNode(Tree, *NewRightHandSibling, 0, 0);
2280#endif
2281
2282 return STATUS_SUCCESS;
2283}
CRegistryTree Tree
#define ALIGN_UP_BY(size, align)
unsigned char BOOLEAN
Definition: actypes.h:127
LONG NTSTATUS
Definition: precomp.h:26
#define DPRINT1
Definition: precomp.h:8
struct NTFS_ATTR_RECORD * PNTFS_ATTR_RECORD
#define NTFS_INDEX_ENTRY_NODE
Definition: ntfs.h:60
#define NTFS_INDEX_ENTRY_END
Definition: ntfs.h:61
VOID DumpBTreeNode(PB_TREE Tree, PB_TREE_FILENAME_NODE Node, ULONG Number, ULONG Depth)
Definition: btree.c:1588
PB_TREE_FILENAME_NODE CreateBTreeNodeFromIndexNode(PDEVICE_EXTENSION Vcb, PINDEX_ROOT_ATTRIBUTE IndexRoot, PNTFS_ATTR_CONTEXT IndexAllocationAttributeCtx, PINDEX_ENTRY_ATTRIBUTE NodeEntry)
Definition: btree.c:504
ULONG CountBTreeKeys(PB_TREE_KEY FirstKey)
Definition: btree.c:489
VOID DestroyBTreeNode(PB_TREE_FILENAME_NODE Node)
Definition: btree.c:1516
NTSTATUS CreateBTreeFromIndex(PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER FileRecordWithIndex, PNTFS_ATTR_CONTEXT IndexRootContext, PINDEX_ROOT_ATTRIBUTE IndexRoot, PB_TREE *NewTree)
Definition: btree.c:687
NTSTATUS NtfsRemoveKey(PB_TREE_FILENAME_NODE Node, PB_TREE_KEY SearchKey, BOOLEAN CaseSensitive)
Definition: btree.c:2065
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:2136
VOID DestroyBTreeKey(PB_TREE_KEY Key)
Definition: btree.c:1504
NTSTATUS AllocateIndexNode(PDEVICE_EXTENSION DeviceExt, PFILE_RECORD_HEADER FileRecord, ULONG IndexBufferSize, PNTFS_ATTR_CONTEXT IndexAllocationCtx, ULONG IndexAllocationOffset, PULONGLONG NewVCN)
Definition: btree.c:117
static VOID FindLargestKeyInSubtree(PB_TREE_FILENAME_NODE Node, PB_TREE_FILENAME_NODE *OwnerNode, PB_TREE_KEY *PreviousKey, PB_TREE_KEY *LargestKey)
Definition: btree.c:1885
ULONGLONG GetIndexEntryVCN(PINDEX_ENTRY_ATTRIBUTE IndexEntry)
Definition: btree.c:1646
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:1696
static NTSTATUS ReplaceKeyIndexEntry(PB_TREE_KEY Key, PB_TREE_KEY Source)
Definition: btree.c:1945
ULONGLONG GetAllocationOffsetFromVCN(PDEVICE_EXTENSION DeviceExt, ULONG IndexBufferSize, ULONGLONG Vcn)
Definition: btree.c:1635
NTSTATUS UpdateIndexAllocation(PDEVICE_EXTENSION DeviceExt, PB_TREE Tree, ULONG IndexBufferSize, PFILE_RECORD_HEADER FileRecord)
Definition: btree.c:1187
VOID DestroyBTree(PB_TREE Tree)
Definition: btree.c:1547
VOID PrintAllVCNs(PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT IndexAllocationContext, ULONG NodeSize)
Definition: btree.c:38
static NTSTATUS RemoveKeyFromNode(PB_TREE_FILENAME_NODE Node, PB_TREE_KEY PreviousKey, PB_TREE_KEY Key)
Definition: btree.c:1993
NTSTATUS CreateIndexBufferFromBTreeNode(PDEVICE_EXTENSION DeviceExt, PB_TREE_FILENAME_NODE Node, ULONG BufferSize, BOOLEAN HasChildren, PINDEX_BUFFER IndexBuffer)
Definition: btree.c:1006
NTSTATUS DemoteBTreeRoot(PB_TREE Tree)
Definition: btree.c:1094
ULONG GetSizeOfIndexEntries(PB_TREE_FILENAME_NODE Node)
Definition: btree.c:860
PB_TREE_KEY CreateDummyKey(BOOLEAN HasChildNode)
Definition: btree.c:294
VOID DumpBTree(PB_TREE Tree)
Definition: btree.c:1627
NTSTATUS CreateEmptyBTree(PB_TREE *NewTree)
Definition: btree.c:353
NTSTATUS CreateIndexRootFromBTree(PDEVICE_EXTENSION DeviceExt, PB_TREE Tree, ULONG MaxIndexSize, PINDEX_ROOT_ATTRIBUTE *IndexRoot, ULONG *Length)
Definition: btree.c:915
VOID SetIndexEntryVCN(PINDEX_ENTRY_ATTRIBUTE IndexEntry, ULONGLONG VCN)
Definition: btree.c:1177
PB_TREE_KEY CreateBTreeKeyFromFilename(ULONGLONG FileReference, PFILENAME_ATTRIBUTE FileNameAttribute)
Definition: btree.c:1466
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
VOID DumpBTreeKey(PB_TREE Tree, PB_TREE_KEY Key, ULONG Number, ULONG Depth)
Definition: btree.c:1554
Definition: bufpool.h:45
Definition: Header.h:9
UINT Depth
Definition: d3d12.idl:4644
#define STATUS_NOT_IMPLEMENTED
Definition: d3dkmdt.h:42
#define RtlInitializeBitMap
Definition: dbgbitmap.h:326
#define RtlSetBits
Definition: dbgbitmap.h:345
#define BufferSize
Definition: mmc.h:75
_In_ ULONG BufferSize
Definition: dispmprt.h:490
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:33
union node Node
Definition: types.h:1255
static const WCHAR Cleanup[]
Definition: register.c:80
#define L(x)
Definition: resources.c:13
ULONG GetFileNameAttributeLength(PFILENAME_ATTRIBUTE FileNameAttribute)
Definition: attrib.c:2008
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
struct INDEX_BUFFER * PINDEX_BUFFER
#define INDEX_ROOT_SMALL
Definition: ntfs.h:235
struct INDEX_ENTRY_ATTRIBUTE * PINDEX_ENTRY_ATTRIBUTE
#define COLLATION_FILE_NAME
Definition: ntfs.h:228
#define INDEX_NODE_LARGE
Definition: ntfs.h:239
#define INDEX_ROOT_LARGE
Definition: ntfs.h:236
#define NRH_INDX_TYPE
Definition: ntfs.h:278
@ AttributeFileName
Definition: ntfs.h:190
@ AttributeIndexAllocation
Definition: ntfs.h:197
@ 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
ULONG RtlCompareUnicodeString(PUNICODE_STRING s1, PUNICODE_STRING s2, BOOLEAN UpCase)
Definition: string_lib.cpp:31
#define NonPagedPool
Definition: env_spec_w32.h:307
#define BooleanFlagOn(F, SF)
Definition: ext2fs.h:183
struct _FileName FileName
Definition: fatprocs.h:897
Status
Definition: gdiplustypes.h:24
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
#define DbgPrint
Definition: hal.h:12
PCONFIGURATION_COMPONENT_DATA RootNode
Definition: macharm.c:19
NTSTATUS UpdateFileRecord(PDEVICE_EXTENSION Vcb, ULONGLONG MftIndex, PFILE_RECORD_HEADER FileRecord)
Definition: mft.c:2260
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:1494
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:1698
NTSTATUS AddFixupArray(PDEVICE_EXTENSION Vcb, PNTFS_RECORD_HEADER Record)
Definition: mft.c:3444
NTSTATUS SetNonResidentAttributeDataLength(PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT AttrContext, ULONG AttrOffset, PFILE_RECORD_HEADER FileRecord, PLARGE_INTEGER DataSize)
Definition: mft.c:937
NTSTATUS SetResidentAttributeDataLength(PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT AttrContext, ULONG AttrOffset, PFILE_RECORD_HEADER FileRecord, PLARGE_INTEGER DataSize)
Definition: mft.c:1020
NTSTATUS FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb, PNTFS_RECORD_HEADER Record)
Definition: mft.c:2294
#define ASSERT(a)
Definition: mode.c:44
#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
_In_ UINT _In_ UINT _In_ PNDIS_PACKET Source
Definition: ndis.h:3169
_In_ ULONG Number
Definition: haltypes.h:1940
_In_ PUNICODE_STRING _Inout_ PUNICODE_STRING Destination
Definition: rtlfuncs.h:3051
int Count
Definition: noreturn.cpp:7
__GNU_EXTENSION typedef unsigned __int64 * PULONGLONG
Definition: ntbasedef.h:395
_In_ ULONG _In_ ULONG _In_ ULONG Length
Definition: ntddpcm.h:102
short WCHAR
Definition: pedump.c:58
long LONG
Definition: pedump.c:60
#define Vcb
Definition: cdprocs.h:1415
#define STATUS_END_OF_FILE
Definition: shellext.h:67
#define STATUS_SUCCESS
Definition: shellext.h:65
#define DPRINT
Definition: sndvol32.h:73
Definition: ntfs.h:485
UCHAR NameLength
Definition: ntfs.h:408
WCHAR Name[1]
Definition: ntfs.h:410
INDEX_HEADER_ATTRIBUTE Header
Definition: ntfs.h:436
ULONGLONG VCN
Definition: ntfs.h:435
NTFS_RECORD_HEADER Ntfs
Definition: ntfs.h:434
Definition: ntfs.h:440
union INDEX_ENTRY_ATTRIBUTE::@969 Data
FILENAME_ATTRIBUTE FileName
Definition: ntfs.h:458
USHORT Length
Definition: ntfs.h:454
USHORT KeyLength
Definition: ntfs.h:455
USHORT Flags
Definition: ntfs.h:456
struct INDEX_ENTRY_ATTRIBUTE::@969::@970 Directory
ULONG FirstEntryOffset
Definition: ntfs.h:415
ULONG TotalSizeOfEntries
Definition: ntfs.h:416
ULONG AttributeType
Definition: ntfs.h:424
ULONG CollationRule
Definition: ntfs.h:425
INDEX_HEADER_ATTRIBUTE Header
Definition: ntfs.h:429
UCHAR ClustersPerIndexRecord
Definition: ntfs.h:427
ULONG SizeOfEntry
Definition: ntfs.h:426
ULONG Length
Definition: ntfs.h:135
USHORT UsaCount
Definition: ntfs.h:272
USHORT UsaOffset
Definition: ntfs.h:271
ULONGLONG VCN
Definition: ntfs.h:480
BOOLEAN HasValidVCN
Definition: ntfs.h:478
BOOLEAN DiskNeedsUpdating
Definition: ntfs.h:479
PB_TREE_KEY FirstKey
Definition: ntfs.h:481
struct _B_TREE_KEY * NextKey
Definition: ntfs.h:467
B_TREE_FILENAME_NODE * LesserChild
Definition: ntfs.h:468
PINDEX_ENTRY_ATTRIBUTE IndexEntry
Definition: ntfs.h:469
ULONG BytesInUse
Definition: ntfs.h:288
USHORT MaximumLength
Definition: env_spec_w32.h:370
uint32_t * PULONG
Definition: typedefs.h:59
#define FIELD_OFFSET(t, f)
Definition: typedefs.h:255
uint64_t ULONGLONG
Definition: typedefs.h:67
#define RtlCopyMemory(Destination, Source, Length)
Definition: typedefs.h:263
#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_OBJECT_NAME_COLLISION
Definition: udferr_usr.h:150
#define STATUS_FILE_CORRUPT_ERROR
Definition: udferr_usr.h:168
#define STATUS_INSUFFICIENT_RESOURCES
Definition: udferr_usr.h:158
#define STATUS_OBJECT_NAME_NOT_FOUND
Definition: udferr_usr.h:149
#define ALIGN_UP(size, type)
Definition: umtypes.h:91
Definition: dlist.c:348
_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
_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
_In_ UCHAR EntrySize
Definition: iofuncs.h:642
#define NT_ASSERT
Definition: rtlfuncs.h:3334