https://git.reactos.org/?p=reactos.git;a=commitdiff;h=920e2f02161f08274a271…
commit 920e2f02161f08274a27186b3dfa2e27e16203ad
Author: Trevor Thompson <tmt256(a)email.vccs.edu>
AuthorDate: Tue Jul 19 15:31:22 2016 +0000
[NTFS]
+FreeClusters(). Fix a DPRINT.
svn path=/branches/GSoC_2016/NTFS/; revision=71968
---
drivers/filesystems/ntfs/attrib.c | 202 ++++++++++++++++++++++++++++++++++++++
drivers/filesystems/ntfs/mft.c | 18 ++--
drivers/filesystems/ntfs/ntfs.h | 7 ++
3 files changed, 215 insertions(+), 12 deletions(-)
diff --git a/drivers/filesystems/ntfs/attrib.c b/drivers/filesystems/ntfs/attrib.c
index e0c41fa0e4..61f2d6fa7a 100644
--- a/drivers/filesystems/ntfs/attrib.c
+++ b/drivers/filesystems/ntfs/attrib.c
@@ -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)
diff --git a/drivers/filesystems/ntfs/mft.c b/drivers/filesystems/ntfs/mft.c
index 7b3dcb8d37..f3ac3182dc 100644
--- a/drivers/filesystems/ntfs/mft.c
+++ b/drivers/filesystems/ntfs/mft.c
@@ -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)
diff --git a/drivers/filesystems/ntfs/ntfs.h b/drivers/filesystems/ntfs/ntfs.h
index bc6a8d9089..29a6c2af00 100644
--- a/drivers/filesystems/ntfs/ntfs.h
+++ b/drivers/filesystems/ntfs/ntfs.h
@@ -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