mirror of
https://github.com/reactos/reactos.git
synced 2024-12-29 10:35:28 +00:00
[NTFS] - Add support for expanding the master file table. Fix a bug with BrowseIndexEntries(). Improve diagnostic output.
-AddNewMftEntry() - Increase size of MFT as needed. Fix math for bitmap length. Don't assign file records to MFT indices 0x10 - 0x17; In Windows, these records aren't used unless they have to be, even though they are marked as unused in the bitmap. +IncreaseMftSize() - Adds room for additional file records in the master file table. -BrowseIndexEntries() - allow for the rare situation when a non-system file has an MFT index of 0x10. svn path=/branches/GSoC_2016/NTFS/; revision=75056
This commit is contained in:
parent
1417f286e0
commit
9ab86116a9
2 changed files with 192 additions and 9 deletions
|
@ -649,6 +649,8 @@ NtfsCreateFileRecord(PDEVICE_EXTENSION DeviceExt,
|
||||||
ULONGLONG ParentMftIndex;
|
ULONGLONG ParentMftIndex;
|
||||||
ULONGLONG FileMftIndex;
|
ULONGLONG FileMftIndex;
|
||||||
|
|
||||||
|
DPRINT1("NtfsCreateFileRecord(%p, %p)\n", DeviceExt, FileObject);
|
||||||
|
|
||||||
// allocate memory for file record
|
// allocate memory for file record
|
||||||
FileRecord = ExAllocatePoolWithTag(NonPagedPool,
|
FileRecord = ExAllocatePoolWithTag(NonPagedPool,
|
||||||
DeviceExt->NtfsInfo.BytesPerFileRecord,
|
DeviceExt->NtfsInfo.BytesPerFileRecord,
|
||||||
|
|
|
@ -186,6 +186,161 @@ AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord)
|
||||||
return AttrRecord->Resident.ValueLength;
|
return AttrRecord->Resident.ValueLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name IncreaseMftSize
|
||||||
|
* @implemented
|
||||||
|
*
|
||||||
|
* Increases the size of the master file table on a volume, increasing the space available for file records.
|
||||||
|
*
|
||||||
|
* @param Vcb
|
||||||
|
* Pointer to the VCB (DEVICE_EXTENSION) of the target volume.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* STATUS_SUCCESS on success.
|
||||||
|
* STATUS_INSUFFICIENT_RESOURCES if an allocation fails.
|
||||||
|
* STATUS_INVALID_PARAMETER if there was an error reading the Mft's bitmap.
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* Increases the size of the Master File Table by 8 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.
|
||||||
|
*/
|
||||||
|
NTSTATUS
|
||||||
|
IncreaseMftSize(PDEVICE_EXTENSION Vcb)
|
||||||
|
{
|
||||||
|
PNTFS_ATTR_CONTEXT BitmapContext;
|
||||||
|
LARGE_INTEGER BitmapSize;
|
||||||
|
LARGE_INTEGER DataSize;
|
||||||
|
LONGLONG BitmapSizeDifference;
|
||||||
|
ULONG DataSizeDifference = Vcb->NtfsInfo.BytesPerFileRecord * 8;
|
||||||
|
ULONG BitmapOffset;
|
||||||
|
PUCHAR BitmapBuffer;
|
||||||
|
ULONGLONG BitmapBytes;
|
||||||
|
ULONGLONG NewBitmapSize;
|
||||||
|
ULONG BytesRead;
|
||||||
|
ULONG LengthWritten;
|
||||||
|
NTSTATUS Status;
|
||||||
|
|
||||||
|
DPRINT1("IncreaseMftSize(%p)\n", Vcb);
|
||||||
|
|
||||||
|
// We need exclusive access to the mft while we change its size
|
||||||
|
if (!ExAcquireResourceExclusiveLite(&(Vcb->DirResource), TRUE))
|
||||||
|
{
|
||||||
|
return STATUS_CANT_WAIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the bitmap attribute of master file table
|
||||||
|
Status = FindAttribute(Vcb, Vcb->MasterFileTable, AttributeBitmap, L"", 0, &BitmapContext, &BitmapOffset);
|
||||||
|
if (!NT_SUCCESS(Status))
|
||||||
|
{
|
||||||
|
DPRINT1("ERROR: Couldn't find $BITMAP attribute of Mft!\n");
|
||||||
|
ExReleaseResourceLite(&(Vcb->DirResource));
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get size of Bitmap Attribute
|
||||||
|
BitmapSize.QuadPart = AttributeDataLength(&BitmapContext->Record);
|
||||||
|
|
||||||
|
// Calculate the new mft size
|
||||||
|
DataSize.QuadPart = AttributeDataLength(&(Vcb->MFTContext->Record)) + DataSizeDifference;
|
||||||
|
|
||||||
|
// Determine how many bytes will make up the bitmap
|
||||||
|
BitmapBytes = DataSize.QuadPart / Vcb->NtfsInfo.BytesPerFileRecord / 8;
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// Allocate memory for the bitmap
|
||||||
|
BitmapBuffer = ExAllocatePoolWithTag(NonPagedPool, NewBitmapSize, TAG_NTFS);
|
||||||
|
if (!BitmapBuffer)
|
||||||
|
{
|
||||||
|
DPRINT1("ERROR: Unable to allocate memory for bitmap attribute!\n");
|
||||||
|
ExReleaseResourceLite(&(Vcb->DirResource));
|
||||||
|
ReleaseAttributeContext(BitmapContext);
|
||||||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zero the bytes we'll be adding
|
||||||
|
RtlZeroMemory((PUCHAR)((ULONG_PTR)BitmapBuffer), NewBitmapSize);
|
||||||
|
|
||||||
|
// Read the bitmap attribute
|
||||||
|
BytesRead = ReadAttribute(Vcb,
|
||||||
|
BitmapContext,
|
||||||
|
0,
|
||||||
|
(PCHAR)BitmapBuffer,
|
||||||
|
BitmapSize.LowPart);
|
||||||
|
if (BytesRead != BitmapSize.LowPart)
|
||||||
|
{
|
||||||
|
DPRINT1("ERROR: Bytes read != Bitmap size!\n");
|
||||||
|
ExReleaseResourceLite(&(Vcb->DirResource));
|
||||||
|
ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
|
||||||
|
ReleaseAttributeContext(BitmapContext);
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increase the mft size
|
||||||
|
Status = SetNonResidentAttributeDataLength(Vcb, Vcb->MFTContext, Vcb->MftDataOffset, Vcb->MasterFileTable, &DataSize);
|
||||||
|
if (!NT_SUCCESS(Status))
|
||||||
|
{
|
||||||
|
DPRINT1("ERROR: Failed to set size of $MFT data attribute!\n");
|
||||||
|
ExReleaseResourceLite(&(Vcb->DirResource));
|
||||||
|
ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
|
||||||
|
ReleaseAttributeContext(BitmapContext);
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the bitmap grew
|
||||||
|
if (BitmapSizeDifference > 0)
|
||||||
|
{
|
||||||
|
// Set the new bitmap size
|
||||||
|
BitmapSize.QuadPart += BitmapSizeDifference;
|
||||||
|
if (BitmapContext->Record.IsNonResident)
|
||||||
|
Status = SetNonResidentAttributeDataLength(Vcb, BitmapContext, BitmapOffset, Vcb->MasterFileTable, &BitmapSize);
|
||||||
|
else
|
||||||
|
Status = SetResidentAttributeDataLength(Vcb, BitmapContext, BitmapOffset, Vcb->MasterFileTable, &BitmapSize);
|
||||||
|
|
||||||
|
if (!NT_SUCCESS(Status))
|
||||||
|
{
|
||||||
|
DPRINT1("ERROR: Failed to set size of bitmap attribute!\n");
|
||||||
|
ExReleaseResourceLite(&(Vcb->DirResource));
|
||||||
|
ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
|
||||||
|
ReleaseAttributeContext(BitmapContext);
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//NtfsDumpFileAttributes(Vcb, FileRecord);
|
||||||
|
|
||||||
|
// 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");
|
||||||
|
ExReleaseResourceLite(&(Vcb->DirResource));
|
||||||
|
ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
|
||||||
|
ReleaseAttributeContext(BitmapContext);
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write out the new bitmap
|
||||||
|
Status = WriteAttribute(Vcb, BitmapContext, BitmapOffset, BitmapBuffer, BitmapSize.LowPart, &LengthWritten);
|
||||||
|
if (!NT_SUCCESS(Status))
|
||||||
|
{
|
||||||
|
ExReleaseResourceLite(&(Vcb->DirResource));
|
||||||
|
ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
|
||||||
|
ReleaseAttributeContext(BitmapContext);
|
||||||
|
DPRINT1("ERROR: Couldn't write to bitmap attribute of $MFT!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
ExReleaseResourceLite(&(Vcb->DirResource));
|
||||||
|
ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
|
||||||
|
ReleaseAttributeContext(BitmapContext);
|
||||||
|
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
InternalSetResidentAttributeLength(PNTFS_ATTR_CONTEXT AttrContext,
|
InternalSetResidentAttributeLength(PNTFS_ATTR_CONTEXT AttrContext,
|
||||||
PFILE_RECORD_HEADER FileRecord,
|
PFILE_RECORD_HEADER FileRecord,
|
||||||
|
@ -351,7 +506,7 @@ SetFileRecordEnd(PFILE_RECORD_HEADER FileRecord,
|
||||||
* STATUS_INVALID_PARAMETER if we can't find the last cluster in the data run.
|
* STATUS_INVALID_PARAMETER if we can't find the last cluster in the data run.
|
||||||
*
|
*
|
||||||
* @remarks
|
* @remarks
|
||||||
* Called by SetAttributeDataLength(). Use SetAttributeDataLength() unless you have a good
|
* Called by SetAttributeDataLength() and IncreaseMftSize(). Use SetAttributeDataLength() unless you have a good
|
||||||
* reason to use this. Doesn't update the file record on disk. Doesn't inform the cache controller of changes with
|
* reason to use this. Doesn't update the file record on disk. Doesn't inform the cache controller of changes with
|
||||||
* any associated files. Synchronization is the callers responsibility.
|
* any associated files. Synchronization is the callers responsibility.
|
||||||
*/
|
*/
|
||||||
|
@ -485,7 +640,7 @@ SetNonResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
|
||||||
* last attribute listed in the file record.
|
* last attribute listed in the file record.
|
||||||
*
|
*
|
||||||
* @remarks
|
* @remarks
|
||||||
* Called by SetAttributeDataLength(). Use SetAttributeDataLength() unless you have a good
|
* Called by SetAttributeDataLength() and IncreaseMftSize(). Use SetAttributeDataLength() unless you have a good
|
||||||
* reason to use this. Doesn't update the file record on disk. Doesn't inform the cache controller of changes with
|
* reason to use this. Doesn't update the file record on disk. Doesn't inform the cache controller of changes with
|
||||||
* any associated files. Synchronization is the callers responsibility.
|
* any associated files. Synchronization is the callers responsibility.
|
||||||
*/
|
*/
|
||||||
|
@ -1488,7 +1643,6 @@ FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb,
|
||||||
* STATUS_OBJECT_NAME_NOT_FOUND if we can't find the MFT's $Bitmap or if we weren't able
|
* STATUS_OBJECT_NAME_NOT_FOUND if we can't find the MFT's $Bitmap or if we weren't able
|
||||||
* to read the attribute.
|
* to read the attribute.
|
||||||
* STATUS_INSUFFICIENT_RESOURCES if we can't allocate enough memory for a copy of $Bitmap.
|
* STATUS_INSUFFICIENT_RESOURCES if we can't allocate enough memory for a copy of $Bitmap.
|
||||||
* STATUS_NOT_IMPLEMENTED if we need to increase the size of the MFT.
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
|
@ -1503,9 +1657,13 @@ AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
|
||||||
ULONGLONG AttrBytesRead;
|
ULONGLONG AttrBytesRead;
|
||||||
PVOID BitmapData;
|
PVOID BitmapData;
|
||||||
ULONG LengthWritten;
|
ULONG LengthWritten;
|
||||||
|
PNTFS_ATTR_CONTEXT BitmapContext;
|
||||||
|
LARGE_INTEGER BitmapBits;
|
||||||
|
UCHAR SystemReservedBits;
|
||||||
|
|
||||||
|
DPRINT1("AddNewMftEntry(%p, %p, %p)\n", FileRecord, DeviceExt, DestinationIndex);
|
||||||
|
|
||||||
// First, we have to read the mft's $Bitmap attribute
|
// First, we have to read the mft's $Bitmap attribute
|
||||||
PNTFS_ATTR_CONTEXT BitmapContext;
|
|
||||||
Status = FindAttribute(DeviceExt, DeviceExt->MasterFileTable, AttributeBitmap, L"", 0, &BitmapContext, NULL);
|
Status = FindAttribute(DeviceExt, DeviceExt->MasterFileTable, AttributeBitmap, L"", 0, &BitmapContext, NULL);
|
||||||
if (!NT_SUCCESS(Status))
|
if (!NT_SUCCESS(Status))
|
||||||
{
|
{
|
||||||
|
@ -1533,20 +1691,40 @@ AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
|
||||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we need to backup the bits for records 0x10 - 0x17 and leave them unassigned if they aren't assigned
|
||||||
|
RtlCopyMemory(&SystemReservedBits, (PVOID)((ULONG_PTR)BitmapData + 2), 1);
|
||||||
|
RtlFillMemory((PVOID)((ULONG_PTR)BitmapData + 2), 1, (UCHAR)0xFF);
|
||||||
|
|
||||||
|
// Calculate bit count
|
||||||
|
BitmapBits.QuadPart = AttributeDataLength(&(DeviceExt->MFTContext->Record)) /
|
||||||
|
DeviceExt->NtfsInfo.BytesPerFileRecord;
|
||||||
|
if (BitmapBits.HighPart != 0)
|
||||||
|
{
|
||||||
|
DPRINT1("\tFIXME: bitmap sizes beyond 32bits are not yet supported!\n");
|
||||||
|
BitmapBits.LowPart = 0xFFFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
// convert buffer into bitmap
|
// convert buffer into bitmap
|
||||||
RtlInitializeBitMap(&Bitmap, (PULONG)BitmapData, BitmapDataSize * 8);
|
RtlInitializeBitMap(&Bitmap, (PULONG)BitmapData, BitmapBits.LowPart);
|
||||||
|
|
||||||
// set next available bit, preferrably after 23rd bit
|
// set next available bit, preferrably after 23rd bit
|
||||||
MftIndex = RtlFindClearBitsAndSet(&Bitmap, 1, 24);
|
MftIndex = RtlFindClearBitsAndSet(&Bitmap, 1, 24);
|
||||||
if ((LONG)MftIndex == -1)
|
if ((LONG)MftIndex == -1)
|
||||||
{
|
{
|
||||||
DPRINT1("ERROR: Couldn't find free space in MFT for file record!\n");
|
DPRINT1("Couldn't find free space in MFT for file record, increasing MFT size.\n");
|
||||||
|
|
||||||
ExFreePoolWithTag(BitmapData, TAG_NTFS);
|
ExFreePoolWithTag(BitmapData, TAG_NTFS);
|
||||||
ReleaseAttributeContext(BitmapContext);
|
ReleaseAttributeContext(BitmapContext);
|
||||||
|
|
||||||
// TODO: increase mft size
|
// Couldn't find a free record in the MFT, add some blank records and try again
|
||||||
return STATUS_NOT_IMPLEMENTED;
|
Status = IncreaseMftSize(DeviceExt);
|
||||||
|
if (!NT_SUCCESS(Status))
|
||||||
|
{
|
||||||
|
DPRINT1("ERROR: Couldn't find space in MFT for file or increase MFT size!\n");
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AddNewMftEntry(FileRecord, DeviceExt, DestinationIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
DPRINT1("Creating file record at MFT index: %I64u\n", MftIndex);
|
DPRINT1("Creating file record at MFT index: %I64u\n", MftIndex);
|
||||||
|
@ -1556,6 +1734,9 @@ AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
|
||||||
|
|
||||||
// [BitmapData should have been updated via RtlFindClearBitsAndSet()]
|
// [BitmapData should have been updated via RtlFindClearBitsAndSet()]
|
||||||
|
|
||||||
|
// Restore the system reserved bits
|
||||||
|
RtlCopyMemory((PVOID)((ULONG_PTR)BitmapData + 2), &SystemReservedBits, 1);
|
||||||
|
|
||||||
// write the bitmap back to the MFT's $Bitmap attribute
|
// write the bitmap back to the MFT's $Bitmap attribute
|
||||||
Status = WriteAttribute(DeviceExt, BitmapContext, 0, BitmapData, BitmapDataSize, &LengthWritten);
|
Status = WriteAttribute(DeviceExt, BitmapContext, 0, BitmapData, BitmapDataSize, &LengthWritten);
|
||||||
if (!NT_SUCCESS(Status))
|
if (!NT_SUCCESS(Status))
|
||||||
|
@ -1723,7 +1904,7 @@ BrowseIndexEntries(PDEVICE_EXTENSION Vcb,
|
||||||
while (IndexEntry < LastEntry &&
|
while (IndexEntry < LastEntry &&
|
||||||
!(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
|
!(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
|
||||||
{
|
{
|
||||||
if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) > 0x10 &&
|
if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) >= 0x10 &&
|
||||||
*CurrentEntry >= *StartEntry &&
|
*CurrentEntry >= *StartEntry &&
|
||||||
IndexEntry->FileName.NameType != NTFS_FILE_NAME_DOS &&
|
IndexEntry->FileName.NameType != NTFS_FILE_NAME_DOS &&
|
||||||
CompareFileName(FileName, IndexEntry, DirSearch))
|
CompareFileName(FileName, IndexEntry, DirSearch))
|
||||||
|
|
Loading…
Reference in a new issue