ReactOS 0.4.17-dev-1190-g4c812f4
mft.c
Go to the documentation of this file.
1/*
2 * ReactOS kernel
3 * Copyright (C) 2002, 2014 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/mft.c
22 * PURPOSE: NTFS filesystem driver
23 * PROGRAMMERS: Eric Kohl
24 * Valentin Verkhovsky
25 * Pierre Schweitzer (pierre@reactos.org)
26 * Hervé Poussineau (hpoussin@reactos.org)
27 * Trevor Thompson
28 */
29
30/* INCLUDES *****************************************************************/
31
32#include "ntfs.h"
33#include <ntintsafe.h>
34
35#define NDEBUG
36#include <debug.h>
37
38/* FUNCTIONS ****************************************************************/
39
42{
44
45 Context = ExAllocateFromNPagedLookasideList(&NtfsGlobalData->AttrCtxtLookasideList);
46 if(!Context)
47 {
48 DPRINT1("Error: Unable to allocate memory for context!\n");
49 return NULL;
50 }
51
52 // Allocate memory for a copy of the attribute
54 if(!Context->pRecord)
55 {
56 DPRINT1("Error: Unable to allocate memory for attribute record!\n");
57 ExFreeToNPagedLookasideList(&NtfsGlobalData->AttrCtxtLookasideList, Context);
58 return NULL;
59 }
60
61 // Copy the attribute
62 RtlCopyMemory(Context->pRecord, AttrRecord, AttrRecord->Length);
63
64 if (AttrRecord->IsNonResident)
65 {
66 LONGLONG DataRunOffset;
67 ULONGLONG DataRunLength;
68 ULONGLONG NextVBN = 0;
69 PUCHAR DataRun = (PUCHAR)((ULONG_PTR)Context->pRecord + Context->pRecord->NonResident.MappingPairsOffset);
70
71 Context->CacheRun = DataRun;
72 Context->CacheRunOffset = 0;
73 Context->CacheRun = DecodeRun(Context->CacheRun, &DataRunOffset, &DataRunLength);
74 Context->CacheRunLength = DataRunLength;
75 if (DataRunOffset != -1)
76 {
77 /* Normal run. */
78 Context->CacheRunStartLCN =
79 Context->CacheRunLastLCN = DataRunOffset;
80 }
81 else
82 {
83 /* Sparse run. */
84 Context->CacheRunStartLCN = -1;
85 Context->CacheRunLastLCN = 0;
86 }
87 Context->CacheRunCurrentOffset = 0;
88
89 // Convert the data runs to a map control block
90 if (!NT_SUCCESS(ConvertDataRunsToLargeMCB(DataRun, &Context->DataRunsMCB, &NextVBN)))
91 {
92 DPRINT1("Unable to convert data runs to MCB!\n");
94 ExFreeToNPagedLookasideList(&NtfsGlobalData->AttrCtxtLookasideList, Context);
95 return NULL;
96 }
97 }
98
99 return Context;
100}
101
102
103VOID
105{
106 if (Context->pRecord)
107 {
108 if (Context->pRecord->IsNonResident)
109 {
110 FsRtlUninitializeLargeMcb(&Context->DataRunsMCB);
111 }
112
114 }
115
116 ExFreeToNPagedLookasideList(&NtfsGlobalData->AttrCtxtLookasideList, Context);
117}
118
119
132 PFILE_RECORD_HEADER MftRecord,
133 ULONG Type,
134 PCWSTR Name,
135 ULONG NameLength,
136 PNTFS_ATTR_CONTEXT * AttrCtx,
138{
142 PNTFS_ATTR_RECORD Attribute;
143 PNTFS_ATTRIBUTE_LIST_ITEM AttrListItem;
144
145 DPRINT("FindAttribute(%p, %p, 0x%x, %S, %lu, %p, %p)\n", Vcb, MftRecord, Type, Name, NameLength, AttrCtx, Offset);
146
147 Found = FALSE;
148 Status = FindFirstAttribute(&Context, Vcb, MftRecord, FALSE, &Attribute);
149 while (NT_SUCCESS(Status))
150 {
151 if (Attribute->Type == Type && Attribute->NameLength == NameLength)
152 {
153 if (NameLength != 0)
154 {
155 PWCHAR AttrName;
156
157 AttrName = (PWCHAR)((PCHAR)Attribute + Attribute->NameOffset);
158 DPRINT("%.*S, %.*S\n", Attribute->NameLength, AttrName, NameLength, Name);
159 if (RtlCompareMemory(AttrName, Name, NameLength * sizeof(WCHAR)) == (NameLength * sizeof(WCHAR)))
160 {
161 Found = TRUE;
162 }
163 }
164 else
165 {
166 Found = TRUE;
167 }
168
169 if (Found)
170 {
171 /* Found it, fill up the context and return. */
172 DPRINT("Found context\n");
173 *AttrCtx = PrepareAttributeContext(Attribute);
174
175 (*AttrCtx)->FileMFTIndex = MftRecord->MFTRecordNumber;
176
177 if (Offset != NULL)
178 *Offset = Context.Offset;
179
181 return STATUS_SUCCESS;
182 }
183 }
184
185 Status = FindNextAttribute(&Context, &Attribute);
186 }
187
188 /* No attribute found, check if it is referenced in another file record */
189 Status = FindFirstAttributeListItem(&Context, &AttrListItem);
190 while (NT_SUCCESS(Status))
191 {
192 if (AttrListItem->Type == Type && AttrListItem->NameLength == NameLength)
193 {
194 if (NameLength != 0)
195 {
196 PWCHAR AttrName;
197
198 AttrName = (PWCHAR)((PCHAR)AttrListItem + AttrListItem->NameOffset);
199 DPRINT("%.*S, %.*S\n", AttrListItem->NameLength, AttrName, NameLength, Name);
200 if (RtlCompareMemory(AttrName, Name, NameLength * sizeof(WCHAR)) == (NameLength * sizeof(WCHAR)))
201 {
202 Found = TRUE;
203 }
204 }
205 else
206 {
207 Found = TRUE;
208 }
209
210 if (Found == TRUE)
211 {
212 /* Get the MFT Index of attribute */
213 ULONGLONG MftIndex;
214 PFILE_RECORD_HEADER RemoteHdr;
215
216 MftIndex = AttrListItem->MFTIndex & NTFS_MFT_MASK;
217 RemoteHdr = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
218
219 if (RemoteHdr == NULL)
220 {
223 }
224
225 /* Check we are not reading ourselves */
226 if (MftRecord->MFTRecordNumber == MftIndex)
227 {
228 DPRINT1("Attribute list references missing attribute to this file entry !");
229 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, RemoteHdr);
232 }
233 /* Read the new file record */
234 ReadFileRecord(Vcb, MftIndex, RemoteHdr);
235 Status = FindAttribute(Vcb, RemoteHdr, Type, Name, NameLength, AttrCtx, Offset);
236 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, RemoteHdr);
238 return Status;
239 }
240 }
241 Status = FindNextAttributeListItem(&Context, &AttrListItem);
242 }
245}
246
247
250{
251 if (AttrRecord->IsNonResident)
252 return AttrRecord->NonResident.AllocatedSize;
253 else
254 return ALIGN_UP_BY(AttrRecord->Resident.ValueLength, ATTR_RECORD_ALIGNMENT);
255}
256
257
260{
261 if (AttrRecord->IsNonResident)
262 return AttrRecord->NonResident.DataSize;
263 else
264 return AttrRecord->Resident.ValueLength;
265}
266
294{
295 PNTFS_ATTR_CONTEXT BitmapContext;
296 LARGE_INTEGER BitmapSize;
298 LONGLONG BitmapSizeDifference;
299 ULONG NewRecords = ATTR_RECORD_ALIGNMENT * 8; // Allocate one new record for every bit of every byte we'll be adding to the bitmap
300 ULONG DataSizeDifference = Vcb->NtfsInfo.BytesPerFileRecord * NewRecords;
301 ULONG BitmapOffset;
303 ULONGLONG BitmapBytes;
304 ULONGLONG NewBitmapSize;
305 ULONGLONG FirstNewMftIndex;
307 ULONG LengthWritten;
308 PFILE_RECORD_HEADER BlankFileRecord;
309 ULONG i;
311
312 DPRINT("IncreaseMftSize(%p, %s)\n", Vcb, CanWait ? "TRUE" : "FALSE");
313
314 // We need exclusive access to the mft while we change its size
315 if (!ExAcquireResourceExclusiveLite(&(Vcb->DirResource), CanWait))
316 {
317 return STATUS_CANT_WAIT;
318 }
319
320 // Create a blank file record that will be used later
321 BlankFileRecord = NtfsCreateEmptyFileRecord(Vcb);
322 if (!BlankFileRecord)
323 {
324 DPRINT1("Error: Unable to create empty file record!\n");
326 }
327
328 // Clear the flags (file record is not in use)
329 BlankFileRecord->Flags = 0;
330
331 // Find the bitmap attribute of master file table
332 Status = FindAttribute(Vcb, Vcb->MasterFileTable, AttributeBitmap, L"", 0, &BitmapContext, NULL);
333 if (!NT_SUCCESS(Status))
334 {
335 DPRINT1("ERROR: Couldn't find $BITMAP attribute of Mft!\n");
336 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, BlankFileRecord);
337 ExReleaseResourceLite(&(Vcb->DirResource));
338 return Status;
339 }
340
341 // Get size of Bitmap Attribute
342 BitmapSize.QuadPart = AttributeDataLength(BitmapContext->pRecord);
343
344 // Calculate the new mft size
345 DataSize.QuadPart = AttributeDataLength(Vcb->MFTContext->pRecord) + DataSizeDifference;
346
347 // Find the index of the first Mft entry that will be created
348 FirstNewMftIndex = AttributeDataLength(Vcb->MFTContext->pRecord) / Vcb->NtfsInfo.BytesPerFileRecord;
349
350 // Determine how many bytes will make up the bitmap
351 BitmapBytes = DataSize.QuadPart / Vcb->NtfsInfo.BytesPerFileRecord / 8;
352 if ((DataSize.QuadPart / Vcb->NtfsInfo.BytesPerFileRecord) % 8 != 0)
353 BitmapBytes++;
354
355 // Windows will always keep the number of bytes in a bitmap as a multiple of 8, so no bytes are wasted on slack
356 BitmapBytes = ALIGN_UP_BY(BitmapBytes, ATTR_RECORD_ALIGNMENT);
357
358 // Determine how much we need to adjust the bitmap size (it's possible we don't)
359 BitmapSizeDifference = BitmapBytes - BitmapSize.QuadPart;
360 NewBitmapSize = max(BitmapSize.QuadPart + BitmapSizeDifference, BitmapSize.QuadPart);
361
362 // Allocate memory for the bitmap
364 if (!BitmapBuffer)
365 {
366 DPRINT1("ERROR: Unable to allocate memory for bitmap attribute!\n");
367 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, BlankFileRecord);
368 ExReleaseResourceLite(&(Vcb->DirResource));
369 ReleaseAttributeContext(BitmapContext);
371 }
372
373 // Zero the bytes we'll be adding
374 RtlZeroMemory(BitmapBuffer, NewBitmapSize);
375
376 // Read the bitmap attribute
378 BitmapContext,
379 0,
381 BitmapSize.LowPart);
382 if (BytesRead != BitmapSize.LowPart)
383 {
384 DPRINT1("ERROR: Bytes read != Bitmap size!\n");
385 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, BlankFileRecord);
386 ExReleaseResourceLite(&(Vcb->DirResource));
388 ReleaseAttributeContext(BitmapContext);
390 }
391
392 // Increase the mft size
393 Status = SetNonResidentAttributeDataLength(Vcb, Vcb->MFTContext, Vcb->MftDataOffset, Vcb->MasterFileTable, &DataSize);
394 if (!NT_SUCCESS(Status))
395 {
396 DPRINT1("ERROR: Failed to set size of $MFT data attribute!\n");
397 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, BlankFileRecord);
398 ExReleaseResourceLite(&(Vcb->DirResource));
400 ReleaseAttributeContext(BitmapContext);
401 return Status;
402 }
403
404 // We'll need to find the bitmap again, because its offset will have changed after resizing the data attribute
405 ReleaseAttributeContext(BitmapContext);
406 Status = FindAttribute(Vcb, Vcb->MasterFileTable, AttributeBitmap, L"", 0, &BitmapContext, &BitmapOffset);
407 if (!NT_SUCCESS(Status))
408 {
409 DPRINT1("ERROR: Couldn't find $BITMAP attribute of Mft!\n");
410 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, BlankFileRecord);
411 ExReleaseResourceLite(&(Vcb->DirResource));
412 return Status;
413 }
414
415 // If the bitmap grew
416 if (BitmapSizeDifference > 0)
417 {
418 // Set the new bitmap size
419 BitmapSize.QuadPart = NewBitmapSize;
420 if (BitmapContext->pRecord->IsNonResident)
421 Status = SetNonResidentAttributeDataLength(Vcb, BitmapContext, BitmapOffset, Vcb->MasterFileTable, &BitmapSize);
422 else
423 Status = SetResidentAttributeDataLength(Vcb, BitmapContext, BitmapOffset, Vcb->MasterFileTable, &BitmapSize);
424
425 if (!NT_SUCCESS(Status))
426 {
427 DPRINT1("ERROR: Failed to set size of bitmap attribute!\n");
428 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, BlankFileRecord);
429 ExReleaseResourceLite(&(Vcb->DirResource));
431 ReleaseAttributeContext(BitmapContext);
432 return Status;
433 }
434 }
435
436 NtfsDumpFileAttributes(Vcb, Vcb->MasterFileTable);
437
438 // Update the file record with the new attribute sizes
439 Status = UpdateFileRecord(Vcb, Vcb->VolumeFcb->MFTIndex, Vcb->MasterFileTable);
440 if (!NT_SUCCESS(Status))
441 {
442 DPRINT1("ERROR: Failed to update $MFT file record!\n");
443 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, BlankFileRecord);
444 ExReleaseResourceLite(&(Vcb->DirResource));
446 ReleaseAttributeContext(BitmapContext);
447 return Status;
448 }
449
450 // Write out the new bitmap
451 Status = WriteAttribute(Vcb, BitmapContext, 0, BitmapBuffer, NewBitmapSize, &LengthWritten, Vcb->MasterFileTable);
452 if (!NT_SUCCESS(Status))
453 {
454 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, BlankFileRecord);
455 ExReleaseResourceLite(&(Vcb->DirResource));
457 ReleaseAttributeContext(BitmapContext);
458 DPRINT1("ERROR: Couldn't write to bitmap attribute of $MFT!\n");
459 return Status;
460 }
461
462 // Create blank records for the new file record entries.
463 for (i = 0; i < NewRecords; i++)
464 {
465 Status = UpdateFileRecord(Vcb, FirstNewMftIndex + i, BlankFileRecord);
466 if (!NT_SUCCESS(Status))
467 {
468 DPRINT1("ERROR: Failed to write blank file record!\n");
469 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, BlankFileRecord);
470 ExReleaseResourceLite(&(Vcb->DirResource));
472 ReleaseAttributeContext(BitmapContext);
473 return Status;
474 }
475 }
476
477 // Update the mft mirror
479
480 // Cleanup
481 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, BlankFileRecord);
482 ExReleaseResourceLite(&(Vcb->DirResource));
484 ReleaseAttributeContext(BitmapContext);
485
486 return Status;
487}
488
513 PNTFS_ATTR_RECORD FirstAttributeToMove,
514 ULONG FirstAttributeOffset,
515 ULONG_PTR MoveTo)
516{
517 // Get the size of all attributes after this one
518 ULONG MemBlockSize = 0;
519 PNTFS_ATTR_RECORD CurrentAttribute = FirstAttributeToMove;
520 ULONG CurrentOffset = FirstAttributeOffset;
521 PNTFS_ATTR_RECORD FinalAttribute;
522
523 while (CurrentAttribute->Type != AttributeEnd && CurrentOffset < DeviceExt->NtfsInfo.BytesPerFileRecord)
524 {
525 CurrentOffset += CurrentAttribute->Length;
526 MemBlockSize += CurrentAttribute->Length;
527 CurrentAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)CurrentAttribute + CurrentAttribute->Length);
528 }
529
530 FinalAttribute = (PNTFS_ATTR_RECORD)(MoveTo + MemBlockSize);
531 MemBlockSize += sizeof(ULONG) * 2; // Add the AttributeEnd and file record end
532
533 ASSERT(MemBlockSize % ATTR_RECORD_ALIGNMENT == 0);
534
535 // Move the attributes after this one
536 RtlMoveMemory((PCHAR)MoveTo, FirstAttributeToMove, MemBlockSize);
537
538 return FinalAttribute;
539}
540
543 PNTFS_ATTR_CONTEXT AttrContext,
544 PFILE_RECORD_HEADER FileRecord,
545 ULONG AttrOffset,
547{
548 PNTFS_ATTR_RECORD Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
550 PNTFS_ATTR_RECORD FinalAttribute;
551 ULONG OldAttributeLength = Destination->Length;
552 ULONG NextAttributeOffset;
553
554 DPRINT("InternalSetResidentAttributeLength( %p, %p, %p, %lu, %lu )\n", DeviceExt, AttrContext, FileRecord, AttrOffset, DataSize);
555
556 ASSERT(!AttrContext->pRecord->IsNonResident);
557
558 // Update ValueLength Field
559 Destination->Resident.ValueLength = DataSize;
560
561 // Calculate the record length and end marker offset
562 Destination->Length = ALIGN_UP_BY(DataSize + AttrContext->pRecord->Resident.ValueOffset, ATTR_RECORD_ALIGNMENT);
563 NextAttributeOffset = AttrOffset + Destination->Length;
564
565 // Ensure NextAttributeOffset is aligned to an 8-byte boundary
566 ASSERT(NextAttributeOffset % ATTR_RECORD_ALIGNMENT == 0);
567
568 // Will the new attribute be larger than the old one?
569 if (Destination->Length > OldAttributeLength)
570 {
571 // Free the old copy of the attribute in the context, as it will be the wrong length
572 ExFreePoolWithTag(AttrContext->pRecord, TAG_NTFS);
573
574 // Create a new copy of the attribute record for the context
576 if (!AttrContext->pRecord)
577 {
578 DPRINT1("Unable to allocate memory for attribute!\n");
580 }
581 RtlZeroMemory((PVOID)((ULONG_PTR)AttrContext->pRecord + OldAttributeLength), Destination->Length - OldAttributeLength);
582 RtlCopyMemory(AttrContext->pRecord, Destination, OldAttributeLength);
583 }
584
585 // Are there attributes after this one that need to be moved?
586 if (NextAttribute->Type != AttributeEnd)
587 {
588 // Move the attributes after this one
589 FinalAttribute = MoveAttributes(DeviceExt, NextAttribute, NextAttributeOffset, (ULONG_PTR)Destination + Destination->Length);
590 }
591 else
592 {
593 // advance to the final "attribute," adjust for the changed length of the attribute we're resizing
594 FinalAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute - OldAttributeLength + Destination->Length);
595 }
596
597 // Update pRecord's length
598 AttrContext->pRecord->Length = Destination->Length;
599 AttrContext->pRecord->Resident.ValueLength = DataSize;
600
601 // set the file record end
602 SetFileRecordEnd(FileRecord, FinalAttribute, FILE_RECORD_END);
603
604 //NtfsDumpFileRecord(DeviceExt, FileRecord);
605
606 return STATUS_SUCCESS;
607}
608
617 PNTFS_ATTR_CONTEXT AttrContext,
618 ULONG AttrOffset,
619 PFILE_RECORD_HEADER FileRecord,
621{
623
624 DPRINT("SetAttributeDataLength(%p, %p, %p, %lu, %p, %I64u)\n",
626 Fcb,
627 AttrContext,
628 AttrOffset,
629 FileRecord,
630 DataSize->QuadPart);
631
632 // are we truncating the file?
633 if (DataSize->QuadPart < AttributeDataLength(AttrContext->pRecord))
634 {
635 if (!MmCanFileBeTruncated(FileObject->SectionObjectPointer, DataSize))
636 {
637 DPRINT1("Can't truncate a memory-mapped file!\n");
639 }
640 }
641
642 if (AttrContext->pRecord->IsNonResident)
643 {
645 AttrContext,
646 AttrOffset,
647 FileRecord,
648 DataSize);
649 }
650 else
651 {
652 // resident attribute
654 AttrContext,
655 AttrOffset,
656 FileRecord,
657 DataSize);
658 }
659
660 if (!NT_SUCCESS(Status))
661 {
662 DPRINT1("ERROR: Failed to set size of attribute!\n");
663 return Status;
664 }
665
666 //NtfsDumpFileAttributes(Fcb->Vcb, FileRecord);
667
668 // write the updated file record back to disk
669 Status = UpdateFileRecord(Fcb->Vcb, Fcb->MFTIndex, FileRecord);
670
671 if (NT_SUCCESS(Status))
672 {
673 if (AttrContext->pRecord->IsNonResident)
674 Fcb->RFCB.AllocationSize.QuadPart = AttrContext->pRecord->NonResident.AllocatedSize;
675 else
680 }
681
682 return STATUS_SUCCESS;
683}
684
705VOID
707 PNTFS_ATTR_RECORD AttrEnd,
708 ULONG EndMarker)
709{
710 // Ensure AttrEnd is aligned on an 8-byte boundary, relative to FileRecord
711 ASSERT(((ULONG_PTR)AttrEnd - (ULONG_PTR)FileRecord) % ATTR_RECORD_ALIGNMENT == 0);
712
713 // mark the end of attributes
714 AttrEnd->Type = AttributeEnd;
715
716 // Restore the "file-record-end marker." The value is never checked but this behavior is consistent with Win2k3.
717 AttrEnd->Length = EndMarker;
718
719 // recalculate bytes in use
720 FileRecord->BytesInUse = (ULONG_PTR)AttrEnd - (ULONG_PTR)FileRecord + sizeof(ULONG) * 2;
721}
722
755 _In_ PNTFS_ATTR_CONTEXT AttrContext,
756 _In_ ULONG AttrOffset,
757 _In_ PFILE_RECORD_HEADER FileRecord,
759{
761 ULONG BytesPerCluster = Vcb->NtfsInfo.BytesPerCluster;
762 PNTFS_ATTR_RECORD DestinationAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
763 ULONGLONG ExistingClusters = AttrContext->pRecord->NonResident.AllocatedSize / BytesPerCluster;
764 ULONG ClustersNeeded;
765 ULONG DesiredCluster = 0;
766 LONGLONG LastVbn;
767 LONGLONG LastLbn;
768 ULONG NextAssignedCluster;
769 ULONG AssignedClusters;
770
771 ASSERT(AttrContext->pRecord->IsNonResident);
772 ASSERT(AllocationSize % BytesPerCluster == 0);
773
774 if (AttrContext->pRecord->NonResident.AllocatedSize >= AllocationSize)
775 return STATUS_SUCCESS;
776
777 ClustersNeeded = (ULONG)(AllocationSize / BytesPerCluster - ExistingClusters);
778
779 /* Ask for the clusters right after the last one the stream owns. The
780 * tail of a sparse stream is a hole, so look at the last mapping, not
781 * at HighestVCN. */
782 if (ExistingClusters != 0 &&
783 FsRtlLookupLastLargeMcbEntry(&AttrContext->DataRunsMCB, &LastVbn, &LastLbn) &&
784 LastLbn != -1)
785 {
786 DesiredCluster = (ULONG)LastLbn + 1;
787 }
788
789 DPRINT("DesiredCluster: %lu\n", DesiredCluster);
790 DPRINT("Highest VCN of record: %I64u\n", AttrContext->pRecord->NonResident.HighestVCN);
791
792 while (ClustersNeeded > 0)
793 {
795 DesiredCluster,
796 ClustersNeeded,
797 &NextAssignedCluster,
798 &AssignedClusters);
799
800 if (!NT_SUCCESS(Status))
801 {
802 DPRINT1("Error: Unable to allocate requested clusters!\n");
803 return Status;
804 }
805
806 // now we need to add the clusters we allocated to the data run
807 Status = AddRun(Vcb, AttrContext, AttrOffset, FileRecord, NextAssignedCluster, AssignedClusters);
808 if (!NT_SUCCESS(Status))
809 {
810 DPRINT1("Error: Unable to add data run!\n");
811 return Status;
812 }
813
814 /* AddRun() places the next run after HighestVCN only once the
815 * attribute owns clusters, so count these before looping */
816 AttrContext->pRecord->NonResident.AllocatedSize += (ULONGLONG)AssignedClusters * BytesPerCluster;
817
818 ClustersNeeded -= AssignedClusters;
819 DesiredCluster = NextAssignedCluster + AssignedClusters;
820 }
821
822 DestinationAttribute->NonResident.AllocatedSize = AttrContext->pRecord->NonResident.AllocatedSize;
823 DestinationAttribute->NonResident.HighestVCN = AttrContext->pRecord->NonResident.HighestVCN;
824
825 return STATUS_SUCCESS;
826}
827
844{
846 ULONG BytesPerCluster = Vcb->NtfsInfo.BytesPerCluster;
847 PFILE_RECORD_HEADER FileRecord;
848 PNTFS_ATTR_CONTEXT DataContext;
849 PNTFS_ATTR_RECORD DestinationAttribute;
850 ULONG AttributeOffset;
851 ULONGLONG NeededSize = 0;
853
854 FileRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
855 if (FileRecord == NULL)
857
858 /* Paging writes look the runs up again; keep them out while the tail goes */
860
861 Status = ReadFileRecord(Vcb, Fcb->MFTIndex, FileRecord);
862 if (!NT_SUCCESS(Status))
863 goto Quit;
864
865 Status = FindAttribute(Vcb, FileRecord, AttributeData, Fcb->Stream, wcslen(Fcb->Stream), &DataContext, &AttributeOffset);
866 if (!NT_SUCCESS(Status))
867 goto Quit;
868
869 if (DataContext->pRecord->IsNonResident)
870 NeededSize = ROUND_UP(DataContext->pRecord->NonResident.DataSize, BytesPerCluster);
871
872 /* Compressed and sparse streams round their allocation differently; leave them be */
873 if (DataContext->pRecord->IsNonResident &&
874 !(DataContext->pRecord->Flags & (NTFS_ATTR_COMPRESSION_MASK | NTFS_ATTR_SPARSE)) &&
875 DataContext->pRecord->NonResident.AllocatedSize > NeededSize)
876 {
878 DataContext,
879 AttributeOffset,
880 FileRecord,
881 (ULONG)((DataContext->pRecord->NonResident.AllocatedSize - NeededSize) / BytesPerCluster));
882 if (NT_SUCCESS(Status))
883 {
884 DestinationAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttributeOffset);
885 DestinationAttribute->NonResident.AllocatedSize = NeededSize;
886
887 Status = UpdateFileRecord(Vcb, Fcb->MFTIndex, FileRecord);
888 if (NT_SUCCESS(Status))
889 Fcb->RFCB.AllocationSize.QuadPart = NeededSize;
890 }
891 }
892
893 ReleaseAttributeContext(DataContext);
894
895Quit:
897 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord);
898
899 return Status;
900}
901
927 _In_ BOOLEAN CaseSensitive,
929{
931 ULONG BytesPerCluster = Vcb->NtfsInfo.BytesPerCluster;
932 PFILE_RECORD_HEADER FileRecord;
933 PNTFS_ATTR_CONTEXT DataContext;
934 PNTFS_ATTR_RECORD DestinationAttribute;
935 ULONG AttributeOffset;
936 ULONGLONG CurrentSize;
937 BOOLEAN Changed = FALSE;
939
940 ASSERT(AllocationSize % BytesPerCluster == 0);
941
942 FileRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
943 if (FileRecord == NULL)
945
946 Status = ReadFileRecord(Vcb, Fcb->MFTIndex, FileRecord);
947 if (!NT_SUCCESS(Status))
948 goto Quit;
949
950 Status = FindAttribute(Vcb, FileRecord, AttributeData, Fcb->Stream, wcslen(Fcb->Stream), &DataContext, &AttributeOffset);
951 if (!NT_SUCCESS(Status))
952 goto Quit;
953
954 /* Resident data lives in the record, and compressed or sparse streams
955 * round their allocation differently; there is nothing to do for those */
956 if (!DataContext->pRecord->IsNonResident ||
957 (DataContext->pRecord->Flags & (NTFS_ATTR_COMPRESSION_MASK | NTFS_ATTR_SPARSE)))
958 {
959 ReleaseAttributeContext(DataContext);
960 goto Quit;
961 }
962
963 AllocationSize = max(AllocationSize, ROUND_UP(DataContext->pRecord->NonResident.DataSize, BytesPerCluster));
964 CurrentSize = DataContext->pRecord->NonResident.AllocatedSize;
965
966 if (AllocationSize < CurrentSize)
967 {
969
970 NewSize.QuadPart = AllocationSize;
971 if (!MmCanFileBeTruncated(FileObject->SectionObjectPointer, &NewSize))
972 {
973 ReleaseAttributeContext(DataContext);
975 goto Quit;
976 }
977 }
978
979 /* Paging writes look the runs up again, keep them out while those change */
981
982 if (AllocationSize > CurrentSize)
983 {
984 Status = NtfsExtendAllocation(Vcb, DataContext, AttributeOffset, FileRecord, AllocationSize);
985 Changed = NT_SUCCESS(Status);
986 }
987 else if (AllocationSize < CurrentSize)
988 {
990 DataContext,
991 AttributeOffset,
992 FileRecord,
993 (ULONG)((CurrentSize - AllocationSize) / BytesPerCluster));
994 if (NT_SUCCESS(Status))
995 {
996 DestinationAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttributeOffset);
997 DestinationAttribute->NonResident.AllocatedSize = AllocationSize;
998 Changed = TRUE;
999 }
1000 }
1001
1002 if (Changed)
1003 {
1004 Status = UpdateFileRecord(Vcb, Fcb->MFTIndex, FileRecord);
1005 if (NT_SUCCESS(Status))
1007 }
1008
1010 ReleaseAttributeContext(DataContext);
1011
1012 if (Changed && NT_SUCCESS(Status))
1013 {
1015
1016 /* The directory entries carry the allocation too */
1018 FileRecord,
1019 Fcb->MFTIndex,
1021 CaseSensitive);
1022 }
1023
1024Quit:
1025 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord);
1026
1027 return Status;
1028}
1029
1066 PNTFS_ATTR_CONTEXT AttrContext,
1067 ULONG AttrOffset,
1068 PFILE_RECORD_HEADER FileRecord,
1070{
1072 ULONG BytesPerCluster = Vcb->NtfsInfo.BytesPerCluster;
1073 ULONGLONG AllocationSize = ROUND_UP(DataSize->QuadPart, BytesPerCluster);
1074 PNTFS_ATTR_RECORD DestinationAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
1075 ULONG ExistingClusters = AttrContext->pRecord->NonResident.AllocatedSize / BytesPerCluster;
1076
1077 ASSERT(AttrContext->pRecord->IsNonResident);
1078
1079 // do we need to increase the allocation size?
1080 if (AttrContext->pRecord->NonResident.AllocatedSize < AllocationSize)
1081 {
1082 Status = NtfsExtendAllocation(Vcb, AttrContext, AttrOffset, FileRecord, AllocationSize);
1083 if (!NT_SUCCESS(Status))
1084 return Status;
1085 }
1086 else if (AttrContext->pRecord->NonResident.AllocatedSize > AllocationSize &&
1087 (ULONGLONG)DataSize->QuadPart < AttrContext->pRecord->NonResident.DataSize)
1088 {
1089 // shrink allocation size
1090 ULONG ClustersToFree = ExistingClusters - (AllocationSize / BytesPerCluster);
1091 Status = FreeClusters(Vcb, AttrContext, AttrOffset, FileRecord, ClustersToFree);
1092 AttrContext->pRecord->NonResident.AllocatedSize = AllocationSize;
1093 }
1094
1095 // TODO: is the file compressed, encrypted, or sparse?
1096
1097 AttrContext->pRecord->NonResident.DataSize = DataSize->QuadPart;
1098 AttrContext->pRecord->NonResident.InitializedSize = DataSize->QuadPart;
1099
1100 DestinationAttribute->NonResident.AllocatedSize = AttrContext->pRecord->NonResident.AllocatedSize;
1101 DestinationAttribute->NonResident.DataSize = DataSize->QuadPart;
1102 DestinationAttribute->NonResident.InitializedSize = DataSize->QuadPart;
1103
1104 /* AddRun() and FreeClusters() keep HighestVCN on the last VCN the runs
1105 * describe, holes included, so it's carried over rather than recomputed */
1106 DestinationAttribute->NonResident.HighestVCN = AttrContext->pRecord->NonResident.HighestVCN;
1107
1108 DPRINT("Allocated Size: %I64u\n", DestinationAttribute->NonResident.AllocatedSize);
1109
1110 return Status;
1111}
1112
1149 PNTFS_ATTR_CONTEXT AttrContext,
1150 ULONG AttrOffset,
1151 PFILE_RECORD_HEADER FileRecord,
1153{
1155
1156 // find the next attribute
1157 ULONG NextAttributeOffset = AttrOffset + AttrContext->pRecord->Length;
1158 PNTFS_ATTR_RECORD NextAttribute = (PNTFS_ATTR_RECORD)((PCHAR)FileRecord + NextAttributeOffset);
1159
1160 ASSERT(!AttrContext->pRecord->IsNonResident);
1161
1162 //NtfsDumpFileAttributes(Vcb, FileRecord);
1163
1164 // Do we need to increase the data length?
1165 if (DataSize->QuadPart > AttrContext->pRecord->Resident.ValueLength)
1166 {
1167 // There's usually padding at the end of a record. Do we need to extend past it?
1168 ULONG MaxValueLength = AttrContext->pRecord->Length - AttrContext->pRecord->Resident.ValueOffset;
1169 if (MaxValueLength < (ULONGLONG)DataSize->QuadPart)
1170 {
1171 // If this is the last attribute, we could move the end marker to the very end of the file record
1172 MaxValueLength += Vcb->NtfsInfo.BytesPerFileRecord - NextAttributeOffset - (sizeof(ULONG) * 2);
1173
1174 if (MaxValueLength < (ULONGLONG)DataSize->QuadPart || NextAttribute->Type != AttributeEnd)
1175 {
1176 // convert attribute to non-resident
1177 PNTFS_ATTR_RECORD Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
1178 PNTFS_ATTR_RECORD NewRecord;
1179 LARGE_INTEGER AttribDataSize;
1180 PVOID AttribData;
1181 ULONG NewRecordLength;
1182 ULONG EndAttributeOffset;
1183 ULONG LengthWritten;
1184
1185 DPRINT("Converting attribute to non-resident.\n");
1186
1187 AttribDataSize.QuadPart = AttrContext->pRecord->Resident.ValueLength;
1188
1189 // Is there existing data we need to back-up?
1190 if (AttribDataSize.QuadPart > 0)
1191 {
1192 AttribData = ExAllocatePoolWithTag(NonPagedPool, AttribDataSize.QuadPart, TAG_NTFS);
1193 if (AttribData == NULL)
1194 {
1195 DPRINT1("ERROR: Couldn't allocate memory for attribute data. Can't migrate to non-resident!\n");
1197 }
1198
1199 // read data to temp buffer
1200 Status = ReadAttribute(Vcb, AttrContext, 0, AttribData, AttribDataSize.QuadPart);
1201 if (!NT_SUCCESS(Status))
1202 {
1203 DPRINT1("ERROR: Unable to read attribute before migrating!\n");
1204 ExFreePoolWithTag(AttribData, TAG_NTFS);
1205 return Status;
1206 }
1207 }
1208
1209 // Start by turning this attribute into a 0-length, non-resident attribute, then enlarge it.
1210
1211 // The size of a 0-length, non-resident attribute will be 0x41 + the size of the attribute name, aligned to an 8-byte boundary
1212 NewRecordLength = ALIGN_UP_BY(0x41 + (AttrContext->pRecord->NameLength * sizeof(WCHAR)), ATTR_RECORD_ALIGNMENT);
1213
1214 // Create a new attribute record that will store the 0-length, non-resident attribute
1215 NewRecord = ExAllocatePoolWithTag(NonPagedPool, NewRecordLength, TAG_NTFS);
1216
1217 // Zero out the NonResident structure
1218 RtlZeroMemory(NewRecord, NewRecordLength);
1219
1220 // Copy the data that's common to both non-resident and resident attributes
1221 RtlCopyMemory(NewRecord, AttrContext->pRecord, FIELD_OFFSET(NTFS_ATTR_RECORD, Resident.ValueLength));
1222
1223 // The name follows the non-resident header, not the resident one
1224 NewRecord->NameOffset = FIELD_OFFSET(NTFS_ATTR_RECORD, NonResident.CompressedSize);
1225
1226 // if there's a name
1227 if (AttrContext->pRecord->NameLength != 0)
1228 {
1229 // copy the name
1230 // An attribute name will be located at offset 0x18 for a resident attribute, 0x40 for non-resident
1231 RtlCopyMemory((PCHAR)((ULONG_PTR)NewRecord + 0x40),
1232 (PCHAR)((ULONG_PTR)AttrContext->pRecord + 0x18),
1233 AttrContext->pRecord->NameLength * sizeof(WCHAR));
1234 }
1235
1236 // update the mapping pairs offset, which will be 0x40 (size of a non-resident header) + length in bytes of the name
1237 NewRecord->NonResident.MappingPairsOffset = 0x40 + (AttrContext->pRecord->NameLength * sizeof(WCHAR));
1238
1239 // update the end of the file record
1240 // calculate position of end markers (1 byte for empty data run)
1241 EndAttributeOffset = AttrOffset + NewRecord->NonResident.MappingPairsOffset + 1;
1242 EndAttributeOffset = ALIGN_UP_BY(EndAttributeOffset, ATTR_RECORD_ALIGNMENT);
1243
1244 // Update the length
1245 NewRecord->Length = EndAttributeOffset - AttrOffset;
1246
1247 ASSERT(NewRecord->Length == NewRecordLength);
1248
1249 // Copy the new attribute record into the file record
1250 RtlCopyMemory(Destination, NewRecord, NewRecord->Length);
1251
1252 // Update the file record end
1253 SetFileRecordEnd(FileRecord,
1254 (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + EndAttributeOffset),
1256
1257 // Initialize the MCB, potentially catch an exception
1258 _SEH2_TRY
1259 {
1260 FsRtlInitializeLargeMcb(&AttrContext->DataRunsMCB, NonPagedPool);
1261 }
1263 {
1264 DPRINT1("Unable to create LargeMcb!\n");
1265 if (AttribDataSize.QuadPart > 0)
1266 ExFreePoolWithTag(AttribData, TAG_NTFS);
1267 ExFreePoolWithTag(NewRecord, TAG_NTFS);
1269 } _SEH2_END;
1270
1271 // Mark the attribute as non-resident (we wait until after we know the LargeMcb was initialized)
1272 NewRecord->IsNonResident = Destination->IsNonResident = 1;
1273
1274 // Update file record on disk
1275 Status = UpdateFileRecord(Vcb, AttrContext->FileMFTIndex, FileRecord);
1276 if (!NT_SUCCESS(Status))
1277 {
1278 DPRINT1("ERROR: Couldn't update file record to continue migration!\n");
1279 if (AttribDataSize.QuadPart > 0)
1280 ExFreePoolWithTag(AttribData, TAG_NTFS);
1281 ExFreePoolWithTag(NewRecord, TAG_NTFS);
1282 return Status;
1283 }
1284
1285 // Now we need to free the old copy of the attribute record in the context and replace it with the new one
1286 ExFreePoolWithTag(AttrContext->pRecord, TAG_NTFS);
1287 AttrContext->pRecord = NewRecord;
1288
1289 // Now we can treat the attribute as non-resident and enlarge it normally
1290 Status = SetNonResidentAttributeDataLength(Vcb, AttrContext, AttrOffset, FileRecord, DataSize);
1291 if (!NT_SUCCESS(Status))
1292 {
1293 DPRINT1("ERROR: Unable to migrate resident attribute!\n");
1294 if (AttribDataSize.QuadPart > 0)
1295 ExFreePoolWithTag(AttribData, TAG_NTFS);
1296 return Status;
1297 }
1298
1299 // restore the back-up attribute, if we made one
1300 if (AttribDataSize.QuadPart > 0)
1301 {
1302 Status = WriteAttribute(Vcb, AttrContext, 0, AttribData, AttribDataSize.QuadPart, &LengthWritten, FileRecord);
1303 if (!NT_SUCCESS(Status))
1304 {
1305 DPRINT1("ERROR: Unable to write attribute data to non-resident clusters during migration!\n");
1306 // TODO: Reverse migration so no data is lost
1307 ExFreePoolWithTag(AttribData, TAG_NTFS);
1308 return Status;
1309 }
1310
1311 ExFreePoolWithTag(AttribData, TAG_NTFS);
1312 }
1313 }
1314 }
1315 }
1316
1317 // set the new length of the resident attribute (if we didn't migrate it)
1318 if (!AttrContext->pRecord->IsNonResident)
1319 return InternalSetResidentAttributeLength(Vcb, AttrContext, FileRecord, AttrOffset, DataSize->LowPart);
1320
1321 return STATUS_SUCCESS;
1322}
1323
1360static
1365 ULONG Length,
1366 PNTFS_IO_RUN_LIST RunList,
1367 PULONG MappedLength)
1368{
1369 ULONGLONG LastLCN = 0;
1370 ULONGLONG CurrentOffset = 0;
1371 PUCHAR DataRun;
1372 LONGLONG DataRunOffset;
1373 ULONGLONG DataRunLength = 0;
1374 LONGLONG DataRunStartLCN = -1;
1375 ULONG RunLength;
1376 ULONG Mapped = 0;
1377 ULONG UsedBufferSize;
1379
1380 //TEMPTEMP
1381 PUCHAR TempBuffer;
1382
1383 *MappedLength = 0;
1384
1385 // This will be rewritten in the next iteration to just use the DataRuns MCB directly
1386 TempBuffer = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
1387 if (TempBuffer == NULL)
1388 {
1389 DPRINT1("Not enough memory!\n");
1391 }
1392 RtlZeroMemory(TempBuffer, Vcb->NtfsInfo.BytesPerFileRecord);
1393
1394 ConvertLargeMCBToDataRuns(&Context->DataRunsMCB,
1395 Context->pRecord->NonResident.HighestVCN + 1,
1396 TempBuffer,
1397 Vcb->NtfsInfo.BytesPerFileRecord,
1398 &UsedBufferSize);
1399
1400 DataRun = TempBuffer;
1401
1402 // I. Walk forward to the data run that holds Offset.
1403 while (1)
1404 {
1405 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
1406 if (DataRunOffset != (LONGLONG)-1)
1407 {
1408 /* Normal data run. */
1409 DataRunStartLCN = LastLCN + DataRunOffset;
1410 LastLCN = DataRunStartLCN;
1411 }
1412 else
1413 {
1414 /* Sparse data run. */
1415 DataRunStartLCN = -1;
1416 }
1417
1418 if (Offset >= CurrentOffset &&
1419 Offset < CurrentOffset + (DataRunLength * Vcb->NtfsInfo.BytesPerCluster))
1420 {
1421 break;
1422 }
1423
1424 if (*DataRun == 0)
1425 {
1426 /* Offset is past the last cluster the attribute has allocated. */
1428 goto Cleanup;
1429 }
1430
1431 CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
1432 }
1433
1434 // II. Collect the runs. The first is special only in that the request
1435 // usually starts partway into it.
1436
1437 RunLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset), Length);
1438
1439 Status = NtfsAddIoRun(RunList,
1440 (DataRunStartLCN == -1) ? NTFS_SPARSE_LBO :
1441 DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster + Offset - CurrentOffset,
1442 RunLength);
1443 if (!NT_SUCCESS(Status))
1444 goto Cleanup;
1445
1446 Length -= RunLength;
1447 Mapped += RunLength;
1448
1449 /* Did that consume the rest of this data run? */
1450 if (RunLength == DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset))
1451 {
1452 /* Never decode past the terminator; the caller gets a short map */
1453 if (*DataRun == 0)
1454 goto Cleanup;
1455
1456 CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
1457 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
1458 if (DataRunOffset != (LONGLONG)-1)
1459 {
1460 DataRunStartLCN = LastLCN + DataRunOffset;
1461 LastLCN = DataRunStartLCN;
1462 }
1463 else
1464 DataRunStartLCN = -1;
1465 }
1466
1467 while (Length > 0)
1468 {
1469 RunLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster, Length);
1470
1471 Status = NtfsAddIoRun(RunList,
1472 (DataRunStartLCN == -1) ? NTFS_SPARSE_LBO :
1473 DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster,
1474 RunLength);
1475 if (!NT_SUCCESS(Status))
1476 goto Cleanup;
1477
1478 Length -= RunLength;
1479 Mapped += RunLength;
1480
1481 /* We satisfied the request, but there is still data in this run. */
1482 if (Length == 0 && RunLength != DataRunLength * Vcb->NtfsInfo.BytesPerCluster)
1483 break;
1484
1485 /* Last run the attribute has, so the caller gets a short map */
1486 if (*DataRun == 0)
1487 break;
1488
1489 CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
1490 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
1491 if (DataRunOffset != (LONGLONG)-1)
1492 {
1493 /* Normal data run. */
1494 DataRunStartLCN = LastLCN + DataRunOffset;
1495 LastLCN = DataRunStartLCN;
1496 }
1497 else
1498 {
1499 /* Sparse data run. */
1500 DataRunStartLCN = -1;
1501 }
1502 }
1503
1504Cleanup:
1505 Context->CacheRun = DataRun;
1506 Context->CacheRunOffset = Offset + Mapped;
1507 Context->CacheRunStartLCN = DataRunStartLCN;
1508 Context->CacheRunLength = DataRunLength;
1509 Context->CacheRunLastLCN = LastLCN;
1510 Context->CacheRunCurrentOffset = CurrentOffset;
1511
1512 //TEMPTEMP
1513 ExFreePoolWithTag(TempBuffer, TAG_NTFS);
1514
1515 *MappedLength = Mapped;
1516
1517 return Status;
1518}
1519
1555ULONG
1559 PCHAR Buffer,
1560 ULONG Length,
1561 PIRP Irp)
1562{
1563 ULONG AlreadyRead = 0;
1564 ULONG Transferred = 0;
1566 NTFS_IO_RUN_LIST RunList;
1567
1568 if (!Context->pRecord->IsNonResident)
1569 {
1570 // We need to truncate Offset to a ULONG for pointer arithmetic
1571 // The check below should ensure that Offset is well within the range of 32 bits
1572 ULONG LittleOffset = (ULONG)Offset;
1573
1574 // Ensure that offset isn't beyond the end of the attribute
1575 if (Offset > Context->pRecord->Resident.ValueLength)
1576 return 0;
1577 if (Offset + Length > Context->pRecord->Resident.ValueLength)
1578 Length = (ULONG)(Context->pRecord->Resident.ValueLength - Offset);
1579
1580 RtlCopyMemory(Buffer, (PVOID)((ULONG_PTR)Context->pRecord + Context->pRecord->Resident.ValueOffset + LittleOffset), Length);
1581 return Length;
1582 }
1583
1584 /*
1585 * Non-resident attribute: map the requested range onto the volume, then
1586 * issue every run it spans in a single parallel batch.
1587 */
1588
1589 NtfsInitIoRunList(&RunList);
1590
1591 Status = NtfsMapAttributeRuns(Vcb, Context, Offset, Length, &RunList, &AlreadyRead);
1592
1593 if (NT_SUCCESS(Status) && AlreadyRead != 0)
1594 {
1595 Status = NtfsPerformIrpIoRuns(Vcb->StorageDevice,
1597 Vcb->NtfsInfo.BytesPerSector,
1598 Irp,
1599 (PUCHAR)Buffer,
1600 &RunList,
1601 FALSE,
1602 &Transferred);
1603 }
1604
1605 NtfsFreeIoRunList(&RunList);
1606
1607 if (!NT_SUCCESS(Status))
1608 {
1609 /* Our callers read the return value as a byte count and treat zero as
1610 * failure, so we cannot report a partial transfer here. */
1611 DPRINT1("Read failure (Status %lx)\n", Status);
1612 return 0;
1613 }
1614
1615 if (Transferred < AlreadyRead)
1616 {
1617 DPRINT1("Short read: expected %lu bytes, got %lu\n", AlreadyRead, Transferred);
1618 AlreadyRead = Transferred;
1619 }
1620
1621 return AlreadyRead;
1622}
1623
1624ULONG
1628 PCHAR Buffer,
1629 ULONG Length)
1630{
1632}
1633
1634
1680 const PUCHAR Buffer,
1681 ULONG Length,
1682 PULONG RealLengthWritten,
1683 PFILE_RECORD_HEADER FileRecord,
1684 PIRP Irp)
1685{
1686 ULONG MappedLength = 0;
1687 ULONG Transferred = 0;
1689 BOOLEAN FileRecordAllocated = FALSE;
1690 NTFS_IO_RUN_LIST RunList;
1691
1692
1693 DPRINT("WriteAttribute(%p, %p, %I64u, %p, %lu, %p, %p)\n", Vcb, Context, Offset, Buffer, Length, RealLengthWritten, FileRecord);
1694
1695 *RealLengthWritten = 0;
1696 NtfsInitIoRunList(&RunList);
1697
1698 // is this a resident attribute?
1699 if (!Context->pRecord->IsNonResident)
1700 {
1701 ULONG AttributeOffset;
1702 PNTFS_ATTR_CONTEXT FoundContext;
1704
1705 // Ensure requested data is within the bounds of the attribute
1706 ASSERT(Offset + Length <= Context->pRecord->Resident.ValueLength);
1707
1708 if (Offset + Length > Context->pRecord->Resident.ValueLength)
1709 {
1710 DPRINT1("DRIVER ERROR: Attribute is too small!\n");
1712 }
1713
1714 // Do we need to read the file record?
1715 if (FileRecord == NULL)
1716 {
1717 FileRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
1718 if (!FileRecord)
1719 {
1720 DPRINT1("Error: Couldn't allocate file record!\n");
1721 return STATUS_NO_MEMORY;
1722 }
1723
1724 FileRecordAllocated = TRUE;
1725
1726 // read the file record
1727 ReadFileRecord(Vcb, Context->FileMFTIndex, FileRecord);
1728 }
1729
1730 // find where to write the attribute data to
1731 Status = FindAttribute(Vcb, FileRecord,
1732 Context->pRecord->Type,
1733 (PCWSTR)((ULONG_PTR)Context->pRecord + Context->pRecord->NameOffset),
1734 Context->pRecord->NameLength,
1735 &FoundContext,
1736 &AttributeOffset);
1737
1738 if (!NT_SUCCESS(Status))
1739 {
1740 DPRINT1("ERROR: Couldn't find matching attribute!\n");
1741 if(FileRecordAllocated)
1742 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord);
1743 return Status;
1744 }
1745
1746 Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttributeOffset);
1747
1748 DPRINT("Offset: %I64u, AttributeOffset: %u, ValueOffset: %u\n", Offset, AttributeOffset, Context->pRecord->Resident.ValueLength);
1749
1750 // Will we be writing past the end of the allocated file record?
1751 if (Offset + Length + AttributeOffset + Context->pRecord->Resident.ValueOffset > Vcb->NtfsInfo.BytesPerFileRecord)
1752 {
1753 DPRINT1("DRIVER ERROR: Data being written extends past end of file record!\n");
1754 ReleaseAttributeContext(FoundContext);
1755 if (FileRecordAllocated)
1756 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord);
1758 }
1759
1760 // copy the data being written into the file record. We cast Offset to ULONG, which is safe because it's range has been verified.
1761 RtlCopyMemory((PCHAR)((ULONG_PTR)Destination + Context->pRecord->Resident.ValueOffset + (ULONG)Offset), Buffer, Length);
1762
1763 Status = UpdateFileRecord(Vcb, Context->FileMFTIndex, FileRecord);
1764
1765 // Update the context's copy of the resident attribute
1766 ASSERT(Context->pRecord->Length == Destination->Length);
1767 RtlCopyMemory((PVOID)Context->pRecord, Destination, Context->pRecord->Length);
1768
1769 ReleaseAttributeContext(FoundContext);
1770 if (FileRecordAllocated)
1771 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord);
1772
1773 if (NT_SUCCESS(Status))
1774 *RealLengthWritten = Length;
1775
1776 return Status;
1777 }
1778
1779 // This is a non-resident attribute: map the requested range onto the
1780 // volume, then issue every run it spans in a single parallel batch.
1781
1782 Status = NtfsMapAttributeRuns(Vcb, Context, Offset, Length, &RunList, &MappedLength);
1783
1784 if (NT_SUCCESS(Status) && MappedLength < Length)
1785 {
1786 // The attribute's allocation ends before the request does.
1787 // TODO: assign new clusters to the end of the file.
1788 // [We can reach here by creating a new file record when the MFT isn't large enough]
1789 DPRINT1("FIXME: Attribute needs to be enlarged (mapped %lu of %lu bytes).\n",
1790 MappedLength, Length);
1792 }
1793
1794 if (NT_SUCCESS(Status))
1795 {
1796 // Writing into a hole would mean allocating clusters, which we cannot
1797 // do here; NtfsPerformIrpIoRuns() rejects sparse runs on IRP_MJ_WRITE.
1798 Status = NtfsPerformIrpIoRuns(Vcb->StorageDevice,
1800 Vcb->NtfsInfo.BytesPerSector,
1801 Irp,
1802 Buffer,
1803 &RunList,
1804 FALSE,
1805 &Transferred);
1806 }
1807
1808 NtfsFreeIoRunList(&RunList);
1809
1810 if (!NT_SUCCESS(Status))
1811 {
1812 DPRINT1("Write failure (Status %lx)\n", Status);
1813 *RealLengthWritten = 0;
1814 return Status;
1815 }
1816
1817 *RealLengthWritten = Transferred;
1818
1819 if (Transferred < Length)
1820 {
1821 DPRINT1("Short write: expected %lu bytes, wrote %lu\n", Length, Transferred);
1823 }
1824
1825 return Status;
1826}
1827
1832 const PUCHAR Buffer,
1833 ULONG Length,
1834 PULONG RealLengthWritten,
1835 PFILE_RECORD_HEADER FileRecord)
1836{
1838 Context,
1839 Offset,
1840 Buffer,
1841 Length,
1842 RealLengthWritten,
1843 FileRecord,
1844 NULL);
1845}
1846
1864static
1867{
1868 PFILE_RECORD_HEADER FileRecord;
1869 PNTFS_ATTR_CONTEXT DataContext;
1872 ULONG Read;
1873
1874 FileRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
1875 if (FileRecord == NULL)
1876 {
1878 }
1879
1880 Status = ReadFileRecord(Vcb, NTFS_FILE_UPCASE, FileRecord);
1881 if (!NT_SUCCESS(Status))
1882 {
1883 DPRINT1("Couldn't read the $UpCase file record!\n");
1884 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord);
1885 return Status;
1886 }
1887
1888 Status = FindAttribute(Vcb, FileRecord, AttributeData, L"", 0, &DataContext, NULL);
1889 if (!NT_SUCCESS(Status))
1890 {
1891 DPRINT1("$UpCase has no data stream!\n");
1892 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord);
1893 return Status;
1894 }
1895
1896 Size = AttributeDataLength(DataContext->pRecord);
1897 if (Size != (0x10000 * sizeof(WCHAR)))
1898 {
1899 DPRINT1("$UpCase is %I64u bytes, expected %u\n", Size, 0x10000 * sizeof(WCHAR));
1900 ReleaseAttributeContext(DataContext);
1901 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord);
1903 }
1904
1906 if (Vcb->UpCaseTable == NULL)
1907 {
1908 ReleaseAttributeContext(DataContext);
1909 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord);
1911 }
1912
1913 Read = ReadAttribute(Vcb, DataContext, 0, (PCHAR)Vcb->UpCaseTable, (ULONG)Size);
1914
1915 ReleaseAttributeContext(DataContext);
1916 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord);
1917
1918 if (Read != Size)
1919 {
1920 DPRINT1("Short read of $UpCase: %lu of %I64u bytes\n", Read, Size);
1921 ExFreePoolWithTag(Vcb->UpCaseTable, TAG_NTFS);
1922 Vcb->UpCaseTable = NULL;
1923 return STATUS_UNSUCCESSFUL;
1924 }
1925
1926 return STATUS_SUCCESS;
1927}
1928
1931{
1932 return NtfsReadUpCaseTable(Vcb);
1933}
1934
1939{
1941
1942 DPRINT("ReadFileRecord(%p, %I64x, %p)\n", Vcb, index, file);
1943
1944 BytesRead = ReadAttribute(Vcb, Vcb->MFTContext, index * Vcb->NtfsInfo.BytesPerFileRecord, (PCHAR)file, Vcb->NtfsInfo.BytesPerFileRecord);
1945 if (BytesRead != Vcb->NtfsInfo.BytesPerFileRecord)
1946 {
1947 DPRINT1("ReadFileRecord failed: %I64u read, %lu expected\n", BytesRead, Vcb->NtfsInfo.BytesPerFileRecord);
1948 return STATUS_PARTIAL_COPY;
1949 }
1950
1951 /* Apply update sequence array fixups. */
1952 DPRINT("Sequence number: %u\n", file->SequenceNumber);
1953 return FixupUpdateSequenceArray(Vcb, &file->Ntfs);
1954}
1955
1956
1996 PFILE_RECORD_HEADER FileRecord,
1997 ULONGLONG MFTIndex,
1998 ULONG Flags,
1999 BOOLEAN CaseSensitive)
2000{
2002 PSTANDARD_INFORMATION StdInfo;
2005 ULONGLONG ParentMFTIndex;
2006 ULONG DirFlag;
2008
2009 if (Flags == 0)
2010 return STATUS_SUCCESS;
2011
2012 StdInfo = GetStandardInformationFromRecord(Vcb, FileRecord);
2013 FileName = GetBestFileNameFromRecord(Vcb, FileRecord);
2014 if (StdInfo == NULL || FileName == NULL)
2015 {
2016 DPRINT1("Record %I64u is missing $STANDARD_INFORMATION or $FILE_NAME\n", MFTIndex);
2018 }
2019
2020 DirFlag = (FileRecord->Flags & FRH_DIRECTORY) ? NTFS_FILE_TYPE_DIRECTORY : 0;
2021
2022 RtlZeroMemory(&Update, sizeof(Update));
2023 Update.Flags = Flags;
2024
2026 {
2027 Update.CreationTime = StdInfo->CreationTime;
2028 Update.LastWriteTime = StdInfo->LastWriteTime;
2029 Update.ChangeTime = StdInfo->ChangeTime;
2030 Update.LastAccessTime = StdInfo->LastAccessTime;
2031
2032 FileName->CreationTime = StdInfo->CreationTime;
2033 FileName->LastWriteTime = StdInfo->LastWriteTime;
2034 FileName->ChangeTime = StdInfo->ChangeTime;
2035 FileName->LastAccessTime = StdInfo->LastAccessTime;
2036 }
2037
2039 {
2040 Update.FileAttributes = StdInfo->FileAttribute | DirFlag;
2041 FileName->FileAttributes = Update.FileAttributes;
2042 }
2043
2045 {
2046 /* Sizes come from the unnamed $DATA attribute, the same way the times
2047 * come from $STANDARD_INFORMATION: one source of truth per field. */
2048 Update.DataSize = NtfsGetFileSize(Vcb, FileRecord, L"", 0, &Update.AllocatedSize);
2049
2050 FileName->DataSize = Update.DataSize;
2051 FileName->AllocatedSize = Update.AllocatedSize;
2052 }
2053
2054 /* Persist the $FILE_NAME just modified. Callers have no reason to write the record again,
2055 * and leaving it unwritten would let the copy in the parent index disagree with it. */
2056 Status = UpdateFileRecord(Vcb, MFTIndex, FileRecord);
2057 if (!NT_SUCCESS(Status))
2058 {
2059 DPRINT1("Failed to write file record %I64u (Status %lx)\n", MFTIndex, Status);
2060 return Status;
2061 }
2062
2063 /* Now the parent's index entry, which carries the same values */
2064 ParentMFTIndex = FileName->DirectoryFileReferenceNumber & NTFS_MFT_MASK;
2065
2066 Name.Buffer = FileName->Name;
2067 Name.Length = FileName->NameLength * sizeof(WCHAR);
2068 Name.MaximumLength = Name.Length;
2069
2070 return UpdateFileNameRecord(Vcb, ParentMFTIndex, &Name, FALSE, &Update, CaseSensitive);
2071}
2072
2080static
2083 ULONGLONG ParentMFTIndex,
2085 BOOLEAN DirSearch,
2087 BOOLEAN CaseSensitive)
2088{
2089 PFILE_RECORD_HEADER MftRecord;
2090 PNTFS_ATTR_CONTEXT IndexRootCtx;
2091 PINDEX_ROOT_ATTRIBUTE IndexRoot;
2092 PCHAR IndexRecord;
2093 PINDEX_ENTRY_ATTRIBUTE IndexEntry, IndexEntryEnd;
2095 ULONG CurrentEntry = 0;
2096
2097 DPRINT("UpdateFileNameRecord(%p, %I64d, %wZ, %s, %lx, %s)\n",
2098 Vcb,
2099 ParentMFTIndex,
2100 FileName,
2101 DirSearch ? "TRUE" : "FALSE",
2102 Update->Flags,
2103 CaseSensitive ? "TRUE" : "FALSE");
2104
2105 if (Update->Flags == 0)
2106 return STATUS_SUCCESS;
2107
2108 MftRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
2109 if (MftRecord == NULL)
2110 {
2112 }
2113
2114 Status = ReadFileRecord(Vcb, ParentMFTIndex, MftRecord);
2115 if (!NT_SUCCESS(Status))
2116 {
2117 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
2118 return Status;
2119 }
2120
2121 ASSERT(MftRecord->Ntfs.Type == NRH_FILE_TYPE);
2122 Status = FindAttribute(Vcb, MftRecord, AttributeIndexRoot, L"$I30", 4, &IndexRootCtx, NULL);
2123 if (!NT_SUCCESS(Status))
2124 {
2125 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
2126 return Status;
2127 }
2128
2129 IndexRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerIndexRecord, TAG_NTFS);
2130 if (IndexRecord == NULL)
2131 {
2132 ReleaseAttributeContext(IndexRootCtx);
2133 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
2135 }
2136
2137 Status = ReadAttribute(Vcb, IndexRootCtx, 0, IndexRecord, AttributeDataLength(IndexRootCtx->pRecord));
2138 if (!NT_SUCCESS(Status))
2139 {
2140 DPRINT1("ERROR: Failed to read Index Root!\n");
2141 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
2142 ReleaseAttributeContext(IndexRootCtx);
2143 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
2144 return Status;
2145 }
2146
2147 IndexRoot = (PINDEX_ROOT_ATTRIBUTE)IndexRecord;
2148 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)&IndexRoot->Header + IndexRoot->Header.FirstEntryOffset);
2149 // Index root is always resident.
2150 /* TotalSizeOfEntries is measured from the index header, not from the
2151 * start of the attribute, and FirstEntryOffset above already is. */
2152 IndexEntryEnd = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexRoot->Header + IndexRoot->Header.TotalSizeOfEntries);
2153
2154 DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb->NtfsInfo.BytesPerIndexRecord, IndexRoot->SizeOfEntry);
2155
2157 MftRecord,
2158 IndexRecord,
2159 IndexRoot->SizeOfEntry,
2160 IndexEntry,
2161 IndexEntryEnd,
2162 FileName,
2163 &CurrentEntry,
2164 &CurrentEntry,
2165 DirSearch,
2166 Update,
2167 CaseSensitive);
2168
2169 if (Status == STATUS_PENDING)
2170 {
2171 // we need to write the index root attribute back to disk
2172 ULONG LengthWritten;
2173 Status = WriteAttribute(Vcb, IndexRootCtx, 0, (PUCHAR)IndexRecord, AttributeDataLength(IndexRootCtx->pRecord), &LengthWritten, MftRecord);
2174 if (!NT_SUCCESS(Status))
2175 {
2176 DPRINT1("ERROR: Couldn't update Index Root!\n");
2177 }
2178
2179 }
2180
2181 ReleaseAttributeContext(IndexRootCtx);
2182 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
2183 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
2184
2185 return Status;
2186}
2187
2188/* The worker rewrites the directory record from a copy it reads, so it
2189 * must not overlap any other update of a directory index */
2192 ULONGLONG ParentMFTIndex,
2194 BOOLEAN DirSearch,
2196 BOOLEAN CaseSensitive)
2197{
2199
2201 ExAcquireResourceExclusiveLite(&Vcb->IndexResource, TRUE);
2202 Status = UpdateFileNameRecordWorker(Vcb, ParentMFTIndex, FileName, DirSearch, Update, CaseSensitive);
2203 ExReleaseResourceLite(&Vcb->IndexResource);
2205
2206 return Status;
2207}
2208
2216 PFILE_RECORD_HEADER MftRecord,
2217 PCHAR IndexRecord,
2218 ULONG IndexBlockSize,
2219 PINDEX_ENTRY_ATTRIBUTE FirstEntry,
2220 PINDEX_ENTRY_ATTRIBUTE LastEntry,
2222 PULONG StartEntry,
2223 PULONG CurrentEntry,
2224 BOOLEAN DirSearch,
2226 BOOLEAN CaseSensitive)
2227{
2229 ULONG RecordOffset;
2230 PINDEX_ENTRY_ATTRIBUTE IndexEntry;
2231 PNTFS_ATTR_CONTEXT IndexAllocationCtx;
2232 ULONGLONG IndexAllocationSize;
2233 PINDEX_BUFFER IndexBuffer;
2234
2235 DPRINT("UpdateIndexEntryFileName(%p, %p, %p, %lu, %p, %p, %wZ, %lu, %lu, %s, %lx, %s)\n",
2236 Vcb,
2237 MftRecord,
2238 IndexRecord,
2239 IndexBlockSize,
2240 FirstEntry,
2241 LastEntry,
2242 FileName,
2243 *StartEntry,
2244 *CurrentEntry,
2245 DirSearch ? "TRUE" : "FALSE",
2246 Update->Flags,
2247 CaseSensitive ? "TRUE" : "FALSE");
2248
2249 // find the index entry responsible for the file we're trying to update
2250 IndexEntry = FirstEntry;
2251 while (IndexEntry < LastEntry &&
2252 !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
2253 {
2254 if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) > NTFS_FILE_FIRST_USER_FILE &&
2255 *CurrentEntry >= *StartEntry &&
2256 IndexEntry->FileName.NameType != NTFS_FILE_NAME_DOS &&
2257 CompareFileName(Vcb, FileName, IndexEntry, DirSearch, CaseSensitive))
2258 {
2259 *StartEntry = *CurrentEntry;
2260
2262 {
2263 IndexEntry->FileName.DataSize = Update->DataSize;
2264 IndexEntry->FileName.AllocatedSize = Update->AllocatedSize;
2265 }
2266
2268 {
2269 IndexEntry->FileName.CreationTime = Update->CreationTime;
2270 IndexEntry->FileName.ChangeTime = Update->ChangeTime;
2271 IndexEntry->FileName.LastWriteTime = Update->LastWriteTime;
2272 IndexEntry->FileName.LastAccessTime = Update->LastAccessTime;
2273 }
2274
2276 {
2277 IndexEntry->FileName.FileAttributes = Update->FileAttributes;
2278 }
2279
2280 // indicate that the caller will still need to write the structure to the disk
2281 return STATUS_PENDING;
2282 }
2283
2284 (*CurrentEntry) += 1;
2285 ASSERT(IndexEntry->Length >= sizeof(INDEX_ENTRY_ATTRIBUTE));
2286 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length);
2287 }
2288
2289 /* If we're already browsing a subnode */
2290 if (IndexRecord == NULL)
2291 {
2293 }
2294
2295 /* If there's no subnode */
2296 if (!(IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE))
2297 {
2299 }
2300
2301 Status = FindAttribute(Vcb, MftRecord, AttributeIndexAllocation, L"$I30", 4, &IndexAllocationCtx, NULL);
2302 if (!NT_SUCCESS(Status))
2303 {
2304 DPRINT("Corrupted filesystem!\n");
2305 return Status;
2306 }
2307
2308 IndexAllocationSize = AttributeDataLength(IndexAllocationCtx->pRecord);
2310 for (RecordOffset = 0; RecordOffset < IndexAllocationSize; RecordOffset += IndexBlockSize)
2311 {
2312 ReadAttribute(Vcb, IndexAllocationCtx, RecordOffset, IndexRecord, IndexBlockSize);
2313 Status = FixupUpdateSequenceArray(Vcb, &((PFILE_RECORD_HEADER)IndexRecord)->Ntfs);
2314 if (!NT_SUCCESS(Status))
2315 {
2316 break;
2317 }
2318
2319 IndexBuffer = (PINDEX_BUFFER)IndexRecord;
2320 ASSERT(IndexBuffer->Ntfs.Type == NRH_INDX_TYPE);
2321 ASSERT(IndexBuffer->Header.AllocatedSize + FIELD_OFFSET(INDEX_BUFFER, Header) == IndexBlockSize);
2322 FirstEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexBuffer->Header + IndexBuffer->Header.FirstEntryOffset);
2323 LastEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexBuffer->Header + IndexBuffer->Header.TotalSizeOfEntries);
2324 ASSERT(LastEntry <= (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)IndexBuffer + IndexBlockSize));
2325
2327 NULL,
2328 NULL,
2329 0,
2330 FirstEntry,
2331 LastEntry,
2332 FileName,
2333 StartEntry,
2334 CurrentEntry,
2335 DirSearch,
2336 Update,
2337 CaseSensitive);
2338 if (Status == STATUS_PENDING)
2339 {
2340 // write the index record back to disk
2341 ULONG Written;
2342
2343 // first we need to update the fixup values for the index block
2344 Status = AddFixupArray(Vcb, &((PFILE_RECORD_HEADER)IndexRecord)->Ntfs);
2345 if (!NT_SUCCESS(Status))
2346 {
2347 DPRINT1("Error: Failed to update fixup sequence array!\n");
2348 break;
2349 }
2350
2351 Status = WriteAttribute(Vcb, IndexAllocationCtx, RecordOffset, (const PUCHAR)IndexRecord, IndexBlockSize, &Written, MftRecord);
2352 if (!NT_SUCCESS(Status))
2353 {
2354 DPRINT1("ERROR Performing write!\n");
2355 break;
2356 }
2357
2359 break;
2360 }
2361 if (NT_SUCCESS(Status))
2362 {
2363 break;
2364 }
2365 }
2366
2367 ReleaseAttributeContext(IndexAllocationCtx);
2368 return Status;
2369}
2370
2392 ULONGLONG MftIndex,
2393 PFILE_RECORD_HEADER FileRecord)
2394{
2397
2398 DPRINT("UpdateFileRecord(%p, 0x%I64x, %p)\n", Vcb, MftIndex, FileRecord);
2399
2400 // Add the fixup array to prepare the data for writing to disk
2401 AddFixupArray(Vcb, &FileRecord->Ntfs);
2402
2403 // write the file record to the master file table
2405 Vcb->MFTContext,
2406 MftIndex * Vcb->NtfsInfo.BytesPerFileRecord,
2407 (const PUCHAR)FileRecord,
2408 Vcb->NtfsInfo.BytesPerFileRecord,
2409 &BytesWritten,
2410 FileRecord);
2411
2412 if (!NT_SUCCESS(Status))
2413 {
2414 DPRINT1("UpdateFileRecord failed: %lu written, %lu expected\n", BytesWritten, Vcb->NtfsInfo.BytesPerFileRecord);
2415 }
2416
2417 // remove the fixup array (so the file record pointer can still be used)
2418 FixupUpdateSequenceArray(Vcb, &FileRecord->Ntfs);
2419
2420 return Status;
2421}
2422
2423
2427{
2428 USHORT *USA;
2429 USHORT USANumber;
2430 USHORT USACount;
2431 USHORT *Block;
2432
2433 USA = (USHORT*)((PCHAR)Record + Record->UsaOffset);
2434 USANumber = *(USA++);
2435 USACount = Record->UsaCount - 1; /* Exclude the USA Number. */
2436 Block = (USHORT*)((PCHAR)Record + Vcb->NtfsInfo.BytesPerSector - 2);
2437
2438 DPRINT("FixupUpdateSequenceArray(%p, %p)\nUSANumber: %u\tUSACount: %u\n", Vcb, Record, USANumber, USACount);
2439
2440 while (USACount)
2441 {
2442 if (*Block != USANumber)
2443 {
2444 DPRINT1("Mismatch with USA: %u read, %u expected\n" , *Block, USANumber);
2445 return STATUS_UNSUCCESSFUL;
2446 }
2447 *Block = *(USA++);
2448 Block = (USHORT*)((PCHAR)Block + Vcb->NtfsInfo.BytesPerSector);
2449 USACount--;
2450 }
2451
2452 return STATUS_SUCCESS;
2453}
2454
2483 PDEVICE_EXTENSION DeviceExt,
2484 PULONGLONG DestinationIndex,
2485 BOOLEAN CanWait)
2486{
2488 ULONGLONG MftIndex;
2490 ULONGLONG BitmapDataSize;
2491 ULONGLONG AttrBytesRead;
2494 ULONG LengthWritten;
2495 PNTFS_ATTR_CONTEXT BitmapContext;
2496 LARGE_INTEGER BitmapBits;
2497 UCHAR SystemReservedBits;
2498
2499 DPRINT("AddNewMftEntry(%p, %p, %p, %s)\n", FileRecord, DeviceExt, DestinationIndex, CanWait ? "TRUE" : "FALSE");
2500
2501 // First, we have to read the mft's $Bitmap attribute
2502
2503 // Find the attribute
2504 Status = FindAttribute(DeviceExt, DeviceExt->MasterFileTable, AttributeBitmap, L"", 0, &BitmapContext, NULL);
2505 if (!NT_SUCCESS(Status))
2506 {
2507 DPRINT1("ERROR: Couldn't find $Bitmap attribute of master file table!\n");
2508 return Status;
2509 }
2510
2511 // Get size of bitmap
2512 BitmapDataSize = AttributeDataLength(BitmapContext->pRecord);
2513
2514 // RtlInitializeBitmap wants a ULONG-aligned pointer, and wants the memory passed to it to be a ULONG-multiple
2515 // Allocate a buffer for the $Bitmap attribute plus enough to ensure we can get a ULONG-aligned pointer
2516 BitmapBuffer = ExAllocatePoolWithTag(NonPagedPool, BitmapDataSize + sizeof(ULONG), TAG_NTFS);
2517 if (!BitmapBuffer)
2518 {
2519 ReleaseAttributeContext(BitmapContext);
2521 }
2522 RtlZeroMemory(BitmapBuffer, BitmapDataSize + sizeof(ULONG));
2523
2524 // Get a ULONG-aligned pointer for the bitmap itself
2526
2527 // read $Bitmap attribute
2528 AttrBytesRead = ReadAttribute(DeviceExt, BitmapContext, 0, (PCHAR)BitmapData, BitmapDataSize);
2529
2530 if (AttrBytesRead != BitmapDataSize)
2531 {
2532 DPRINT1("ERROR: Unable to read $Bitmap attribute of master file table!\n");
2534 ReleaseAttributeContext(BitmapContext);
2536 }
2537
2538 // We need to backup the bits for records 0x10 - 0x17 (3rd byte of bitmap) and mark these records
2539 // as in-use so we don't assign files to those indices. They're reserved for the system (e.g. ChkDsk).
2540 SystemReservedBits = BitmapData[2];
2541 BitmapData[2] = 0xff;
2542
2543 // Calculate bit count
2544 BitmapBits.QuadPart = AttributeDataLength(DeviceExt->MFTContext->pRecord) /
2545 DeviceExt->NtfsInfo.BytesPerFileRecord;
2546 if (BitmapBits.HighPart != 0)
2547 {
2548 DPRINT1("\tFIXME: bitmap sizes beyond 32bits are not yet supported! (Your NTFS volume is too large)\n");
2551 ReleaseAttributeContext(BitmapContext);
2553 }
2554
2555 // convert buffer into bitmap
2557
2558 // set next available bit, preferrably after 23rd bit
2559 MftIndex = RtlFindClearBitsAndSet(&Bitmap, 1, 24);
2560 if ((LONG)MftIndex == -1)
2561 {
2562 DPRINT1("Couldn't find free space in MFT for file record, increasing MFT size.\n");
2563
2565 ReleaseAttributeContext(BitmapContext);
2566
2567 // Couldn't find a free record in the MFT, add some blank records and try again
2568 Status = IncreaseMftSize(DeviceExt, CanWait);
2569 if (!NT_SUCCESS(Status))
2570 {
2571 DPRINT1("ERROR: Couldn't find space in MFT for file or increase MFT size!\n");
2572 return Status;
2573 }
2574
2575 return AddNewMftEntry(FileRecord, DeviceExt, DestinationIndex, CanWait);
2576 }
2577
2578 DPRINT("Creating file record at MFT index: %I64u\n", MftIndex);
2579
2580 // update file record with index
2581 FileRecord->MFTRecordNumber = MftIndex;
2582
2583 // [BitmapData should have been updated via RtlFindClearBitsAndSet()]
2584
2585 // Restore the system reserved bits
2586 BitmapData[2] = SystemReservedBits;
2587
2588 // write the bitmap back to the MFT's $Bitmap attribute
2589 Status = WriteAttribute(DeviceExt, BitmapContext, 0, BitmapData, BitmapDataSize, &LengthWritten, FileRecord);
2590 if (!NT_SUCCESS(Status))
2591 {
2592 DPRINT1("ERROR encountered when writing $Bitmap attribute!\n");
2594 ReleaseAttributeContext(BitmapContext);
2595 return Status;
2596 }
2597
2598 // update the file record (write it to disk)
2599 Status = UpdateFileRecord(DeviceExt, MftIndex, FileRecord);
2600
2601 if (!NT_SUCCESS(Status))
2602 {
2603 DPRINT1("ERROR: Unable to write file record!\n");
2605 ReleaseAttributeContext(BitmapContext);
2606 return Status;
2607 }
2608
2609 *DestinationIndex = MftIndex;
2610
2612 ReleaseAttributeContext(BitmapContext);
2613
2614 return Status;
2615}
2616
2640static
2641ULONG
2643 PFILE_RECORD_HEADER FileRecord,
2644 ULONG IndexRootOffset,
2645 PNTFS_ATTR_RECORD IndexRootRecord)
2646{
2647 ULONG MaxIndexRootSize;
2648 PNTFS_ATTR_RECORD CurrentAttribute;
2649
2650 // First, find the max index size assuming index root is the last attribute
2651 MaxIndexRootSize = DeviceExt->NtfsInfo.BytesPerFileRecord // Start with the size of a file record
2652 - IndexRootOffset // Subtract the length of everything that comes before index root
2653 - IndexRootRecord->Resident.ValueOffset // Subtract the length of the attribute header for index root
2654 - sizeof(INDEX_ROOT_ATTRIBUTE) // Subtract the length of the index root header
2655 - (sizeof(ULONG) * 2); // Subtract the length of the file record end marker and padding
2656
2657 // Leave room for any attributes after this one, not counting the end marker
2658 CurrentAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + IndexRootOffset + IndexRootRecord->Length);
2659 while (CurrentAttribute->Type != AttributeEnd)
2660 {
2661 MaxIndexRootSize -= CurrentAttribute->Length;
2662 CurrentAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)CurrentAttribute + CurrentAttribute->Length);
2663 }
2664
2665 return MaxIndexRootSize;
2666}
2667
2702static
2705 ULONGLONG DirectoryMftIndex,
2706 ULONGLONG FileReferenceNumber,
2707 PFILENAME_ATTRIBUTE FilenameAttribute,
2708 BOOLEAN CaseSensitive)
2709{
2711 PFILE_RECORD_HEADER ParentFileRecord;
2712 PNTFS_ATTR_CONTEXT IndexRootContext;
2713 PINDEX_ROOT_ATTRIBUTE I30IndexRoot;
2714 ULONG IndexRootOffset;
2715 ULONGLONG I30IndexRootLength;
2716 ULONG LengthWritten;
2717 PINDEX_ROOT_ATTRIBUTE NewIndexRoot;
2718 ULONG AttributeLength;
2719 PB_TREE NewTree;
2720 ULONG BtreeIndexLength;
2721 ULONG MaxIndexRootSize;
2722 PB_TREE_KEY NewLeftKey;
2723 PB_TREE_FILENAME_NODE NewRightHandNode;
2724 LARGE_INTEGER MinIndexRootSize;
2725 ULONG NewMaxIndexRootSize;
2726 ULONG NodeSize;
2727
2728 // Allocate memory for the parent directory
2729 ParentFileRecord = ExAllocateFromNPagedLookasideList(&DeviceExt->FileRecLookasideList);
2730 if (!ParentFileRecord)
2731 {
2732 DPRINT1("ERROR: Couldn't allocate memory for file record!\n");
2734 }
2735
2736 // Open the parent directory
2737 Status = ReadFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord);
2738 if (!NT_SUCCESS(Status))
2739 {
2740 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2741 DPRINT1("ERROR: Couldn't read parent directory with index %I64u\n",
2742 DirectoryMftIndex);
2743 return Status;
2744 }
2745
2746#ifndef NDEBUG
2747 DPRINT1("Dumping old parent file record:\n");
2748 NtfsDumpFileRecord(DeviceExt, ParentFileRecord);
2749#endif
2750
2751 // Find the index root attribute for the directory
2752 Status = FindAttribute(DeviceExt,
2753 ParentFileRecord,
2755 L"$I30",
2756 4,
2757 &IndexRootContext,
2758 &IndexRootOffset);
2759 if (!NT_SUCCESS(Status))
2760 {
2761 DPRINT1("ERROR: Couldn't find $I30 $INDEX_ROOT attribute for parent directory with MFT #: %I64u!\n",
2762 DirectoryMftIndex);
2763 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2764 return Status;
2765 }
2766
2767 // Find the maximum index size given what the file record can hold
2768 MaxIndexRootSize = GetMaxIndexRootSize(DeviceExt, ParentFileRecord, IndexRootOffset, IndexRootContext->pRecord);
2769
2770 // Allocate memory for the index root data
2771 I30IndexRootLength = AttributeDataLength(IndexRootContext->pRecord);
2772 I30IndexRoot = ExAllocatePoolWithTag(NonPagedPool, I30IndexRootLength, TAG_NTFS);
2773 if (!I30IndexRoot)
2774 {
2775 DPRINT1("ERROR: Couldn't allocate memory for index root attribute!\n");
2776 ReleaseAttributeContext(IndexRootContext);
2777 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2779 }
2780
2781 // Read the Index Root
2782 Status = ReadAttribute(DeviceExt, IndexRootContext, 0, (PCHAR)I30IndexRoot, I30IndexRootLength);
2783 if (!NT_SUCCESS(Status))
2784 {
2785 DPRINT1("ERROR: Couln't read index root attribute for Mft index #%I64u\n", DirectoryMftIndex);
2786 ReleaseAttributeContext(IndexRootContext);
2787 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2788 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2789 return Status;
2790 }
2791
2792 // Convert the index to a B*Tree
2793 Status = CreateBTreeFromIndex(DeviceExt,
2794 ParentFileRecord,
2795 IndexRootContext,
2796 I30IndexRoot,
2797 &NewTree);
2798 if (!NT_SUCCESS(Status))
2799 {
2800 DPRINT1("ERROR: Failed to create B-Tree from Index!\n");
2801 ReleaseAttributeContext(IndexRootContext);
2802 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2803 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2804 return Status;
2805 }
2806
2807#ifndef NDEBUG
2808 DumpBTree(NewTree);
2809#endif
2810
2811 // Insert the key for the file we're adding
2812 Status = NtfsInsertKey(NewTree,
2813 FileReferenceNumber,
2814 FilenameAttribute,
2815 NewTree->RootNode,
2816 CaseSensitive,
2817 MaxIndexRootSize,
2818 I30IndexRoot->SizeOfEntry,
2819 &NewLeftKey,
2820 &NewRightHandNode);
2821 if (!NT_SUCCESS(Status))
2822 {
2823 DPRINT1("ERROR: Failed to insert '%.*S' into the index of MFT record %I64u (Status %lx)\n",
2824 FilenameAttribute->NameLength,
2825 FilenameAttribute->Name,
2826 DirectoryMftIndex,
2827 Status);
2828 DestroyBTree(NewTree);
2829 ReleaseAttributeContext(IndexRootContext);
2830 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2831 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2832 return Status;
2833 }
2834
2835#ifndef NDEBUG
2836 DumpBTree(NewTree);
2837#endif
2838
2839 // The root node can't be split
2840 ASSERT(NewLeftKey == NULL);
2841 ASSERT(NewRightHandNode == NULL);
2842
2843 // Convert B*Tree back to Index
2844
2845 // Updating the index allocation can change the size available for the index root,
2846 // And if the index root is demoted, the index allocation will need to be updated again,
2847 // which may change the size available for index root... etc.
2848 // My solution is to decrease index root to the size it would be if it was demoted,
2849 // then UpdateIndexAllocation will have an accurate representation of the maximum space
2850 // it can use in the file record. There's still a chance that the act of allocating an
2851 // index node after demoting the index root will increase the size of the file record beyond
2852 // it's limit, but if that happens, an attribute-list will most definitely be needed.
2853 // This a bit hacky, but it seems to be functional.
2854
2855 // Calculate the minimum size of the index root attribute, considering one dummy key and one VCN
2856 MinIndexRootSize.QuadPart = sizeof(INDEX_ROOT_ATTRIBUTE) // size of the index root headers
2857 + 0x18; // Size of dummy key with a VCN for a subnode
2858 ASSERT(MinIndexRootSize.QuadPart % ATTR_RECORD_ALIGNMENT == 0);
2859
2860 // Temporarily shrink the index root to it's minimal size
2861 AttributeLength = MinIndexRootSize.LowPart;
2862 AttributeLength += sizeof(INDEX_ROOT_ATTRIBUTE);
2863
2864
2865 // FIXME: IndexRoot will probably be invalid until we're finished. If we fail before we finish, the directory will probably be toast.
2866 // The potential for catastrophic data-loss exists!!! :)
2867
2868 // Update the length of the attribute in the file record of the parent directory
2870 IndexRootContext,
2871 ParentFileRecord,
2872 IndexRootOffset,
2873 AttributeLength);
2874 if (!NT_SUCCESS(Status))
2875 {
2876 DPRINT1("ERROR: Unable to set length of index root!\n");
2877 DestroyBTree(NewTree);
2878 ReleaseAttributeContext(IndexRootContext);
2879 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2880 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2881 return Status;
2882 }
2883
2884 // Update the index allocation
2885 Status = UpdateIndexAllocation(DeviceExt, NewTree, I30IndexRoot->SizeOfEntry, ParentFileRecord);
2886 if (!NT_SUCCESS(Status))
2887 {
2888 DPRINT1("ERROR: Failed to update index allocation from B-Tree!\n");
2889 DestroyBTree(NewTree);
2890 ReleaseAttributeContext(IndexRootContext);
2891 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2892 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2893 return Status;
2894 }
2895
2896#ifndef NDEBUG
2897 DPRINT1("Index Allocation updated\n");
2898 DumpBTree(NewTree);
2899#endif
2900
2901 // Find the maximum index root size given what the file record can hold
2902 NewMaxIndexRootSize = GetMaxIndexRootSize(DeviceExt, ParentFileRecord, IndexRootOffset, IndexRootContext->pRecord);
2903
2904 // The index allocation and index bitmap may have grown, leaving less room for the index root,
2905 // so now we need to double-check that index root isn't too large
2906 NodeSize = GetSizeOfIndexEntries(NewTree->RootNode);
2907 if (NodeSize > NewMaxIndexRootSize)
2908 {
2909 DPRINT("Demoting index root.\nNodeSize: 0x%lx\nNewMaxIndexRootSize: 0x%lx\n", NodeSize, NewMaxIndexRootSize);
2910
2911 Status = DemoteBTreeRoot(NewTree);
2912 if (!NT_SUCCESS(Status))
2913 {
2914 DPRINT1("ERROR: Failed to demote index root!\n");
2915 DestroyBTree(NewTree);
2916 ReleaseAttributeContext(IndexRootContext);
2917 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2918 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2919 return Status;
2920 }
2921
2922 // We need to update the index allocation once more
2923 Status = UpdateIndexAllocation(DeviceExt, NewTree, I30IndexRoot->SizeOfEntry, ParentFileRecord);
2924 if (!NT_SUCCESS(Status))
2925 {
2926 DPRINT1("ERROR: Failed to update index allocation from B-Tree!\n");
2927 DestroyBTree(NewTree);
2928 ReleaseAttributeContext(IndexRootContext);
2929 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2930 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2931 return Status;
2932 }
2933
2934 // re-recalculate max size of index root
2935 NewMaxIndexRootSize = GetMaxIndexRootSize(DeviceExt, ParentFileRecord, IndexRootOffset, IndexRootContext->pRecord);
2936 }
2937
2938 // Create the Index Root from the B*Tree
2939 Status = CreateIndexRootFromBTree(DeviceExt, NewTree, NewMaxIndexRootSize, &NewIndexRoot, &BtreeIndexLength);
2940 if (!NT_SUCCESS(Status))
2941 {
2942 DPRINT1("ERROR: Failed to create Index root from B-Tree!\n");
2943 DestroyBTree(NewTree);
2944 ReleaseAttributeContext(IndexRootContext);
2945 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2946 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2947 return Status;
2948 }
2949
2950 // We're done with the B-Tree now
2951 DestroyBTree(NewTree);
2952
2953 // Write back the new index root attribute to the parent directory file record
2954
2955 // First, we need to resize the attribute.
2956 // CreateIndexRootFromBTree() should have verified that the index root fits within MaxIndexSize.
2957 // We can't set the size as we normally would, because $INDEX_ROOT must always be resident.
2958 AttributeLength = NewIndexRoot->Header.AllocatedSize + FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE, Header);
2959
2960 if (AttributeLength != IndexRootContext->pRecord->Resident.ValueLength)
2961 {
2962 // Update the length of the attribute in the file record of the parent directory
2964 IndexRootContext,
2965 ParentFileRecord,
2966 IndexRootOffset,
2967 AttributeLength);
2968 if (!NT_SUCCESS(Status))
2969 {
2970 ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
2971 ReleaseAttributeContext(IndexRootContext);
2972 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2973 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2974 DPRINT1("ERROR: Unable to set resident attribute length!\n");
2975 return Status;
2976 }
2977
2978 }
2979
2980 NT_ASSERT(ParentFileRecord->BytesInUse <= DeviceExt->NtfsInfo.BytesPerFileRecord);
2981
2982 Status = UpdateFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord);
2983 if (!NT_SUCCESS(Status))
2984 {
2985 DPRINT1("ERROR: Failed to update file record of directory with index: %llx\n", DirectoryMftIndex);
2986 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2987 ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
2988 ReleaseAttributeContext(IndexRootContext);
2989 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2990 return Status;
2991 }
2992
2993 // Write the new index root to disk
2994 Status = WriteAttribute(DeviceExt,
2995 IndexRootContext,
2996 0,
2997 (PUCHAR)NewIndexRoot,
2998 AttributeLength,
2999 &LengthWritten,
3000 ParentFileRecord);
3001 if (!NT_SUCCESS(Status) || LengthWritten != AttributeLength)
3002 {
3003 DPRINT1("ERROR: Unable to write new index root attribute to parent directory!\n");
3004 ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
3005 ReleaseAttributeContext(IndexRootContext);
3006 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
3007 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
3008 return Status;
3009 }
3010
3011 // re-read the parent file record, so we can dump it
3012 Status = ReadFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord);
3013 if (!NT_SUCCESS(Status))
3014 {
3015 DPRINT1("ERROR: Couldn't read parent directory after messing with it!\n");
3016 }
3017 else
3018 {
3019#ifndef NDEBUG
3020 DPRINT1("Dumping new B-Tree:\n");
3021
3022 Status = CreateBTreeFromIndex(DeviceExt, ParentFileRecord, IndexRootContext, NewIndexRoot, &NewTree);
3023 if (!NT_SUCCESS(Status))
3024 {
3025 DPRINT1("ERROR: Couldn't re-create b-tree\n");
3026 return Status;
3027 }
3028
3029 DumpBTree(NewTree);
3030
3031 DestroyBTree(NewTree);
3032
3033 NtfsDumpFileRecord(DeviceExt, ParentFileRecord);
3034#endif
3035 }
3036
3037 // Cleanup
3038 ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
3039 ReleaseAttributeContext(IndexRootContext);
3040 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
3041 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
3042
3043 return Status;
3044}
3045
3046/* The worker rewrites the directory record from a copy it reads, so it
3047 * must not overlap any other update of a directory index */
3050 ULONGLONG DirectoryMftIndex,
3051 ULONGLONG FileReferenceNumber,
3052 PFILENAME_ATTRIBUTE FilenameAttribute,
3053 BOOLEAN CaseSensitive)
3054{
3056
3058 ExAcquireResourceExclusiveLite(&DeviceExt->IndexResource, TRUE);
3059 Status = AddFilenameToDirectoryWorker(DeviceExt, DirectoryMftIndex, FileReferenceNumber, FilenameAttribute, CaseSensitive);
3060 ExReleaseResourceLite(&DeviceExt->IndexResource);
3062
3063 return Status;
3064}
3065
3094static
3097 ULONGLONG DirectoryMftIndex,
3098 PFILENAME_ATTRIBUTE FilenameAttribute,
3099 BOOLEAN CaseSensitive)
3100{
3102 PFILE_RECORD_HEADER ParentFileRecord;
3103 PNTFS_ATTR_CONTEXT IndexRootContext;
3104 PINDEX_ROOT_ATTRIBUTE I30IndexRoot;
3105 PINDEX_ROOT_ATTRIBUTE NewIndexRoot;
3106 ULONG IndexRootOffset;
3107 ULONGLONG I30IndexRootLength;
3108 ULONG LengthWritten;
3109 ULONG AttributeLength;
3110 ULONG BtreeIndexLength;
3111 ULONG MaxIndexRootSize;
3112 PB_TREE Tree;
3113 PB_TREE_KEY SearchKey;
3114
3115 DPRINT("NtfsRemoveFilenameFromDirectory(%p, %I64u, %p, %s)\n",
3116 DeviceExt, DirectoryMftIndex, FilenameAttribute, CaseSensitive ? "TRUE" : "FALSE");
3117
3118 ParentFileRecord = ExAllocateFromNPagedLookasideList(&DeviceExt->FileRecLookasideList);
3119 if (!ParentFileRecord)
3120 {
3121 DPRINT1("ERROR: Couldn't allocate memory for file record!\n");
3123 }
3124
3125 Status = ReadFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord);
3126 if (!NT_SUCCESS(Status))
3127 {
3128 DPRINT1("ERROR: Couldn't read parent directory with index %I64u\n", DirectoryMftIndex);
3129 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
3130 return Status;
3131 }
3132
3133 Status = FindAttribute(DeviceExt,
3134 ParentFileRecord,
3136 L"$I30",
3137 4,
3138 &IndexRootContext,
3139 &IndexRootOffset);
3140 if (!NT_SUCCESS(Status))
3141 {
3142 DPRINT1("ERROR: Couldn't find $I30 $INDEX_ROOT attribute for parent directory with MFT #: %I64u!\n",
3143 DirectoryMftIndex);
3144 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
3145 return Status;
3146 }
3147
3148 I30IndexRootLength = AttributeDataLength(IndexRootContext->pRecord);
3149 I30IndexRoot = ExAllocatePoolWithTag(NonPagedPool, I30IndexRootLength, TAG_NTFS);
3150 if (!I30IndexRoot)
3151 {
3152 DPRINT1("ERROR: Couldn't allocate memory for index root attribute!\n");
3153 ReleaseAttributeContext(IndexRootContext);
3154 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
3156 }
3157
3158 Status = ReadAttribute(DeviceExt, IndexRootContext, 0, (PCHAR)I30IndexRoot, I30IndexRootLength);
3159 if (!NT_SUCCESS(Status))
3160 {
3161 DPRINT1("ERROR: Couldn't read index root attribute for Mft index #%I64u\n", DirectoryMftIndex);
3162 ReleaseAttributeContext(IndexRootContext);
3163 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
3164 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
3165 return Status;
3166 }
3167
3168 Status = CreateBTreeFromIndex(DeviceExt, ParentFileRecord, IndexRootContext, I30IndexRoot, &Tree);
3169 if (!NT_SUCCESS(Status))
3170 {
3171 DPRINT1("ERROR: Failed to create B-Tree from Index!\n");
3172 ReleaseAttributeContext(IndexRootContext);
3173 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
3174 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
3175 return Status;
3176 }
3177
3178 // NtfsRemoveKey() compares filenames, so the file reference here is only a placeholder
3179 SearchKey = CreateBTreeKeyFromFilename(0, FilenameAttribute);
3180 if (!SearchKey)
3181 {
3183 ReleaseAttributeContext(IndexRootContext);
3184 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
3185 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
3187 }
3188
3189 Status = NtfsRemoveKey(Tree->RootNode, SearchKey, CaseSensitive);
3190
3191 DestroyBTreeKey(SearchKey);
3192
3193 if (!NT_SUCCESS(Status))
3194 {
3195 DPRINT1("ERROR: Failed to remove '%.*S' from the index of MFT record %I64u (Status %lx)\n",
3196 FilenameAttribute->NameLength,
3197 FilenameAttribute->Name,
3198 DirectoryMftIndex,
3199 Status);
3201 ReleaseAttributeContext(IndexRootContext);
3202 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
3203 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
3204 return Status;
3205 }
3206
3207 // Push the tree back out to the index allocation before sizing the index root, for the same
3208 // reason the insert path does: growing or shrinking the allocation changes how much of the
3209 // file record is left for the root.
3210 Status = UpdateIndexAllocation(DeviceExt, Tree, I30IndexRoot->SizeOfEntry, ParentFileRecord);
3211 if (!NT_SUCCESS(Status))
3212 {
3213 DPRINT1("ERROR: Failed to update index allocation from B-Tree!\n");
3215 ReleaseAttributeContext(IndexRootContext);
3216 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
3217 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
3218 return Status;
3219 }
3220
3221 // Find the maximum index root size given what the file record can hold
3222 MaxIndexRootSize = GetMaxIndexRootSize(DeviceExt, ParentFileRecord, IndexRootOffset, IndexRootContext->pRecord);
3223
3224 Status = CreateIndexRootFromBTree(DeviceExt, Tree, MaxIndexRootSize, &NewIndexRoot, &BtreeIndexLength);
3225 if (!NT_SUCCESS(Status))
3226 {
3227 DPRINT1("ERROR: Failed to create Index root from B-Tree!\n");
3229 ReleaseAttributeContext(IndexRootContext);
3230 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
3231 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
3232 return Status;
3233 }
3234
3236
3237 // $INDEX_ROOT must always be resident, so its length is set directly rather than through
3238 // the usual attribute-resizing path.
3239 AttributeLength = NewIndexRoot->Header.AllocatedSize + FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE, Header);
3240
3241 if (AttributeLength != IndexRootContext->pRecord->Resident.ValueLength)
3242 {
3244 IndexRootContext,
3245 ParentFileRecord,
3246 IndexRootOffset,
3247 AttributeLength);
3248 if (!NT_SUCCESS(Status))
3249 {
3250 DPRINT1("ERROR: Unable to set resident attribute length!\n");
3251 ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
3252 ReleaseAttributeContext(IndexRootContext);
3253 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
3254 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
3255 return Status;
3256 }
3257 }
3258
3259 NT_ASSERT(ParentFileRecord->BytesInUse <= DeviceExt->NtfsInfo.BytesPerFileRecord);
3260
3261 Status = UpdateFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord);
3262 if (!NT_SUCCESS(Status))
3263 {
3264 DPRINT1("ERROR: Failed to update file record of directory with index: %I64x\n", DirectoryMftIndex);
3265 ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
3266 ReleaseAttributeContext(IndexRootContext);
3267 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
3268 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
3269 return Status;
3270 }
3271
3272 Status = WriteAttribute(DeviceExt,
3273 IndexRootContext,
3274 0,
3275 (PUCHAR)NewIndexRoot,
3276 AttributeLength,
3277 &LengthWritten,
3278 ParentFileRecord);
3279 if (!NT_SUCCESS(Status) || LengthWritten != AttributeLength)
3280 {
3281 DPRINT1("ERROR: Unable to write new index root attribute to parent directory!\n");
3282 if (NT_SUCCESS(Status))
3284 }
3285
3286 ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
3287 ReleaseAttributeContext(IndexRootContext);
3288 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
3289 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
3290
3291 return Status;
3292}
3293
3294/* The worker rewrites the directory record from a copy it reads, so it
3295 * must not overlap any other update of a directory index */
3298 ULONGLONG DirectoryMftIndex,
3299 PFILENAME_ATTRIBUTE FilenameAttribute,
3300 BOOLEAN CaseSensitive)
3301{
3303
3305 ExAcquireResourceExclusiveLite(&DeviceExt->IndexResource, TRUE);
3306 Status = RemoveFilenameFromDirectoryWorker(DeviceExt, DirectoryMftIndex, FilenameAttribute, CaseSensitive);
3307 ExReleaseResourceLite(&DeviceExt->IndexResource);
3309
3310 return Status;
3311}
3312
3337 ULONGLONG MftIndex)
3338{
3341 ULONGLONG BitmapDataSize;
3342 ULONGLONG AttrBytesRead;
3345 ULONG LengthWritten;
3346 PNTFS_ATTR_CONTEXT BitmapContext;
3347 LARGE_INTEGER BitmapBits;
3348 PFILE_RECORD_HEADER FileRecord;
3349
3350 DPRINT("FreeMftEntry(%p, %I64u)\n", DeviceExt, MftIndex);
3351
3352 // Mark the record itself as free first, so a bitmap bit is never handed out for a record
3353 // that still looks in use.
3354 FileRecord = ExAllocateFromNPagedLookasideList(&DeviceExt->FileRecLookasideList);
3355 if (!FileRecord)
3357
3358 Status = ReadFileRecord(DeviceExt, MftIndex, FileRecord);
3359 if (!NT_SUCCESS(Status))
3360 {
3361 DPRINT1("ERROR: Couldn't read file record %I64u to free it!\n", MftIndex);
3362 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
3363 return Status;
3364 }
3365
3366 FileRecord->Flags &= ~FRH_IN_USE;
3367 FileRecord->SequenceNumber++;
3368
3369 // Sequence number zero is reserved to mean "no reference"
3370 if (FileRecord->SequenceNumber == 0)
3371 FileRecord->SequenceNumber = 1;
3372
3373 Status = UpdateFileRecord(DeviceExt, MftIndex, FileRecord);
3374 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
3375
3376 if (!NT_SUCCESS(Status))
3377 {
3378 DPRINT1("ERROR: Couldn't write file record %I64u while freeing it!\n", MftIndex);
3379 return Status;
3380 }
3381
3382 Status = FindAttribute(DeviceExt, DeviceExt->MasterFileTable, AttributeBitmap, L"", 0, &BitmapContext, NULL);
3383 if (!NT_SUCCESS(Status))
3384 {
3385 DPRINT1("ERROR: Couldn't find $Bitmap attribute of master file table!\n");
3386 return Status;
3387 }
3388
3389 BitmapDataSize = AttributeDataLength(BitmapContext->pRecord);
3390
3391 // RtlInitializeBitmap wants a ULONG-aligned pointer and a ULONG-multiple of memory
3392 BitmapBuffer = ExAllocatePoolWithTag(NonPagedPool, BitmapDataSize + sizeof(ULONG), TAG_NTFS);
3393 if (!BitmapBuffer)
3394 {
3395 ReleaseAttributeContext(BitmapContext);
3397 }
3398 RtlZeroMemory(BitmapBuffer, BitmapDataSize + sizeof(ULONG));
3399
3401
3402 AttrBytesRead = ReadAttribute(DeviceExt, BitmapContext, 0, (PCHAR)BitmapData, BitmapDataSize);
3403 if (AttrBytesRead != BitmapDataSize)
3404 {
3405 DPRINT1("ERROR: Unable to read $Bitmap attribute of master file table!\n");
3407 ReleaseAttributeContext(BitmapContext);
3409 }
3410
3411 BitmapBits.QuadPart = AttributeDataLength(DeviceExt->MFTContext->pRecord) /
3412 DeviceExt->NtfsInfo.BytesPerFileRecord;
3413 if (BitmapBits.HighPart != 0)
3414 {
3415 DPRINT1("\tFIXME: bitmap sizes beyond 32bits are not yet supported! (Your NTFS volume is too large)\n");
3417 ReleaseAttributeContext(BitmapContext);
3419 }
3420
3421 if (MftIndex >= BitmapBits.QuadPart)
3422 {
3423 DPRINT1("ERROR: Mft index %I64u is beyond the end of the master file table!\n", MftIndex);
3425 ReleaseAttributeContext(BitmapContext);
3427 }
3428
3430 RtlClearBits(&Bitmap, (ULONG)MftIndex, 1);
3431
3432 Status = WriteAttribute(DeviceExt, BitmapContext, 0, BitmapData, BitmapDataSize, &LengthWritten, NULL);
3433 if (!NT_SUCCESS(Status))
3434 {
3435 DPRINT1("ERROR encountered when writing $Bitmap attribute!\n");
3436 }
3437
3439 ReleaseAttributeContext(BitmapContext);
3440
3441 return Status;
3442}
3443
3471 ULONGLONG MftIndex,
3472 BOOLEAN CaseSensitive)
3473{
3475 PFILE_RECORD_HEADER FileRecord;
3476 PNTFS_ATTR_RECORD Attribute;
3477 PNTFS_ATTR_CONTEXT AttributeContext;
3478 ULONG AttributeOffset;
3479
3480 DPRINT("NtfsDeleteFileRecord(%p, %I64u, %s)\n", DeviceExt, MftIndex, CaseSensitive ? "TRUE" : "FALSE");
3481
3482 FileRecord = ExAllocateFromNPagedLookasideList(&DeviceExt->FileRecLookasideList);
3483 if (!FileRecord)
3485
3486 Status = ReadFileRecord(DeviceExt, MftIndex, FileRecord);
3487 if (!NT_SUCCESS(Status))
3488 {
3489 DPRINT1("ERROR: Couldn't read file record %I64u to delete it!\n", MftIndex);
3490 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
3491 return Status;
3492 }
3493
3494 // Remove every name this file has from its parent's index before anything is freed, so a
3495 // failure leaves the file whole instead of an entry pointing at a released record. A file
3496 // with both a long name and an 8.3 name is indexed twice, and both parents are named by
3497 // the attributes themselves.
3498 Attribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset);
3499 AttributeOffset = FileRecord->AttributeOffset;
3500
3501 while (AttributeOffset < DeviceExt->NtfsInfo.BytesPerFileRecord &&
3502 Attribute->Type != AttributeEnd)
3503 {
3504 if (Attribute->Type == AttributeFileName && !Attribute->IsNonResident)
3505 {
3507 (PFILENAME_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
3508
3510 FileName->DirectoryFileReferenceNumber & NTFS_MFT_MASK,
3511 FileName,
3512 CaseSensitive);
3513 if (!NT_SUCCESS(Status))
3514 {
3515 DPRINT1("ERROR: Failed to remove '%.*S' from directory %I64u!\n",
3516 FileName->NameLength,
3517 FileName->Name,
3518 FileName->DirectoryFileReferenceNumber & NTFS_MFT_MASK);
3519 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
3520 return Status;
3521 }
3522 }
3523
3524 if (Attribute->Length == 0)
3525 break;
3526
3527 AttributeOffset += Attribute->Length;
3528 Attribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttributeOffset);
3529 }
3530
3531 // Release the clusters held by every non-resident attribute
3532 Attribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset);
3533 AttributeOffset = FileRecord->AttributeOffset;
3534
3535 while (AttributeOffset < DeviceExt->NtfsInfo.BytesPerFileRecord &&
3536 Attribute->Type != AttributeEnd)
3537 {
3538 if (Attribute->IsNonResident)
3539 {
3540 Status = FindAttribute(DeviceExt,
3541 FileRecord,
3542 Attribute->Type,
3543 (PCWSTR)((ULONG_PTR)Attribute + Attribute->NameOffset),
3544 Attribute->NameLength,
3545 &AttributeContext,
3546 NULL);
3547 if (NT_SUCCESS(Status))
3548 {
3549 ULONGLONG ClusterCount = Attribute->NonResident.HighestVCN - Attribute->NonResident.LowestVCN + 1;
3550
3551 Status = FreeClusters(DeviceExt, AttributeContext, AttributeOffset, FileRecord, (ULONG)ClusterCount);
3552 if (!NT_SUCCESS(Status))
3553 {
3554 DPRINT1("ERROR: Failed to free clusters of attribute 0x%lx of file %I64u!\n",
3555 Attribute->Type, MftIndex);
3556 }
3557
3558 ReleaseAttributeContext(AttributeContext);
3559 }
3560 }
3561
3562 if (Attribute->Length == 0)
3563 break;
3564
3565 AttributeOffset += Attribute->Length;
3566 Attribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttributeOffset);
3567 }
3568
3569 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
3570
3571 return FreeMftEntry(DeviceExt, MftIndex);
3572}
3573
3577{
3578 USHORT *pShortToFixUp;
3579 ULONG ArrayEntryCount = Record->UsaCount - 1;
3580 ULONG Offset = Vcb->NtfsInfo.BytesPerSector - 2;
3581 ULONG i;
3582
3583 PFIXUP_ARRAY fixupArray = (PFIXUP_ARRAY)((UCHAR*)Record + Record->UsaOffset);
3584
3585 DPRINT("AddFixupArray(%p, %p)\n fixupArray->USN: %u, ArrayEntryCount: %u\n", Vcb, Record, fixupArray->USN, ArrayEntryCount);
3586
3587 fixupArray->USN++;
3588
3589 for (i = 0; i < ArrayEntryCount; i++)
3590 {
3591 DPRINT("USN: %u\tOffset: %u\n", fixupArray->USN, Offset);
3592
3593 pShortToFixUp = (USHORT*)((PCHAR)Record + Offset);
3594 fixupArray->Array[i] = *pShortToFixUp;
3595 *pShortToFixUp = fixupArray->USN;
3596 Offset += Vcb->NtfsInfo.BytesPerSector;
3597 }
3598
3599 return STATUS_SUCCESS;
3600}
3601
3604 ULONGLONG lcn,
3605 ULONG count,
3606 PVOID buffer)
3607{
3608 LARGE_INTEGER DiskSector;
3609
3610 DiskSector.QuadPart = lcn;
3611
3612 return NtfsReadSectors(Vcb->StorageDevice,
3613 DiskSector.u.LowPart * Vcb->NtfsInfo.SectorsPerCluster,
3614 count * Vcb->NtfsInfo.SectorsPerCluster,
3615 Vcb->NtfsInfo.BytesPerSector,
3616 buffer,
3617 FALSE);
3618}
3619
3620
3621BOOLEAN
3624 PINDEX_ENTRY_ATTRIBUTE IndexEntry,
3625 BOOLEAN DirSearch,
3626 BOOLEAN CaseSensitive)
3627{
3628 BOOLEAN Ret, Alloc = FALSE;
3629 UNICODE_STRING EntryName;
3630
3631 EntryName.Buffer = IndexEntry->FileName.Name;
3632 EntryName.Length =
3633 EntryName.MaximumLength = IndexEntry->FileName.NameLength * sizeof(WCHAR);
3634
3635 if (DirSearch)
3636 {
3637 UNICODE_STRING IntFileName;
3638 if (!CaseSensitive)
3639 {
3641 Alloc = TRUE;
3642 }
3643 else
3644 {
3645 IntFileName = *FileName;
3646 }
3647
3648 /* With a real upcase table the matcher folds both sides through it,
3649 * which is what NTFS means by case-insensitive. */
3650 Ret = FsRtlIsNameInExpression(&IntFileName, &EntryName, !CaseSensitive,
3651 Vcb ? Vcb->UpCaseTable : NULL);
3652
3653 if (Alloc)
3654 {
3655 RtlFreeUnicodeString(&IntFileName);
3656 }
3657
3658 return Ret;
3659 }
3660 else
3661 {
3662 return (RtlCompareUnicodeString(FileName, &EntryName, !CaseSensitive) == 0);
3663 }
3664}
3665
3691{
3692 PFILE_RECORD_HEADER MirrorFileRecord;
3693 PNTFS_ATTR_CONTEXT MirrDataContext;
3694 PNTFS_ATTR_CONTEXT MftDataContext;
3695 PCHAR DataBuffer;
3699 ULONG LengthWritten;
3700
3701 // Allocate memory for the Mft mirror file record
3702 MirrorFileRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
3703 if (!MirrorFileRecord)
3704 {
3705 DPRINT1("Error: Failed to allocate memory for $MFTMirr!\n");
3707 }
3708
3709 // Read the Mft Mirror file record
3710 Status = ReadFileRecord(Vcb, NTFS_FILE_MFTMIRR, MirrorFileRecord);
3711 if (!NT_SUCCESS(Status))
3712 {
3713 DPRINT1("ERROR: Failed to read $MFTMirr!\n");
3714 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MirrorFileRecord);
3715 return Status;
3716 }
3717
3718 // Find the $DATA attribute of $MFTMirr
3719 Status = FindAttribute(Vcb, MirrorFileRecord, AttributeData, L"", 0, &MirrDataContext, NULL);
3720 if (!NT_SUCCESS(Status))
3721 {
3722 DPRINT1("ERROR: Couldn't find $DATA attribute!\n");
3723 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MirrorFileRecord);
3724 return Status;
3725 }
3726
3727 // Find the $DATA attribute of $MFT
3728 Status = FindAttribute(Vcb, Vcb->MasterFileTable, AttributeData, L"", 0, &MftDataContext, NULL);
3729 if (!NT_SUCCESS(Status))
3730 {
3731 DPRINT1("ERROR: Couldn't find $DATA attribute!\n");
3732 ReleaseAttributeContext(MirrDataContext);
3733 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MirrorFileRecord);
3734 return Status;
3735 }
3736
3737 // Get the size of the mirror's $DATA attribute
3738 DataLength = AttributeDataLength(MirrDataContext->pRecord);
3739
3740 ASSERT(DataLength % Vcb->NtfsInfo.BytesPerFileRecord == 0);
3741
3742 // Create buffer for the mirror's $DATA attribute
3744 if (!DataBuffer)
3745 {
3746 DPRINT1("Error: Couldn't allocate memory for $DATA buffer!\n");
3747 ReleaseAttributeContext(MftDataContext);
3748 ReleaseAttributeContext(MirrDataContext);
3749 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MirrorFileRecord);
3751 }
3752
3754
3755 // Back up the first several entries of the Mft's $DATA Attribute
3756 BytesRead = ReadAttribute(Vcb, MftDataContext, 0, DataBuffer, (ULONG)DataLength);
3757 if (BytesRead != (ULONG)DataLength)
3758 {
3759 DPRINT1("Error: Failed to read $DATA for $MFTMirr!\n");
3760 ReleaseAttributeContext(MftDataContext);
3761 ReleaseAttributeContext(MirrDataContext);
3762 ExFreePoolWithTag(DataBuffer, TAG_NTFS);
3763 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MirrorFileRecord);
3764 return STATUS_UNSUCCESSFUL;
3765 }
3766
3767 // Write the mirror's $DATA attribute
3769 MirrDataContext,
3770 0,
3771 (PUCHAR)DataBuffer,
3772 DataLength,
3773 &LengthWritten,
3774 MirrorFileRecord);
3775 if (!NT_SUCCESS(Status))
3776 {
3777 DPRINT1("ERROR: Failed to write $DATA attribute of $MFTMirr!\n");
3778 }
3779
3780 // Cleanup
3781 ReleaseAttributeContext(MftDataContext);
3782 ReleaseAttributeContext(MirrDataContext);
3783 ExFreePoolWithTag(DataBuffer, TAG_NTFS);
3784 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MirrorFileRecord);
3785
3786 return Status;
3787}
3788
3789#if 0
3790static
3791VOID
3792DumpIndexEntry(PINDEX_ENTRY_ATTRIBUTE IndexEntry)
3793{
3794 DPRINT1("Entry: %p\n", IndexEntry);
3795 DPRINT1("\tData.Directory.IndexedFile: %I64x\n", IndexEntry->Data.Directory.IndexedFile);
3796 DPRINT1("\tLength: %u\n", IndexEntry->Length);
3797 DPRINT1("\tKeyLength: %u\n", IndexEntry->KeyLength);
3798 DPRINT1("\tFlags: %x\n", IndexEntry->Flags);
3799 DPRINT1("\tReserved: %x\n", IndexEntry->Reserved);
3800 DPRINT1("\t\tDirectoryFileReferenceNumber: %I64x\n", IndexEntry->FileName.DirectoryFileReferenceNumber);
3801 DPRINT1("\t\tCreationTime: %I64u\n", IndexEntry->FileName.CreationTime);
3802 DPRINT1("\t\tChangeTime: %I64u\n", IndexEntry->FileName.ChangeTime);
3803 DPRINT1("\t\tLastWriteTime: %I64u\n", IndexEntry->FileName.LastWriteTime);
3804 DPRINT1("\t\tLastAccessTime: %I64u\n", IndexEntry->FileName.LastAccessTime);
3805 DPRINT1("\t\tAllocatedSize: %I64u\n", IndexEntry->FileName.AllocatedSize);
3806 DPRINT1("\t\tDataSize: %I64u\n", IndexEntry->FileName.DataSize);
3807 DPRINT1("\t\tFileAttributes: %x\n", IndexEntry->FileName.FileAttributes);
3808 DPRINT1("\t\tNameLength: %u\n", IndexEntry->FileName.NameLength);
3809 DPRINT1("\t\tNameType: %x\n", IndexEntry->FileName.NameType);
3810 DPRINT1("\t\tName: %.*S\n", IndexEntry->FileName.NameLength, IndexEntry->FileName.Name);
3811}
3812#endif
3813
3816 PFILE_RECORD_HEADER MftRecord,
3817 ULONG IndexBlockSize,
3819 PNTFS_ATTR_CONTEXT IndexAllocationContext,
3821 ULONGLONG VCN,
3822 PULONG StartEntry,
3823 PULONG CurrentEntry,
3824 BOOLEAN DirSearch,
3825 BOOLEAN CaseSensitive,
3826 ULONGLONG *OutMFTIndex)
3827{
3828 PINDEX_BUFFER IndexRecord;
3831 PINDEX_ENTRY_ATTRIBUTE FirstEntry;
3832 PINDEX_ENTRY_ATTRIBUTE LastEntry;
3833 PINDEX_ENTRY_ATTRIBUTE IndexEntry;
3836
3837 DPRINT("BrowseSubNodeIndexEntries(%p, %p, %lu, %wZ, %p, %p, %I64d, %lu, %lu, %s, %s, %p)\n",
3838 Vcb,
3839 MftRecord,
3840 IndexBlockSize,
3841 FileName,
3842 IndexAllocationContext,
3843 Bitmap,
3844 VCN,
3845 *StartEntry,
3846 *CurrentEntry,
3847 "FALSE",
3848 DirSearch ? "TRUE" : "FALSE",
3849 CaseSensitive ? "TRUE" : "FALSE",
3850 OutMFTIndex);
3851
3852 // Calculate node number as VCN / Clusters per index record. An index record smaller than
3853 // a cluster is addressed in sectors, matching GetAllocationOffsetFromVCN().
3854 if (IndexBlockSize < Vcb->NtfsInfo.BytesPerCluster)
3855 NodeNumber = VCN / (IndexBlockSize / Vcb->NtfsInfo.BytesPerSector);
3856 else
3857 NodeNumber = VCN / (IndexBlockSize / Vcb->NtfsInfo.BytesPerCluster);
3858
3859 // Is the bit for this node clear in the bitmap?
3861 {
3862 DPRINT1("File system corruption detected, node with VCN %I64u is marked as deleted.\n", VCN);
3863 return STATUS_DATA_ERROR;
3864 }
3865
3866 // Allocate memory for the index record
3867 IndexRecord = ExAllocatePoolWithTag(NonPagedPool, IndexBlockSize, TAG_NTFS);
3868 if (!IndexRecord)
3869 {
3870 DPRINT1("Unable to allocate memory for index record!\n");
3872 }
3873
3874 // Calculate offset of index record, using the same helper the write path does
3875 Offset = GetAllocationOffsetFromVCN(Vcb, IndexBlockSize, VCN);
3876
3877 // Read the index record
3878 BytesRead = ReadAttribute(Vcb, IndexAllocationContext, Offset, (PCHAR)IndexRecord, IndexBlockSize);
3879 if (BytesRead != IndexBlockSize)
3880 {
3881 DPRINT1("Unable to read index record!\n");
3882 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
3883 return STATUS_UNSUCCESSFUL;
3884 }
3885
3886 if (IndexRecord->Ntfs.Type != NRH_INDX_TYPE)
3887 {
3888 DPRINT1("File system corruption detected, no index record at VCN %I64u of MFT record %I64u "
3889 "(offset 0x%I64x, block size %lu, cluster %lu, index record %lu, signature 0x%08lx)\n",
3890 VCN,
3891 MftRecord->MFTRecordNumber & NTFS_MFT_MASK,
3892 Offset,
3893 IndexBlockSize,
3894 Vcb->NtfsInfo.BytesPerCluster,
3895 Vcb->NtfsInfo.BytesPerIndexRecord,
3896 IndexRecord->Ntfs.Type);
3897 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
3898 return STATUS_DATA_ERROR;
3899 }
3900
3901 // Apply the fixup array to the index record
3902 Status = FixupUpdateSequenceArray(Vcb, &((PFILE_RECORD_HEADER)IndexRecord)->Ntfs);
3903 if (!NT_SUCCESS(Status))
3904 {
3905 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
3906 DPRINT1("Failed to apply fixup array!\n");
3907 return Status;
3908 }
3909
3910 ASSERT(IndexRecord->Header.AllocatedSize + FIELD_OFFSET(INDEX_BUFFER, Header) == IndexBlockSize);
3911 FirstEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexRecord->Header + IndexRecord->Header.FirstEntryOffset);
3912 LastEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexRecord->Header + IndexRecord->Header.TotalSizeOfEntries);
3913 ASSERT(LastEntry <= (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)IndexRecord + IndexBlockSize));
3914
3915 // Loop through all Index Entries of index, starting with FirstEntry
3916 IndexEntry = FirstEntry;
3917 while (IndexEntry <= LastEntry)
3918 {
3919 // Does IndexEntry have a sub-node?
3920 if (IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE)
3921 {
3922 if (!(IndexRecord->Header.Flags & INDEX_NODE_LARGE) || !IndexAllocationContext)
3923 {
3924 DPRINT1("Filesystem corruption detected!\n");
3925 }
3926 else
3927 {
3929 MftRecord,
3930 IndexBlockSize,
3931 FileName,
3932 IndexAllocationContext,
3933 Bitmap,
3934 GetIndexEntryVCN(IndexEntry),
3935 StartEntry,
3936 CurrentEntry,
3937 DirSearch,
3938 CaseSensitive,
3939 OutMFTIndex);
3940 if (NT_SUCCESS(Status))
3941 {
3942 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
3943 return Status;
3944 }
3945 }
3946 }
3947
3948 // Are we done?
3949 if (IndexEntry->Flags & NTFS_INDEX_ENTRY_END)
3950 break;
3951
3952 // If we've found a file whose index is greater than or equal to StartEntry that matches the search criteria
3953 if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) >= NTFS_FILE_FIRST_USER_FILE &&
3954 *CurrentEntry >= *StartEntry &&
3955 IndexEntry->FileName.NameType != NTFS_FILE_NAME_DOS &&
3956 CompareFileName(Vcb, FileName, IndexEntry, DirSearch, CaseSensitive))
3957 {
3958 *StartEntry = *CurrentEntry;
3959 *OutMFTIndex = (IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK);
3960 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
3961 return STATUS_SUCCESS;
3962 }
3963
3964 // Advance to the next index entry
3965 (*CurrentEntry) += 1;
3966 ASSERT(IndexEntry->Length >= sizeof(INDEX_ENTRY_ATTRIBUTE));
3967 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length);
3968 }
3969
3970 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
3971
3973}
3974
3977 PFILE_RECORD_HEADER MftRecord,
3978 PINDEX_ROOT_ATTRIBUTE IndexRecord,
3979 ULONG IndexBlockSize,
3980 PINDEX_ENTRY_ATTRIBUTE FirstEntry,
3981 PINDEX_ENTRY_ATTRIBUTE LastEntry,
3983 PULONG StartEntry,
3984 PULONG CurrentEntry,
3985 BOOLEAN DirSearch,
3986 BOOLEAN CaseSensitive,
3987 ULONGLONG *OutMFTIndex)
3988{
3990 PINDEX_ENTRY_ATTRIBUTE IndexEntry;
3991 PNTFS_ATTR_CONTEXT IndexAllocationContext;
3992 PNTFS_ATTR_CONTEXT BitmapContext;
3993 PCHAR *BitmapMem;
3994 ULONG *BitmapPtr;
3996
3997 DPRINT("BrowseIndexEntries(%p, %p, %p, %lu, %p, %p, %wZ, %lu, %lu, %s, %s, %p)\n",
3998 Vcb,
3999 MftRecord,
4000 IndexRecord,
4001 IndexBlockSize,
4002 FirstEntry,
4003 LastEntry,
4004 FileName,
4005 *StartEntry,
4006 *CurrentEntry,
4007 DirSearch ? "TRUE" : "FALSE",
4008 CaseSensitive ? "TRUE" : "FALSE",
4009 OutMFTIndex);
4010
4011 // Find the $I30 index allocation, if there is one
4012 Status = FindAttribute(Vcb, MftRecord, AttributeIndexAllocation, L"$I30", 4, &IndexAllocationContext, NULL);
4013 if (NT_SUCCESS(Status))
4014 {
4015 ULONGLONG BitmapLength;
4016 // Find the bitmap attribute for the index
4017 Status = FindAttribute(Vcb, MftRecord, AttributeBitmap, L"$I30", 4, &BitmapContext, NULL);
4018 if (!NT_SUCCESS(Status))
4019 {
4020 DPRINT1("Potential file system corruption detected!\n");
4021 ReleaseAttributeContext(IndexAllocationContext);
4022 return Status;
4023 }
4024
4025 // Get the length of the bitmap attribute
4026 BitmapLength = AttributeDataLength(BitmapContext->pRecord);
4027
4028 // Allocate memory for the bitmap, including some padding; RtlInitializeBitmap() wants a pointer
4029 // that's ULONG-aligned, and it wants the size of the memory allocated for it to be a ULONG-multiple.
4030 BitmapMem = ExAllocatePoolWithTag(NonPagedPool, BitmapLength + sizeof(ULONG), TAG_NTFS);
4031 if (!BitmapMem)
4032 {
4033 DPRINT1("Error: failed to allocate bitmap!");
4034 ReleaseAttributeContext(BitmapContext);
4035 ReleaseAttributeContext(IndexAllocationContext);
4037 }
4038
4039 RtlZeroMemory(BitmapMem, BitmapLength + sizeof(ULONG));
4040
4041 // RtlInitializeBitmap() wants a pointer that's ULONG-aligned.
4042 BitmapPtr = (PULONG)ALIGN_UP_BY((ULONG_PTR)BitmapMem, sizeof(ULONG));
4043
4044 // Read the existing bitmap data
4045 Status = ReadAttribute(Vcb, BitmapContext, 0, (PCHAR)BitmapPtr, BitmapLength);
4046 if (!NT_SUCCESS(Status))
4047 {
4048 DPRINT1("ERROR: Failed to read bitmap attribute!\n");
4049 ExFreePoolWithTag(BitmapMem, TAG_NTFS);
4050 ReleaseAttributeContext(BitmapContext);
4051 ReleaseAttributeContext(IndexAllocationContext);
4052 return Status;
4053 }
4054
4055 // Initialize bitmap
4056 RtlInitializeBitMap(&Bitmap, BitmapPtr, BitmapLength * 8);
4057 }
4058 else
4059 {
4060 // Couldn't find an index allocation
4061 IndexAllocationContext = NULL;
4062 }
4063
4064
4065 // Loop through all Index Entries of index, starting with FirstEntry
4066 IndexEntry = FirstEntry;
4067 while (IndexEntry <= LastEntry)
4068 {
4069 // Does IndexEntry have a sub-node?
4070 if (IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE)
4071 {
4072 if (!(IndexRecord->Header.Flags & INDEX_ROOT_LARGE) || !IndexAllocationContext)
4073 {
4074 DPRINT1("Filesystem corruption detected!\n");
4075 }
4076 else
4077 {
4079 MftRecord,
4080 IndexBlockSize,
4081 FileName,
4082 IndexAllocationContext,
4083 &Bitmap,
4084 GetIndexEntryVCN(IndexEntry),
4085 StartEntry,
4086 CurrentEntry,
4087 DirSearch,
4088 CaseSensitive,
4089 OutMFTIndex);
4090
4091 /* Anything other than "not in this subtree" means we gave up on
4092 * a subtree that may well have held the entry */
4094 {
4095 DPRINT1("Sub-node at VCN %I64u abandoned while looking for '%wZ' (Status %lx)\n",
4096 GetIndexEntryVCN(IndexEntry), FileName, Status);
4097 }
4098
4099 if (NT_SUCCESS(Status))
4100 {
4101 ExFreePoolWithTag(BitmapMem, TAG_NTFS);
4102 ReleaseAttributeContext(BitmapContext);
4103 ReleaseAttributeContext(IndexAllocationContext);
4104 return Status;
4105 }
4106 }
4107 }
4108
4109 // Are we done?
4110 if (IndexEntry->Flags & NTFS_INDEX_ENTRY_END)
4111 break;
4112
4113 // If we've found a file whose index is greater than or equal to StartEntry that matches the search criteria
4114 if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) >= NTFS_FILE_FIRST_USER_FILE &&
4115 *CurrentEntry >= *StartEntry &&
4116 IndexEntry->FileName.NameType != NTFS_FILE_NAME_DOS &&
4117 CompareFileName(Vcb, FileName, IndexEntry, DirSearch, CaseSensitive))
4118 {
4119 *StartEntry = *CurrentEntry;
4120 *OutMFTIndex = (IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK);
4121 if (IndexAllocationContext)
4122 {
4123 ExFreePoolWithTag(BitmapMem, TAG_NTFS);
4124 ReleaseAttributeContext(BitmapContext);
4125 ReleaseAttributeContext(IndexAllocationContext);
4126 }
4127 return STATUS_SUCCESS;
4128 }
4129
4130 // Advance to the next index entry
4131 (*CurrentEntry) += 1;
4132 ASSERT(IndexEntry->Length >= sizeof(INDEX_ENTRY_ATTRIBUTE));
4133 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length);
4134 }
4135
4136 if (IndexAllocationContext)
4137 {
4138 DPRINT("'%wZ' not found after scanning %lu entries of MFT record %I64u "
4139 "(index allocation present)\n",
4140 FileName, *CurrentEntry, MftRecord->MFTRecordNumber & NTFS_MFT_MASK);
4141
4142 ExFreePoolWithTag(BitmapMem, TAG_NTFS);
4143 ReleaseAttributeContext(BitmapContext);
4144 ReleaseAttributeContext(IndexAllocationContext);
4145 }
4146
4148}
4149
4152 ULONGLONG MFTIndex,
4154 PULONG FirstEntry,
4155 BOOLEAN DirSearch,
4156 BOOLEAN CaseSensitive,
4157 ULONGLONG *OutMFTIndex)
4158{
4159 PFILE_RECORD_HEADER MftRecord;
4160 PNTFS_ATTR_CONTEXT IndexRootCtx;
4161 PINDEX_ROOT_ATTRIBUTE IndexRoot;
4162 PCHAR IndexRecord;
4163 PINDEX_ENTRY_ATTRIBUTE IndexEntry, IndexEntryEnd;
4165 ULONG CurrentEntry = 0;
4166
4167 DPRINT("NtfsFindMftRecord(%p, %I64d, %wZ, %lu, %s, %s, %p)\n",
4168 Vcb,
4169 MFTIndex,
4170 FileName,
4171 *FirstEntry,
4172 DirSearch ? "TRUE" : "FALSE",
4173 CaseSensitive ? "TRUE" : "FALSE",
4174 OutMFTIndex);
4175
4176 MftRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
4177 if (MftRecord == NULL)
4178 {
4180 }
4181
4182 Status = ReadFileRecord(Vcb, MFTIndex, MftRecord);
4183 if (!NT_SUCCESS(Status))
4184 {
4185 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
4186 return Status;
4187 }
4188
4189 ASSERT(MftRecord->Ntfs.Type == NRH_FILE_TYPE);
4190 Status = FindAttribute(Vcb, MftRecord, AttributeIndexRoot, L"$I30", 4, &IndexRootCtx, NULL);
4191 if (!NT_SUCCESS(Status))
4192 {
4193 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
4194 return Status;
4195 }
4196
4197 IndexRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerIndexRecord, TAG_NTFS);
4198 if (IndexRecord == NULL)
4199 {
4200 ReleaseAttributeContext(IndexRootCtx);
4201 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
4203 }
4204
4205 ReadAttribute(Vcb, IndexRootCtx, 0, IndexRecord, Vcb->NtfsInfo.BytesPerIndexRecord);
4206 IndexRoot = (PINDEX_ROOT_ATTRIBUTE)IndexRecord;
4207 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)&IndexRoot->Header + IndexRoot->Header.FirstEntryOffset);
4208 /* Index root is always resident. */
4209 /* TotalSizeOfEntries is measured from the index header, not from the
4210 * start of the attribute, and FirstEntryOffset above already is. */
4211 IndexEntryEnd = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexRoot->Header + IndexRoot->Header.TotalSizeOfEntries);
4212 ReleaseAttributeContext(IndexRootCtx);
4213
4214 DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb->NtfsInfo.BytesPerIndexRecord, IndexRoot->SizeOfEntry);
4215
4217 MftRecord,
4218 (PINDEX_ROOT_ATTRIBUTE)IndexRecord,
4219 IndexRoot->SizeOfEntry,
4220 IndexEntry,
4221 IndexEntryEnd,
4222 FileName,
4223 FirstEntry,
4224 &CurrentEntry,
4225 DirSearch,
4226 CaseSensitive,
4227 OutMFTIndex);
4228
4229 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
4230 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
4231
4232 return Status;
4233}
4234
4237 PUNICODE_STRING PathName,
4238 BOOLEAN CaseSensitive,
4239 PFILE_RECORD_HEADER *FileRecord,
4240 PULONGLONG MFTIndex,
4241 ULONGLONG CurrentMFTIndex)
4242{
4243 UNICODE_STRING Current, Remaining;
4245 ULONG FirstEntry = 0;
4246
4247 DPRINT("NtfsLookupFileAt(%p, %wZ, %s, %p, %p, %I64x)\n",
4248 Vcb,
4249 PathName,
4250 CaseSensitive ? "TRUE" : "FALSE",
4251 FileRecord,
4252 MFTIndex,
4253 CurrentMFTIndex);
4254
4255 FsRtlDissectName(*PathName, &Current, &Remaining);
4256
4257 while (Current.Length != 0)
4258 {
4259 DPRINT("Current: %wZ\n", &Current);
4260
4261 Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, &Current, &FirstEntry, FALSE, CaseSensitive, &CurrentMFTIndex);
4262 if (!NT_SUCCESS(Status))
4263 {
4264 return Status;
4265 }
4266
4267 if (Remaining.Length == 0)
4268 break;
4269
4270 /* Split what is left of the path, not the component we just resolved */
4271 FsRtlDissectName(Remaining, &Current, &Remaining);
4272 }
4273
4274 *FileRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
4275 if (*FileRecord == NULL)
4276 {
4277 DPRINT("NtfsLookupFileAt: Can't allocate MFT record\n");
4279 }
4280
4281 Status = ReadFileRecord(Vcb, CurrentMFTIndex, *FileRecord);
4282 if (!NT_SUCCESS(Status))
4283 {
4284 DPRINT("NtfsLookupFileAt: Can't read MFT record\n");
4285 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, *FileRecord);
4286 return Status;
4287 }
4288
4289 *MFTIndex = CurrentMFTIndex;
4290
4291 return STATUS_SUCCESS;
4292}
4293
4296 PUNICODE_STRING PathName,
4297 BOOLEAN CaseSensitive,
4298 PFILE_RECORD_HEADER *FileRecord,
4299 PULONGLONG MFTIndex)
4300{
4301 return NtfsLookupFileAt(Vcb, PathName, CaseSensitive, FileRecord, MFTIndex, NTFS_FILE_ROOT);
4302}
4303
4304void
4306{
4307 ULONG i, j;
4308
4309 // dump binary data, 8 bytes at a time
4310 for (i = 0; i < Length; i += 8)
4311 {
4312 // display current offset, in hex
4313 DbgPrint("\t%03x\t", i);
4314
4315 // display hex value of each of the next 8 bytes
4316 for (j = 0; j < 8; j++)
4317 DbgPrint("%02x ", *(PUCHAR)(Buffer + i + j));
4318 DbgPrint("\n");
4319 }
4320}
4321
4341VOID
4343 PFILE_RECORD_HEADER FileRecord)
4344{
4345 ULONG i, j;
4346
4347 // dump binary data, 8 bytes at a time
4348 for (i = 0; i < FileRecord->BytesInUse; i += 8)
4349 {
4350 // display current offset, in hex
4351 DbgPrint("\t%03x\t", i);
4352
4353 // display hex value of each of the next 8 bytes
4354 for (j = 0; j < 8; j++)
4355 DbgPrint("%02x ", *(PUCHAR)((ULONG_PTR)FileRecord + i + j));
4356 DbgPrint("\n");
4357 }
4358
4359 NtfsDumpFileAttributes(Vcb, FileRecord);
4360}
4361
4364 PUNICODE_STRING SearchPattern,
4365 PULONG FirstEntry,
4366 PFILE_RECORD_HEADER *FileRecord,
4367 PULONGLONG MFTIndex,
4368 ULONGLONG CurrentMFTIndex,
4369 BOOLEAN CaseSensitive)
4370{
4372
4373 DPRINT("NtfsFindFileAt(%p, %wZ, %lu, %p, %p, %I64x, %s)\n",
4374 Vcb,
4375 SearchPattern,
4376 *FirstEntry,
4377 FileRecord,
4378 MFTIndex,
4379 CurrentMFTIndex,
4380 (CaseSensitive ? "TRUE" : "FALSE"));
4381
4382 Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, SearchPattern, FirstEntry, TRUE, CaseSensitive, &CurrentMFTIndex);
4383 if (!NT_SUCCESS(Status))
4384 {
4385 DPRINT("NtfsFindFileAt: NtfsFindMftRecord() failed with status 0x%08lx\n", Status);
4386 return Status;
4387 }
4388
4389 *FileRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
4390 if (*FileRecord == NULL)
4391 {
4392 DPRINT("NtfsFindFileAt: Can't allocate MFT record\n");
4394 }
4395
4396 Status = ReadFileRecord(Vcb, CurrentMFTIndex, *FileRecord);
4397 if (!NT_SUCCESS(Status))
4398 {
4399 DPRINT("NtfsFindFileAt: Can't read MFT record\n");
4400 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, *FileRecord);
4401 return Status;
4402 }
4403
4404 *MFTIndex = CurrentMFTIndex;
4405
4406 return STATUS_SUCCESS;
4407}
4408
4409
4410/* Deeper than any index this driver or Windows builds; past it the tree loops */
4411#define NTFS_MAX_INDEX_DEPTH 32
4412
4413static
4417 _In_ PNTFS_ATTR_CONTEXT IndexAllocationContext,
4418 _In_ ULONG IndexBlockSize,
4422
4428static
4432 _In_ PNTFS_ATTR_CONTEXT IndexAllocationContext,
4433 _In_ ULONG IndexBlockSize,
4434 _In_ ULONGLONG Vcn,
4437{
4438 PINDEX_BUFFER IndexBuffer;
4441
4442 if (IndexAllocationContext == NULL || Depth >= NTFS_MAX_INDEX_DEPTH)
4444
4445 IndexBuffer = ExAllocatePoolWithTag(NonPagedPool, IndexBlockSize, TAG_NTFS);
4446 if (IndexBuffer == NULL)
4448
4450 IndexAllocationContext,
4451 GetAllocationOffsetFromVCN(Vcb, IndexBlockSize, Vcn),
4452 (PCHAR)IndexBuffer,
4453 IndexBlockSize);
4454 if (BytesRead != IndexBlockSize || IndexBuffer->Ntfs.Type != NRH_INDX_TYPE)
4455 {
4456 DPRINT1("Unable to read index node at VCN %I64u\n", Vcn);
4457 ExFreePoolWithTag(IndexBuffer, TAG_NTFS);
4459 }
4460
4461 Status = FixupUpdateSequenceArray(Vcb, &((PFILE_RECORD_HEADER)IndexBuffer)->Ntfs);
4462 if (NT_SUCCESS(Status) &&
4463 FIELD_OFFSET(INDEX_BUFFER, Header) + IndexBuffer->Header.TotalSizeOfEntries > IndexBlockSize)
4464 {
4466 }
4467
4468 if (NT_SUCCESS(Status))
4469 {
4471 IndexAllocationContext,
4472 IndexBlockSize,
4473 &IndexBuffer->Header,
4474 Depth + 1,
4475 Empty);
4476 }
4477
4478 ExFreePoolWithTag(IndexBuffer, TAG_NTFS);
4479 return Status;
4480}
4481
4489static
4493 _In_ PNTFS_ATTR_CONTEXT IndexAllocationContext,
4494 _In_ ULONG IndexBlockSize,
4498{
4499 PINDEX_ENTRY_ATTRIBUTE IndexEntry;
4500
4501 /* A terminator has no key, so only the fixed header is guaranteed */
4502 if (Header->FirstEntryOffset + FIELD_OFFSET(INDEX_ENTRY_ATTRIBUTE, FileName) > Header->TotalSizeOfEntries)
4504
4505 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)Header + Header->FirstEntryOffset);
4506
4507 if (Header->FirstEntryOffset + IndexEntry->Length > Header->TotalSizeOfEntries)
4509
4510 if (!(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
4511 {
4512 *Empty = FALSE;
4513 return STATUS_SUCCESS;
4514 }
4515
4516 if (IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE)
4517 {
4518 /* The child's VCN sits in the last 8 bytes of the entry */
4519 if (IndexEntry->Length < FIELD_OFFSET(INDEX_ENTRY_ATTRIBUTE, FileName) + sizeof(ULONGLONG))
4521
4523 IndexAllocationContext,
4524 IndexBlockSize,
4525 GetIndexEntryVCN(IndexEntry),
4526 Depth,
4527 Empty);
4528 }
4529
4530 *Empty = TRUE;
4531 return STATUS_SUCCESS;
4532}
4533
4556 _In_ ULONGLONG MftIndex,
4558{
4559 PFILE_RECORD_HEADER FileRecord;
4560 PNTFS_ATTR_CONTEXT IndexRootContext = NULL;
4561 PNTFS_ATTR_CONTEXT IndexAllocationContext = NULL;
4562 PINDEX_ROOT_ATTRIBUTE IndexRoot = NULL;
4563 ULONG IndexRootLength;
4565
4566 *Empty = FALSE;
4567
4568 FileRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
4569 if (FileRecord == NULL)
4571
4572 Status = ReadFileRecord(Vcb, MftIndex, FileRecord);
4573 if (!NT_SUCCESS(Status))
4574 goto Quit;
4575
4576 Status = FindAttribute(Vcb, FileRecord, AttributeIndexRoot, L"$I30", 4, &IndexRootContext, NULL);
4577 if (!NT_SUCCESS(Status))
4578 goto Quit;
4579
4580 IndexRootLength = (ULONG)AttributeDataLength(IndexRootContext->pRecord);
4581 if (IndexRootLength < sizeof(*IndexRoot))
4582 {
4584 goto Quit;
4585 }
4586
4587 IndexRoot = ExAllocatePoolWithTag(NonPagedPool, IndexRootLength, TAG_NTFS);
4588 if (IndexRoot == NULL)
4589 {
4591 goto Quit;
4592 }
4593
4594 if (ReadAttribute(Vcb, IndexRootContext, 0, (PCHAR)IndexRoot, IndexRootLength) != IndexRootLength)
4595 {
4597 goto Quit;
4598 }
4599
4600 if (FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE, Header) + IndexRoot->Header.TotalSizeOfEntries > IndexRootLength)
4601 {
4603 goto Quit;
4604 }
4605
4606 /* Only a root that points down needs the allocation */
4607 if (IndexRoot->Header.Flags & INDEX_ROOT_LARGE)
4608 {
4609 Status = FindAttribute(Vcb, FileRecord, AttributeIndexAllocation, L"$I30", 4, &IndexAllocationContext, NULL);
4610 if (!NT_SUCCESS(Status))
4611 {
4613 goto Quit;
4614 }
4615 }
4616
4618 IndexAllocationContext,
4619 IndexRoot->SizeOfEntry,
4620 &IndexRoot->Header,
4621 0,
4622 Empty);
4623
4624Quit:
4625 if (IndexRoot != NULL)
4626 ExFreePoolWithTag(IndexRoot, TAG_NTFS);
4627 if (IndexAllocationContext != NULL)
4628 ReleaseAttributeContext(IndexAllocationContext);
4629 if (IndexRootContext != NULL)
4630 ReleaseAttributeContext(IndexRootContext);
4631 ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord);
4632
4633 return Status;
4634}
4635
4636/* EOF */
CRegistryTree Tree
#define ALIGN_UP_BY(size, align)
Type
Definition: Type.h:7
unsigned char BOOLEAN
Definition: actypes.h:127
LONG NTSTATUS
Definition: precomp.h:26
#define DPRINT1
Definition: precomp.h:8
@ Update
Definition: registry.c:565
PVOID Alloc(IN DWORD dwFlags, IN SIZE_T dwBytes)
Definition: main.c:63
struct NTFS_ATTR_RECORD * PNTFS_ATTR_RECORD
#define NTFS_FILE_NAME_DOS
Definition: ntfs.h:65
#define NTFS_INDEX_ENTRY_NODE
Definition: ntfs.h:60
#define NTFS_MFT_MASK
Definition: ntfs.h:76
#define NTFS_FILE_ROOT
Definition: ntfs.h:28
#define NTFS_FILE_UPCASE
Definition: ntfs.h:33
#define NTFS_INDEX_ENTRY_END
Definition: ntfs.h:61
#define NTFS_FILE_MFTMIRR
Definition: ntfs.h:24
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
VOID DestroyBTreeKey(PB_TREE_KEY Key)
Definition: btree.c:1506
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
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
NTSTATUS DemoteBTreeRoot(PB_TREE Tree)
Definition: btree.c:1094
ULONG GetSizeOfIndexEntries(PB_TREE_FILENAME_NODE Node)
Definition: btree.c:860
VOID DumpBTree(PB_TREE Tree)
Definition: btree.c:1629
NTSTATUS CreateIndexRootFromBTree(PDEVICE_EXTENSION DeviceExt, PB_TREE Tree, ULONG MaxIndexSize, PINDEX_ROOT_ATTRIBUTE *IndexRoot, ULONG *Length)
Definition: btree.c:915
PB_TREE_KEY CreateBTreeKeyFromFilename(ULONGLONG FileReference, PFILENAME_ATTRIBUTE FileNameAttribute)
Definition: btree.c:1466
return Found
Definition: dirsup.c:1270
_In_ PFCB Fcb
Definition: cdprocs.h:159
Definition: bufpool.h:45
Definition: Header.h:9
UINT Depth
Definition: d3d12.idl:4644
#define STATUS_NOT_IMPLEMENTED
Definition: d3dkmdt.h:42
#define STATUS_NO_MEMORY
Definition: d3dkmdt.h:51
@ NonResident
Definition: data.h:24
@ Resident
Definition: data.h:23
#define RtlClearBits
Definition: dbgbitmap.h:331
#define RtlInitializeBitMap
Definition: dbgbitmap.h:326
#define RtlFindClearBitsAndSet
Definition: dbgbitmap.h:333
#define RtlCheckBit
Definition: dbgbitmap.h:349
LPWSTR Name
Definition: desk.c:124
_In_ D3DDDI_VIDEO_PRESENT_TARGET_ID _In_ ULONG _In_ ULONG DataLength
Definition: dispmprt.h:233
_In_ ULONG AllocationSize
Definition: dispmprt.h:712
_In_ D3DDDI_VIDEO_PRESENT_TARGET_ID _In_ ULONG _In_ ULONG Flags
Definition: dispmprt.h:245
#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
_ACRTIMP size_t __cdecl wcslen(const wchar_t *)
Definition: wcs.c:2988
#define ULONG_MAX
Definition: limits.h:31
static const WCHAR Cleanup[]
Definition: register.c:80
#define L(x)
Definition: resources.c:13
@ Empty
Definition: npfs.h:126
NTSTATUS AddRun(PNTFS_VCB Vcb, PNTFS_ATTR_CONTEXT AttrContext, ULONG AttrOffset, PFILE_RECORD_HEADER FileRecord, ULONGLONG NextAssignedCluster, ULONG RunLength)
Definition: attrib.c:591
PSTANDARD_INFORMATION GetStandardInformationFromRecord(PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER FileRecord)
Definition: attrib.c:1970
NTSTATUS ConvertLargeMCBToDataRuns(PLARGE_MCB DataRunsMCB, ULONGLONG VcnCount, PUCHAR RunBuffer, ULONG MaxBufferSize, PULONG UsedBufferSize)
Definition: attrib.c:917
PUCHAR DecodeRun(PUCHAR DataRun, LONGLONG *DataRunOffset, ULONGLONG *DataRunLength)
Definition: attrib.c:1010
NTSTATUS ConvertDataRunsToLargeMCB(PUCHAR DataRun, PLARGE_MCB DataRunsMCB, PULONGLONG pNextVBN)
Definition: attrib.c:842
NTSTATUS FindFirstAttribute(PFIND_ATTR_CONTXT Context, PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER FileRecord, BOOLEAN OnlyResident, PNTFS_ATTR_RECORD *Attribute)
Definition: attrib.c:1413
NTSTATUS FindFirstAttributeListItem(PFIND_ATTR_CONTXT Context, PNTFS_ATTRIBUTE_LIST_ITEM *Item)
Definition: attrib.c:1337
NTSTATUS FreeClusters(PNTFS_VCB Vcb, PNTFS_ATTR_CONTEXT AttrContext, ULONG AttrOffset, PFILE_RECORD_HEADER FileRecord, ULONG ClustersToFree)
Definition: attrib.c:1101
VOID FindCloseAttribute(PFIND_ATTR_CONTXT Context)
Definition: attrib.c:1495
VOID NtfsDumpFileAttributes(PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER FileRecord)
Definition: attrib.c:1820
NTSTATUS FindNextAttribute(PFIND_ATTR_CONTXT Context, PNTFS_ATTR_RECORD *Attribute)
Definition: attrib.c:1461
NTSTATUS FindNextAttributeListItem(PFIND_ATTR_CONTXT Context, PNTFS_ATTRIBUTE_LIST_ITEM *Item)
Definition: attrib.c:1351
PFILENAME_ATTRIBUTE GetBestFileNameFromRecord(PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER FileRecord)
Definition: attrib.c:2015
PFILE_RECORD_HEADER NtfsCreateEmptyFileRecord(PDEVICE_EXTENSION DeviceExt)
Definition: create.c:1026
PNTFS_GLOBAL_DATA NtfsGlobalData
Definition: ntfs.c:36
struct FIXUP_ARRAY * PFIXUP_ARRAY
struct INDEX_BUFFER * PINDEX_BUFFER
struct FILENAME_ATTRIBUTE * PFILENAME_ATTRIBUTE
#define NRH_FILE_TYPE
Definition: ntfs.h:277
struct INDEX_ENTRY_ATTRIBUTE * PINDEX_ENTRY_ATTRIBUTE
#define NTFS_FILENAME_UPDATE_SIZES
Definition: ntfs.h:1332
#define NTFS_FILENAME_UPDATE_ATTRS
Definition: ntfs.h:1334
#define NTFS_SPARSE_LBO
Definition: ntfs.h:510
#define FRH_DIRECTORY
Definition: ntfs.h:299
#define NTFS_FILE_FIRST_USER_FILE
Definition: ntfs.h:223
#define INDEX_NODE_LARGE
Definition: ntfs.h:239
#define INDEX_ROOT_LARGE
Definition: ntfs.h:236
#define NTFS_FILE_TYPE_DIRECTORY
Definition: ntfs.h:259
#define NRH_INDX_TYPE
Definition: ntfs.h:278
#define NTFS_FILENAME_UPDATE_TIMES
Definition: ntfs.h:1333
struct INDEX_ROOT_ATTRIBUTE * PINDEX_ROOT_ATTRIBUTE
#define FILE_RECORD_END
Definition: ntfs.h:209
#define NTFS_ATTR_SPARSE
Definition: ntfs.h:263
@ AttributeEnd
Definition: ntfs.h:204
@ AttributeIndexRoot
Definition: ntfs.h:196
@ AttributeFileName
Definition: ntfs.h:190
@ AttributeIndexAllocation
Definition: ntfs.h:197
@ AttributeData
Definition: ntfs.h:195
@ AttributeBitmap
Definition: ntfs.h:198
#define TAG_NTFS
Definition: ntfs.h:12
#define ATTR_RECORD_ALIGNMENT
Definition: ntfs.h:351
#define NTFS_ATTR_COMPRESSION_MASK
Definition: ntfs.h:262
NTSTATUS NtfsAllocateClusters(PDEVICE_EXTENSION DeviceExt, ULONG FirstDesiredCluster, ULONG DesiredClusters, PULONG FirstAssignedCluster, PULONG AssignedClusters)
Definition: volinfo.c:213
#define ULONG_PTR
Definition: config.h:101
#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 RtlCompareMemory(s1, s2, l)
Definition: env_spec_w32.h:465
NTSTATUS RtlUpcaseUnicodeString(PUNICODE_STRING dst, PUNICODE_STRING src, BOOLEAN Alloc)
Definition: string_lib.cpp:46
#define ExAcquireResourceExclusiveLite(res, wait)
Definition: env_spec_w32.h:615
#define NonPagedPool
Definition: env_spec_w32.h:307
#define ROUND_UP(n, align)
Definition: eventvwr.h:34
struct _FileName FileName
Definition: fatprocs.h:897
_Must_inspect_result_ _In_ USHORT NewSize
Definition: fltkernel.h:975
VOID NTAPI CcSetFileSizes(IN PFILE_OBJECT FileObject, IN PCC_FILE_SIZES FileSizes)
Definition: fssup.c:356
Status
Definition: gdiplustypes.h:24
GLuint GLuint GLsizei count
Definition: gl.h:1545
GLuint buffer
Definition: glext.h:5915
GLuint index
Definition: glext.h:6031
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
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 GLint GLint j
Definition: glfuncs.h:250
#define DbgPrint
Definition: hal.h:12
static ULONG BitmapBuffer[(XMS_BLOCKS+31)/32]
Definition: himem.c:86
#define EXCEPTION_EXECUTE_HANDLER
Definition: excpt.h:90
#define KeLeaveCriticalRegion()
Definition: ke_x.h:119
#define KeEnterCriticalRegion()
Definition: ke_x.h:88
_In_ PIRP Irp
Definition: kse.h:79
_In_ PIRP _In_ PIO_COMPLETION_ROUTINE _In_ PVOID Context
Definition: kse.h:81
VOID NTAPI FsRtlUninitializeLargeMcb(IN PLARGE_MCB Mcb)
Definition: largemcb.c:1096
VOID NTAPI FsRtlInitializeLargeMcb(IN PLARGE_MCB Mcb, IN POOL_TYPE PoolType)
Definition: largemcb.c:451
BOOLEAN NTAPI FsRtlLookupLastLargeMcbEntry(IN PLARGE_MCB Mcb, OUT PLONGLONG Vbn, OUT PLONGLONG Lbn)
Definition: largemcb.c:718
NTSTATUS BrowseSubNodeIndexEntries(PNTFS_VCB Vcb, PFILE_RECORD_HEADER MftRecord, ULONG IndexBlockSize, PUNICODE_STRING FileName, PNTFS_ATTR_CONTEXT IndexAllocationContext, PRTL_BITMAP Bitmap, ULONGLONG VCN, PULONG StartEntry, PULONG CurrentEntry, BOOLEAN DirSearch, BOOLEAN CaseSensitive, ULONGLONG *OutMFTIndex)
Definition: mft.c:3815
void NtfsDumpData(ULONG_PTR Buffer, ULONG Length)
Definition: mft.c:4305
static NTSTATUS NtfsReadUpCaseTable(PDEVICE_EXTENSION Vcb)
Definition: mft.c:1866
NTSTATUS ReadFileRecord(PDEVICE_EXTENSION Vcb, ULONGLONG index, PFILE_RECORD_HEADER file)
Definition: mft.c:1936
static NTSTATUS NtfsIndexEntriesAreEmpty(_In_ PDEVICE_EXTENSION Vcb, _In_ PNTFS_ATTR_CONTEXT IndexAllocationContext, _In_ ULONG IndexBlockSize, _In_ PINDEX_HEADER_ATTRIBUTE Header, _In_ ULONG Depth, _Out_ PBOOLEAN Empty)
Definition: mft.c:4491
static NTSTATUS UpdateFileNameRecordWorker(PDEVICE_EXTENSION Vcb, ULONGLONG ParentMFTIndex, PUNICODE_STRING FileName, BOOLEAN DirSearch, PNTFS_FILENAME_UPDATE Update, BOOLEAN CaseSensitive)
Definition: mft.c:2082
NTSTATUS NtfsLookupFileAt(PDEVICE_EXTENSION Vcb, PUNICODE_STRING PathName, BOOLEAN CaseSensitive, PFILE_RECORD_HEADER *FileRecord, PULONGLONG MFTIndex, ULONGLONG CurrentMFTIndex)
Definition: mft.c:4236
NTSTATUS UpdateFileRecord(PDEVICE_EXTENSION Vcb, ULONGLONG MftIndex, PFILE_RECORD_HEADER FileRecord)
Definition: mft.c:2391
NTSTATUS NtfsUpdateDuplicatedInformation(PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER FileRecord, ULONGLONG MFTIndex, ULONG Flags, BOOLEAN CaseSensitive)
Definition: mft.c:1995
NTSTATUS NtfsLookupFile(PDEVICE_EXTENSION Vcb, PUNICODE_STRING PathName, BOOLEAN CaseSensitive, PFILE_RECORD_HEADER *FileRecord, PULONGLONG MFTIndex)
Definition: mft.c:4295
static ULONG GetMaxIndexRootSize(PDEVICE_EXTENSION DeviceExt, PFILE_RECORD_HEADER FileRecord, ULONG IndexRootOffset, PNTFS_ATTR_RECORD IndexRootRecord)
Definition: mft.c:2642
VOID ReleaseAttributeContext(PNTFS_ATTR_CONTEXT Context)
Definition: mft.c:104
NTSTATUS NtfsIsDirectoryEmpty(_In_ PDEVICE_EXTENSION Vcb, _In_ ULONGLONG MftIndex, _Out_ PBOOLEAN Empty)
Definition: mft.c:4554
NTSTATUS NtfsRemoveFilenameFromDirectory(PDEVICE_EXTENSION DeviceExt, ULONGLONG DirectoryMftIndex, PFILENAME_ATTRIBUTE FilenameAttribute, BOOLEAN CaseSensitive)
Definition: mft.c:3297
NTSTATUS NtfsExtendAllocation(_In_ PDEVICE_EXTENSION Vcb, _In_ PNTFS_ATTR_CONTEXT AttrContext, _In_ ULONG AttrOffset, _In_ PFILE_RECORD_HEADER FileRecord, _In_ ULONGLONG AllocationSize)
Definition: mft.c:753
NTSTATUS NtfsDeleteFileRecord(PDEVICE_EXTENSION DeviceExt, ULONGLONG MftIndex, BOOLEAN CaseSensitive)
Definition: mft.c:3470
NTSTATUS UpdateMftMirror(PNTFS_VCB Vcb)
Definition: mft.c:3690
PNTFS_ATTR_CONTEXT PrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord)
Definition: mft.c:41
ULONGLONG AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord)
Definition: mft.c:259
NTSTATUS UpdateIndexEntryFileName(PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER MftRecord, PCHAR IndexRecord, ULONG IndexBlockSize, PINDEX_ENTRY_ATTRIBUTE FirstEntry, PINDEX_ENTRY_ATTRIBUTE LastEntry, PUNICODE_STRING FileName, PULONG StartEntry, PULONG CurrentEntry, BOOLEAN DirSearch, PNTFS_FILENAME_UPDATE Update, BOOLEAN CaseSensitive)
Definition: mft.c:2215
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 BrowseIndexEntries(PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER MftRecord, PINDEX_ROOT_ATTRIBUTE IndexRecord, ULONG IndexBlockSize, PINDEX_ENTRY_ATTRIBUTE FirstEntry, PINDEX_ENTRY_ATTRIBUTE LastEntry, PUNICODE_STRING FileName, PULONG StartEntry, PULONG CurrentEntry, BOOLEAN DirSearch, BOOLEAN CaseSensitive, ULONGLONG *OutMFTIndex)
Definition: mft.c:3976
VOID SetFileRecordEnd(PFILE_RECORD_HEADER FileRecord, PNTFS_ATTR_RECORD AttrEnd, ULONG EndMarker)
Definition: mft.c:706
NTSTATUS IncreaseMftSize(PDEVICE_EXTENSION Vcb, BOOLEAN CanWait)
Definition: mft.c:293
NTSTATUS WriteAttributeFromIrp(PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset, const PUCHAR Buffer, ULONG Length, PULONG RealLengthWritten, PFILE_RECORD_HEADER FileRecord, PIRP Irp)
Definition: mft.c:1677
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
PNTFS_ATTR_RECORD MoveAttributes(PDEVICE_EXTENSION DeviceExt, PNTFS_ATTR_RECORD FirstAttributeToMove, ULONG FirstAttributeOffset, ULONG_PTR MoveTo)
Definition: mft.c:512
#define NTFS_MAX_INDEX_DEPTH
Definition: mft.c:4411
NTSTATUS UpdateFileNameRecord(PDEVICE_EXTENSION Vcb, ULONGLONG ParentMFTIndex, PUNICODE_STRING FileName, BOOLEAN DirSearch, PNTFS_FILENAME_UPDATE Update, BOOLEAN CaseSensitive)
Definition: mft.c:2191
NTSTATUS NtfsAddFilenameToDirectory(PDEVICE_EXTENSION DeviceExt, ULONGLONG DirectoryMftIndex, ULONGLONG FileReferenceNumber, PFILENAME_ATTRIBUTE FilenameAttribute, BOOLEAN CaseSensitive)
Definition: mft.c:3049
NTSTATUS NtfsResizeAllocation(_In_ PNTFS_FCB Fcb, _In_ PFILE_OBJECT FileObject, _In_ BOOLEAN CaseSensitive, _In_ ULONGLONG AllocationSize)
Definition: mft.c:924
static NTSTATUS AddFilenameToDirectoryWorker(PDEVICE_EXTENSION DeviceExt, ULONGLONG DirectoryMftIndex, ULONGLONG FileReferenceNumber, PFILENAME_ATTRIBUTE FilenameAttribute, BOOLEAN CaseSensitive)
Definition: mft.c:2704
ULONGLONG AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord)
Definition: mft.c:249
NTSTATUS AddNewMftEntry(PFILE_RECORD_HEADER FileRecord, PDEVICE_EXTENSION DeviceExt, PULONGLONG DestinationIndex, BOOLEAN CanWait)
Definition: mft.c:2482
VOID NtfsDumpFileRecord(PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER FileRecord)
Definition: mft.c:4342
BOOLEAN CompareFileName(PDEVICE_EXTENSION Vcb, PUNICODE_STRING FileName, PINDEX_ENTRY_ATTRIBUTE IndexEntry, BOOLEAN DirSearch, BOOLEAN CaseSensitive)
Definition: mft.c:3622
ULONG ReadAttributeToIrp(PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset, PCHAR Buffer, ULONG Length, PIRP Irp)
Definition: mft.c:1556
NTSTATUS FreeMftEntry(PDEVICE_EXTENSION DeviceExt, ULONGLONG MftIndex)
Definition: mft.c:3336
NTSTATUS SetResidentAttributeDataLength(PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT AttrContext, ULONG AttrOffset, PFILE_RECORD_HEADER FileRecord, PLARGE_INTEGER DataSize)
Definition: mft.c:1148
NTSTATUS NtfsFindMftRecord(PDEVICE_EXTENSION Vcb, ULONGLONG MFTIndex, PUNICODE_STRING FileName, PULONG FirstEntry, BOOLEAN DirSearch, BOOLEAN CaseSensitive, ULONGLONG *OutMFTIndex)
Definition: mft.c:4151
NTSTATUS NtfsTrimAllocation(_In_ PNTFS_FCB Fcb)
Definition: mft.c:842
NTSTATUS ReadLCN(PDEVICE_EXTENSION Vcb, ULONGLONG lcn, ULONG count, PVOID buffer)
Definition: mft.c:3603
NTSTATUS NtfsLoadUpCaseTable(PDEVICE_EXTENSION Vcb)
Definition: mft.c:1930
NTSTATUS FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb, PNTFS_RECORD_HEADER Record)
Definition: mft.c:2425
NTSTATUS InternalSetResidentAttributeLength(PDEVICE_EXTENSION DeviceExt, PNTFS_ATTR_CONTEXT AttrContext, PFILE_RECORD_HEADER FileRecord, ULONG AttrOffset, ULONG DataSize)
Definition: mft.c:542
NTSTATUS NtfsFindFileAt(PDEVICE_EXTENSION Vcb, PUNICODE_STRING SearchPattern, PULONG FirstEntry, PFILE_RECORD_HEADER *FileRecord, PULONGLONG MFTIndex, ULONGLONG CurrentMFTIndex, BOOLEAN CaseSensitive)
Definition: mft.c:4363
static NTSTATUS RemoveFilenameFromDirectoryWorker(PDEVICE_EXTENSION DeviceExt, ULONGLONG DirectoryMftIndex, PFILENAME_ATTRIBUTE FilenameAttribute, BOOLEAN CaseSensitive)
Definition: mft.c:3096
static NTSTATUS NtfsMapAttributeRuns(PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset, ULONG Length, PNTFS_IO_RUN_LIST RunList, PULONG MappedLength)
Definition: mft.c:1362
static NTSTATUS NtfsIndexNodeIsEmpty(_In_ PDEVICE_EXTENSION Vcb, _In_ PNTFS_ATTR_CONTEXT IndexAllocationContext, _In_ ULONG IndexBlockSize, _In_ ULONGLONG Vcn, _In_ ULONG Depth, _Out_ PBOOLEAN Empty)
Definition: mft.c:4430
NTSTATUS SetAttributeDataLength(PFILE_OBJECT FileObject, PNTFS_FCB Fcb, PNTFS_ATTR_CONTEXT AttrContext, ULONG AttrOffset, PFILE_RECORD_HEADER FileRecord, PLARGE_INTEGER DataSize)
Definition: mft.c:615
#define ASSERT(a)
Definition: mode.c:44
#define ExFreePoolWithTag(_P, _T)
Definition: module.h:1109
#define min(a, b)
Definition: monoChain.cc:55
_In_ NDIS_STATUS _In_ ULONG _In_ USHORT _In_opt_ PVOID _In_ ULONG DataSize
Definition: ndis.h:4755
_In_ PUNICODE_STRING _Inout_ PUNICODE_STRING Destination
Definition: rtlfuncs.h:3051
#define _Out_
Definition: no_sal2.h:160
#define _In_
Definition: no_sal2.h:158
NTSYSAPI VOID NTAPI RtlFreeUnicodeString(PUNICODE_STRING UnicodeString)
__GNU_EXTENSION typedef unsigned __int64 * PULONGLONG
Definition: ntbasedef.h:395
_In_ ULONG _In_ ULONG Offset
Definition: ntddpcm.h:101
_In_ ULONG _In_ ULONG _In_ ULONG Length
Definition: ntddpcm.h:102
VOID NtfsFreeIoRunList(IN OUT PNTFS_IO_RUN_LIST RunList)
Definition: blockdev.c:140
NTSTATUS NtfsAddIoRun(IN OUT PNTFS_IO_RUN_LIST RunList, IN LONGLONG Lbo, IN ULONG ByteCount)
Definition: blockdev.c:74
NTSTATUS NtfsReadSectors(IN PDEVICE_OBJECT DeviceObject, IN ULONG DiskSector, IN ULONG SectorCount, IN ULONG SectorSize, IN OUT PUCHAR Buffer, IN BOOLEAN Override)
Definition: blockdev.c:1011
VOID NtfsInitIoRunList(OUT PNTFS_IO_RUN_LIST RunList)
Definition: blockdev.c:40
NTSTATUS NtfsPerformIrpIoRuns(IN PDEVICE_OBJECT DeviceObject, IN UCHAR MajorFunction, IN ULONG SectorSize, IN PIRP Irp, IN OUT PUCHAR Buffer, IN PNTFS_IO_RUN_LIST RunList, IN BOOLEAN Override, OUT PULONG BytesTransferred)
Definition: blockdev.c:743
ULONGLONG NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt, PFILE_RECORD_HEADER FileRecord, PCWSTR Stream, ULONG StreamLength, PULONGLONG AllocatedSize)
Definition: dirctl.c:38
VOID FASTCALL ExReleaseResourceLite(IN PERESOURCE Resource)
Definition: resource.c:1822
VOID NTAPI FsRtlDissectName(IN UNICODE_STRING Name, OUT PUNICODE_STRING FirstPart, OUT PUNICODE_STRING RemainingPart)
Definition: name.c:147
#define STATUS_CANT_WAIT
Definition: ntstatus.h:546
#define STATUS_USER_MAPPED_FILE
Definition: ntstatus.h:833
#define STATUS_UNEXPECTED_IO_ERROR
Definition: ntstatus.h:563
#define STATUS_PARTIAL_COPY
Definition: ntstatus.h:273
#define STATUS_DATA_ERROR
Definition: ntstatus.h:392
short WCHAR
Definition: pedump.c:58
long LONG
Definition: pedump.c:60
unsigned short USHORT
Definition: pedump.c:61
#define Vcb
Definition: cdprocs.h:1415
#define _SEH2_GetExceptionCode()
Definition: pseh2_64.h:204
#define _SEH2_EXCEPT(...)
Definition: pseh2_64.h:104
#define _SEH2_END
Definition: pseh2_64.h:194
#define _SEH2_TRY
Definition: pseh2_64.h:93
#define _SEH2_YIELD(__stmt)
Definition: pseh2_64.h:207
#define IRP_MJ_READ
Definition: rdpdr.c:46
#define IRP_MJ_WRITE
Definition: rdpdr.c:47
BOOLEAN NTAPI MmCanFileBeTruncated(_In_ PSECTION_OBJECT_POINTERS SectionObjectPointer, _In_opt_ PLARGE_INTEGER NewFileSize)
Definition: section.c:4312
#define STATUS_END_OF_FILE
Definition: shellext.h:67
#define STATUS_SUCCESS
Definition: shellext.h:65
#define DPRINT
Definition: sndvol32.h:73
_In_ BOOLEAN Read
Definition: strmini.h:479
Definition: ntfs.h:485
PB_TREE_FILENAME_NODE RootNode
Definition: ntfs.h:486
ULONGLONG LastWriteTime
Definition: ntfs.h:393
UCHAR NameLength
Definition: ntfs.h:408
ULONGLONG DirectoryFileReferenceNumber
Definition: ntfs.h:391
ULONGLONG DataSize
Definition: ntfs.h:397
UCHAR NameType
Definition: ntfs.h:409
ULONGLONG AllocatedSize
Definition: ntfs.h:396
ULONG FileAttributes
Definition: ntfs.h:398
ULONGLONG LastAccessTime
Definition: ntfs.h:395
ULONGLONG CreationTime
Definition: ntfs.h:392
WCHAR Name[1]
Definition: ntfs.h:410
ULONGLONG ChangeTime
Definition: ntfs.h:394
USHORT Array[]
Definition: ntfs.h:645
USHORT USN
Definition: ntfs.h:644
INDEX_HEADER_ATTRIBUTE Header
Definition: ntfs.h:436
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
ULONG Reserved
Definition: ntfs.h:451
USHORT Flags
Definition: ntfs.h:456
ULONG FirstEntryOffset
Definition: ntfs.h:415
ULONG TotalSizeOfEntries
Definition: ntfs.h:416
INDEX_HEADER_ATTRIBUTE Header
Definition: ntfs.h:429
ULONG SizeOfEntry
Definition: ntfs.h:426
ULONGLONG MFTIndex
Definition: ntfs.h:345
struct NTFS_ATTR_RECORD::@170::@172 Resident
ULONG Length
Definition: ntfs.h:135
USHORT NameOffset
Definition: ntfs.h:138
struct NTFS_ATTR_RECORD::@170::@173 NonResident
UCHAR NameLength
Definition: ntfs.h:137
UCHAR IsNonResident
Definition: ntfs.h:136
ULONG Type
Definition: ntfs.h:134
BOOLEAN EnableWriteSupport
Definition: ntfs.h:182
NPAGED_LOOKASIDE_LIST AttrCtxtLookasideList
Definition: ntfs.h:181
ULONGLONG LastAccessTime
Definition: ntfs.h:364
ULONGLONG ChangeTime
Definition: ntfs.h:363
ULONGLONG LastWriteTime
Definition: ntfs.h:362
ULONG FileAttribute
Definition: ntfs.h:365
ULONGLONG CreationTime
Definition: ntfs.h:361
Definition: cdstruc.h:902
PVCB Vcb
Definition: cdstruc.h:933
FSRTL_COMMON_FCB_HEADER RFCB
Definition: ntfs.h:593
ULONGLONG MFTIndex
Definition: ntfs.h:622
WCHAR Stream[MAX_PATH]
Definition: ntfs.h:602
ERESOURCE PagingIoResource
Definition: ntfs.h:606
USHORT SequenceNumber
Definition: ntfs.h:284
USHORT Flags
Definition: ntfs.h:287
NTFS_RECORD_HEADER Ntfs
Definition: ntfs.h:283
ULONG MFTRecordNumber
Definition: ntfs.h:293
USHORT AttributeOffset
Definition: ntfs.h:286
ULONG BytesInUse
Definition: ntfs.h:288
LARGE_INTEGER AllocationSize
Definition: env_spec_w32.h:755
LARGE_INTEGER ValidDataLength
Definition: env_spec_w32.h:757
TCHAR Name[MAX_PATH]
Definition: filecomp.c:349
USHORT MaximumLength
Definition: env_spec_w32.h:370
Definition: fci.c:123
#define max(a, b)
Definition: svc.c:63
#define STATUS_PENDING
Definition: telnetd.h:14
uint32_t * PULONG
Definition: typedefs.h:59
const uint16_t * PCWSTR
Definition: typedefs.h:57
unsigned char UCHAR
Definition: typedefs.h:53
#define FIELD_OFFSET(t, f)
Definition: typedefs.h:255
unsigned char * PBOOLEAN
Definition: typedefs.h:53
int64_t LONGLONG
Definition: typedefs.h:68
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
#define RtlMoveMemory(Destination, Source, Length)
Definition: typedefs.h:264
uint16_t * PWCHAR
Definition: typedefs.h:56
unsigned char * PUCHAR
Definition: typedefs.h:53
uint32_t ULONG
Definition: typedefs.h:59
char * PCHAR
Definition: typedefs.h:51
#define STATUS_OBJECT_PATH_NOT_FOUND
Definition: udferr_usr.h:151
#define STATUS_INVALID_PARAMETER
Definition: udferr_usr.h:135
#define STATUS_UNRECOGNIZED_VOLUME
Definition: udferr_usr.h:173
#define STATUS_UNSUCCESSFUL
Definition: udferr_usr.h:132
#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
LONGLONG QuadPart
Definition: typedefs.h:114
ULONG LowPart
Definition: typedefs.h:106
struct _LARGE_INTEGER::@2753 u
_In_ WDFREQUEST _In_ WDFFILEOBJECT FileObject
Definition: wdfdevice.h:550
_Must_inspect_result_ _In_ WDFDEVICE _In_ PWDF_DEVICE_PROPERTY_DATA _In_ DEVPROPTYPE _In_ ULONG Size
Definition: wdfdevice.h:4539
_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
_Out_ PUSHORT NodeNumber
Definition: iofuncs.h:2545
* PFILE_OBJECT
Definition: iotypes.h:1998
_In_ struct _KBUGCHECK_REASON_CALLBACK_RECORD * Record
Definition: ketypes.h:320
#define NT_ASSERT
Definition: rtlfuncs.h:3334
#define NT_VERIFY(exp)
Definition: rtlfuncs.h:3311