https://git.reactos.org/?p=reactos.git;a=commitdiff;h=5e7c11842ad0b24a9a7bc8...
commit 5e7c11842ad0b24a9a7bc847e938a9cda0b2748e Author: Trevor Thompson tmt256@email.vccs.edu AuthorDate: Mon Aug 28 03:11:38 2017 +0000
[NTFS] - Fix increasing the mft size, to keep chkdsk happy. IncreaseMftSize() - Add some fixes. Write blank records to newly-allocated mft entries, and update $MFTMirr when finished; these changes are needed for chkdsk. Increase size by 64 records instead of 8. +UpdateMftMirror() - Backs up the first ~4 master file table entries to the $MFTMirr file.
svn path=/branches/GSoC_2016/NTFS/; revision=75694 --- drivers/filesystems/ntfs/mft.c | 200 ++++++++++++++++++++++++++++++++++++++-- drivers/filesystems/ntfs/ntfs.h | 3 + 2 files changed, 195 insertions(+), 8 deletions(-)
diff --git a/drivers/filesystems/ntfs/mft.c b/drivers/filesystems/ntfs/mft.c index 7d0157a761..547ab64a2b 100644 --- a/drivers/filesystems/ntfs/mft.c +++ b/drivers/filesystems/ntfs/mft.c @@ -30,6 +30,7 @@ /* INCLUDES *****************************************************************/
#include "ntfs.h" +#include <ntintsafe.h>
#define NDEBUG #include <debug.h> @@ -228,7 +229,7 @@ AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord) * STATUS_CANT_WAIT if CanWait was FALSE and the function could not get immediate, exclusive access to the MFT. * * @remarks -* Increases the size of the Master File Table by 8 records. Bitmap entries for the new records are cleared, +* Increases the size of the Master File Table by 64 records. Bitmap entries for the new records are cleared, * and the bitmap is also enlarged if needed. Mimicking Windows' behavior when enlarging the mft is still TODO. * This function will wait for exlusive access to the volume fcb. */ @@ -239,13 +240,17 @@ IncreaseMftSize(PDEVICE_EXTENSION Vcb, BOOLEAN CanWait) LARGE_INTEGER BitmapSize; LARGE_INTEGER DataSize; LONGLONG BitmapSizeDifference; - ULONG DataSizeDifference = Vcb->NtfsInfo.BytesPerFileRecord * 8; + ULONG NewRecords = ATTR_RECORD_ALIGNMENT * 8; // Allocate one new record for every bit of every byte we'll be adding to the bitmap + ULONG DataSizeDifference = Vcb->NtfsInfo.BytesPerFileRecord * NewRecords; ULONG BitmapOffset; PUCHAR BitmapBuffer; ULONGLONG BitmapBytes; ULONGLONG NewBitmapSize; + ULONGLONG FirstNewMftIndex; ULONG BytesRead; ULONG LengthWritten; + PFILE_RECORD_HEADER BlankFileRecord; + ULONG i; NTSTATUS Status;
DPRINT1("IncreaseMftSize(%p, %s)\n", Vcb, CanWait ? "TRUE" : "FALSE"); @@ -256,11 +261,23 @@ IncreaseMftSize(PDEVICE_EXTENSION Vcb, BOOLEAN CanWait) return STATUS_CANT_WAIT; }
+ // Create a blank file record that will be used later + BlankFileRecord = NtfsCreateEmptyFileRecord(Vcb); + if (!BlankFileRecord) + { + DPRINT1("Error: Unable to create empty file record!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // Clear the flags (file record is not in use) + BlankFileRecord->Flags = 0; + // Find the bitmap attribute of master file table - Status = FindAttribute(Vcb, Vcb->MasterFileTable, AttributeBitmap, L"", 0, &BitmapContext, &BitmapOffset); + Status = FindAttribute(Vcb, Vcb->MasterFileTable, AttributeBitmap, L"", 0, &BitmapContext, NULL); if (!NT_SUCCESS(Status)) { DPRINT1("ERROR: Couldn't find $BITMAP attribute of Mft!\n"); + ExFreePoolWithTag(BlankFileRecord, TAG_NTFS); ExReleaseResourceLite(&(Vcb->DirResource)); return Status; } @@ -271,9 +288,17 @@ IncreaseMftSize(PDEVICE_EXTENSION Vcb, BOOLEAN CanWait) // Calculate the new mft size DataSize.QuadPart = AttributeDataLength(Vcb->MFTContext->pRecord) + DataSizeDifference;
+ // Find the index of the first Mft entry that will be created + FirstNewMftIndex = AttributeDataLength(Vcb->MFTContext->pRecord) / Vcb->NtfsInfo.BytesPerFileRecord; + // Determine how many bytes will make up the bitmap BitmapBytes = DataSize.QuadPart / Vcb->NtfsInfo.BytesPerFileRecord / 8; - + if ((DataSize.QuadPart / Vcb->NtfsInfo.BytesPerFileRecord) % 8 != 0) + BitmapBytes++; + + // Windows will always keep the number of bytes in a bitmap as a multiple of 8, so no bytes are wasted on slack + BitmapBytes = ALIGN_UP_BY(BitmapBytes, ATTR_RECORD_ALIGNMENT); + // Determine how much we need to adjust the bitmap size (it's possible we don't) BitmapSizeDifference = BitmapBytes - BitmapSize.QuadPart; NewBitmapSize = max(BitmapSize.QuadPart + BitmapSizeDifference, BitmapSize.QuadPart); @@ -283,6 +308,7 @@ IncreaseMftSize(PDEVICE_EXTENSION Vcb, BOOLEAN CanWait) if (!BitmapBuffer) { DPRINT1("ERROR: Unable to allocate memory for bitmap attribute!\n"); + ExFreePoolWithTag(BlankFileRecord, TAG_NTFS); ExReleaseResourceLite(&(Vcb->DirResource)); ReleaseAttributeContext(BitmapContext); return STATUS_INSUFFICIENT_RESOURCES; @@ -300,6 +326,7 @@ IncreaseMftSize(PDEVICE_EXTENSION Vcb, BOOLEAN CanWait) if (BytesRead != BitmapSize.LowPart) { DPRINT1("ERROR: Bytes read != Bitmap size!\n"); + ExFreePoolWithTag(BlankFileRecord, TAG_NTFS); ExReleaseResourceLite(&(Vcb->DirResource)); ExFreePoolWithTag(BitmapBuffer, TAG_NTFS); ReleaseAttributeContext(BitmapContext); @@ -311,17 +338,29 @@ IncreaseMftSize(PDEVICE_EXTENSION Vcb, BOOLEAN CanWait) if (!NT_SUCCESS(Status)) { DPRINT1("ERROR: Failed to set size of $MFT data attribute!\n"); + ExFreePoolWithTag(BlankFileRecord, TAG_NTFS); ExReleaseResourceLite(&(Vcb->DirResource)); ExFreePoolWithTag(BitmapBuffer, TAG_NTFS); ReleaseAttributeContext(BitmapContext); return Status; } + + // We'll need to find the bitmap again, because its offset will have changed after resizing the data attribute + ReleaseAttributeContext(BitmapContext); + Status = FindAttribute(Vcb, Vcb->MasterFileTable, AttributeBitmap, L"", 0, &BitmapContext, &BitmapOffset); + if (!NT_SUCCESS(Status)) + { + DPRINT1("ERROR: Couldn't find $BITMAP attribute of Mft!\n"); + ExFreePoolWithTag(BlankFileRecord, TAG_NTFS); + ExReleaseResourceLite(&(Vcb->DirResource)); + return Status; + }
// If the bitmap grew if (BitmapSizeDifference > 0) { // Set the new bitmap size - BitmapSize.QuadPart += BitmapSizeDifference; + BitmapSize.QuadPart = NewBitmapSize; if (BitmapContext->pRecord->IsNonResident) Status = SetNonResidentAttributeDataLength(Vcb, BitmapContext, BitmapOffset, Vcb->MasterFileTable, &BitmapSize); else @@ -330,6 +369,7 @@ IncreaseMftSize(PDEVICE_EXTENSION Vcb, BOOLEAN CanWait) if (!NT_SUCCESS(Status)) { DPRINT1("ERROR: Failed to set size of bitmap attribute!\n"); + ExFreePoolWithTag(BlankFileRecord, TAG_NTFS); ExReleaseResourceLite(&(Vcb->DirResource)); ExFreePoolWithTag(BitmapBuffer, TAG_NTFS); ReleaseAttributeContext(BitmapContext); @@ -337,13 +377,14 @@ IncreaseMftSize(PDEVICE_EXTENSION Vcb, BOOLEAN CanWait) } }
- //NtfsDumpFileAttributes(Vcb, FileRecord); + NtfsDumpFileAttributes(Vcb, Vcb->MasterFileTable);
// Update the file record with the new attribute sizes Status = UpdateFileRecord(Vcb, Vcb->VolumeFcb->MFTIndex, Vcb->MasterFileTable); if (!NT_SUCCESS(Status)) { DPRINT1("ERROR: Failed to update $MFT file record!\n"); + ExFreePoolWithTag(BlankFileRecord, TAG_NTFS); ExReleaseResourceLite(&(Vcb->DirResource)); ExFreePoolWithTag(BitmapBuffer, TAG_NTFS); ReleaseAttributeContext(BitmapContext); @@ -351,9 +392,10 @@ IncreaseMftSize(PDEVICE_EXTENSION Vcb, BOOLEAN CanWait) }
// Write out the new bitmap - Status = WriteAttribute(Vcb, BitmapContext, BitmapOffset, BitmapBuffer, BitmapSize.LowPart, &LengthWritten, Vcb->MasterFileTable); + Status = WriteAttribute(Vcb, BitmapContext, 0, BitmapBuffer, NewBitmapSize, &LengthWritten, Vcb->MasterFileTable); if (!NT_SUCCESS(Status)) { + ExFreePoolWithTag(BlankFileRecord, TAG_NTFS); ExReleaseResourceLite(&(Vcb->DirResource)); ExFreePoolWithTag(BitmapBuffer, TAG_NTFS); ReleaseAttributeContext(BitmapContext); @@ -361,12 +403,31 @@ IncreaseMftSize(PDEVICE_EXTENSION Vcb, BOOLEAN CanWait) return Status; }
+ // Create blank records for the new file record entries. + for (i = 0; i < NewRecords; i++) + { + Status = UpdateFileRecord(Vcb, FirstNewMftIndex + i, BlankFileRecord); + if (!NT_SUCCESS(Status)) + { + DPRINT1("ERROR: Failed to write blank file record!\n"); + ExFreePoolWithTag(BlankFileRecord, TAG_NTFS); + ExReleaseResourceLite(&(Vcb->DirResource)); + ExFreePoolWithTag(BitmapBuffer, TAG_NTFS); + ReleaseAttributeContext(BitmapContext); + return Status; + } + } + + // Update the mft mirror + Status = UpdateMftMirror(Vcb); + // Cleanup + ExFreePoolWithTag(BlankFileRecord, TAG_NTFS); ExReleaseResourceLite(&(Vcb->DirResource)); ExFreePoolWithTag(BitmapBuffer, TAG_NTFS); ReleaseAttributeContext(BitmapContext);
- return STATUS_SUCCESS; + return Status; }
/** @@ -2384,6 +2445,129 @@ CompareFileName(PUNICODE_STRING FileName, } }
+/** +* @name UpdateMftMirror +* @implemented +* +* Backs-up the first ~4 master file table entries to the $MFTMirr file. +* +* @param Vcb +* Pointer to an NTFS_VCB for the volume whose Mft mirror is being updated. +* +* @returninja livecd + +* STATUS_SUCCESS on success. +* STATUS_INSUFFICIENT_RESOURCES if an allocation failed. +* STATUS_UNSUCCESSFUL if we couldn't read the master file table. +* +* @remarks +* NTFS maintains up-to-date copies of the first several mft entries in the $MFTMirr file. Usually, the first 4 file +* records from the mft are stored. The exact number of entries is determined by the size of $MFTMirr's $DATA. +* If $MFTMirr is not up-to-date, chkdsk will reject every change it can find prior to when $MFTMirr was last updated. +* Therefore, it's recommended to call this function if the volume changes considerably. For instance, IncreaseMftSize() +* relies on this function to keep chkdsk from deleting the mft entries it creates. Note that under most instances, creating +* or deleting a file will not affect the first ~four mft entries, and so will not require updating the mft mirror. +*/ +NTSTATUS +UpdateMftMirror(PNTFS_VCB Vcb) +{ + PFILE_RECORD_HEADER MirrorFileRecord; + PNTFS_ATTR_CONTEXT MirrDataContext; + PNTFS_ATTR_CONTEXT MftDataContext; + PCHAR DataBuffer; + ULONGLONG DataLength; + NTSTATUS Status; + ULONG BytesRead; + ULONG LengthWritten; + + // Allocate memory for the Mft mirror file record + MirrorFileRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS); + if (!MirrorFileRecord) + { + DPRINT1("Error: Failed to allocate memory for $MFTMirr!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // Read the Mft Mirror file record + Status = ReadFileRecord(Vcb, NTFS_FILE_MFTMIRR, MirrorFileRecord); + if (!NT_SUCCESS(Status)) + { + DPRINT1("ERROR: Failed to read $MFTMirr!\n"); + ExFreePoolWithTag(MirrorFileRecord, TAG_NTFS); + return Status; + } + + // Find the $DATA attribute of $MFTMirr + Status = FindAttribute(Vcb, MirrorFileRecord, AttributeData, L"", 0, &MirrDataContext, NULL); + if (!NT_SUCCESS(Status)) + { + DPRINT1("ERROR: Couldn't find $DATA attribute!\n"); + ExFreePoolWithTag(MirrorFileRecord, TAG_NTFS); + return Status; + } + + // Find the $DATA attribute of $MFT + Status = FindAttribute(Vcb, Vcb->MasterFileTable, AttributeData, L"", 0, &MftDataContext, NULL); + if (!NT_SUCCESS(Status)) + { + DPRINT1("ERROR: Couldn't find $DATA attribute!\n"); + ReleaseAttributeContext(MirrDataContext); + ExFreePoolWithTag(MirrorFileRecord, TAG_NTFS); + return Status; + } + + // Get the size of the mirror's $DATA attribute + DataLength = AttributeDataLength(MirrDataContext->pRecord); + + ASSERT(DataLength % Vcb->NtfsInfo.BytesPerFileRecord == 0); + + // Create buffer for the mirror's $DATA attribute + DataBuffer = ExAllocatePoolWithTag(NonPagedPool, DataLength, TAG_NTFS); + if (!DataBuffer) + { + DPRINT1("Error: Couldn't allocate memory for $DATA buffer!\n"); + ReleaseAttributeContext(MftDataContext); + ReleaseAttributeContext(MirrDataContext); + ExFreePoolWithTag(MirrorFileRecord, TAG_NTFS); + return STATUS_INSUFFICIENT_RESOURCES; + } + + ASSERT(DataLength < ULONG_MAX); + + // Back up the first several entries of the Mft's $DATA Attribute + BytesRead = ReadAttribute(Vcb, MftDataContext, 0, DataBuffer, (ULONG)DataLength); + if (BytesRead != (ULONG)DataLength) + { + DPRINT1("Error: Failed to read $DATA for $MFTMirr!\n"); + ReleaseAttributeContext(MftDataContext); + ReleaseAttributeContext(MirrDataContext); + ExFreePoolWithTag(DataBuffer, TAG_NTFS); + ExFreePoolWithTag(MirrorFileRecord, TAG_NTFS); + return STATUS_UNSUCCESSFUL; + } + + // Write the mirror's $DATA attribute + Status = WriteAttribute(Vcb, + MirrDataContext, + 0, + (PUCHAR)DataBuffer, + DataLength, + &LengthWritten, + MirrorFileRecord); + if (!NT_SUCCESS(Status)) + { + DPRINT1("ERROR: Failed to write $DATA attribute of $MFTMirr!\n"); + } + + // Cleanup + ReleaseAttributeContext(MftDataContext); + ReleaseAttributeContext(MirrDataContext); + ExFreePoolWithTag(DataBuffer, TAG_NTFS); + ExFreePoolWithTag(MirrorFileRecord, TAG_NTFS); + + return Status; +} + #if 0 static VOID diff --git a/drivers/filesystems/ntfs/ntfs.h b/drivers/filesystems/ntfs/ntfs.h index abd2d2791d..aba8b29196 100644 --- a/drivers/filesystems/ntfs/ntfs.h +++ b/drivers/filesystems/ntfs/ntfs.h @@ -1047,6 +1047,9 @@ CompareFileName(PUNICODE_STRING FileName, BOOLEAN DirSearch, BOOLEAN CaseSensitive);
+NTSTATUS +UpdateMftMirror(PNTFS_VCB Vcb); + NTSTATUS ReadFileRecord(PDEVICE_EXTENSION Vcb, ULONGLONG index,