[NTFS] - Restructure some code in preparation for the next commit:

-SetAttributeDataLength() has been split into two functions, SetNonResidentAttributeDataLength() and SetResidentAttributeDataLength(). This should improve code readibility and allows for resizing an attribute when there's no FileObject associated with it.
-Added "MftDataOffset" member to DEVICE_EXTENSION, which stores the offset of the Mft's $DATA attribute. (I'm starting to think it's better to add a member for offset to NTFS_ATTR_CONTEXT directly, but I'll save that level of restructuring for a future commit.)

svn path=/branches/GSoC_2016/NTFS/; revision=75055
This commit is contained in:
Trevor Thompson 2017-06-16 05:43:52 +00:00 committed by Thomas Faber
parent 9dce6f4db1
commit 1417f286e0
3 changed files with 360 additions and 220 deletions

View file

@ -295,7 +295,13 @@ NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject,
return Status;
}
Status = FindAttribute(DeviceExt, DeviceExt->MasterFileTable, AttributeData, L"", 0, &DeviceExt->MFTContext, NULL);
Status = FindAttribute(DeviceExt,
DeviceExt->MasterFileTable,
AttributeData,
L"",
0,
&DeviceExt->MFTContext,
&DeviceExt->MftDataOffset);
if (!NT_SUCCESS(Status))
{
DPRINT1("Can't find data attribute for Master File Table.\n");

View file

@ -234,7 +234,6 @@ SetAttributeDataLength(PFILE_OBJECT FileObject,
PLARGE_INTEGER DataSize)
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG BytesPerCluster = Fcb->Vcb->NtfsInfo.BytesPerCluster;
// are we truncating the file?
if (DataSize->QuadPart < AttributeDataLength(&AttrContext->Record))
@ -248,229 +247,26 @@ SetAttributeDataLength(PFILE_OBJECT FileObject,
if (AttrContext->Record.IsNonResident)
{
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;
// do we need to increase the allocation size?
if (AttrContext->Record.NonResident.AllocatedSize < AllocationSize)
{
ULONG ClustersNeeded = (AllocationSize / BytesPerCluster) - ExistingClusters;
LARGE_INTEGER LastClusterInDataRun;
ULONG NextAssignedCluster;
ULONG AssignedClusters;
if (ExistingClusters == 0)
{
LastClusterInDataRun.QuadPart = 0;
}
else
{
if (!FsRtlLookupLargeMcbEntry(&AttrContext->DataRunsMCB,
(LONGLONG)AttrContext->Record.NonResident.HighestVCN,
(PLONGLONG)&LastClusterInDataRun.QuadPart,
NULL,
NULL,
NULL,
NULL))
{
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);
return STATUS_INVALID_PARAMETER;
}
}
DPRINT("LastClusterInDataRun: %I64u\n", LastClusterInDataRun.QuadPart);
DPRINT("Highest VCN of record: %I64u\n", AttrContext->Record.NonResident.HighestVCN);
while (ClustersNeeded > 0)
{
Status = NtfsAllocateClusters(Fcb->Vcb,
LastClusterInDataRun.LowPart + 1,
ClustersNeeded,
&NextAssignedCluster,
&AssignedClusters);
if (!NT_SUCCESS(Status))
{
DPRINT1("Error: Unable to allocate requested clusters!\n");
return Status;
}
// now we need to add the clusters we allocated to the data run
Status = AddRun(Fcb->Vcb, AttrContext, AttrOffset, FileRecord, NextAssignedCluster, AssignedClusters);
if (!NT_SUCCESS(Status))
{
DPRINT1("Error: Unable to add data run!\n");
return Status;
}
ClustersNeeded -= AssignedClusters;
LastClusterInDataRun.LowPart = NextAssignedCluster + AssignedClusters - 1;
}
}
else if (AttrContext->Record.NonResident.AllocatedSize > AllocationSize)
{
// shrink allocation size
ULONG ClustersToFree = ExistingClusters - (AllocationSize / BytesPerCluster);
Status = FreeClusters(Fcb->Vcb, AttrContext, AttrOffset, FileRecord, ClustersToFree);
}
// TODO: is the file compressed, encrypted, or sparse?
// NOTE: we need to have acquired the main resource exclusively, as well as(?) the PagingIoResource
Fcb->RFCB.AllocationSize.QuadPart = AllocationSize;
AttrContext->Record.NonResident.AllocatedSize = AllocationSize;
AttrContext->Record.NonResident.DataSize = DataSize->QuadPart;
AttrContext->Record.NonResident.InitializedSize = DataSize->QuadPart;
DestinationAttribute->NonResident.AllocatedSize = AllocationSize;
DestinationAttribute->NonResident.DataSize = DataSize->QuadPart;
DestinationAttribute->NonResident.InitializedSize = DataSize->QuadPart;
DPRINT("Allocated Size: %I64u\n", DestinationAttribute->NonResident.AllocatedSize);
Status = SetNonResidentAttributeDataLength(Fcb->Vcb,
AttrContext,
AttrOffset,
FileRecord,
DataSize);
}
else
{
// resident attribute
Status = SetResidentAttributeDataLength(Fcb->Vcb,
AttrContext,
AttrOffset,
FileRecord,
DataSize);
}
// find the next attribute
ULONG NextAttributeOffset = AttrOffset + AttrContext->Record.Length;
PNTFS_ATTR_RECORD NextAttribute = (PNTFS_ATTR_RECORD)((PCHAR)FileRecord + NextAttributeOffset);
//NtfsDumpFileAttributes(Fcb->Vcb, FileRecord);
// Do we need to increase the data length?
if (DataSize->QuadPart > AttrContext->Record.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;
if (MaxValueLength < DataSize->LowPart)
{
// If this is the last attribute, we could move the end marker to the very end of the file record
MaxValueLength += Fcb->Vcb->NtfsInfo.BytesPerFileRecord - NextAttributeOffset - (sizeof(ULONG) * 2);
if (MaxValueLength < DataSize->LowPart || NextAttribute->Type != AttributeEnd)
{
// convert attribute to non-resident
PNTFS_ATTR_RECORD Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
LARGE_INTEGER AttribDataSize;
PVOID AttribData;
ULONG EndAttributeOffset;
ULONG LengthWritten;
DPRINT1("Converting attribute to non-resident.\n");
AttribDataSize.QuadPart = AttrContext->Record.Resident.ValueLength;
// Is there existing data we need to back-up?
if (AttribDataSize.QuadPart > 0)
{
AttribData = ExAllocatePoolWithTag(NonPagedPool, AttribDataSize.QuadPart, TAG_NTFS);
if (AttribData == NULL)
{
DPRINT1("ERROR: Couldn't allocate memory for attribute data. Can't migrate to non-resident!\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
// read data to temp buffer
Status = ReadAttribute(Fcb->Vcb, AttrContext, 0, AttribData, AttribDataSize.QuadPart);
if (!NT_SUCCESS(Status))
{
DPRINT1("ERROR: Unable to read attribute before migrating!\n");
ExFreePoolWithTag(AttribData, TAG_NTFS);
return Status;
}
}
// 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));
// 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);
// mark the attribute as non-resident
AttrContext->Record.IsNonResident = Destination->IsNonResident = 1;
// 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 = ALIGN_UP_BY(EndAttributeOffset, 8);
// Update the length
Destination->Length = EndAttributeOffset - AttrOffset;
AttrContext->Record.Length = Destination->Length;
// Update the file record end
SetFileRecordEnd(FileRecord,
(PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + EndAttributeOffset),
FILE_RECORD_END);
// update file record on disk
Status = UpdateFileRecord(Fcb->Vcb, AttrContext->FileMFTIndex, FileRecord);
if (!NT_SUCCESS(Status))
{
DPRINT1("ERROR: Couldn't update file record to continue migration!\n");
if (AttribDataSize.QuadPart > 0)
ExFreePoolWithTag(AttribData, TAG_NTFS);
return Status;
}
// Initialize the MCB, potentially catch an exception
_SEH2_TRY{
FsRtlInitializeLargeMcb(&AttrContext->DataRunsMCB, NonPagedPool);
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
_SEH2_YIELD(return _SEH2_GetExceptionCode());
} _SEH2_END;
// Now we can treat the attribute as non-resident and enlarge it normally
Status = SetAttributeDataLength(FileObject, Fcb, AttrContext, AttrOffset, FileRecord, DataSize);
if (!NT_SUCCESS(Status))
{
DPRINT1("ERROR: Unable to migrate resident attribute!\n");
if (AttribDataSize.QuadPart > 0)
ExFreePoolWithTag(AttribData, TAG_NTFS);
return Status;
}
// restore the back-up attribute, if we made one
if (AttribDataSize.QuadPart > 0)
{
Status = WriteAttribute(Fcb->Vcb, AttrContext, 0, AttribData, AttribDataSize.QuadPart, &LengthWritten);
if (!NT_SUCCESS(Status))
{
DPRINT1("ERROR: Unable to write attribute data to non-resident clusters during migration!\n");
// TODO: Reverse migration so no data is lost
ExFreePoolWithTag(AttribData, TAG_NTFS);
return Status;
}
ExFreePoolWithTag(AttribData, TAG_NTFS);
}
}
}
}
else if (DataSize->LowPart < AttrContext->Record.Resident.ValueLength)
{
// we need to decrease the length
if (NextAttribute->Type != AttributeEnd)
{
DPRINT1("FIXME: Don't know how to decrease length of resident attribute unless it's the final attribute!\n");
return STATUS_NOT_IMPLEMENTED;
}
}
// 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 (!NT_SUCCESS(Status))
{
DPRINT1("ERROR: Failed to set size of attribute!\n");
return Status;
}
//NtfsDumpFileAttributes(Fcb->Vcb, FileRecord);
@ -480,6 +276,10 @@ SetAttributeDataLength(PFILE_OBJECT FileObject,
if (NT_SUCCESS(Status))
{
if(AttrContext->Record.IsNonResident)
Fcb->RFCB.AllocationSize.QuadPart = AttrContext->Record.NonResident.AllocatedSize;
else
Fcb->RFCB.AllocationSize = *DataSize;
Fcb->RFCB.FileSize = *DataSize;
Fcb->RFCB.ValidDataLength = *DataSize;
CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&Fcb->RFCB.AllocationSize);
@ -523,6 +323,325 @@ SetFileRecordEnd(PFILE_RECORD_HEADER FileRecord,
FileRecord->BytesInUse = (ULONG_PTR)AttrEnd - (ULONG_PTR)FileRecord + sizeof(ULONG) * 2;
}
/**
* @name SetNonResidentAttributeDataLength
* @implemented
*
* Called by SetAttributeDataLength() to set the size of a non-resident attribute. Doesn't update the file record.
*
* @param Vcb
* Pointer to a DEVICE_EXTENSION describing the target disk.
*
* @param AttrContext
* PNTFS_ATTR_CONTEXT describing the location of the attribute whose size is being set.
*
* @param AttrOffset
* Offset, from the beginning of the record, of the attribute being sized.
*
* @param FileRecord
* Pointer to a file record containing the attribute to be resized. Must be a complete file record,
* not just the header.
*
* @param DataSize
* Pointer to a LARGE_INTEGER describing the new size of the attribute's data.
*
* @return
* STATUS_SUCCESS on success;
* STATUS_INSUFFICIENT_RESOURCES if an allocation fails.
* STATUS_INVALID_PARAMETER if we can't find the last cluster in the data run.
*
* @remarks
* Called by SetAttributeDataLength(). 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
* any associated files. Synchronization is the callers responsibility.
*/
NTSTATUS
SetNonResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
PNTFS_ATTR_CONTEXT AttrContext,
ULONG AttrOffset,
PFILE_RECORD_HEADER FileRecord,
PLARGE_INTEGER DataSize)
{
NTSTATUS Status = STATUS_SUCCESS;
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;
if (!AttrContext->Record.IsNonResident)
{
DPRINT1("ERROR: SetNonResidentAttributeDataLength() called for resident attribute!\n");
return STATUS_INVALID_PARAMETER;
}
// do we need to increase the allocation size?
if (AttrContext->Record.NonResident.AllocatedSize < AllocationSize)
{
ULONG ClustersNeeded = (AllocationSize / BytesPerCluster) - ExistingClusters;
LARGE_INTEGER LastClusterInDataRun;
ULONG NextAssignedCluster;
ULONG AssignedClusters;
if (ExistingClusters == 0)
{
LastClusterInDataRun.QuadPart = 0;
}
else
{
if (!FsRtlLookupLargeMcbEntry(&AttrContext->DataRunsMCB,
(LONGLONG)AttrContext->Record.NonResident.HighestVCN,
(PLONGLONG)&LastClusterInDataRun.QuadPart,
NULL,
NULL,
NULL,
NULL))
{
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);
return STATUS_INVALID_PARAMETER;
}
}
DPRINT("LastClusterInDataRun: %I64u\n", LastClusterInDataRun.QuadPart);
DPRINT("Highest VCN of record: %I64u\n", AttrContext->Record.NonResident.HighestVCN);
while (ClustersNeeded > 0)
{
Status = NtfsAllocateClusters(Vcb,
LastClusterInDataRun.LowPart + 1,
ClustersNeeded,
&NextAssignedCluster,
&AssignedClusters);
if (!NT_SUCCESS(Status))
{
DPRINT1("Error: Unable to allocate requested clusters!\n");
return Status;
}
// now we need to add the clusters we allocated to the data run
Status = AddRun(Vcb, AttrContext, AttrOffset, FileRecord, NextAssignedCluster, AssignedClusters);
if (!NT_SUCCESS(Status))
{
DPRINT1("Error: Unable to add data run!\n");
return Status;
}
ClustersNeeded -= AssignedClusters;
LastClusterInDataRun.LowPart = NextAssignedCluster + AssignedClusters - 1;
}
}
else if (AttrContext->Record.NonResident.AllocatedSize > AllocationSize)
{
// shrink allocation size
ULONG ClustersToFree = ExistingClusters - (AllocationSize / BytesPerCluster);
Status = FreeClusters(Vcb, AttrContext, AttrOffset, FileRecord, ClustersToFree);
}
// 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;
DestinationAttribute->NonResident.AllocatedSize = AllocationSize;
DestinationAttribute->NonResident.DataSize = DataSize->QuadPart;
DestinationAttribute->NonResident.InitializedSize = DataSize->QuadPart;
DPRINT("Allocated Size: %I64u\n", DestinationAttribute->NonResident.AllocatedSize);
return Status;
}
/**
* @name SetResidentAttributeDataLength
* @implemented
*
* Called by SetAttributeDataLength() to set the size of a non-resident attribute. Doesn't update the file record.
*
* @param Vcb
* Pointer to a DEVICE_EXTENSION describing the target disk.
*
* @param AttrContext
* PNTFS_ATTR_CONTEXT describing the location of the attribute whose size is being set.
*
* @param AttrOffset
* Offset, from the beginning of the record, of the attribute being sized.
*
* @param FileRecord
* Pointer to a file record containing the attribute to be resized. Must be a complete file record,
* not just the header.
*
* @param DataSize
* Pointer to a LARGE_INTEGER describing the new size of the attribute's data.
*
* @return
* STATUS_SUCCESS on success;
* STATUS_INSUFFICIENT_RESOURCES if an allocation fails.
* STATUS_INVALID_PARAMETER if AttrContext describes a non-resident attribute.
* STATUS_NOT_IMPLEMENTED if requested to decrease the size of an attribute that isn't the
* last attribute listed in the file record.
*
* @remarks
* Called by SetAttributeDataLength(). 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
* any associated files. Synchronization is the callers responsibility.
*/
NTSTATUS
SetResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
PNTFS_ATTR_CONTEXT AttrContext,
ULONG AttrOffset,
PFILE_RECORD_HEADER FileRecord,
PLARGE_INTEGER DataSize)
{
NTSTATUS Status;
// find the next attribute
ULONG NextAttributeOffset = AttrOffset + AttrContext->Record.Length;
PNTFS_ATTR_RECORD NextAttribute = (PNTFS_ATTR_RECORD)((PCHAR)FileRecord + NextAttributeOffset);
if (AttrContext->Record.IsNonResident)
{
DPRINT1("ERROR: SetResidentAttributeDataLength() called for non-resident attribute!\n");
return STATUS_INVALID_PARAMETER;
}
//NtfsDumpFileAttributes(Vcb, FileRecord);
// Do we need to increase the data length?
if (DataSize->QuadPart > AttrContext->Record.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;
if (MaxValueLength < DataSize->LowPart)
{
// If this is the last attribute, we could move the end marker to the very end of the file record
MaxValueLength += Vcb->NtfsInfo.BytesPerFileRecord - NextAttributeOffset - (sizeof(ULONG) * 2);
if (MaxValueLength < DataSize->LowPart || NextAttribute->Type != AttributeEnd)
{
// convert attribute to non-resident
PNTFS_ATTR_RECORD Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
LARGE_INTEGER AttribDataSize;
PVOID AttribData;
ULONG EndAttributeOffset;
ULONG LengthWritten;
DPRINT1("Converting attribute to non-resident.\n");
AttribDataSize.QuadPart = AttrContext->Record.Resident.ValueLength;
// Is there existing data we need to back-up?
if (AttribDataSize.QuadPart > 0)
{
AttribData = ExAllocatePoolWithTag(NonPagedPool, AttribDataSize.QuadPart, TAG_NTFS);
if (AttribData == NULL)
{
DPRINT1("ERROR: Couldn't allocate memory for attribute data. Can't migrate to non-resident!\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
// read data to temp buffer
Status = ReadAttribute(Vcb, AttrContext, 0, AttribData, AttribDataSize.QuadPart);
if (!NT_SUCCESS(Status))
{
DPRINT1("ERROR: Unable to read attribute before migrating!\n");
ExFreePoolWithTag(AttribData, TAG_NTFS);
return Status;
}
}
// 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));
// 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);
// mark the attribute as non-resident
AttrContext->Record.IsNonResident = Destination->IsNonResident = 1;
// 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 = ALIGN_UP_BY(EndAttributeOffset, 8);
// Update the length
Destination->Length = EndAttributeOffset - AttrOffset;
AttrContext->Record.Length = Destination->Length;
// Update the file record end
SetFileRecordEnd(FileRecord,
(PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + EndAttributeOffset),
FILE_RECORD_END);
// update file record on disk
Status = UpdateFileRecord(Vcb, AttrContext->FileMFTIndex, FileRecord);
if (!NT_SUCCESS(Status))
{
DPRINT1("ERROR: Couldn't update file record to continue migration!\n");
if (AttribDataSize.QuadPart > 0)
ExFreePoolWithTag(AttribData, TAG_NTFS);
return Status;
}
// Initialize the MCB, potentially catch an exception
_SEH2_TRY{
FsRtlInitializeLargeMcb(&AttrContext->DataRunsMCB, NonPagedPool);
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
_SEH2_YIELD(return _SEH2_GetExceptionCode());
} _SEH2_END;
// Now we can treat the attribute as non-resident and enlarge it normally
Status = SetNonResidentAttributeDataLength(Vcb, AttrContext, AttrOffset, FileRecord, DataSize);
if (!NT_SUCCESS(Status))
{
DPRINT1("ERROR: Unable to migrate resident attribute!\n");
if (AttribDataSize.QuadPart > 0)
ExFreePoolWithTag(AttribData, TAG_NTFS);
return Status;
}
// restore the back-up attribute, if we made one
if (AttribDataSize.QuadPart > 0)
{
Status = WriteAttribute(Vcb, AttrContext, 0, AttribData, AttribDataSize.QuadPart, &LengthWritten);
if (!NT_SUCCESS(Status))
{
DPRINT1("ERROR: Unable to write attribute data to non-resident clusters during migration!\n");
// TODO: Reverse migration so no data is lost
ExFreePoolWithTag(AttribData, TAG_NTFS);
return Status;
}
ExFreePoolWithTag(AttribData, TAG_NTFS);
}
}
}
}
else if (DataSize->LowPart < AttrContext->Record.Resident.ValueLength)
{
// we need to decrease the length
if (NextAttribute->Type != AttributeEnd)
{
DPRINT1("FIXME: Don't know how to decrease length of resident attribute unless it's the final attribute!\n");
return STATUS_NOT_IMPLEMENTED;
}
}
// set the new length of the resident attribute (if we didn't migrate it)
if (!AttrContext->Record.IsNonResident)
InternalSetResidentAttributeLength(AttrContext, FileRecord, AttrOffset, DataSize->LowPart);
return STATUS_SUCCESS;
}
ULONG
ReadAttribute(PDEVICE_EXTENSION Vcb,
PNTFS_ATTR_CONTEXT Context,

View file

@ -116,6 +116,7 @@ typedef struct
NTFS_INFO NtfsInfo;
ULONG MftDataOffset;
ULONG Flags;
ULONG OpenHandleCount;
@ -869,6 +870,20 @@ SetFileRecordEnd(PFILE_RECORD_HEADER FileRecord,
PNTFS_ATTR_RECORD AttrEnd,
ULONG EndMarker);
NTSTATUS
SetNonResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
PNTFS_ATTR_CONTEXT AttrContext,
ULONG AttrOffset,
PFILE_RECORD_HEADER FileRecord,
PLARGE_INTEGER DataSize);
NTSTATUS
SetResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
PNTFS_ATTR_CONTEXT AttrContext,
ULONG AttrOffset,
PFILE_RECORD_HEADER FileRecord,
PLARGE_INTEGER DataSize);
ULONGLONG
AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord);