[NTFS] - Refactor to allow the copy of the attribute stored in NTFS_ATTR_CONTEXT to have a dynamic length; change Record member from an NTFS_ATTR_RECORD to a PNTFS_ATTR_RECORD. Rename it pRecord to reinforce the change. Fix some bugs related to the record size changing.

-PrepareAttributeContext() - update to allocate memory for pRecord. Don't assume allocations are succeeding.
-ReleaseAttributeContext() - update to free memory for pRecord.
-InternalSetResidentAttributeLength() - Increase size of AttrContext->pRecord as needed. Update to return an NTSTATUS.
-SetResidentAttributeDataLength() - Fix bug that could occur when migrating resident attributes to non-resident if AttrContext->pRecord is too small for the new attribute.
-AddRun() - Fix a bug by reallocating AttrContext->pRecord if the record needs to be enlarged.

svn path=/branches/GSoC_2016/NTFS/; revision=75493
This commit is contained in:
Trevor Thompson 2017-08-06 02:54:15 +00:00 committed by Thomas Faber
parent f2e47474f0
commit 4dfcd1d582
10 changed files with 216 additions and 120 deletions

View file

@ -303,17 +303,17 @@ AddRun(PNTFS_VCB Vcb,
NTSTATUS Status;
int DataRunMaxLength;
PNTFS_ATTR_RECORD DestinationAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
ULONG NextAttributeOffset = AttrOffset + AttrContext->Record.Length;
ULONG NextAttributeOffset = AttrOffset + AttrContext->pRecord->Length;
ULONGLONG NextVBN = 0;
PUCHAR RunBuffer;
ULONG RunBufferSize;
if (!AttrContext->Record.IsNonResident)
if (!AttrContext->pRecord->IsNonResident)
return STATUS_INVALID_PARAMETER;
if (AttrContext->Record.NonResident.AllocatedSize != 0)
NextVBN = AttrContext->Record.NonResident.HighestVCN + 1;
if (AttrContext->pRecord->NonResident.AllocatedSize != 0)
NextVBN = AttrContext->pRecord->NonResident.HighestVCN + 1;
// Add newly-assigned clusters to mcb
_SEH2_TRY
@ -344,12 +344,14 @@ AddRun(PNTFS_VCB Vcb,
ConvertLargeMCBToDataRuns(&AttrContext->DataRunsMCB, RunBuffer, Vcb->NtfsInfo.BytesPerCluster, &RunBufferSize);
// Get the amount of free space between the start of the of the first data run and the attribute end
DataRunMaxLength = AttrContext->Record.Length - AttrContext->Record.NonResident.MappingPairsOffset;
DataRunMaxLength = AttrContext->pRecord->Length - AttrContext->pRecord->NonResident.MappingPairsOffset;
// Do we need to extend the attribute (or convert to attribute list)?
if (DataRunMaxLength < RunBufferSize)
{
PNTFS_ATTR_RECORD NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + NextAttributeOffset);
PNTFS_ATTR_RECORD NewRecord;
DataRunMaxLength += Vcb->NtfsInfo.BytesPerFileRecord - NextAttributeOffset - (sizeof(ULONG) * 2);
// Can we move the end of the attribute?
@ -363,12 +365,22 @@ AddRun(PNTFS_VCB Vcb,
}
// calculate position of end markers
NextAttributeOffset = AttrOffset + AttrContext->Record.NonResident.MappingPairsOffset + RunBufferSize;
NextAttributeOffset = AttrOffset + AttrContext->pRecord->NonResident.MappingPairsOffset + RunBufferSize;
NextAttributeOffset = ALIGN_UP_BY(NextAttributeOffset, ATTR_RECORD_ALIGNMENT);
// Update the length
// Update the length of the destination attribute
DestinationAttribute->Length = NextAttributeOffset - AttrOffset;
AttrContext->Record.Length = DestinationAttribute->Length;
// Create a new copy of the attribute
NewRecord = ExAllocatePoolWithTag(NonPagedPool, DestinationAttribute->Length, TAG_NTFS);
RtlCopyMemory(NewRecord, AttrContext->pRecord, AttrContext->pRecord->Length);
NewRecord->Length = DestinationAttribute->Length;
// Free the old copy of the attribute, which won't be large enough
ExFreePoolWithTag(AttrContext->pRecord, TAG_NTFS);
// Set the attribute context's record to the new copy
AttrContext->pRecord = NewRecord;
// End the file record
NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + NextAttributeOffset);
@ -377,14 +389,19 @@ AddRun(PNTFS_VCB Vcb,
// Update HighestVCN
DestinationAttribute->NonResident.HighestVCN =
AttrContext->Record.NonResident.HighestVCN = max(NextVBN - 1 + RunLength,
AttrContext->Record.NonResident.HighestVCN);
AttrContext->pRecord->NonResident.HighestVCN = max(NextVBN - 1 + RunLength,
AttrContext->pRecord->NonResident.HighestVCN);
// Write data runs to destination attribute
RtlCopyMemory((PVOID)((ULONG_PTR)DestinationAttribute + DestinationAttribute->NonResident.MappingPairsOffset),
RunBuffer,
RunBufferSize);
// Update the attribute copy in the attribute context
RtlCopyMemory((PVOID)((ULONG_PTR)AttrContext->pRecord + AttrContext->pRecord->NonResident.MappingPairsOffset),
RunBuffer,
RunBufferSize);
// Update the file record
Status = UpdateFileRecord(Vcb, AttrContext->FileMFTIndex, FileRecord);
@ -723,7 +740,7 @@ FreeClusters(PNTFS_VCB Vcb,
ULONG ClustersLeftToFree = ClustersToFree;
PNTFS_ATTR_RECORD DestinationAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
ULONG NextAttributeOffset = AttrOffset + AttrContext->Record.Length;
ULONG NextAttributeOffset = AttrOffset + AttrContext->pRecord->Length;
PNTFS_ATTR_RECORD NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + NextAttributeOffset);
PUCHAR RunBuffer;
@ -736,7 +753,7 @@ FreeClusters(PNTFS_VCB Vcb,
RTL_BITMAP Bitmap;
ULONG LengthWritten;
if (!AttrContext->Record.IsNonResident)
if (!AttrContext->pRecord->IsNonResident)
{
return STATUS_INVALID_PARAMETER;
}
@ -767,7 +784,7 @@ FreeClusters(PNTFS_VCB Vcb,
return 0;
}
BitmapDataSize = AttributeDataLength(&DataContext->Record);
BitmapDataSize = AttributeDataLength(DataContext->pRecord);
BitmapDataSize = min(BitmapDataSize, ULONG_MAX);
ASSERT((BitmapDataSize * 8) >= Vcb->NtfsInfo.ClusterCount);
BitmapData = ExAllocatePoolWithTag(NonPagedPool, ROUND_UP(BitmapDataSize, Vcb->NtfsInfo.BytesPerSector), TAG_NTFS);
@ -802,10 +819,10 @@ FreeClusters(PNTFS_VCB Vcb,
// deallocate this cluster
RtlClearBits(&Bitmap, LargeLbn, 1);
}
FsRtlTruncateLargeMcb(&AttrContext->DataRunsMCB, AttrContext->Record.NonResident.HighestVCN);
FsRtlTruncateLargeMcb(&AttrContext->DataRunsMCB, AttrContext->pRecord->NonResident.HighestVCN);
// decrement HighestVCN, but don't let it go below 0
AttrContext->Record.NonResident.HighestVCN = min(AttrContext->Record.NonResident.HighestVCN, AttrContext->Record.NonResident.HighestVCN - 1);
AttrContext->pRecord->NonResident.HighestVCN = min(AttrContext->pRecord->NonResident.HighestVCN, AttrContext->pRecord->NonResident.HighestVCN - 1);
ClustersLeftToFree--;
}
@ -837,7 +854,7 @@ FreeClusters(PNTFS_VCB Vcb,
ConvertLargeMCBToDataRuns(&AttrContext->DataRunsMCB, RunBuffer, Vcb->NtfsInfo.BytesPerCluster, &RunBufferSize);
// Update HighestVCN
DestinationAttribute->NonResident.HighestVCN = AttrContext->Record.NonResident.HighestVCN;
DestinationAttribute->NonResident.HighestVCN = AttrContext->pRecord->NonResident.HighestVCN;
// Write data runs to destination attribute
RtlCopyMemory((PVOID)((ULONG_PTR)DestinationAttribute + DestinationAttribute->NonResident.MappingPairsOffset),
@ -848,9 +865,12 @@ FreeClusters(PNTFS_VCB Vcb,
if (NextAttribute->Type == AttributeEnd)
{
// update attribute length
AttrContext->Record.Length = ALIGN_UP_BY(AttrContext->Record.NonResident.MappingPairsOffset + RunBufferSize,
DestinationAttribute->Length = ALIGN_UP_BY(AttrContext->pRecord->NonResident.MappingPairsOffset + RunBufferSize,
ATTR_RECORD_ALIGNMENT);
DestinationAttribute->Length = AttrContext->Record.Length;
ASSERT(DestinationAttribute->Length <= AttrContext->pRecord->Length);
AttrContext->pRecord->Length = DestinationAttribute->Length;
// write end markers
NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)DestinationAttribute + DestinationAttribute->Length);
@ -893,7 +913,7 @@ InternalReadNonResidentAttributes(PFIND_ATTR_CONTXT Context)
}
ListContext = PrepareAttributeContext(Attribute);
ListSize = AttributeDataLength(&ListContext->Record);
ListSize = AttributeDataLength(ListContext->pRecord);
if (ListSize > 0xFFFFFFFF)
{
ReleaseAttributeContext(ListContext);

View file

@ -41,7 +41,7 @@ PrintAllVCNs(PDEVICE_EXTENSION Vcb,
{
ULONGLONG CurrentOffset = 0;
PINDEX_BUFFER CurrentNode, Buffer;
ULONGLONG BufferSize = AttributeDataLength(&IndexAllocationContext->Record);
ULONGLONG BufferSize = AttributeDataLength(IndexAllocationContext->pRecord);
ULONG BytesRead;
ULONGLONG i;
int Count = 0;
@ -393,7 +393,7 @@ CreateBTreeFromIndex(PDEVICE_EXTENSION Vcb,
Tree->RootNode = RootNode;
// Make sure we won't try reading past the attribute-end
if (FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE, Header) + IndexRoot->Header.TotalSizeOfEntries > IndexRootContext->Record.Resident.ValueLength)
if (FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE, Header) + IndexRoot->Header.TotalSizeOfEntries > IndexRootContext->pRecord->Resident.ValueLength)
{
DPRINT1("Filesystem corruption detected!\n");
DestroyBTree(Tree);

View file

@ -49,8 +49,8 @@ NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt,
Status = FindAttribute(DeviceExt, FileRecord, AttributeData, Stream, StreamLength, &DataContext, NULL);
if (NT_SUCCESS(Status))
{
Size = AttributeDataLength(&DataContext->Record);
Allocated = AttributeAllocatedLength(&DataContext->Record);
Size = AttributeDataLength(DataContext->pRecord);
Allocated = AttributeAllocatedLength(DataContext->pRecord);
ReleaseAttributeContext(DataContext);
}

View file

@ -756,7 +756,7 @@ NtfsReadFCBAttribute(PNTFS_VCB Vcb,
return Status;
}
AttrLength = AttributeDataLength(&AttrCtxt->Record);
AttrLength = AttributeDataLength(AttrCtxt->pRecord);
*Data = ExAllocatePoolWithTag(NonPagedPool, AttrLength, TAG_NTFS);
if (*Data == NULL)
{

View file

@ -631,7 +631,7 @@ NtfsSetEndOfFile(PNTFS_FCB Fcb,
}
// Get the size of the data attribute
CurrentFileSize.QuadPart = AttributeDataLength(&DataContext->Record);
CurrentFileSize.QuadPart = AttributeDataLength(DataContext->pRecord);
// Are we enlarging the attribute?
if (NewFileSize->QuadPart > CurrentFileSize.QuadPart)
@ -673,7 +673,7 @@ NtfsSetEndOfFile(PNTFS_FCB Fcb,
FileName.Length = FileNameAttribute->NameLength * sizeof(WCHAR);
FileName.MaximumLength = FileName.Length;
AllocationSize = AttributeAllocatedLength(&DataContext->Record);
AllocationSize = AttributeAllocatedLength(DataContext->pRecord);
Status = UpdateFileNameRecord(Fcb->Vcb,
ParentMFTId,

View file

@ -341,9 +341,9 @@ NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject,
/* Get volume name */
Status = FindAttribute(DeviceExt, VolumeRecord, AttributeVolumeName, L"", 0, &AttrCtxt, NULL);
if (NT_SUCCESS(Status) && AttrCtxt->Record.Resident.ValueLength != 0)
if (NT_SUCCESS(Status) && AttrCtxt->pRecord->Resident.ValueLength != 0)
{
Attribute = &AttrCtxt->Record;
Attribute = AttrCtxt->pRecord;
DPRINT("Data length %lu\n", AttributeDataLength(Attribute));
NtfsInfo->VolumeLabelLength =
min (Attribute->Resident.ValueLength, MAXIMUM_VOLUME_LABEL_LENGTH);
@ -382,9 +382,9 @@ NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject,
/* Get volume information */
Status = FindAttribute(DeviceExt, VolumeRecord, AttributeVolumeInformation, L"", 0, &AttrCtxt, NULL);
if (NT_SUCCESS(Status) && AttrCtxt->Record.Resident.ValueLength != 0)
if (NT_SUCCESS(Status) && AttrCtxt->pRecord->Resident.ValueLength != 0)
{
Attribute = &AttrCtxt->Record;
Attribute = AttrCtxt->pRecord;
DPRINT("Data length %lu\n", AttributeDataLength (Attribute));
VolumeInfo = (PVOID)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);

View file

@ -42,15 +42,32 @@ PrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord)
PNTFS_ATTR_CONTEXT Context;
Context = ExAllocatePoolWithTag(NonPagedPool,
FIELD_OFFSET(NTFS_ATTR_CONTEXT, Record) + AttrRecord->Length,
sizeof(NTFS_ATTR_CONTEXT),
TAG_NTFS);
RtlCopyMemory(&Context->Record, AttrRecord, AttrRecord->Length);
if(!Context)
{
DPRINT1("Error: Unable to allocate memory for context!\n");
return NULL;
}
// Allocate memory for a copy of the attribute
Context->pRecord = ExAllocatePoolWithTag(NonPagedPool, AttrRecord->Length, TAG_NTFS);
if(!Context->pRecord)
{
DPRINT1("Error: Unable to allocate memory for attribute record!\n");
ExFreePoolWithTag(Context, TAG_NTFS);
return NULL;
}
// Copy the attribute
RtlCopyMemory(Context->pRecord, AttrRecord, AttrRecord->Length);
if (AttrRecord->IsNonResident)
{
LONGLONG DataRunOffset;
ULONGLONG DataRunLength;
ULONGLONG NextVBN = 0;
PUCHAR DataRun = (PUCHAR)&Context->Record + Context->Record.NonResident.MappingPairsOffset;
PUCHAR DataRun = (PUCHAR)((ULONG_PTR)Context->pRecord + Context->pRecord->NonResident.MappingPairsOffset);
Context->CacheRun = DataRun;
Context->CacheRunOffset = 0;
@ -74,6 +91,7 @@ PrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord)
if (!NT_SUCCESS(ConvertDataRunsToLargeMCB(DataRun, &Context->DataRunsMCB, &NextVBN)))
{
DPRINT1("Unable to convert data runs to MCB!\n");
ExFreePoolWithTag(Context->pRecord, TAG_NTFS);
ExFreePoolWithTag(Context, TAG_NTFS);
return NULL;
}
@ -86,11 +104,14 @@ PrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord)
VOID
ReleaseAttributeContext(PNTFS_ATTR_CONTEXT Context)
{
if (Context->Record.IsNonResident)
if (Context->pRecord->IsNonResident)
{
FsRtlUninitializeLargeMcb(&Context->DataRunsMCB);
}
if(Context->pRecord)
ExFreePoolWithTag(Context->pRecord, TAG_NTFS);
ExFreePoolWithTag(Context, TAG_NTFS);
}
@ -245,10 +266,10 @@ IncreaseMftSize(PDEVICE_EXTENSION Vcb, BOOLEAN CanWait)
}
// Get size of Bitmap Attribute
BitmapSize.QuadPart = AttributeDataLength(&BitmapContext->Record);
BitmapSize.QuadPart = AttributeDataLength(BitmapContext->pRecord);
// Calculate the new mft size
DataSize.QuadPart = AttributeDataLength(&(Vcb->MFTContext->Record)) + DataSizeDifference;
DataSize.QuadPart = AttributeDataLength(Vcb->MFTContext->pRecord) + DataSizeDifference;
// Determine how many bytes will make up the bitmap
BitmapBytes = DataSize.QuadPart / Vcb->NtfsInfo.BytesPerFileRecord / 8;
@ -301,7 +322,7 @@ IncreaseMftSize(PDEVICE_EXTENSION Vcb, BOOLEAN CanWait)
{
// Set the new bitmap size
BitmapSize.QuadPart += BitmapSizeDifference;
if (BitmapContext->Record.IsNonResident)
if (BitmapContext->pRecord->IsNonResident)
Status = SetNonResidentAttributeDataLength(Vcb, BitmapContext, BitmapOffset, Vcb->MasterFileTable, &BitmapSize);
else
Status = SetResidentAttributeDataLength(Vcb, BitmapContext, BitmapOffset, Vcb->MasterFileTable, &BitmapSize);
@ -348,7 +369,7 @@ IncreaseMftSize(PDEVICE_EXTENSION Vcb, BOOLEAN CanWait)
return STATUS_SUCCESS;
}
VOID
NTSTATUS
InternalSetResidentAttributeLength(PNTFS_ATTR_CONTEXT AttrContext,
PFILE_RECORD_HEADER FileRecord,
ULONG AttrOffset,
@ -356,32 +377,45 @@ InternalSetResidentAttributeLength(PNTFS_ATTR_CONTEXT AttrContext,
{
PNTFS_ATTR_RECORD Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
ULONG NextAttributeOffset;
USHORT ValueOffset;
DPRINT("InternalSetResidentAttributeLength( %p, %p, %lu, %lu )\n", AttrContext, FileRecord, AttrOffset, DataSize);
ASSERT(!AttrContext->Record.IsNonResident);
ASSERT(!AttrContext->pRecord->IsNonResident);
// update ValueLength Field
AttrContext->Record.Resident.ValueLength =
// Update ValueLength Field
Destination->Resident.ValueLength = DataSize;
// calculate the record length and end marker offset
AttrContext->Record.Length =
Destination->Length = DataSize + AttrContext->Record.Resident.ValueOffset;
NextAttributeOffset = AttrOffset + AttrContext->Record.Length;
// Calculate the record length and end marker offset
Destination->Length = ALIGN_UP_BY(DataSize + AttrContext->pRecord->Resident.ValueOffset, ATTR_RECORD_ALIGNMENT);
NextAttributeOffset = AttrOffset + Destination->Length;
// Ensure FileRecord has an up-to-date copy of the attribute
ValueOffset = AttrContext->pRecord->Resident.ValueOffset;
RtlCopyMemory((PCHAR)((ULONG_PTR)FileRecord + AttrOffset + ValueOffset),
(PCHAR)((ULONG_PTR)AttrContext->pRecord + ValueOffset),
min(DataSize, AttrContext->pRecord->Resident.ValueLength));
// Free the old copy of the attribute in the context, as it will be the wrong length
ExFreePoolWithTag(AttrContext->pRecord, TAG_NTFS);
// Create a new copy of the attribute for the context
AttrContext->pRecord = ExAllocatePoolWithTag(NonPagedPool, Destination->Length, TAG_NTFS);
if (!AttrContext->pRecord)
{
DPRINT1("Unable to allocate memory for attribute!\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(AttrContext->pRecord, Destination, Destination->Length);
// Ensure NextAttributeOffset is aligned to an 8-byte boundary
if (NextAttributeOffset % 8 != 0)
{
USHORT Padding = ATTR_RECORD_ALIGNMENT - (NextAttributeOffset % ATTR_RECORD_ALIGNMENT);
NextAttributeOffset += Padding;
AttrContext->Record.Length += Padding;
Destination->Length += Padding;
}
ASSERT(NextAttributeOffset % ATTR_RECORD_ALIGNMENT == 0);
// advance Destination to the final "attribute" and set the file record end
Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)Destination + Destination->Length);
SetFileRecordEnd(FileRecord, Destination, FILE_RECORD_END);
return STATUS_SUCCESS;
}
/**
@ -408,7 +442,7 @@ SetAttributeDataLength(PFILE_OBJECT FileObject,
DataSize->QuadPart);
// are we truncating the file?
if (DataSize->QuadPart < AttributeDataLength(&AttrContext->Record))
if (DataSize->QuadPart < AttributeDataLength(AttrContext->pRecord))
{
if (!MmCanFileBeTruncated(FileObject->SectionObjectPointer, DataSize))
{
@ -417,7 +451,7 @@ SetAttributeDataLength(PFILE_OBJECT FileObject,
}
}
if (AttrContext->Record.IsNonResident)
if (AttrContext->pRecord->IsNonResident)
{
Status = SetNonResidentAttributeDataLength(Fcb->Vcb,
AttrContext,
@ -448,8 +482,8 @@ SetAttributeDataLength(PFILE_OBJECT FileObject,
if (NT_SUCCESS(Status))
{
if (AttrContext->Record.IsNonResident)
Fcb->RFCB.AllocationSize.QuadPart = AttrContext->Record.NonResident.AllocatedSize;
if (AttrContext->pRecord->IsNonResident)
Fcb->RFCB.AllocationSize.QuadPart = AttrContext->pRecord->NonResident.AllocatedSize;
else
Fcb->RFCB.AllocationSize = *DataSize;
Fcb->RFCB.FileSize = *DataSize;
@ -538,12 +572,12 @@ SetNonResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
ULONG BytesPerCluster = Vcb->NtfsInfo.BytesPerCluster;
ULONGLONG AllocationSize = ROUND_UP(DataSize->QuadPart, BytesPerCluster);
PNTFS_ATTR_RECORD DestinationAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
ULONG ExistingClusters = AttrContext->Record.NonResident.AllocatedSize / BytesPerCluster;
ULONG ExistingClusters = AttrContext->pRecord->NonResident.AllocatedSize / BytesPerCluster;
ASSERT(AttrContext->Record.IsNonResident);
ASSERT(AttrContext->pRecord->IsNonResident);
// do we need to increase the allocation size?
if (AttrContext->Record.NonResident.AllocatedSize < AllocationSize)
if (AttrContext->pRecord->NonResident.AllocatedSize < AllocationSize)
{
ULONG ClustersNeeded = (AllocationSize / BytesPerCluster) - ExistingClusters;
LARGE_INTEGER LastClusterInDataRun;
@ -557,7 +591,7 @@ SetNonResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
else
{
if (!FsRtlLookupLargeMcbEntry(&AttrContext->DataRunsMCB,
(LONGLONG)AttrContext->Record.NonResident.HighestVCN,
(LONGLONG)AttrContext->pRecord->NonResident.HighestVCN,
(PLONGLONG)&LastClusterInDataRun.QuadPart,
NULL,
NULL,
@ -567,13 +601,13 @@ SetNonResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
DPRINT1("Error looking up final large MCB entry!\n");
// Most likely, HighestVCN went above the largest mapping
DPRINT1("Highest VCN of record: %I64u\n", AttrContext->Record.NonResident.HighestVCN);
DPRINT1("Highest VCN of record: %I64u\n", AttrContext->pRecord->NonResident.HighestVCN);
return STATUS_INVALID_PARAMETER;
}
}
DPRINT("LastClusterInDataRun: %I64u\n", LastClusterInDataRun.QuadPart);
DPRINT("Highest VCN of record: %I64u\n", AttrContext->Record.NonResident.HighestVCN);
DPRINT("Highest VCN of record: %I64u\n", AttrContext->pRecord->NonResident.HighestVCN);
while (ClustersNeeded > 0)
{
@ -601,7 +635,7 @@ SetNonResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
LastClusterInDataRun.LowPart = NextAssignedCluster + AssignedClusters - 1;
}
}
else if (AttrContext->Record.NonResident.AllocatedSize > AllocationSize)
else if (AttrContext->pRecord->NonResident.AllocatedSize > AllocationSize)
{
// shrink allocation size
ULONG ClustersToFree = ExistingClusters - (AllocationSize / BytesPerCluster);
@ -610,9 +644,9 @@ SetNonResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
// TODO: is the file compressed, encrypted, or sparse?
AttrContext->Record.NonResident.AllocatedSize = AllocationSize;
AttrContext->Record.NonResident.DataSize = DataSize->QuadPart;
AttrContext->Record.NonResident.InitializedSize = DataSize->QuadPart;
AttrContext->pRecord->NonResident.AllocatedSize = AllocationSize;
AttrContext->pRecord->NonResident.DataSize = DataSize->QuadPart;
AttrContext->pRecord->NonResident.InitializedSize = DataSize->QuadPart;
DestinationAttribute->NonResident.AllocatedSize = AllocationSize;
DestinationAttribute->NonResident.DataSize = DataSize->QuadPart;
@ -667,18 +701,18 @@ SetResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
NTSTATUS Status;
// find the next attribute
ULONG NextAttributeOffset = AttrOffset + AttrContext->Record.Length;
ULONG NextAttributeOffset = AttrOffset + AttrContext->pRecord->Length;
PNTFS_ATTR_RECORD NextAttribute = (PNTFS_ATTR_RECORD)((PCHAR)FileRecord + NextAttributeOffset);
ASSERT(!AttrContext->Record.IsNonResident);
ASSERT(!AttrContext->pRecord->IsNonResident);
//NtfsDumpFileAttributes(Vcb, FileRecord);
// Do we need to increase the data length?
if (DataSize->QuadPart > AttrContext->Record.Resident.ValueLength)
if (DataSize->QuadPart > AttrContext->pRecord->Resident.ValueLength)
{
// There's usually padding at the end of a record. Do we need to extend past it?
ULONG MaxValueLength = AttrContext->Record.Length - AttrContext->Record.Resident.ValueOffset;
ULONG MaxValueLength = AttrContext->pRecord->Length - AttrContext->pRecord->Resident.ValueOffset;
if (MaxValueLength < DataSize->LowPart)
{
// If this is the last attribute, we could move the end marker to the very end of the file record
@ -688,14 +722,16 @@ SetResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
{
// convert attribute to non-resident
PNTFS_ATTR_RECORD Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
PNTFS_ATTR_RECORD NewRecord;
LARGE_INTEGER AttribDataSize;
PVOID AttribData;
ULONG NewRecordLength;
ULONG EndAttributeOffset;
ULONG LengthWritten;
DPRINT1("Converting attribute to non-resident.\n");
AttribDataSize.QuadPart = AttrContext->Record.Resident.ValueLength;
AttribDataSize.QuadPart = AttrContext->pRecord->Resident.ValueLength;
// Is there existing data we need to back-up?
if (AttribDataSize.QuadPart > 0)
@ -719,23 +755,43 @@ SetResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
// Start by turning this attribute into a 0-length, non-resident attribute, then enlarge it.
// Zero out the NonResident structure
RtlZeroMemory(&AttrContext->Record.NonResident.LowestVCN,
FIELD_OFFSET(NTFS_ATTR_RECORD, NonResident.CompressedSize) - FIELD_OFFSET(NTFS_ATTR_RECORD, NonResident.LowestVCN));
RtlZeroMemory(&Destination->NonResident.LowestVCN,
FIELD_OFFSET(NTFS_ATTR_RECORD, NonResident.CompressedSize) - FIELD_OFFSET(NTFS_ATTR_RECORD, NonResident.LowestVCN));
// The size of a 0-length, non-resident attribute will be 0x41 + the size of the attribute name, aligned to an 8-byte boundary
NewRecordLength = ALIGN_UP_BY(0x41 + (AttrContext->pRecord->NameLength * sizeof(WCHAR)), ATTR_RECORD_ALIGNMENT);
// update the mapping pairs offset, which will be 0x40 + length in bytes of the name
AttrContext->Record.NonResident.MappingPairsOffset = Destination->NonResident.MappingPairsOffset = 0x40 + (Destination->NameLength * 2);
// Create a new attribute record that will store the 0-length, non-resident attribute
NewRecord = ExAllocatePoolWithTag(NonPagedPool, NewRecordLength, TAG_NTFS);
// Zero out the NonResident structure
RtlZeroMemory(NewRecord, NewRecordLength);
// Copy the data that's common to both non-resident and resident attributes
RtlCopyMemory(NewRecord, AttrContext->pRecord, FIELD_OFFSET(NTFS_ATTR_RECORD, Resident.ValueLength));
// if there's a name
if (AttrContext->pRecord->NameLength != 0)
{
// copy the name
// An attribute name will be located at offset 0x18 for a resident attribute, 0x40 for non-resident
RtlCopyMemory((PCHAR)((ULONG_PTR)NewRecord + 0x40),
(PCHAR)((ULONG_PTR)AttrContext->pRecord + 0x18),
AttrContext->pRecord->NameLength * sizeof(WCHAR));
}
// update the mapping pairs offset, which will be 0x40 (size of a non-resident header) + length in bytes of the name
NewRecord->NonResident.MappingPairsOffset = 0x40 + (AttrContext->pRecord->NameLength * sizeof(WCHAR));
// update the end of the file record
// calculate position of end markers (1 byte for empty data run)
EndAttributeOffset = AttrOffset + AttrContext->Record.NonResident.MappingPairsOffset + 1;
EndAttributeOffset = AttrOffset + NewRecord->NonResident.MappingPairsOffset + 1;
EndAttributeOffset = ALIGN_UP_BY(EndAttributeOffset, ATTR_RECORD_ALIGNMENT);
// Update the length
Destination->Length = EndAttributeOffset - AttrOffset;
AttrContext->Record.Length = Destination->Length;
NewRecord->Length = EndAttributeOffset - AttrOffset;
ASSERT(NewRecord->Length == NewRecordLength);
// Copy the new attribute record into the file record
RtlCopyMemory(Destination, NewRecord, NewRecord->Length);
// Update the file record end
SetFileRecordEnd(FileRecord,
@ -756,7 +812,7 @@ SetResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
} _SEH2_END;
// Mark the attribute as non-resident (we wait until after we know the LargeMcb was initialized)
AttrContext->Record.IsNonResident = Destination->IsNonResident = 1;
NewRecord->IsNonResident = Destination->IsNonResident = 1;
// Update file record on disk
Status = UpdateFileRecord(Vcb, AttrContext->FileMFTIndex, FileRecord);
@ -768,6 +824,10 @@ SetResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
return Status;
}
// Now we need to free the old copy of the attribute record in the context and replace it with the new one
ExFreePoolWithTag(AttrContext->pRecord, TAG_NTFS);
AttrContext->pRecord = NewRecord;
// Now we can treat the attribute as non-resident and enlarge it normally
Status = SetNonResidentAttributeDataLength(Vcb, AttrContext, AttrOffset, FileRecord, DataSize);
if (!NT_SUCCESS(Status))
@ -795,7 +855,7 @@ SetResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
}
}
}
else if (DataSize->LowPart < AttrContext->Record.Resident.ValueLength)
else if (DataSize->LowPart < AttrContext->pRecord->Resident.ValueLength)
{
// we need to decrease the length
if (NextAttribute->Type != AttributeEnd)
@ -806,8 +866,8 @@ SetResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
}
// set the new length of the resident attribute (if we didn't migrate it)
if (!AttrContext->Record.IsNonResident)
InternalSetResidentAttributeLength(AttrContext, FileRecord, AttrOffset, DataSize->LowPart);
if (!AttrContext->pRecord->IsNonResident)
return InternalSetResidentAttributeLength(AttrContext, FileRecord, AttrOffset, DataSize->LowPart);
return STATUS_SUCCESS;
}
@ -832,13 +892,19 @@ ReadAttribute(PDEVICE_EXTENSION Vcb,
//TEMPTEMP
PUCHAR TempBuffer;
if (!Context->Record.IsNonResident)
if (!Context->pRecord->IsNonResident)
{
if (Offset > Context->Record.Resident.ValueLength)
// We need to truncate Offset to a ULONG for pointer arithmetic
// The check below should ensure that Offset is well within the range of 32 bits
ULONG LittleOffset = (ULONG)Offset;
// Ensure that offset isn't beyond the end of the attribute
if (Offset > Context->pRecord->Resident.ValueLength)
return 0;
if (Offset + Length > Context->Record.Resident.ValueLength)
Length = (ULONG)(Context->Record.Resident.ValueLength - Offset);
RtlCopyMemory(Buffer, (PCHAR)&Context->Record + Context->Record.Resident.ValueOffset + Offset, Length);
if (Offset + Length > Context->pRecord->Resident.ValueLength)
Length = (ULONG)(Context->pRecord->Resident.ValueLength - Offset);
RtlCopyMemory(Buffer, (PVOID)((ULONG_PTR)Context->pRecord + Context->pRecord->Resident.ValueOffset + LittleOffset), Length);
return Length;
}
@ -996,7 +1062,7 @@ ReadAttribute(PDEVICE_EXTENSION Vcb,
} /* if Disk */
// TEMPTEMP
if (Context->Record.IsNonResident)
if (Context->pRecord->IsNonResident)
ExFreePoolWithTag(TempBuffer, TAG_NTFS);
Context->CacheRun = DataRun;
@ -1073,13 +1139,13 @@ WriteAttribute(PDEVICE_EXTENSION Vcb,
*RealLengthWritten = 0;
// is this a resident attribute?
if (!Context->Record.IsNonResident)
if (!Context->pRecord->IsNonResident)
{
ULONG AttributeOffset;
PNTFS_ATTR_CONTEXT FoundContext;
PFILE_RECORD_HEADER FileRecord;
if (Offset + Length > Context->Record.Resident.ValueLength)
if (Offset + Length > Context->pRecord->Resident.ValueLength)
{
DPRINT1("DRIVER ERROR: Attribute is too small!\n");
return STATUS_INVALID_PARAMETER;
@ -1098,9 +1164,9 @@ WriteAttribute(PDEVICE_EXTENSION Vcb,
// find where to write the attribute data to
Status = FindAttribute(Vcb, FileRecord,
Context->Record.Type,
(PCWSTR)((PCHAR)&Context->Record + Context->Record.NameOffset),
Context->Record.NameLength,
Context->pRecord->Type,
(PCWSTR)((ULONG_PTR)Context->pRecord + Context->pRecord->NameOffset),
Context->pRecord->NameLength,
&FoundContext,
&AttributeOffset);
@ -1111,8 +1177,8 @@ WriteAttribute(PDEVICE_EXTENSION Vcb,
return Status;
}
DPRINT("Offset: %I64u, AttributeOffset: %u, ValueOffset: %u\n", Offset, AttributeOffset, Context->Record.Resident.ValueLength);
Offset += AttributeOffset + Context->Record.Resident.ValueOffset;
DPRINT("Offset: %I64u, AttributeOffset: %u, ValueOffset: %u\n", Offset, AttributeOffset, Context->pRecord->Resident.ValueLength);
Offset += AttributeOffset + Context->pRecord->Resident.ValueOffset;
if (Offset + Length > Vcb->NtfsInfo.BytesPerFileRecord)
{
@ -1320,7 +1386,7 @@ WriteAttribute(PDEVICE_EXTENSION Vcb,
} // end while (Length > 0) [more data to write]
// TEMPTEMP
if (Context->Record.IsNonResident)
if (Context->pRecord->IsNonResident)
ExFreePoolWithTag(TempBuffer, TAG_NTFS);
Context->CacheRun = DataRun;
@ -1419,7 +1485,7 @@ UpdateFileNameRecord(PDEVICE_EXTENSION Vcb,
return STATUS_INSUFFICIENT_RESOURCES;
}
Status = ReadAttribute(Vcb, IndexRootCtx, 0, IndexRecord, AttributeDataLength(&IndexRootCtx->Record));
Status = ReadAttribute(Vcb, IndexRootCtx, 0, IndexRecord, AttributeDataLength(IndexRootCtx->pRecord));
if (!NT_SUCCESS(Status))
{
DPRINT1("ERROR: Failed to read Index Root!\n");
@ -1453,7 +1519,7 @@ UpdateFileNameRecord(PDEVICE_EXTENSION Vcb,
{
// we need to write the index root attribute back to disk
ULONG LengthWritten;
Status = WriteAttribute(Vcb, IndexRootCtx, 0, (PUCHAR)IndexRecord, AttributeDataLength(&IndexRootCtx->Record), &LengthWritten);
Status = WriteAttribute(Vcb, IndexRootCtx, 0, (PUCHAR)IndexRecord, AttributeDataLength(IndexRootCtx->pRecord), &LengthWritten);
if (!NT_SUCCESS(Status))
{
DPRINT1("ERROR: Couldn't update Index Root!\n");
@ -1551,7 +1617,7 @@ UpdateIndexEntryFileNameSize(PDEVICE_EXTENSION Vcb,
return Status;
}
IndexAllocationSize = AttributeDataLength(&IndexAllocationCtx->Record);
IndexAllocationSize = AttributeDataLength(IndexAllocationCtx->pRecord);
Status = STATUS_OBJECT_PATH_NOT_FOUND;
for (RecordOffset = 0; RecordOffset < IndexAllocationSize; RecordOffset += IndexBlockSize)
{
@ -1747,7 +1813,7 @@ AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
}
// allocate a buffer for the $Bitmap attribute
BitmapDataSize = AttributeDataLength(&BitmapContext->Record);
BitmapDataSize = AttributeDataLength(BitmapContext->pRecord);
BitmapData = ExAllocatePoolWithTag(NonPagedPool, BitmapDataSize, TAG_NTFS);
if (!BitmapData)
{
@ -1772,7 +1838,7 @@ AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
BitmapData[2] = 0xff;
// Calculate bit count
BitmapBits.QuadPart = AttributeDataLength(&(DeviceExt->MFTContext->Record)) /
BitmapBits.QuadPart = AttributeDataLength(DeviceExt->MFTContext->pRecord) /
DeviceExt->NtfsInfo.BytesPerFileRecord;
if (BitmapBits.HighPart != 0)
{
@ -1942,12 +2008,12 @@ NtfsAddFilenameToDirectory(PDEVICE_EXTENSION DeviceExt,
// Find the maximum index size given what the file record can hold
MaxIndexSize = DeviceExt->NtfsInfo.BytesPerFileRecord
- IndexRootOffset
- IndexRootContext->Record.Resident.ValueOffset
- IndexRootContext->pRecord->Resident.ValueOffset
- FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE, Header)
- (sizeof(ULONG) * 2);
// Allocate memory for the index root data
I30IndexRootLength = AttributeDataLength(&IndexRootContext->Record);
I30IndexRootLength = AttributeDataLength(IndexRootContext->pRecord);
I30IndexRoot = ExAllocatePoolWithTag(NonPagedPool, I30IndexRootLength, TAG_NTFS);
if (!I30IndexRoot)
{
@ -2035,7 +2101,7 @@ NtfsAddFilenameToDirectory(PDEVICE_EXTENSION DeviceExt,
// $ATTRIBUTE_LIST's.
AttributeLength = NewIndexRoot->Header.AllocatedSize + FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE, Header);
if (AttributeLength != IndexRootContext->Record.Resident.ValueLength)
if (AttributeLength != IndexRootContext->pRecord->Resident.ValueLength)
{
DestinationAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)ParentFileRecord + IndexRootOffset);
@ -2052,10 +2118,20 @@ NtfsAddFilenameToDirectory(PDEVICE_EXTENSION DeviceExt,
}
// Update the length of the attribute in the file record of the parent directory
InternalSetResidentAttributeLength(IndexRootContext,
ParentFileRecord,
IndexRootOffset,
AttributeLength);
Status = InternalSetResidentAttributeLength(IndexRootContext,
ParentFileRecord,
IndexRootOffset,
AttributeLength);
if (!NT_SUCCESS(Status))
{
ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
ReleaseAttributeContext(IndexRootContext);
ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
DPRINT1("ERROR: Unable to set resident attribute length!\n");
return Status;
}
}
NT_ASSERT(ParentFileRecord->BytesInUse <= DeviceExt->NtfsInfo.BytesPerFileRecord);
@ -2295,7 +2371,7 @@ BrowseIndexEntries(PDEVICE_EXTENSION Vcb,
return Status;
}
IndexAllocationSize = AttributeDataLength(&IndexAllocationCtx->Record);
IndexAllocationSize = AttributeDataLength(IndexAllocationCtx->pRecord);
Status = STATUS_OBJECT_PATH_NOT_FOUND;
for (RecordOffset = 0; RecordOffset < IndexAllocationSize; RecordOffset += IndexBlockSize)
{

View file

@ -480,7 +480,7 @@ typedef struct _NTFS_ATTR_CONTEXT
ULONGLONG CacheRunCurrentOffset;
LARGE_MCB DataRunsMCB;
ULONGLONG FileMFTIndex;
NTFS_ATTR_RECORD Record;
PNTFS_ATTR_RECORD pRecord;
} NTFS_ATTR_CONTEXT, *PNTFS_ATTR_CONTEXT;
#define FCB_CACHE_INITIALIZED 0x0001
@ -959,7 +959,7 @@ WriteAttribute(PDEVICE_EXTENSION Vcb,
ULONGLONG
AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord);
VOID
NTSTATUS
InternalSetResidentAttributeLength(PNTFS_ATTR_CONTEXT AttrContext,
PFILE_RECORD_HEADER FileRecord,
ULONG AttrOffset,

View file

@ -126,7 +126,7 @@ NtfsReadFile(PDEVICE_EXTENSION DeviceExt,
return Status;
}
StreamSize = AttributeDataLength(&DataContext->Record);
StreamSize = AttributeDataLength(DataContext->pRecord);
if (ReadOffset >= StreamSize)
{
DPRINT1("Reading beyond stream end!\n");
@ -149,7 +149,7 @@ NtfsReadFile(PDEVICE_EXTENSION DeviceExt,
/* do we need to extend RealLength by one sector? */
if (RealLength + RealReadOffset < ReadOffset + Length)
{
if (RealReadOffset + RealLength + DeviceExt->NtfsInfo.BytesPerSector <= AttributeAllocatedLength(&DataContext->Record))
if (RealReadOffset + RealLength + DeviceExt->NtfsInfo.BytesPerSector <= AttributeAllocatedLength(DataContext->pRecord))
RealLength += DeviceExt->NtfsInfo.BytesPerSector;
}
@ -413,7 +413,7 @@ NTSTATUS NtfsWriteFile(PDEVICE_EXTENSION DeviceExt,
}
// Get the size of the stream on disk
StreamSize = AttributeDataLength(&DataContext->Record);
StreamSize = AttributeDataLength(DataContext->pRecord);
DPRINT("WriteOffset: %lu\tStreamSize: %I64u\n", WriteOffset, StreamSize);
@ -442,7 +442,7 @@ NTSTATUS NtfsWriteFile(PDEVICE_EXTENSION DeviceExt,
return Status;
}
AllocationSize = AttributeAllocatedLength(&DataContext->Record);
AllocationSize = AttributeAllocatedLength(DataContext->pRecord);
// now we need to update this file's size in every directory index entry that references it
// TODO: put this code in its own function and adapt it to work with every filename / hardlink

View file

@ -69,7 +69,7 @@ NtfsGetFreeClusters(PDEVICE_EXTENSION DeviceExt)
return 0;
}
BitmapDataSize = AttributeDataLength(&DataContext->Record);
BitmapDataSize = AttributeDataLength(DataContext->pRecord);
ASSERT((BitmapDataSize * 8) >= DeviceExt->NtfsInfo.ClusterCount);
BitmapData = ExAllocatePoolWithTag(NonPagedPool, ROUND_UP(BitmapDataSize, DeviceExt->NtfsInfo.BytesPerSector), TAG_NTFS);
if (BitmapData == NULL)
@ -144,7 +144,7 @@ NtfsAllocateClusters(PDEVICE_EXTENSION DeviceExt,
return Status;
}
BitmapDataSize = AttributeDataLength(&DataContext->Record);
BitmapDataSize = AttributeDataLength(DataContext->pRecord);
BitmapDataSize = min(BitmapDataSize, 0xffffffff);
ASSERT((BitmapDataSize * 8) >= DeviceExt->NtfsInfo.ClusterCount);
BitmapData = ExAllocatePoolWithTag(NonPagedPool, ROUND_UP(BitmapDataSize, DeviceExt->NtfsInfo.BytesPerSector), TAG_NTFS);