*
* @return
* STATUS_SUCCESS on success. STATUS_INVALID_PARAMETER if AttrContext describes a resident attribute.
-* STATUS_INSUFFICIENT_RESOURCES if ConvertDataRunsToLargeMCB() fails.
+* STATUS_INSUFFICIENT_RESOURCES if ConvertDataRunsToLargeMCB() fails or if we fail to allocate a
+* buffer for the new data runs.
+* STATUS_INSUFFICIENT_RESOURCES or STATUS_UNSUCCESSFUL if FsRtlAddLargeMcbEntry() fails.
* STATUS_BUFFER_TOO_SMALL if ConvertLargeMCBToDataRuns() fails.
* STATUS_NOT_IMPLEMENTED if we need to migrate the attribute to an attribute list (TODO).
*
ULONGLONG NextVBN = AttrContext->Record.NonResident.LowestVCN;
// Allocate some memory for the RunBuffer
- PUCHAR RunBuffer = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
- int RunBufferOffset = 0;
+ PUCHAR RunBuffer;
+ ULONG RunBufferOffset = 0;
if (!AttrContext->Record.IsNonResident)
return STATUS_INVALID_PARAMETER;
+ RunBuffer = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
+ if (!RunBuffer)
+ {
+ DPRINT1("ERROR: Couldn't allocate memory for data runs!\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
// 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;
}
// Add newly-assigned clusters to mcb
- FsRtlAddLargeMcbEntry(&DataRunsMCB,
- NextVBN,
- NextAssignedCluster,
- RunLength);
+ _SEH2_TRY{
+ if (!FsRtlAddLargeMcbEntry(&DataRunsMCB,
+ NextVBN,
+ NextAssignedCluster,
+ RunLength))
+ {
+ ExRaiseStatus(STATUS_UNSUCCESSFUL);
+ }
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ FsRtlUninitializeLargeMcb(&DataRunsMCB);
+ ExFreePoolWithTag(RunBuffer, TAG_NTFS);
+ _SEH2_YIELD(_SEH2_GetExceptionCode());
+ } _SEH2_END;
+
// Convert the map control block back to encoded data runs
ConvertLargeMCBToDataRuns(&DataRunsMCB, RunBuffer, Vcb->NtfsInfo.BytesPerCluster, &RunBufferOffset);
* Pointer to an unitialized LARGE_MCB structure.
*
* @return
-* STATUS_SUCCESS on success, STATUS_INSUFFICIENT_RESOURCES if we fail to
+* STATUS_SUCCESS on success, STATUS_INSUFFICIENT_RESOURCES or STATUS_UNSUCCESSFUL if we fail to
* initialize the mcb or add an entry.
*
* @remarks
LONGLONG DataRunOffset;
ULONGLONG DataRunLength;
LONGLONG DataRunStartLCN;
- ULONGLONG NextCluster;
-
ULONGLONG LastLCN = 0;
// Initialize the MCB, potentially catch an exception
_SEH2_TRY{
FsRtlInitializeLargeMcb(DataRunsMCB, NonPagedPool);
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
- _SEH2_YIELD(return STATUS_INSUFFICIENT_RESOURCES);
+ _SEH2_YIELD(return _SEH2_GetExceptionCode());
} _SEH2_END;
while (*DataRun != 0)
// Normal data run.
DataRunStartLCN = LastLCN + DataRunOffset;
LastLCN = DataRunStartLCN;
- NextCluster = LastLCN + DataRunLength;
-
_SEH2_TRY{
if (!FsRtlAddLargeMcbEntry(DataRunsMCB,
DataRunStartLCN,
DataRunLength))
{
- FsRtlUninitializeLargeMcb(DataRunsMCB);
- return STATUS_INSUFFICIENT_RESOURCES;
+ ExRaiseStatus(STATUS_UNSUCCESSFUL);
}
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
FsRtlUninitializeLargeMcb(DataRunsMCB);
- _SEH2_YIELD(return STATUS_INSUFFICIENT_RESOURCES);
+ _SEH2_YIELD(return _SEH2_GetExceptionCode());
} _SEH2_END;
}
ULONG RunBufferOffset = 0;
LONGLONG DataRunOffset;
ULONGLONG LastLCN = 0;
-
LONGLONG Vbn, Lbn, Count;
+ ULONG i;
DPRINT("\t[Vbn, Lbn, Count]\n");
// convert each mcb entry to a data run
- for (int i = 0; FsRtlGetNextLargeMcbEntry(DataRunsMCB, i, &Vbn, &Lbn, &Count); i++)
+ for (i = 0; FsRtlGetNextLargeMcbEntry(DataRunsMCB, i, &Vbn, &Lbn, &Count); i++)
{
UCHAR DataRunOffsetSize = 0;
UCHAR DataRunLengthSize = 0;
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 allocating a buffer for the data runs fails or
+* 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;
+ ULONG RunBufferOffset = 0;
+
+ PFILE_RECORD_HEADER BitmapRecord;
+ PNTFS_ATTR_CONTEXT DataContext;
+ ULONGLONG BitmapDataSize;
+ PUCHAR BitmapData;
+ RTL_BITMAP Bitmap;
+ ULONG LengthWritten;
+
+ if (!AttrContext->Record.IsNonResident)
+ {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ RunBuffer = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
+ if (!RunBuffer)
+ {
+ DPRINT1("ERROR: Couldn't allocate memory for data runs!\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ // 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)