From c25b9747a168c659c76e1b41a53655dad53f8007 Mon Sep 17 00:00:00 2001 From: Trevor Thompson Date: Sat, 13 May 2017 08:56:54 +0000 Subject: [PATCH] [NTFS] - Expand support for resizing resident attributes and fix NtfsAllocateClusters(). -Modify SetAttributeDataLength() to allow a resident attribute to migrate to non-resident if the attribute grows too large to remain resident. -Fix values returned by NtfsAllocateClusters() in case of error; return error codes, not 0. svn path=/branches/GSoC_2016/NTFS/; revision=74524 --- drivers/filesystems/ntfs/mft.c | 106 ++++++++++++++++++++++++++++- drivers/filesystems/ntfs/volinfo.c | 8 +-- 2 files changed, 107 insertions(+), 7 deletions(-) diff --git a/drivers/filesystems/ntfs/mft.c b/drivers/filesystems/ntfs/mft.c index e78be291844..1e23675a0cc 100644 --- a/drivers/filesystems/ntfs/mft.c +++ b/drivers/filesystems/ntfs/mft.c @@ -356,8 +356,106 @@ SetAttributeDataLength(PFILE_OBJECT FileObject, if (MaxValueLength < DataSize->LowPart || NextAttribute->Type != AttributeEnd) { - DPRINT1("FIXME: Need to convert attribute to non-resident!\n"); - return STATUS_NOT_IMPLEMENTED; + // 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(AttribData != NULL) + 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); + } } } } @@ -371,7 +469,9 @@ SetAttributeDataLength(PFILE_OBJECT FileObject, } } - InternalSetResidentAttributeLength(AttrContext, FileRecord, AttrOffset, DataSize->LowPart); + // 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); diff --git a/drivers/filesystems/ntfs/volinfo.c b/drivers/filesystems/ntfs/volinfo.c index 338ea847509..1a577d45e23 100644 --- a/drivers/filesystems/ntfs/volinfo.c +++ b/drivers/filesystems/ntfs/volinfo.c @@ -127,21 +127,21 @@ NtfsAllocateClusters(PDEVICE_EXTENSION DeviceExt, TAG_NTFS); if (BitmapRecord == NULL) { - return 0; + return STATUS_INSUFFICIENT_RESOURCES; } Status = ReadFileRecord(DeviceExt, NTFS_FILE_BITMAP, BitmapRecord); if (!NT_SUCCESS(Status)) { ExFreePoolWithTag(BitmapRecord, TAG_NTFS); - return 0; + return Status; } Status = FindAttribute(DeviceExt, BitmapRecord, AttributeData, L"", 0, &DataContext, NULL); if (!NT_SUCCESS(Status)) { ExFreePoolWithTag(BitmapRecord, TAG_NTFS); - return 0; + return Status; } BitmapDataSize = AttributeDataLength(&DataContext->Record); @@ -152,7 +152,7 @@ NtfsAllocateClusters(PDEVICE_EXTENSION DeviceExt, { ReleaseAttributeContext(DataContext); ExFreePoolWithTag(BitmapRecord, TAG_NTFS); - return 0; + return STATUS_INSUFFICIENT_RESOURCES; } DPRINT1("Total clusters: %I64x\n", DeviceExt->NtfsInfo.ClusterCount);