mirror of
https://github.com/reactos/reactos.git
synced 2025-08-06 10:22:59 +00:00
[NTFS]
+FreeClusters(). Fix a DPRINT. svn path=/branches/GSoC_2016/NTFS/; revision=71968
This commit is contained in:
parent
ed1b4bf9bc
commit
920e2f0216
3 changed files with 215 additions and 12 deletions
|
@ -409,6 +409,208 @@ FindRun(PNTFS_ATTR_RECORD NresAttr,
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name FreeClusters
|
||||||
|
* @implemented
|
||||||
|
*
|
||||||
|
* Shrinks the allocation size of a non-resident attribute by a given number of clusters.
|
||||||
|
* Frees the clusters from the volume's $BITMAP file as well as the attribute's data runs.
|
||||||
|
*
|
||||||
|
* @param Vcb
|
||||||
|
* Pointer to an NTFS_VCB for the destination volume.
|
||||||
|
*
|
||||||
|
* @param AttrContext
|
||||||
|
* Pointer to an NTFS_ATTR_CONTEXT describing the attribute from which the clusters will be freed.
|
||||||
|
*
|
||||||
|
* @param AttrOffset
|
||||||
|
* Byte offset of the destination attribute relative to its file record.
|
||||||
|
*
|
||||||
|
* @param FileRecord
|
||||||
|
* Pointer to a complete copy of the file record containing the attribute. Must be at least
|
||||||
|
* Vcb->NtfsInfo.BytesPerFileRecord bytes long.
|
||||||
|
*
|
||||||
|
* @param ClustersToFree
|
||||||
|
* Number of clusters that should be freed from the end of the data stream. Must be no more
|
||||||
|
* Than the number of clusters assigned to the attribute (HighestVCN + 1).
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* STATUS_SUCCESS on success. STATUS_INVALID_PARAMETER if AttrContext describes a resident attribute,
|
||||||
|
* or if the caller requested more clusters be freed than the attribute has been allocated.
|
||||||
|
* STATUS_INSUFFICIENT_RESOURCES if ConvertDataRunsToLargeMCB() fails.
|
||||||
|
* STATUS_BUFFER_TOO_SMALL if ConvertLargeMCBToDataRuns() fails.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
NTSTATUS
|
||||||
|
FreeClusters(PNTFS_VCB Vcb,
|
||||||
|
PNTFS_ATTR_CONTEXT AttrContext,
|
||||||
|
ULONG AttrOffset,
|
||||||
|
PFILE_RECORD_HEADER FileRecord,
|
||||||
|
ULONG ClustersToFree)
|
||||||
|
{
|
||||||
|
NTSTATUS Status = STATUS_SUCCESS;
|
||||||
|
ULONG ClustersLeftToFree = ClustersToFree;
|
||||||
|
|
||||||
|
// convert data runs to mcb
|
||||||
|
PUCHAR DataRun = (PUCHAR)&AttrContext->Record + AttrContext->Record.NonResident.MappingPairsOffset;
|
||||||
|
PNTFS_ATTR_RECORD DestinationAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
|
||||||
|
LARGE_MCB DataRunsMCB;
|
||||||
|
ULONG NextAttributeOffset = AttrOffset + AttrContext->Record.Length;
|
||||||
|
PNTFS_ATTR_RECORD NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + NextAttributeOffset);
|
||||||
|
ULONGLONG NextVBN = AttrContext->Record.NonResident.LowestVCN;
|
||||||
|
|
||||||
|
// Allocate some memory for the RunBuffer
|
||||||
|
PUCHAR RunBuffer = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
|
||||||
|
ULONG RunBufferOffset = 0;
|
||||||
|
|
||||||
|
PFILE_RECORD_HEADER BitmapRecord;
|
||||||
|
PNTFS_ATTR_CONTEXT DataContext;
|
||||||
|
ULONGLONG BitmapDataSize;
|
||||||
|
PUCHAR BitmapData;
|
||||||
|
RTL_BITMAP Bitmap;
|
||||||
|
ULONG LengthWritten;
|
||||||
|
|
||||||
|
if (!AttrContext->Record.IsNonResident)
|
||||||
|
{
|
||||||
|
ExFreePoolWithTag(RunBuffer, TAG_NTFS);
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the data runs to a map control block
|
||||||
|
Status = ConvertDataRunsToLargeMCB(DataRun, &DataRunsMCB, &NextVBN);
|
||||||
|
if (!NT_SUCCESS(Status))
|
||||||
|
{
|
||||||
|
DPRINT1("Unable to convert data runs to MCB (probably ran out of memory)!\n");
|
||||||
|
ExFreePoolWithTag(RunBuffer, TAG_NTFS);
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
BitmapRecord = ExAllocatePoolWithTag(NonPagedPool,
|
||||||
|
Vcb->NtfsInfo.BytesPerFileRecord,
|
||||||
|
TAG_NTFS);
|
||||||
|
if (BitmapRecord == NULL)
|
||||||
|
{
|
||||||
|
DPRINT1("Error: Unable to allocate memory for bitmap file record!\n");
|
||||||
|
FsRtlUninitializeLargeMcb(&DataRunsMCB);
|
||||||
|
ExFreePoolWithTag(RunBuffer, TAG_NTFS);
|
||||||
|
return STATUS_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
Status = ReadFileRecord(Vcb, NTFS_FILE_BITMAP, BitmapRecord);
|
||||||
|
if (!NT_SUCCESS(Status))
|
||||||
|
{
|
||||||
|
DPRINT1("Error: Unable to read file record for bitmap!\n");
|
||||||
|
FsRtlUninitializeLargeMcb(&DataRunsMCB);
|
||||||
|
ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
|
||||||
|
ExFreePoolWithTag(RunBuffer, TAG_NTFS);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Status = FindAttribute(Vcb, BitmapRecord, AttributeData, L"", 0, &DataContext, NULL);
|
||||||
|
if (!NT_SUCCESS(Status))
|
||||||
|
{
|
||||||
|
DPRINT1("Error: Unable to find data attribute for bitmap file!\n");
|
||||||
|
FsRtlUninitializeLargeMcb(&DataRunsMCB);
|
||||||
|
ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
|
||||||
|
ExFreePoolWithTag(RunBuffer, TAG_NTFS);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BitmapDataSize = AttributeDataLength(&DataContext->Record);
|
||||||
|
BitmapDataSize = min(BitmapDataSize, 0xffffffff);
|
||||||
|
ASSERT((BitmapDataSize * 8) >= Vcb->NtfsInfo.ClusterCount);
|
||||||
|
BitmapData = ExAllocatePoolWithTag(NonPagedPool, ROUND_UP(BitmapDataSize, Vcb->NtfsInfo.BytesPerSector), TAG_NTFS);
|
||||||
|
if (BitmapData == NULL)
|
||||||
|
{
|
||||||
|
DPRINT1("Error: Unable to allocate memory for bitmap file data!\n");
|
||||||
|
ReleaseAttributeContext(DataContext);
|
||||||
|
FsRtlUninitializeLargeMcb(&DataRunsMCB);
|
||||||
|
ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
|
||||||
|
ExFreePoolWithTag(RunBuffer, TAG_NTFS);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadAttribute(Vcb, DataContext, 0, (PCHAR)BitmapData, (ULONG)BitmapDataSize);
|
||||||
|
|
||||||
|
RtlInitializeBitMap(&Bitmap, (PULONG)BitmapData, Vcb->NtfsInfo.ClusterCount);
|
||||||
|
|
||||||
|
// free clusters in $BITMAP file
|
||||||
|
while (ClustersLeftToFree > 0)
|
||||||
|
{
|
||||||
|
LONGLONG LargeVbn, LargeLbn;
|
||||||
|
|
||||||
|
if (!FsRtlLookupLastLargeMcbEntry(&DataRunsMCB, &LargeVbn, &LargeLbn))
|
||||||
|
{
|
||||||
|
Status = STATUS_INVALID_PARAMETER;
|
||||||
|
DPRINT1("DRIVER ERROR: FreeClusters called to free %lu clusters, which is %lu more clusters than are assigned to attribute!",
|
||||||
|
ClustersToFree,
|
||||||
|
ClustersLeftToFree);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( LargeLbn != -1)
|
||||||
|
{
|
||||||
|
// deallocate this cluster
|
||||||
|
RtlClearBits(&Bitmap, LargeLbn, 1);
|
||||||
|
}
|
||||||
|
FsRtlTruncateLargeMcb(&DataRunsMCB, AttrContext->Record.NonResident.HighestVCN);
|
||||||
|
AttrContext->Record.NonResident.HighestVCN = min(AttrContext->Record.NonResident.HighestVCN, AttrContext->Record.NonResident.HighestVCN - 1);
|
||||||
|
ClustersLeftToFree--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update $BITMAP file on disk
|
||||||
|
Status = WriteAttribute(Vcb, DataContext, 0, BitmapData, (ULONG)BitmapDataSize, &LengthWritten);
|
||||||
|
if (!NT_SUCCESS(Status))
|
||||||
|
{
|
||||||
|
ReleaseAttributeContext(DataContext);
|
||||||
|
FsRtlUninitializeLargeMcb(&DataRunsMCB);
|
||||||
|
ExFreePoolWithTag(BitmapData, TAG_NTFS);
|
||||||
|
ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
|
||||||
|
ExFreePoolWithTag(RunBuffer, TAG_NTFS);
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReleaseAttributeContext(DataContext);
|
||||||
|
ExFreePoolWithTag(BitmapData, TAG_NTFS);
|
||||||
|
ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
|
||||||
|
|
||||||
|
// Convert the map control block back to encoded data runs
|
||||||
|
ConvertLargeMCBToDataRuns(&DataRunsMCB, RunBuffer, Vcb->NtfsInfo.BytesPerCluster, &RunBufferOffset);
|
||||||
|
|
||||||
|
// Update HighestVCN
|
||||||
|
DestinationAttribute->NonResident.HighestVCN = AttrContext->Record.NonResident.HighestVCN;
|
||||||
|
|
||||||
|
// Write data runs to destination attribute
|
||||||
|
RtlCopyMemory((PVOID)((ULONG_PTR)DestinationAttribute + DestinationAttribute->NonResident.MappingPairsOffset),
|
||||||
|
RunBuffer,
|
||||||
|
RunBufferOffset);
|
||||||
|
|
||||||
|
if (NextAttribute->Type == AttributeEnd)
|
||||||
|
{
|
||||||
|
// update attribute length
|
||||||
|
AttrContext->Record.Length = ALIGN_UP_BY(AttrContext->Record.NonResident.MappingPairsOffset + RunBufferOffset, 8);
|
||||||
|
DestinationAttribute->Length = AttrContext->Record.Length;
|
||||||
|
|
||||||
|
// write end markers
|
||||||
|
NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)DestinationAttribute + DestinationAttribute->Length);
|
||||||
|
NextAttribute->Type = AttributeEnd;
|
||||||
|
NextAttribute->Length = FILE_RECORD_END;
|
||||||
|
|
||||||
|
// update file record length
|
||||||
|
FileRecord->BytesInUse = AttrOffset + DestinationAttribute->Length + (sizeof(ULONG) * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the file record
|
||||||
|
Status = UpdateFileRecord(Vcb, AttrContext->FileMFTIndex, FileRecord);
|
||||||
|
|
||||||
|
FsRtlUninitializeLargeMcb(&DataRunsMCB);
|
||||||
|
ExFreePoolWithTag(RunBuffer, TAG_NTFS);
|
||||||
|
|
||||||
|
NtfsDumpDataRuns((PUCHAR)((ULONG_PTR)DestinationAttribute + DestinationAttribute->NonResident.MappingPairsOffset), 0);
|
||||||
|
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
InternalReadNonResidentAttributes(PFIND_ATTR_CONTXT Context)
|
InternalReadNonResidentAttributes(PFIND_ATTR_CONTXT Context)
|
||||||
|
|
|
@ -225,6 +225,7 @@ 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))
|
||||||
|
@ -238,14 +239,13 @@ SetAttributeDataLength(PFILE_OBJECT FileObject,
|
||||||
|
|
||||||
if (AttrContext->Record.IsNonResident)
|
if (AttrContext->Record.IsNonResident)
|
||||||
{
|
{
|
||||||
ULONG BytesPerCluster = Fcb->Vcb->NtfsInfo.BytesPerCluster;
|
|
||||||
ULONGLONG AllocationSize = ROUND_UP(DataSize->QuadPart, BytesPerCluster);
|
ULONGLONG AllocationSize = ROUND_UP(DataSize->QuadPart, BytesPerCluster);
|
||||||
PNTFS_ATTR_RECORD DestinationAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
|
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?
|
// do we need to increase the allocation size?
|
||||||
if (AttrContext->Record.NonResident.AllocatedSize < AllocationSize)
|
if (AttrContext->Record.NonResident.AllocatedSize < AllocationSize)
|
||||||
{
|
{
|
||||||
ULONG ExistingClusters = AttrContext->Record.NonResident.AllocatedSize / BytesPerCluster;
|
|
||||||
ULONG ClustersNeeded = (AllocationSize / BytesPerCluster) - ExistingClusters;
|
ULONG ClustersNeeded = (AllocationSize / BytesPerCluster) - ExistingClusters;
|
||||||
LARGE_INTEGER LastClusterInDataRun;
|
LARGE_INTEGER LastClusterInDataRun;
|
||||||
ULONG NextAssignedCluster;
|
ULONG NextAssignedCluster;
|
||||||
|
@ -284,15 +284,9 @@ SetAttributeDataLength(PFILE_OBJECT FileObject,
|
||||||
}
|
}
|
||||||
else if (AttrContext->Record.NonResident.AllocatedSize > AllocationSize)
|
else if (AttrContext->Record.NonResident.AllocatedSize > AllocationSize)
|
||||||
{
|
{
|
||||||
// shrink allocation size (TODO)
|
// shrink allocation size
|
||||||
if (AllocationSize == 0)
|
ULONG ClustersToFree = ExistingClusters - (AllocationSize / BytesPerCluster);
|
||||||
{
|
Status = FreeClusters(Fcb->Vcb, AttrContext, AttrOffset, FileRecord, ClustersToFree);
|
||||||
// hack for notepad.exe
|
|
||||||
PUCHAR DataRuns = (PUCHAR)((ULONG_PTR)DestinationAttribute + DestinationAttribute->NonResident.MappingPairsOffset);
|
|
||||||
*DataRuns = 0;
|
|
||||||
DestinationAttribute->NonResident.HighestVCN =
|
|
||||||
AttrContext->Record.NonResident.HighestVCN = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: is the file compressed, encrypted, or sparse?
|
// TODO: is the file compressed, encrypted, or sparse?
|
||||||
|
@ -1098,7 +1092,7 @@ UpdateFileRecord(PDEVICE_EXTENSION Vcb,
|
||||||
|
|
||||||
if (!NT_SUCCESS(Status))
|
if (!NT_SUCCESS(Status))
|
||||||
{
|
{
|
||||||
DPRINT1("UpdateFileRecord failed: %I64u written, %u expected\n", BytesWritten, Vcb->NtfsInfo.BytesPerFileRecord);
|
DPRINT1("UpdateFileRecord failed: %I64u written, %lu expected\n", BytesWritten, Vcb->NtfsInfo.BytesPerFileRecord);
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove the fixup array (so the file record pointer can still be used)
|
// remove the fixup array (so the file record pointer can still be used)
|
||||||
|
|
|
@ -584,6 +584,13 @@ FindNextAttribute(PFIND_ATTR_CONTXT Context,
|
||||||
VOID
|
VOID
|
||||||
FindCloseAttribute(PFIND_ATTR_CONTXT Context);
|
FindCloseAttribute(PFIND_ATTR_CONTXT Context);
|
||||||
|
|
||||||
|
NTSTATUS
|
||||||
|
FreeClusters(PNTFS_VCB Vcb,
|
||||||
|
PNTFS_ATTR_CONTEXT AttrContext,
|
||||||
|
ULONG AttrOffset,
|
||||||
|
PFILE_RECORD_HEADER FileRecord,
|
||||||
|
ULONG ClustersToFree);
|
||||||
|
|
||||||
/* blockdev.c */
|
/* blockdev.c */
|
||||||
|
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue