ReactOS 0.4.17-dev-1190-g4c812f4
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
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}
1504
1505VOID
1507{
1508 if (Key->IndexEntry)
1509 ExFreePoolWithTag(Key->IndexEntry, TAG_NTFS);
1510
1511 if (Key->LesserChild)
1512 DestroyBTreeNode(Key->LesserChild);
1513
1515}
1516
1517VOID
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}
1535
1548VOID
1550{
1551 DestroyBTreeNode(Tree->RootNode);
1553}
1554
1555VOID
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}
1588
1589VOID
1592 ULONG Number,
1593 ULONG Depth)
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}
1615
1628VOID
1630{
1631 DbgPrint("B_TREE @ %p\n", Tree);
1632 DumpBTreeNode(Tree, Tree->RootNode, 0, 0);
1633}
1634
1635// Calculates start of Index Buffer relative to the index allocation, given the node's VCN
1638 ULONG IndexBufferSize,
1639 ULONGLONG Vcn)
1640{
1641 if (IndexBufferSize < DeviceExt->NtfsInfo.BytesPerCluster)
1642 return Vcn * DeviceExt->NtfsInfo.BytesPerSector;
1643
1644 return Vcn * DeviceExt->NtfsInfo.BytesPerCluster;
1645}
1646
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}
1656
1699 ULONGLONG FileReference,
1700 PFILENAME_ATTRIBUTE FileNameAttribute,
1702 BOOLEAN CaseSensitive,
1703 ULONG MaxIndexRootSize,
1704 ULONG IndexRecordSize,
1705 PB_TREE_KEY *MedianKey,
1706 PB_TREE_FILENAME_NODE *NewRightHandSibling)
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}
1861
1885static
1886VOID
1888 PB_TREE_FILENAME_NODE *OwnerNode,
1889 PB_TREE_KEY *PreviousKey,
1890 PB_TREE_KEY *LargestKey)
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}
1924
1945static
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}
1974
1993static
1996 PB_TREE_KEY PreviousKey,
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}
2040
2068 PB_TREE_KEY SearchKey,
2069 BOOLEAN CaseSensitive)
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}
2103
2104
2140 PB_TREE_KEY *MedianKey,
2141 PB_TREE_FILENAME_NODE *NewRightHandSibling,
2142 BOOLEAN CaseSensitive)
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}
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:1590
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:1518
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:2067
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
VOID DestroyBTreeKey(PB_TREE_KEY Key)
Definition: btree.c:1506
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:1887
ULONGLONG GetIndexEntryVCN(PINDEX_ENTRY_ATTRIBUTE IndexEntry)
Definition: btree.c:1648
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
static NTSTATUS ReplaceKeyIndexEntry(PB_TREE_KEY Key, PB_TREE_KEY Source)
Definition: btree.c:1947
ULONGLONG GetAllocationOffsetFromVCN(PDEVICE_EXTENSION DeviceExt, ULONG IndexBufferSize, ULONGLONG Vcn)
Definition: btree.c:1637
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:1549
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:1995
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:1629
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:1556
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: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 AddFixupArray(PDEVICE_EXTENSION Vcb, PNTFS_RECORD_HEADER Record)
Definition: mft.c:3575
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
NTSTATUS FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb, PNTFS_RECORD_HEADER Record)
Definition: mft.c:2425
#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::@970 Data
struct INDEX_ENTRY_ATTRIBUTE::@970::@971 Directory
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
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