mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 18:15:11 +00:00
[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:
parent
9dce6f4db1
commit
1417f286e0
3 changed files with 360 additions and 220 deletions
|
@ -295,7 +295,13 @@ NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject,
|
||||||
return Status;
|
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))
|
if (!NT_SUCCESS(Status))
|
||||||
{
|
{
|
||||||
DPRINT1("Can't find data attribute for Master File Table.\n");
|
DPRINT1("Can't find data attribute for Master File Table.\n");
|
||||||
|
|
|
@ -234,7 +234,6 @@ SetAttributeDataLength(PFILE_OBJECT FileObject,
|
||||||
PLARGE_INTEGER DataSize)
|
PLARGE_INTEGER DataSize)
|
||||||
{
|
{
|
||||||
NTSTATUS Status = STATUS_SUCCESS;
|
NTSTATUS Status = STATUS_SUCCESS;
|
||||||
ULONG BytesPerCluster = Fcb->Vcb->NtfsInfo.BytesPerCluster;
|
|
||||||
|
|
||||||
// are we truncating the file?
|
// are we truncating the file?
|
||||||
if (DataSize->QuadPart < AttributeDataLength(&AttrContext->Record))
|
if (DataSize->QuadPart < AttributeDataLength(&AttrContext->Record))
|
||||||
|
@ -248,229 +247,26 @@ SetAttributeDataLength(PFILE_OBJECT FileObject,
|
||||||
|
|
||||||
if (AttrContext->Record.IsNonResident)
|
if (AttrContext->Record.IsNonResident)
|
||||||
{
|
{
|
||||||
ULONGLONG AllocationSize = ROUND_UP(DataSize->QuadPart, BytesPerCluster);
|
Status = SetNonResidentAttributeDataLength(Fcb->Vcb,
|
||||||
PNTFS_ATTR_RECORD DestinationAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
|
AttrContext,
|
||||||
ULONG ExistingClusters = AttrContext->Record.NonResident.AllocatedSize / BytesPerCluster;
|
AttrOffset,
|
||||||
|
FileRecord,
|
||||||
// do we need to increase the allocation size?
|
DataSize);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// resident attribute
|
// resident attribute
|
||||||
|
Status = SetResidentAttributeDataLength(Fcb->Vcb,
|
||||||
|
AttrContext,
|
||||||
|
AttrOffset,
|
||||||
|
FileRecord,
|
||||||
|
DataSize);
|
||||||
|
}
|
||||||
|
|
||||||
// find the next attribute
|
if (!NT_SUCCESS(Status))
|
||||||
ULONG NextAttributeOffset = AttrOffset + AttrContext->Record.Length;
|
{
|
||||||
PNTFS_ATTR_RECORD NextAttribute = (PNTFS_ATTR_RECORD)((PCHAR)FileRecord + NextAttributeOffset);
|
DPRINT1("ERROR: Failed to set size of attribute!\n");
|
||||||
|
return Status;
|
||||||
//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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//NtfsDumpFileAttributes(Fcb->Vcb, FileRecord);
|
//NtfsDumpFileAttributes(Fcb->Vcb, FileRecord);
|
||||||
|
@ -480,6 +276,10 @@ SetAttributeDataLength(PFILE_OBJECT FileObject,
|
||||||
|
|
||||||
if (NT_SUCCESS(Status))
|
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.FileSize = *DataSize;
|
||||||
Fcb->RFCB.ValidDataLength = *DataSize;
|
Fcb->RFCB.ValidDataLength = *DataSize;
|
||||||
CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&Fcb->RFCB.AllocationSize);
|
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;
|
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
|
ULONG
|
||||||
ReadAttribute(PDEVICE_EXTENSION Vcb,
|
ReadAttribute(PDEVICE_EXTENSION Vcb,
|
||||||
PNTFS_ATTR_CONTEXT Context,
|
PNTFS_ATTR_CONTEXT Context,
|
||||||
|
|
|
@ -116,6 +116,7 @@ typedef struct
|
||||||
|
|
||||||
NTFS_INFO NtfsInfo;
|
NTFS_INFO NtfsInfo;
|
||||||
|
|
||||||
|
ULONG MftDataOffset;
|
||||||
ULONG Flags;
|
ULONG Flags;
|
||||||
ULONG OpenHandleCount;
|
ULONG OpenHandleCount;
|
||||||
|
|
||||||
|
@ -869,6 +870,20 @@ SetFileRecordEnd(PFILE_RECORD_HEADER FileRecord,
|
||||||
PNTFS_ATTR_RECORD AttrEnd,
|
PNTFS_ATTR_RECORD AttrEnd,
|
||||||
ULONG EndMarker);
|
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
|
ULONGLONG
|
||||||
AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord);
|
AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue