diff --git a/drivers/filesystems/ntfs/attrib.c b/drivers/filesystems/ntfs/attrib.c index 1883951c444..12474f6c813 100644 --- a/drivers/filesystems/ntfs/attrib.c +++ b/drivers/filesystems/ntfs/attrib.c @@ -150,6 +150,8 @@ static PNTFS_ATTR_RECORD InternalGetNextAttribute(PFIND_ATTR_CONTXT Context) { + PNTFS_ATTR_RECORD NextAttribute; + if (Context->CurrAttr == (PVOID)-1) { return NULL; @@ -165,7 +167,10 @@ InternalGetNextAttribute(PFIND_ATTR_CONTXT Context) return NULL; } - Context->CurrAttr = (PNTFS_ATTR_RECORD)((ULONG_PTR)Context->CurrAttr + Context->CurrAttr->Length); + NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)Context->CurrAttr + Context->CurrAttr->Length); + Context->Offset += ((ULONG_PTR)NextAttribute - (ULONG_PTR)Context->CurrAttr); + Context->CurrAttr = NextAttribute; + if (Context->CurrAttr < Context->LastAttr && Context->CurrAttr->Type != AttributeEnd) { @@ -186,7 +191,9 @@ InternalGetNextAttribute(PFIND_ATTR_CONTXT Context) } else if (Context->CurrAttr->Length != 0) { - Context->CurrAttr = (PNTFS_ATTR_RECORD)((ULONG_PTR)Context->CurrAttr + Context->CurrAttr->Length); + NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)Context->CurrAttr + Context->CurrAttr->Length); + Context->Offset += ((ULONG_PTR)NextAttribute - (ULONG_PTR)Context->CurrAttr); + Context->CurrAttr = NextAttribute; } else { @@ -223,6 +230,7 @@ FindFirstAttribute(PFIND_ATTR_CONTXT Context, Context->LastAttr = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->BytesInUse); Context->NonResidentStart = NULL; Context->NonResidentEnd = NULL; + Context->Offset = FileRecord->AttributeOffset; if (Context->FirstAttr->Type == AttributeEnd) { @@ -246,6 +254,7 @@ FindFirstAttribute(PFIND_ATTR_CONTXT Context, else { *Attribute = Context->CurrAttr; + Context->Offset = (UCHAR*)Context->CurrAttr - (UCHAR*)FileRecord; } return STATUS_SUCCESS; @@ -307,7 +316,8 @@ NtfsDumpFileNameAttribute(PNTFS_ATTR_RECORD Attribute) FileNameAttr = (PFILENAME_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset); DbgPrint(" (%x) '%.*S' ", FileNameAttr->NameType, FileNameAttr->NameLength, FileNameAttr->Name); - DbgPrint(" '%x' ", FileNameAttr->FileAttributes); + DbgPrint(" '%x' \n", FileNameAttr->FileAttributes); + DbgPrint(" AllocatedSize: %I64u\nDataSize: %I64u\n", FileNameAttr->AllocatedSize, FileNameAttr->DataSize); } @@ -481,8 +491,8 @@ NtfsDumpAttribute(PDEVICE_EXTENSION Vcb, { FindRun(Attribute,0,&lcn, &runcount); - DbgPrint(" AllocatedSize %I64u DataSize %I64u\n", - Attribute->NonResident.AllocatedSize, Attribute->NonResident.DataSize); + DbgPrint(" AllocatedSize %I64u DataSize %I64u InitilizedSize %I64u\n", + Attribute->NonResident.AllocatedSize, Attribute->NonResident.DataSize, Attribute->NonResident.InitializedSize); DbgPrint(" logical clusters: %I64u - %I64u\n", lcn, lcn + runcount - 1); } diff --git a/drivers/filesystems/ntfs/dirctl.c b/drivers/filesystems/ntfs/dirctl.c index 82b8a7cb1b0..aaa6dfcb87f 100644 --- a/drivers/filesystems/ntfs/dirctl.c +++ b/drivers/filesystems/ntfs/dirctl.c @@ -47,7 +47,7 @@ NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt, NTSTATUS Status; PNTFS_ATTR_CONTEXT DataContext; - Status = FindAttribute(DeviceExt, FileRecord, AttributeData, Stream, StreamLength, &DataContext); + Status = FindAttribute(DeviceExt, FileRecord, AttributeData, Stream, StreamLength, &DataContext, NULL); if (NT_SUCCESS(Status)) { Size = AttributeDataLength(&DataContext->Record); diff --git a/drivers/filesystems/ntfs/fcb.c b/drivers/filesystems/ntfs/fcb.c index e88944c3347..111660288ab 100644 --- a/drivers/filesystems/ntfs/fcb.c +++ b/drivers/filesystems/ntfs/fcb.c @@ -568,7 +568,7 @@ NtfsDirFindFile(PNTFS_VCB Vcb, } else if (Colon != 0) { - Status = FindAttribute(Vcb, FileRecord, AttributeData, Colon, wcslen(Colon), &DataContext); + Status = FindAttribute(Vcb, FileRecord, AttributeData, Colon, wcslen(Colon), &DataContext, NULL); if (!NT_SUCCESS(Status)) { return STATUS_OBJECT_NAME_NOT_FOUND; @@ -741,7 +741,7 @@ NtfsReadFCBAttribute(PNTFS_VCB Vcb, return Status; } - Status = FindAttribute(Vcb, FileRecord, Type, Name, NameLength, &AttrCtxt); + Status = FindAttribute(Vcb, FileRecord, Type, Name, NameLength, &AttrCtxt, NULL); if (!NT_SUCCESS(Status)) { ExFreePoolWithTag(FileRecord, TAG_NTFS); diff --git a/drivers/filesystems/ntfs/fsctl.c b/drivers/filesystems/ntfs/fsctl.c index 0ce93a58eaa..a1f3048cd06 100644 --- a/drivers/filesystems/ntfs/fsctl.c +++ b/drivers/filesystems/ntfs/fsctl.c @@ -295,7 +295,7 @@ NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject, return Status; } - Status = FindAttribute(DeviceExt, DeviceExt->MasterFileTable, AttributeData, L"", 0, &DeviceExt->MFTContext); + Status = FindAttribute(DeviceExt, DeviceExt->MasterFileTable, AttributeData, L"", 0, &DeviceExt->MFTContext, NULL); if (!NT_SUCCESS(Status)) { DPRINT1("Can't find data attribute for Master File Table.\n"); @@ -333,7 +333,7 @@ NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject, NtfsDumpFileAttributes(DeviceExt, VolumeRecord); /* Get volume name */ - Status = FindAttribute(DeviceExt, VolumeRecord, AttributeVolumeName, L"", 0, &AttrCtxt); + Status = FindAttribute(DeviceExt, VolumeRecord, AttributeVolumeName, L"", 0, &AttrCtxt, NULL); if (NT_SUCCESS(Status) && AttrCtxt->Record.Resident.ValueLength != 0) { @@ -374,7 +374,7 @@ NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject, DeviceExt->VolumeFcb = VolumeFcb; /* Get volume information */ - Status = FindAttribute(DeviceExt, VolumeRecord, AttributeVolumeInformation, L"", 0, &AttrCtxt); + Status = FindAttribute(DeviceExt, VolumeRecord, AttributeVolumeInformation, L"", 0, &AttrCtxt, NULL); if (NT_SUCCESS(Status) && AttrCtxt->Record.Resident.ValueLength != 0) { @@ -804,7 +804,7 @@ GetVolumeBitmap(PDEVICE_EXTENSION DeviceExt, return Status; } - Status = FindAttribute(DeviceExt, BitmapRecord, AttributeData, L"", 0, &DataContext); + Status = FindAttribute(DeviceExt, BitmapRecord, AttributeData, L"", 0, &DataContext, NULL); if (!NT_SUCCESS(Status)) { DPRINT1("Failed find $DATA for bitmap: %lx\n", Status); diff --git a/drivers/filesystems/ntfs/mft.c b/drivers/filesystems/ntfs/mft.c index 1294264e696..a53b37fb87e 100644 --- a/drivers/filesystems/ntfs/mft.c +++ b/drivers/filesystems/ntfs/mft.c @@ -81,13 +81,24 @@ ReleaseAttributeContext(PNTFS_ATTR_CONTEXT Context) } +/** +* @name FindAttribute +* @implemented +* +* Searches a file record for an attribute matching the given type and name. +* +* @param Offset +* Optional pointer to a ULONG that will receive the offset of the found attribute +* from the beginning of the record. Can be set to NULL. +*/ NTSTATUS FindAttribute(PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER MftRecord, ULONG Type, PCWSTR Name, ULONG NameLength, - PNTFS_ATTR_CONTEXT * AttrCtx) + PNTFS_ATTR_CONTEXT * AttrCtx, + PULONG Offset) { BOOLEAN Found; NTSTATUS Status; @@ -123,6 +134,10 @@ FindAttribute(PDEVICE_EXTENSION Vcb, /* Found it, fill up the context and return. */ DPRINT("Found context\n"); *AttrCtx = PrepareAttributeContext(Attribute); + + if (Offset != NULL) + *Offset = Context.Offset; + FindCloseAttribute(&Context); return STATUS_SUCCESS; } @@ -156,6 +171,61 @@ AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord) } +NTSTATUS +SetAttributeDataLength(PFILE_OBJECT FileObject, + PNTFS_FCB Fcb, + PNTFS_ATTR_CONTEXT AttrContext, + ULONG AttrOffset, + PFILE_RECORD_HEADER FileRecord, + PDEVICE_EXTENSION DeviceExt, + PLARGE_INTEGER DataSize) +{ + if (AttrContext->Record.IsNonResident) + { + // do we need to increase the allocation size? + if (AttrContext->Record.NonResident.AllocatedSize < DataSize->QuadPart) + { + DPRINT1("FixMe: Increasing allocation size is unimplemented!\n"); + return STATUS_NOT_IMPLEMENTED; + } + + // 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); + + AttrContext->Record.NonResident.DataSize = DataSize->QuadPart; + AttrContext->Record.NonResident.InitializedSize = DataSize->QuadPart; + + Fcb->RFCB.FileSize = *DataSize; + Fcb->RFCB.ValidDataLength = *DataSize; + + DPRINT("Data Size: %I64u\n", Fcb->RFCB.FileSize.QuadPart); + + //NtfsDumpFileAttributes(Fcb->Vcb, FileRecord); + + // copy the attribute back into the FileRecord + RtlCopyMemory((PCHAR)FileRecord + AttrOffset, &AttrContext->Record, AttrContext->Record.Length); + + //NtfsDumpFileAttributes(Fcb->Vcb, FileRecord); + + // write the updated file record back to disk + UpdateFileRecord(Fcb->Vcb, Fcb->MFTIndex, FileRecord); + + CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&Fcb->RFCB.AllocationSize); + } + else + { + // we can't yet handle resident attributes + DPRINT1("FixMe: Can't handle increasing length of resident attribute\n"); + return STATUS_NOT_IMPLEMENTED; + } + + return STATUS_SUCCESS; +} + ULONG ReadAttribute(PDEVICE_EXTENSION Vcb, PNTFS_ATTR_CONTEXT Context, @@ -612,8 +682,39 @@ ReadFileRecord(PDEVICE_EXTENSION Vcb, return FixupUpdateSequenceArray(Vcb, &file->Ntfs); } +/** +* UpdateFileRecord +* @implemented +* Writes a file record to the master file table, at a given index. +*/ +NTSTATUS +UpdateFileRecord(PDEVICE_EXTENSION Vcb, + ULONGLONG index, + PFILE_RECORD_HEADER file) +{ + ULONG BytesWritten; + NTSTATUS Status = STATUS_SUCCESS; -NTSTATUS + DPRINT("UpdateFileRecord(%p, %I64x, %p)\n", Vcb, index, file); + + // Add the fixup array to prepare the data for writing to disk + AddFixupArray(Vcb, file); + + // 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); + } + + return Status; +} + + +NTSTATUS FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb, PNTFS_RECORD_HEADER Record) { @@ -627,6 +728,8 @@ FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb, USACount = Record->UsaCount - 1; /* Exclude the USA Number. */ Block = (USHORT*)((PCHAR)Record + Vcb->NtfsInfo.BytesPerSector - 2); + DPRINT("FixupUpdateSequenceArray(%p, %p)\nUSANumber: %u\tUSACount: %u\n", Vcb, Record, USANumber, USACount); + while (USACount) { if (*Block != USANumber) @@ -642,6 +745,36 @@ FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb, return STATUS_SUCCESS; } +NTSTATUS +AddFixupArray(PDEVICE_EXTENSION Vcb, + PFILE_RECORD_HEADER Record) +{ + USHORT *pShortToFixUp; + unsigned int ArrayEntryCount = Record->BytesAllocated / Vcb->NtfsInfo.BytesPerSector; + unsigned int Offset = Vcb->NtfsInfo.BytesPerSector - 2; + int i; + + PFIXUP_ARRAY fixupArray = (PFIXUP_ARRAY)((UCHAR*)Record + Record->Ntfs.UsaOffset); + + DPRINT("AddFixupArray(%p, %p)\n fixupArray->USN: %u, ArrayEntryCount: %u\n", Vcb, Record, fixupArray->USN, ArrayEntryCount); + + if (Record->BytesAllocated % Vcb->NtfsInfo.BytesPerSector != 0) + ArrayEntryCount++; + + fixupArray->USN++; + + for (i = 0; i < ArrayEntryCount; i++) + { + DPRINT("USN: %u\tOffset: %u\n", fixupArray->USN, Offset); + + pShortToFixUp = (USHORT*)((UCHAR*)Record + Offset); + fixupArray->Array[i] = *pShortToFixUp; + *pShortToFixUp = fixupArray->USN; + Offset += Vcb->NtfsInfo.BytesPerSector; + } + + return STATUS_SUCCESS; +} NTSTATUS ReadLCN(PDEVICE_EXTENSION Vcb, @@ -780,7 +913,7 @@ BrowseIndexEntries(PDEVICE_EXTENSION Vcb, return STATUS_OBJECT_PATH_NOT_FOUND; } - Status = FindAttribute(Vcb, MftRecord, AttributeIndexAllocation, L"$I30", 4, &IndexAllocationCtx); + Status = FindAttribute(Vcb, MftRecord, AttributeIndexAllocation, L"$I30", 4, &IndexAllocationCtx, NULL); if (!NT_SUCCESS(Status)) { DPRINT("Corrupted filesystem!\n"); @@ -850,7 +983,7 @@ NtfsFindMftRecord(PDEVICE_EXTENSION Vcb, } ASSERT(MftRecord->Ntfs.Type == NRH_FILE_TYPE); - Status = FindAttribute(Vcb, MftRecord, AttributeIndexRoot, L"$I30", 4, &IndexRootCtx); + Status = FindAttribute(Vcb, MftRecord, AttributeIndexRoot, L"$I30", 4, &IndexRootCtx, NULL); if (!NT_SUCCESS(Status)) { ExFreePoolWithTag(MftRecord, TAG_NTFS); diff --git a/drivers/filesystems/ntfs/ntfs.h b/drivers/filesystems/ntfs/ntfs.h index f80bb973fe6..6b9a86ed07a 100644 --- a/drivers/filesystems/ntfs/ntfs.h +++ b/drivers/filesystems/ntfs/ntfs.h @@ -483,8 +483,15 @@ typedef struct _FIND_ATTR_CONTXT PNTFS_ATTR_RECORD LastAttr; PNTFS_ATTR_RECORD NonResidentStart; PNTFS_ATTR_RECORD NonResidentEnd; + ULONG Offset; } FIND_ATTR_CONTXT, *PFIND_ATTR_CONTXT; +typedef struct +{ + USHORT USN; + USHORT Array[]; +} FIXUP_ARRAY, *PFIXUP_ARRAY; + extern PNTFS_GLOBAL_DATA NtfsGlobalData; FORCEINLINE @@ -555,7 +562,7 @@ NtfsWriteDisk(IN PDEVICE_OBJECT DeviceObject, IN LONGLONG StartingOffset, IN ULONG Length, IN ULONG SectorSize, - IN PUCHAR Buffer); + IN const PUCHAR Buffer); NTSTATUS NtfsReadSectors(IN PDEVICE_OBJECT DeviceObject, @@ -759,6 +766,15 @@ WriteAttribute(PDEVICE_EXTENSION Vcb, ULONGLONG AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord); +NTSTATUS +SetAttributeDataLength(PFILE_OBJECT FileObject, + PNTFS_FCB Fcb, + PNTFS_ATTR_CONTEXT AttrContext, + ULONG AttrOffset, + PFILE_RECORD_HEADER FileRecord, + PDEVICE_EXTENSION DeviceExt, + PLARGE_INTEGER DataSize); + ULONG AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord); @@ -767,13 +783,19 @@ ReadFileRecord(PDEVICE_EXTENSION Vcb, ULONGLONG index, PFILE_RECORD_HEADER file); +NTSTATUS +UpdateFileRecord(PDEVICE_EXTENSION Vcb, + ULONGLONG index, + PFILE_RECORD_HEADER file); + NTSTATUS FindAttribute(PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER MftRecord, ULONG Type, PCWSTR Name, ULONG NameLength, - PNTFS_ATTR_CONTEXT * AttrCtx); + PNTFS_ATTR_CONTEXT * AttrCtx, + PULONG Offset); VOID ReadVCN(PDEVICE_EXTENSION Vcb, @@ -787,6 +809,10 @@ NTSTATUS FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb, PNTFS_RECORD_HEADER Record); +NTSTATUS +AddFixupArray(PDEVICE_EXTENSION Vcb, + PFILE_RECORD_HEADER Record); + NTSTATUS ReadLCN(PDEVICE_EXTENSION Vcb, ULONGLONG lcn, diff --git a/drivers/filesystems/ntfs/rw.c b/drivers/filesystems/ntfs/rw.c index 076a3e85781..5d95300ae2f 100644 --- a/drivers/filesystems/ntfs/rw.c +++ b/drivers/filesystems/ntfs/rw.c @@ -95,7 +95,7 @@ NtfsReadFile(PDEVICE_EXTENSION DeviceExt, } - Status = FindAttribute(DeviceExt, FileRecord, AttributeData, Fcb->Stream, wcslen(Fcb->Stream), &DataContext); + Status = FindAttribute(DeviceExt, FileRecord, AttributeData, Fcb->Stream, wcslen(Fcb->Stream), &DataContext, NULL); if (!NT_SUCCESS(Status)) { NTSTATUS BrowseStatus; @@ -309,6 +309,7 @@ NTSTATUS NtfsWriteFile(PDEVICE_EXTENSION DeviceExt, PNTFS_FCB Fcb; PFILE_RECORD_HEADER FileRecord; PNTFS_ATTR_CONTEXT DataContext; + ULONG AttributeOffset; ULONGLONG StreamSize; DPRINT("NtfsWriteFile(%p, %p, %p, %u, %u, %x, %p)\n", DeviceExt, FileObject, Buffer, Length, WriteOffset, IrpFlags, LengthWritten); @@ -361,9 +362,10 @@ NTSTATUS NtfsWriteFile(PDEVICE_EXTENSION DeviceExt, DPRINT("Found record for %wS\n", Fcb->ObjectName); - // Find the attribute (in the NTFS sense of the word) with the data stream for our file + // Find the attribute with the data stream for our file DPRINT("Finding Data Attribute...\n"); - Status = FindAttribute(DeviceExt, FileRecord, AttributeData, Fcb->Stream, wcslen(Fcb->Stream), &DataContext); + Status = FindAttribute(DeviceExt, FileRecord, AttributeData, Fcb->Stream, wcslen(Fcb->Stream), &DataContext, + &AttributeOffset); // Did we fail to find the attribute? if (!NT_SUCCESS(Status)) @@ -405,13 +407,39 @@ NTSTATUS NtfsWriteFile(PDEVICE_EXTENSION DeviceExt, // Are we trying to write beyond the end of the stream? if (WriteOffset + Length > StreamSize) { - // TODO: allocate additional clusters as needed and expand stream - DPRINT1("WriteOffset: %lu\tLength: %lu\tStreamSize: %I64u\n", WriteOffset, Length, StreamSize); - DPRINT1("TODO: Stream embiggening (appending files) is not yet supported!\n"); - ReleaseAttributeContext(DataContext); - ExFreePoolWithTag(FileRecord, TAG_NTFS); - *LengthWritten = 0; // We didn't write anything - return STATUS_ACCESS_DENIED; // temporarily; we don't change file sizes yet + // is increasing the stream size allowed? + if (!(Fcb->Flags & FCB_IS_VOLUME) && + !(IrpFlags & IRP_PAGING_IO)) + { + LARGE_INTEGER DataSize; + ULONGLONG AllocationSize; + + DataSize.QuadPart = WriteOffset + Length; + + AllocationSize = ROUND_UP(DataSize.QuadPart, Fcb->Vcb->NtfsInfo.BytesPerCluster); + + // set the attribute data length + Status = SetAttributeDataLength(FileObject, Fcb, DataContext, AttributeOffset, FileRecord, DeviceExt, &DataSize); + + if (!NT_SUCCESS(Status)) + { + ReleaseAttributeContext(DataContext); + ExFreePoolWithTag(FileRecord, TAG_NTFS); + *LengthWritten = 0; + return Status; + } + + // now we need to update this file's size in every directory index entry that references it + // (saved for a later commit) + } + else + { + // TODO - just fail for now + ReleaseAttributeContext(DataContext); + ExFreePoolWithTag(FileRecord, TAG_NTFS); + *LengthWritten = 0; + return STATUS_ACCESS_DENIED; + } } DPRINT("Length: %lu\tWriteOffset: %lu\tStreamSize: %I64u\n", Length, WriteOffset, StreamSize); diff --git a/drivers/filesystems/ntfs/volinfo.c b/drivers/filesystems/ntfs/volinfo.c index ddf7f6c58dd..13a3b7af7ff 100644 --- a/drivers/filesystems/ntfs/volinfo.c +++ b/drivers/filesystems/ntfs/volinfo.c @@ -62,7 +62,7 @@ NtfsGetFreeClusters(PDEVICE_EXTENSION DeviceExt) return 0; } - Status = FindAttribute(DeviceExt, BitmapRecord, AttributeData, L"", 0, &DataContext); + Status = FindAttribute(DeviceExt, BitmapRecord, AttributeData, L"", 0, &DataContext, NULL); if (!NT_SUCCESS(Status)) { ExFreePoolWithTag(BitmapRecord, TAG_NTFS);