mirror of
https://github.com/reactos/reactos.git
synced 2025-08-03 14:45:53 +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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
NTSTATUS
|
||||
InternalReadNonResidentAttributes(PFIND_ATTR_CONTXT Context)
|
||||
|
|
|
@ -225,6 +225,7 @@ 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))
|
||||
|
@ -238,14 +239,13 @@ SetAttributeDataLength(PFILE_OBJECT FileObject,
|
|||
|
||||
if (AttrContext->Record.IsNonResident)
|
||||
{
|
||||
ULONG BytesPerCluster = Fcb->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;
|
||||
|
||||
// do we need to increase the allocation size?
|
||||
if (AttrContext->Record.NonResident.AllocatedSize < AllocationSize)
|
||||
{
|
||||
ULONG ExistingClusters = AttrContext->Record.NonResident.AllocatedSize / BytesPerCluster;
|
||||
ULONG ClustersNeeded = (AllocationSize / BytesPerCluster) - ExistingClusters;
|
||||
LARGE_INTEGER LastClusterInDataRun;
|
||||
ULONG NextAssignedCluster;
|
||||
|
@ -284,15 +284,9 @@ SetAttributeDataLength(PFILE_OBJECT FileObject,
|
|||
}
|
||||
else if (AttrContext->Record.NonResident.AllocatedSize > AllocationSize)
|
||||
{
|
||||
// shrink allocation size (TODO)
|
||||
if (AllocationSize == 0)
|
||||
{
|
||||
// hack for notepad.exe
|
||||
PUCHAR DataRuns = (PUCHAR)((ULONG_PTR)DestinationAttribute + DestinationAttribute->NonResident.MappingPairsOffset);
|
||||
*DataRuns = 0;
|
||||
DestinationAttribute->NonResident.HighestVCN =
|
||||
AttrContext->Record.NonResident.HighestVCN = 0;
|
||||
}
|
||||
// shrink allocation size
|
||||
ULONG ClustersToFree = ExistingClusters - (AllocationSize / BytesPerCluster);
|
||||
Status = FreeClusters(Fcb->Vcb, AttrContext, AttrOffset, FileRecord, ClustersToFree);
|
||||
}
|
||||
|
||||
// TODO: is the file compressed, encrypted, or sparse?
|
||||
|
@ -1098,7 +1092,7 @@ UpdateFileRecord(PDEVICE_EXTENSION Vcb,
|
|||
|
||||
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)
|
||||
|
|
|
@ -584,6 +584,13 @@ FindNextAttribute(PFIND_ATTR_CONTXT Context,
|
|||
VOID
|
||||
FindCloseAttribute(PFIND_ATTR_CONTXT Context);
|
||||
|
||||
NTSTATUS
|
||||
FreeClusters(PNTFS_VCB Vcb,
|
||||
PNTFS_ATTR_CONTEXT AttrContext,
|
||||
ULONG AttrOffset,
|
||||
PFILE_RECORD_HEADER FileRecord,
|
||||
ULONG ClustersToFree);
|
||||
|
||||
/* blockdev.c */
|
||||
|
||||
NTSTATUS
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue