https://git.reactos.org/?p=reactos.git;a=commitdiff;h=760cdfb5aa5043c49c30b…
commit 760cdfb5aa5043c49c30bec6066f682df9e12d28
Author: Trevor Thompson <tmt256(a)email.vccs.edu>
AuthorDate: Tue Jul 5 07:00:43 2016 +0000
[NTFS]
Add ability to write to resident attributes.
SetAttributeDataLength() - Check if the file is memory mapped before truncating
+InternalSetResidentAttributeLength() - Used by SetAttributeDataLength()
svn path=/branches/GSoC_2016/NTFS/; revision=71820
---
drivers/filesystems/ntfs/attrib.c | 2 +
drivers/filesystems/ntfs/mft.c | 189 +++++++++++++++++++++++++++++++------
drivers/filesystems/ntfs/ntfs.h | 5 +
drivers/filesystems/ntfs/volinfo.c | 3 +-
4 files changed, 170 insertions(+), 29 deletions(-)
diff --git a/drivers/filesystems/ntfs/attrib.c b/drivers/filesystems/ntfs/attrib.c
index 9608d1d17b..e8625903bc 100644
--- a/drivers/filesystems/ntfs/attrib.c
+++ b/drivers/filesystems/ntfs/attrib.c
@@ -509,6 +509,8 @@ NtfsDumpAttribute(PDEVICE_EXTENSION Vcb,
DbgPrint(" logical clusters: %I64u - %I64u\n",
lcn, lcn + runcount - 1);
}
+ else
+ DbgPrint(" %u bytes of data\n",
Attribute->Resident.ValueLength);
}
}
diff --git a/drivers/filesystems/ntfs/mft.c b/drivers/filesystems/ntfs/mft.c
index e1725f9a50..4e10584ce0 100644
--- a/drivers/filesystems/ntfs/mft.c
+++ b/drivers/filesystems/ntfs/mft.c
@@ -135,6 +135,8 @@ FindAttribute(PDEVICE_EXTENSION Vcb,
DPRINT("Found context\n");
*AttrCtx = PrepareAttributeContext(Attribute);
+ (*AttrCtx)->FileMFTIndex = MftRecord->MFTRecordNumber;
+
if (Offset != NULL)
*Offset = Context.Offset;
@@ -170,6 +172,50 @@ AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord)
return AttrRecord->Resident.ValueLength;
}
+void
+InternalSetResidentAttributeLength(PNTFS_ATTR_CONTEXT AttrContext,
+ PFILE_RECORD_HEADER FileRecord,
+ ULONG AttrOffset,
+ ULONG DataSize)
+{
+ ULONG EndMarker = AttributeEnd;
+ ULONG FinalMarker = FILE_RECORD_END;
+ 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));
+
+ // calculate the record length and end marker offset
+ AttrContext->Record.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);
+ NextAttributeOffset += Padding;
+ AttrContext->Record.Length += Padding;
+ }
+
+ // update the record length
+ Offset = AttrOffset + FIELD_OFFSET(NTFS_ATTR_RECORD, Length);
+ RtlCopyMemory((PCHAR)FileRecord + Offset, &AttrContext->Record.Length,
sizeof(ULONG));
+
+ // 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);
+}
NTSTATUS
SetAttributeDataLength(PFILE_OBJECT FileObject,
@@ -179,6 +225,18 @@ SetAttributeDataLength(PFILE_OBJECT FileObject,
PFILE_RECORD_HEADER FileRecord,
PLARGE_INTEGER DataSize)
{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ // are we truncating the file?
+ if (DataSize->QuadPart < AttributeDataLength(&AttrContext->Record)
+ {
+ if (!MmCanFileBeTruncated(FileObject->SectionObjectPointer,
(PLARGE_INTEGER)&AllocationSize))
+ {
+ DPRINT1("Can't truncate a memory-mapped file!\n");
+ return STATUS_USER_MAPPED_FILE;
+ }
+ }
+
if (AttrContext->Record.IsNonResident)
{
ULONG BytesPerCluster = Fcb->Vcb->NtfsInfo.BytesPerCluster;
@@ -238,28 +296,59 @@ SetAttributeDataLength(PFILE_OBJECT FileObject,
AttrContext->Record.NonResident.DataSize = DataSize->QuadPart;
AttrContext->Record.NonResident.InitializedSize = DataSize->QuadPart;
- Fcb->RFCB.FileSize = *DataSize;
- Fcb->RFCB.ValidDataLength = *DataSize;
+ // copy the attribute record back into the FileRecord
+ RtlCopyMemory((PCHAR)FileRecord + AttrOffset, &AttrContext->Record,
AttrContext->Record.Length);
+ }
+ else
+ {
+ // resident attribute
- DPRINT("Data Size: %I64u\n", Fcb->RFCB.FileSize.QuadPart);
+ // find the next attribute
+ ULONG NextAttributeOffset = AttrOffset + AttrContext->Record.Length;
+ PNTFS_ATTR_RECORD NextAttribute = (PNTFS_ATTR_RECORD)((PCHAR)FileRecord +
NextAttributeOffset);
//NtfsDumpFileAttributes(Fcb->Vcb, FileRecord);
- // copy the attribute back into the FileRecord
- RtlCopyMemory((PCHAR)FileRecord + AttrOffset, &AttrContext->Record,
AttrContext->Record.Length);
-
- //NtfsDumpFileAttributes(Fcb->Vcb, FileRecord);
+ // Do we need to increase the data length?
+ if (DataSize->QuadPart > AttrContext->Record.Resident.ValueLength)
+ {
+ // There's usually padding at the end of a record. Do we need to extend
past it?
+ ULONG MaxValueLength = AttrContext->Record.Length -
AttrContext->Record.Resident.ValueOffset;
+ if (MaxValueLength < DataSize->LowPart)
+ {
+ // If this is the last attribute, we could move the end marker to the
very end of the file record
+ MaxValueLength += Fcb->Vcb->NtfsInfo.BytesPerFileRecord -
NextAttributeOffset - (sizeof(ULONG) * 2);
- // write the updated file record back to disk
- UpdateFileRecord(Fcb->Vcb, Fcb->MFTIndex, FileRecord);
+ if (MaxValueLength < DataSize->LowPart || NextAttribute->Type !=
AttributeEnd)
+ {
+ DPRINT1("FIXME: Need to convert attribute to
non-resident!\n");
+ return STATUS_NOT_IMPLEMENTED;
+ }
+ }
+ }
+ else if (DataSize->LowPart < AttrContext->Record.Resident.ValueLength)
+ {
+ // we need to decrease the length
+ if (NextAttribute->Type != AttributeEnd)
+ {
+ DPRINT1("FIXME: Don't know how to decrease length of resident
attribute unless it's the final attribute!\n");
+ return STATUS_NOT_IMPLEMENTED;
+ }
+ }
- CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&Fcb->RFCB.AllocationSize);
+ InternalSetResidentAttributeLength(AttrContext, FileRecord, AttrOffset,
DataSize->LowPart);
}
- else
+
+ //NtfsDumpFileAttributes(Fcb->Vcb, FileRecord);
+
+ // write the updated file record back to disk
+ Status = UpdateFileRecord(Fcb->Vcb, Fcb->MFTIndex, FileRecord);
+
+ if (NT_SUCCESS(Status))
{
- // we can't yet handle resident attributes
- DPRINT1("FixMe: Can't handle increasing length of resident
attribute\n");
- return STATUS_NOT_IMPLEMENTED;
+ Fcb->RFCB.FileSize = *DataSize;
+ Fcb->RFCB.ValidDataLength = *DataSize;
+ CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&Fcb->RFCB.AllocationSize);
}
return STATUS_SUCCESS;
@@ -501,29 +590,76 @@ WriteAttribute(PDEVICE_EXTENSION Vcb,
DPRINT("WriteAttribute(%p, %p, %I64u, %p, %lu, %p)\n", Vcb, Context,
Offset, Buffer, Length, RealLengthWritten);
+ *RealLengthWritten = 0;
+
// is this a resident attribute?
if (!Context->Record.IsNonResident)
{
- DPRINT1("FIXME: Writing to resident NTFS records (small files) is not
supported at this time.\n");
- // (TODO: This should be really easy to implement)
+ ULONG AttributeOffset;
+ PNTFS_ATTR_CONTEXT FoundContext;
+ PFILE_RECORD_HEADER FileRecord;
- /* LeftOver code from ReadAttribute(), may be helpful:
- if (Offset > Context->Record.Resident.ValueLength)
- return 0;
if (Offset + Length > Context->Record.Resident.ValueLength)
- Length = (ULONG)(Context->Record.Resident.ValueLength - Offset);
- RtlCopyMemory(Buffer, (PCHAR)&Context->Record +
Context->Record.Resident.ValueOffset + Offset, Length);
- return Length;*/
+ {
+ DPRINT1("DRIVER ERROR: Attribute is too small!\n");
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ FileRecord = ExAllocatePoolWithTag(NonPagedPool,
Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
+
+ if (!FileRecord)
+ {
+ DPRINT1("Error: Couldn't allocate file record!\n");
+ return STATUS_NO_MEMORY;
+ }
+
+ // read the file record
+ ReadFileRecord(Vcb, Context->FileMFTIndex, FileRecord);
- return STATUS_NOT_IMPLEMENTED; // until we implement it
+ // find where to write the attribute data to
+ Status = FindAttribute(Vcb, FileRecord,
+ Context->Record.Type,
+ (PCWSTR)((PCHAR)&Context->Record +
Context->Record.NameOffset),
+ Context->Record.NameLength,
+ &FoundContext,
+ &AttributeOffset);
+
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("ERROR: Couldn't find matching attribute!\n");
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+ return Status;
+ }
+
+ DPRINT("Offset: %I64u, AttributeOffset: %u, ValueOffset: %u\n", Offset,
AttributeOffset, Context->Record.Resident.ValueLength);
+ Offset += AttributeOffset + Context->Record.Resident.ValueOffset;
+
+ if (Offset + Length > Vcb->NtfsInfo.BytesPerFileRecord)
+ {
+ DPRINT1("DRIVER ERROR: Data being written extends past end of file
record!\n");
+ ReleaseAttributeContext(FoundContext);
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ // copy the data being written into the file record
+ RtlCopyMemory((PCHAR)FileRecord + Offset, Buffer, Length);
+
+ Status = UpdateFileRecord(Vcb, Context->FileMFTIndex, FileRecord);
+
+ ReleaseAttributeContext(FoundContext);
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+
+ if (NT_SUCCESS(Status))
+ *RealLengthWritten = Length;
+
+ return Status;
}
// This is a non-resident attribute.
// I. Find the corresponding start data run.
- *RealLengthWritten = 0;
-
// FIXME: Cache seems to be non-working. Disable it for now
//if(Context->CacheRunOffset <= Offset && Offset <
Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
/*if (0)
@@ -718,6 +854,7 @@ ReadFileRecord(PDEVICE_EXTENSION Vcb,
}
/* Apply update sequence array fixups. */
+ DPRINT("Sequence number: %u\n", file->SequenceNumber);
return FixupUpdateSequenceArray(Vcb, &file->Ntfs);
}
@@ -948,8 +1085,6 @@ UpdateFileRecord(PDEVICE_EXTENSION Vcb,
// write the file record to the master file table
Status = WriteAttribute(Vcb, Vcb->MFTContext, index *
Vcb->NtfsInfo.BytesPerFileRecord, (const PUCHAR)file,
Vcb->NtfsInfo.BytesPerFileRecord, &BytesWritten);
- // TODO: Update MFT mirror
-
if (!NT_SUCCESS(Status))
{
DPRINT1("UpdateFileRecord failed: %I64u written, %u expected\n",
BytesWritten, Vcb->NtfsInfo.BytesPerFileRecord);
diff --git a/drivers/filesystems/ntfs/ntfs.h b/drivers/filesystems/ntfs/ntfs.h
index 66cb37f4bd..238e6d5b2d 100644
--- a/drivers/filesystems/ntfs/ntfs.h
+++ b/drivers/filesystems/ntfs/ntfs.h
@@ -175,6 +175,10 @@ typedef enum
AttributeEnd = 0xFFFFFFFF
} ATTRIBUTE_TYPE, *PATTRIBUTE_TYPE;
+// FILE_RECORD_END seems to follow AttributeEnd in every file record starting with
$Quota.
+// No clue what data is being represented here.
+#define FILE_RECORD_END 0x11477982
+
#define NTFS_FILE_MFT 0
#define NTFS_FILE_MFTMIRR 1
#define NTFS_FILE_LOGFILE 2
@@ -433,6 +437,7 @@ typedef struct _NTFS_ATTR_CONTEXT
ULONGLONG CacheRunLength;
LONGLONG CacheRunLastLCN;
ULONGLONG CacheRunCurrentOffset;
+ ULONGLONG FileMFTIndex;
NTFS_ATTR_RECORD Record;
} NTFS_ATTR_CONTEXT, *PNTFS_ATTR_CONTEXT;
diff --git a/drivers/filesystems/ntfs/volinfo.c b/drivers/filesystems/ntfs/volinfo.c
index df3af4989a..0a54cf73b8 100644
--- a/drivers/filesystems/ntfs/volinfo.c
+++ b/drivers/filesystems/ntfs/volinfo.c
@@ -116,7 +116,6 @@ NtfsAllocateClusters(PDEVICE_EXTENSION DeviceExt,
ULONGLONG BitmapDataSize;
PCHAR BitmapData;
ULONGLONG FreeClusters = 0;
- ULONG Read = 0;
RTL_BITMAP Bitmap;
DPRINT1("NtfsAllocateClusters(%p, %lu, %lu, %p, %p)\n", DeviceExt,
FirstDesiredCluster, DesiredClusters, FirstAssignedCluster, AssignedClusters);
@@ -158,7 +157,7 @@ NtfsAllocateClusters(PDEVICE_EXTENSION DeviceExt,
DPRINT1("Total clusters in bitmap: %I64x\n", BitmapDataSize * 8);
DPRINT1("Diff in size: %I64d B\n", ((BitmapDataSize * 8) -
DeviceExt->NtfsInfo.ClusterCount) * DeviceExt->NtfsInfo.SectorsPerCluster *
DeviceExt->NtfsInfo.BytesPerSector);
- ReadAttribute(DeviceExt, DataContext, Read, (PCHAR)((ULONG_PTR)BitmapData + Read),
(ULONG)BitmapDataSize);
+ ReadAttribute(DeviceExt, DataContext, 0, (PCHAR)BitmapData, (ULONG)BitmapDataSize);
RtlInitializeBitMap(&Bitmap, (PULONG)BitmapData,
DeviceExt->NtfsInfo.ClusterCount);
FreeClusters = RtlNumberOfClearBits(&Bitmap);