ULONG AttrOffset,
ULONG DataSize)
{
- ULONG EndMarker = AttributeEnd;
- ULONG FinalMarker = FILE_RECORD_END;
+ PNTFS_ATTR_RECORD Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
ULONG NextAttributeOffset;
- ULONG Offset;
- USHORT Padding;
DPRINT("InternalSetResidentAttributeLength( %p, %p, %lu, %lu )\n", AttrContext, FileRecord, AttrOffset, DataSize);
// update ValueLength Field
- AttrContext->Record.Resident.ValueLength = DataSize;
- Offset = AttrOffset + FIELD_OFFSET(NTFS_ATTR_RECORD, Resident.ValueLength);
- RtlCopyMemory((PCHAR)FileRecord + Offset, &DataSize, sizeof(ULONG));
+ AttrContext->Record.Resident.ValueLength =
+ Destination->Resident.ValueLength = DataSize;
// calculate the record length and end marker offset
- AttrContext->Record.Length = DataSize + AttrContext->Record.Resident.ValueOffset;
+ AttrContext->Record.Length =
+ Destination->Length = DataSize + AttrContext->Record.Resident.ValueOffset;
NextAttributeOffset = AttrOffset + AttrContext->Record.Length;
// Ensure NextAttributeOffset is aligned to an 8-byte boundary
if (NextAttributeOffset % 8 != 0)
{
- Padding = 8 - (NextAttributeOffset % 8);
+ USHORT Padding = 8 - (NextAttributeOffset % 8);
NextAttributeOffset += Padding;
AttrContext->Record.Length += Padding;
+ Destination->Length += Padding;
}
+
+ // advance Destination to the final "attribute" and write the end type
+ Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)Destination + Destination->Length);
+ Destination->Type = AttributeEnd;
- // update the record length
- Offset = AttrOffset + FIELD_OFFSET(NTFS_ATTR_RECORD, Length);
- RtlCopyMemory((PCHAR)FileRecord + Offset, &AttrContext->Record.Length, sizeof(ULONG));
+ // write the final marker (which shares the same offset and type as the Length field)
+ Destination->Length = FILE_RECORD_END;
- // write the end marker
- RtlCopyMemory((PCHAR)FileRecord + NextAttributeOffset, &EndMarker, sizeof(ULONG));
-
- // write the final marker
- Offset = NextAttributeOffset + sizeof(ULONG);
- RtlCopyMemory((PCHAR)FileRecord + Offset, &FinalMarker, sizeof(ULONG));
-
- FileRecord->BytesInUse = Offset + sizeof(ULONG);
+ FileRecord->BytesInUse = NextAttributeOffset + (sizeof(ULONG) * 2);
}
+/**
+* @parameter FileRecord
+* Pointer to a file record. Must be a full record at least
+* Fcb->Vcb->NtfsInfo.BytesPerFileRecord bytes large, not just the header.
+*/
NTSTATUS
SetAttributeDataLength(PFILE_OBJECT FileObject,
PNTFS_FCB Fcb,
PLARGE_INTEGER DataSize)
{
NTSTATUS Status = STATUS_SUCCESS;
+ ULONG BytesPerCluster = Fcb->Vcb->NtfsInfo.BytesPerCluster;
// are we truncating the file?
- if (DataSize->QuadPart < AttributeDataLength(&AttrContext->Record)
+ if (DataSize->QuadPart < AttributeDataLength(&AttrContext->Record))
{
- if (!MmCanFileBeTruncated(FileObject->SectionObjectPointer, (PLARGE_INTEGER)&AllocationSize))
+ if (!MmCanFileBeTruncated(FileObject->SectionObjectPointer, DataSize))
{
DPRINT1("Can't truncate a memory-mapped file!\n");
return STATUS_USER_MAPPED_FILE;
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;
ULONG AssignedClusters;
- NTSTATUS Status = GetLastClusterInDataRun(Fcb->Vcb, &AttrContext->Record, &LastClusterInDataRun.QuadPart);
+ NTSTATUS Status = GetLastClusterInDataRun(Fcb->Vcb, &AttrContext->Record, (PULONGLONG)&LastClusterInDataRun.QuadPart);
DPRINT1("GetLastClusterInDataRun returned: %I64u\n", LastClusterInDataRun.QuadPart);
DPRINT1("Highest VCN of record: %I64u\n", AttrContext->Record.NonResident.HighestVCN);
}
// now we need to add the clusters we allocated to the data run
- Status = AddRun(AttrContext, NextAssignedCluster, AssignedClusters);
+ Status = AddRun(Fcb->Vcb, AttrContext, AttrOffset, FileRecord, NextAssignedCluster, AssignedClusters);
if (!NT_SUCCESS(Status))
{
DPRINT1("Error: Unable to add data run!\n");
ClustersNeeded -= AssignedClusters;
LastClusterInDataRun.LowPart = NextAssignedCluster + AssignedClusters - 1;
}
-
- DPRINT1("FixMe: Increasing allocation size is unimplemented!\n");
- return STATUS_NOT_IMPLEMENTED;
+ }
+ else if (AttrContext->Record.NonResident.AllocatedSize > AllocationSize)
+ {
+ // shrink allocation size
+ ULONG ClustersToFree = ExistingClusters - (AllocationSize / BytesPerCluster);
+ Status = FreeClusters(Fcb->Vcb, AttrContext, AttrOffset, FileRecord, ClustersToFree);
}
// TODO: is the file compressed, encrypted, or sparse?
// NOTE: we need to have acquired the main resource exclusively, as well as(?) the PagingIoResource
- // TODO: update the allocated size on-disk
- DPRINT("Allocated Size: %I64u\n", AttrContext->Record.NonResident.AllocatedSize);
-
+ Fcb->RFCB.AllocationSize.QuadPart = AllocationSize;
+ AttrContext->Record.NonResident.AllocatedSize = AllocationSize;
AttrContext->Record.NonResident.DataSize = DataSize->QuadPart;
AttrContext->Record.NonResident.InitializedSize = DataSize->QuadPart;
- // copy the attribute record back into the FileRecord
- RtlCopyMemory((PCHAR)FileRecord + AttrOffset, &AttrContext->Record, AttrContext->Record.Length);
+ DestinationAttribute->NonResident.AllocatedSize = AllocationSize;
+ DestinationAttribute->NonResident.DataSize = DataSize->QuadPart;
+ DestinationAttribute->NonResident.InitializedSize = DataSize->QuadPart;
+
+ DPRINT("Allocated Size: %I64u\n", DestinationAttribute->NonResident.AllocatedSize);
}
else
{
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)
+ FixupUpdateSequenceArray(Vcb, &file->Ntfs);
+
return Status;
}