From e0048b1362c812705d326bf902b3712a258b4611 Mon Sep 17 00:00:00 2001 From: Trevor Thompson Date: Fri, 9 Jun 2017 03:14:30 +0000 Subject: [PATCH] [NTFS] - Add the most basic support for file creation. Expand diagnostic output, especially in NtfsDumpIndexRootAttribute(). Replace an ExFreePool() with ExFreePoolWithTag(). AddFileName() - Add a parameter to receive the Mft index of the parent directory. Fix so the name of the file will be stored in the attribute, not the name of the directory. NtfsCreateFile() - Open a file that was successfully created, instead of assuming failure. NtfsCreateFileRecord() - Add the filename attribute of the created file to the parent directory's index. +NtfsAddFilenameToDirectory() - Adds a $FILE_NAME attribute to a given directory index. Presently, a file can be created in an empty directory only. AddNewMftEntry() - Add a parameter to receive the mft index where the new entry was stored. svn path=/branches/GSoC_2016/NTFS/; revision=74970 --- drivers/filesystems/ntfs/attrib.c | 94 +++++++++-- drivers/filesystems/ntfs/create.c | 44 ++++-- drivers/filesystems/ntfs/dirctl.c | 250 ++++++++++++++++++++++++++++++ drivers/filesystems/ntfs/mft.c | 8 +- drivers/filesystems/ntfs/ntfs.h | 18 ++- 5 files changed, 388 insertions(+), 26 deletions(-) diff --git a/drivers/filesystems/ntfs/attrib.c b/drivers/filesystems/ntfs/attrib.c index bb678e3ef8f..eeed58104e6 100644 --- a/drivers/filesystems/ntfs/attrib.c +++ b/drivers/filesystems/ntfs/attrib.c @@ -112,6 +112,9 @@ AddData(PFILE_RECORD_HEADER FileRecord, * Pointer to the FILE_OBJECT which represents the new name. * This parameter is used to determine the filename and parent directory. * +* @param ParentMftIndex +* Pointer to a ULONGLONG which will receive the index of the parent directory. +* * @return * STATUS_SUCCESS on success. STATUS_NOT_IMPLEMENTED if target address isn't at the end * of the given file record. @@ -128,16 +131,18 @@ NTSTATUS AddFileName(PFILE_RECORD_HEADER FileRecord, PNTFS_ATTR_RECORD AttributeAddress, PDEVICE_EXTENSION DeviceExt, - PFILE_OBJECT FileObject) + PFILE_OBJECT FileObject, + PULONGLONG ParentMftIndex) { ULONG ResidentHeaderLength = FIELD_OFFSET(NTFS_ATTR_RECORD, Resident.Reserved) + sizeof(UCHAR); PFILENAME_ATTRIBUTE FileNameAttribute; LARGE_INTEGER SystemTime; ULONG FileRecordEnd = AttributeAddress->Length; ULONGLONG CurrentMFTIndex = NTFS_FILE_ROOT; - UNICODE_STRING Current, Remaining; + UNICODE_STRING Current, Remaining, FilenameNoPath; NTSTATUS Status = STATUS_SUCCESS; ULONG FirstEntry = 0; + WCHAR Buffer[MAX_PATH]; if (AttributeAddress->Type != AttributeEnd) { @@ -162,21 +167,30 @@ AddFileName(PFILE_RECORD_HEADER FileRecord, // we need to extract the filename from the path DPRINT1("Pathname: %wZ\n", &FileObject->FileName); + RtlZeroMemory(&FilenameNoPath, sizeof(UNICODE_STRING)); + FilenameNoPath.Buffer = Buffer; + FilenameNoPath.MaximumLength = MAX_PATH; + FsRtlDissectName(FileObject->FileName, &Current, &Remaining); while (Current.Length != 0) { DPRINT1("Current: %wZ\n", &Current); + if(Remaining.Length != 0) + RtlCopyUnicodeString(&FilenameNoPath, &Remaining); + Status = NtfsFindMftRecord(DeviceExt, CurrentMFTIndex, &Current, &FirstEntry, FALSE, &CurrentMFTIndex); if (!NT_SUCCESS(Status)) + break; + + if (Remaining.Length == 0 ) { + if(Current.Length != 0) + RtlCopyUnicodeString(&FilenameNoPath, &Current); break; } - if (Remaining.Length == 0) - break; - FsRtlDissectName(Current, &Current, &Remaining); } @@ -184,6 +198,9 @@ AddFileName(PFILE_RECORD_HEADER FileRecord, // set reference to parent directory FileNameAttribute->DirectoryFileReferenceNumber = CurrentMFTIndex; + *ParentMftIndex = CurrentMFTIndex; + + DPRINT1("SequenceNumber: 0x%02x\n", FileRecord->SequenceNumber); // The highest 2 bytes should be the sequence number, unless the parent happens to be root if (CurrentMFTIndex == NTFS_FILE_ROOT) @@ -191,19 +208,19 @@ AddFileName(PFILE_RECORD_HEADER FileRecord, else FileNameAttribute->DirectoryFileReferenceNumber |= (ULONGLONG)FileRecord->SequenceNumber << 48; - DPRINT1("FileNameAttribute->DirectoryFileReferenceNumber: 0x%I64x\n", FileNameAttribute->DirectoryFileReferenceNumber); + DPRINT1("FileNameAttribute->DirectoryFileReferenceNumber: 0x%016I64x\n", FileNameAttribute->DirectoryFileReferenceNumber); - FileNameAttribute->NameLength = Current.Length / 2; + FileNameAttribute->NameLength = FilenameNoPath.Length / 2; // TODO: Get proper nametype, add DOS links as needed FileNameAttribute->NameType = NTFS_FILE_NAME_WIN32_AND_DOS; - RtlCopyMemory(FileNameAttribute->Name, Current.Buffer, Current.Length); + RtlCopyMemory(FileNameAttribute->Name, FilenameNoPath.Buffer, FilenameNoPath.Length); FileRecord->LinkCount++; AttributeAddress->Length = ResidentHeaderLength + - FIELD_OFFSET(FILENAME_ATTRIBUTE, Name) + Current.Length; + FIELD_OFFSET(FILENAME_ATTRIBUTE, Name) + FilenameNoPath.Length; AttributeAddress->Length = ALIGN_UP_BY(AttributeAddress->Length, 8); - AttributeAddress->Resident.ValueLength = FIELD_OFFSET(FILENAME_ATTRIBUTE, Name) + Current.Length; + AttributeAddress->Resident.ValueLength = FIELD_OFFSET(FILENAME_ATTRIBUTE, Name) + FilenameNoPath.Length; AttributeAddress->Resident.ValueOffset = ResidentHeaderLength; AttributeAddress->Resident.Flags = 1; // indexed @@ -1054,6 +1071,7 @@ NtfsDumpFileNameAttribute(PNTFS_ATTR_RECORD Attribute) DbgPrint(" (%x) '%.*S' ", FileNameAttr->NameType, FileNameAttr->NameLength, FileNameAttr->Name); DbgPrint(" '%x' \n", FileNameAttr->FileAttributes); DbgPrint(" AllocatedSize: %I64u\nDataSize: %I64u\n", FileNameAttr->AllocatedSize, FileNameAttr->DataSize); + DbgPrint(" File reference: 0x%016I64x\n", FileNameAttr->DirectoryFileReferenceNumber); } @@ -1110,23 +1128,73 @@ VOID NtfsDumpIndexRootAttribute(PNTFS_ATTR_RECORD Attribute) { PINDEX_ROOT_ATTRIBUTE IndexRootAttr; + ULONG currentOffset; + ULONG currentNode; IndexRootAttr = (PINDEX_ROOT_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset); if (IndexRootAttr->AttributeType == AttributeFileName) ASSERT(IndexRootAttr->CollationRule == COLLATION_FILE_NAME); - DbgPrint(" $INDEX_ROOT (%uB, %u) ", IndexRootAttr->SizeOfEntry, IndexRootAttr->ClustersPerIndexRecord); + DbgPrint(" $INDEX_ROOT (%u bytes per index record, %u clusters) ", IndexRootAttr->SizeOfEntry, IndexRootAttr->ClustersPerIndexRecord); if (IndexRootAttr->Header.Flags == INDEX_ROOT_SMALL) { - DbgPrint(" (small) "); + DbgPrint(" (small)\n"); } else { ASSERT(IndexRootAttr->Header.Flags == INDEX_ROOT_LARGE); - DbgPrint(" (large) "); + DbgPrint(" (large)\n"); } + + DbgPrint(" Offset to first index: 0x%lx\n Total size of index entries: 0x%lx\n Allocated size of node: 0x%lx\n", + IndexRootAttr->Header.FirstEntryOffset, + IndexRootAttr->Header.TotalSizeOfEntries, + IndexRootAttr->Header.AllocatedSize); + currentOffset = IndexRootAttr->Header.FirstEntryOffset; + currentNode = 0; + // print details of every node in the index + while (currentOffset < IndexRootAttr->Header.TotalSizeOfEntries) + { + PINDEX_ENTRY_ATTRIBUTE currentIndexExtry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)IndexRootAttr + 0x10 + currentOffset); + DbgPrint(" Index Node Entry %u", currentNode++); + if (currentIndexExtry->Flags & NTFS_INDEX_ENTRY_NODE) + DbgPrint(" (Branch)"); + else + DbgPrint(" (Leaf)"); + if((currentIndexExtry->Flags & NTFS_INDEX_ENTRY_END)) + { + DbgPrint(" (Dummy Key)"); + } + DbgPrint("\n File Reference: 0x%016I64x\n", currentIndexExtry->Data.Directory.IndexedFile); + DbgPrint(" Index Entry Length: 0x%x\n", currentIndexExtry->Length); + DbgPrint(" Index Key Length: 0x%x\n", currentIndexExtry->KeyLength); + + // if this isn't the final (dummy) node, print info about the key (Filename attribute) + if (!(currentIndexExtry->Flags & NTFS_INDEX_ENTRY_END)) + { + UNICODE_STRING Name; + DbgPrint(" Parent File Reference: 0x%016I64x\n", currentIndexExtry->FileName.DirectoryFileReferenceNumber); + DbgPrint(" $FILENAME indexed: "); + Name.Length = currentIndexExtry->FileName.NameLength * sizeof(WCHAR); + Name.MaximumLength = Name.Length; + Name.Buffer = currentIndexExtry->FileName.Name; + DbgPrint("'%wZ'\n", &Name); + } + + // if this node has a sub-node beneath it + if (currentIndexExtry->Flags & NTFS_INDEX_ENTRY_NODE) + { + // Print the VCN of the sub-node + PULONGLONG SubNodeVCN = (PULONGLONG)((ULONG_PTR)currentIndexExtry + currentIndexExtry->Length - 8); + DbgPrint(" VCN of sub-node: 0x%llx\n", *SubNodeVCN); + } + + currentOffset += currentIndexExtry->Length; + ASSERT(currentIndexExtry->Length); + } + } diff --git a/drivers/filesystems/ntfs/create.c b/drivers/filesystems/ntfs/create.c index 6513827c627..ef39b44373d 100644 --- a/drivers/filesystems/ntfs/create.c +++ b/drivers/filesystems/ntfs/create.c @@ -523,7 +523,7 @@ NtfsCreateFile(PDEVICE_OBJECT DeviceObject, DoneOverwriting: if (fileRecord) - ExFreePool(fileRecord); + ExFreePoolWithTag(fileRecord, TAG_NTFS); if (dataContext) ReleaseAttributeContext(dataContext); @@ -562,13 +562,14 @@ NtfsCreateFile(PDEVICE_OBJECT DeviceObject, // Create the file record on disk Status = NtfsCreateFileRecord(DeviceExt, FileObject); + if (!NT_SUCCESS(Status)) + { + DPRINT1("ERROR: Couldn't create file record!\n"); + return Status; + } - // Update the parent directory index - // Still TODO - - // Call NtfsOpenFile() - - return STATUS_CANNOT_MAKE; + // Now we should be able to open the file + return NtfsCreateFile(DeviceObject, Irp); } } @@ -624,7 +625,8 @@ NtfsCreate(PNTFS_IRP_CONTEXT IrpContext) * @name NtfsCreateFileRecord() * @implemented * -* Creates a file record and saves it to the MFT. +* Creates a file record and saves it to the MFT. Adds the filename attribute of the +* created file to the parent directory's index. * * @param DeviceExt * Points to the target disk's DEVICE_EXTENSION @@ -643,6 +645,9 @@ NtfsCreateFileRecord(PDEVICE_EXTENSION DeviceExt, NTSTATUS Status = STATUS_SUCCESS; PFILE_RECORD_HEADER FileRecord; PNTFS_ATTR_RECORD NextAttribute; + PFILENAME_ATTRIBUTE FilenameAttribute; + ULONGLONG ParentMftIndex; + ULONGLONG FileMftIndex; // allocate memory for file record FileRecord = ExAllocatePoolWithTag(NonPagedPool, @@ -686,7 +691,10 @@ NtfsCreateFileRecord(PDEVICE_EXTENSION DeviceExt, NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length); // Add the $FILE_NAME attribute - AddFileName(FileRecord, NextAttribute, DeviceExt, FileObject); + AddFileName(FileRecord, NextAttribute, DeviceExt, FileObject, &ParentMftIndex); + + // save a pointer to the filename attribute + FilenameAttribute = (PFILENAME_ATTRIBUTE)((ULONG_PTR)NextAttribute + NextAttribute->Resident.ValueOffset); // advance NextAttribute pointer to the next attribute NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length); @@ -698,7 +706,23 @@ NtfsCreateFileRecord(PDEVICE_EXTENSION DeviceExt, NtfsDumpFileRecord(DeviceExt, FileRecord); // Now that we've built the file record in memory, we need to store it in the MFT. - Status = AddNewMftEntry(FileRecord, DeviceExt); + Status = AddNewMftEntry(FileRecord, DeviceExt, &FileMftIndex); + if (NT_SUCCESS(Status)) + { + // The highest 2 bytes should be the sequence number, unless the parent happens to be root + if (FileMftIndex == NTFS_FILE_ROOT) + FileMftIndex = FileMftIndex + ((ULONGLONG)NTFS_FILE_ROOT << 48); + else + FileMftIndex = FileMftIndex + ((ULONGLONG)FileRecord->SequenceNumber << 48); + + DPRINT1("New File Reference: 0x%016I64x\n", FileMftIndex); + + // Add the filename attribute to the filename-index of the parent directory + Status = NtfsAddFilenameToDirectory(DeviceExt, + ParentMftIndex, + FileMftIndex, + FilenameAttribute); + } ExFreePoolWithTag(FileRecord, TAG_NTFS); diff --git a/drivers/filesystems/ntfs/dirctl.c b/drivers/filesystems/ntfs/dirctl.c index aaa6dfcb87f..91f0bc5db70 100644 --- a/drivers/filesystems/ntfs/dirctl.c +++ b/drivers/filesystems/ntfs/dirctl.c @@ -34,6 +34,256 @@ /* FUNCTIONS ****************************************************************/ +/** +* @name NtfsAddFilenameToDirectory +* @implemented +* +* Adds a $FILE_NAME attribute to a given directory index. +* +* @param DeviceExt +* Points to the target disk's DEVICE_EXTENSION. +* +* @param DirectoryMftIndex +* Mft index of the parent directory which will receive the file. +* +* @param FileReferenceNumber +* File reference of the file to be added to the directory. This is a combination of the +* Mft index and sequence number. +* +* @param FilenameAttribute +* Pointer to the FILENAME_ATTRIBUTE of the file being added to the directory. +* +* @return +* STATUS_SUCCESS on success. +* STATUS_INSUFFICIENT_RESOURCES if an allocation fails. +* STATUS_NOT_IMPLEMENTED if target address isn't at the end of the given file record. +* +* @remarks +* WIP - Can only support an empty directory. +* One FILENAME_ATTRIBUTE is added to the directory's index for each link to that file. So, each +* file which contains one FILENAME_ATTRIBUTE for a long name and another for the 8.3 name, will +* get both attributes added to its parent directory. +*/ +NTSTATUS +NtfsAddFilenameToDirectory(PDEVICE_EXTENSION DeviceExt, + ULONGLONG DirectoryMftIndex, + ULONGLONG FileReferenceNumber, + PFILENAME_ATTRIBUTE FilenameAttribute) +{ + NTSTATUS Status = STATUS_SUCCESS; + PFILE_RECORD_HEADER ParentFileRecord; + PNTFS_ATTR_CONTEXT DirectoryContext; + PINDEX_ROOT_ATTRIBUTE I30IndexRoot; + ULONG IndexRootOffset; + ULONGLONG I30IndexRootLength; + PINDEX_ENTRY_ATTRIBUTE IndexNodeEntry; + ULONG LengthWritten; + PNTFS_ATTR_RECORD DestinationAttribute; + PINDEX_ROOT_ATTRIBUTE NewIndexRoot; + ULONG AttributeLength; + PNTFS_ATTR_RECORD NextAttribute; + + // Allocate memory for the parent directory + ParentFileRecord = ExAllocatePoolWithTag(NonPagedPool, + DeviceExt->NtfsInfo.BytesPerFileRecord, + TAG_NTFS); + if (!ParentFileRecord) + { + DPRINT1("ERROR: Couldn't allocate memory for file record!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // Open the parent directory + Status = ReadFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord); + if (!NT_SUCCESS(Status)) + { + ExFreePoolWithTag(ParentFileRecord, TAG_NTFS); + DPRINT1("ERROR: Couldn't read parent directory with index %I64u\n", + DirectoryMftIndex); + return Status; + } + + DPRINT1("Dumping old parent file record:\n"); + NtfsDumpFileRecord(DeviceExt, ParentFileRecord); + + // Find the index root attribute for the directory + Status = FindAttribute(DeviceExt, + ParentFileRecord, + AttributeIndexRoot, + L"$I30", + 4, + &DirectoryContext, + &IndexRootOffset); + if (!NT_SUCCESS(Status)) + { + DPRINT1("ERROR: Couldn't find $I30 $INDEX_ROOT attribute for parent directory with MFT #: %I64u!\n", + DirectoryMftIndex); + ExFreePoolWithTag(ParentFileRecord, TAG_NTFS); + return Status; + } + + I30IndexRootLength = AttributeDataLength(&DirectoryContext->Record); + + // Allocate memory for the index root data + I30IndexRoot = (PINDEX_ROOT_ATTRIBUTE)ExAllocatePoolWithTag(NonPagedPool, I30IndexRootLength, TAG_NTFS); + if (!I30IndexRoot) + { + DPRINT1("ERROR: Couldn't allocate memory for index root attribute!\n"); + ReleaseAttributeContext(DirectoryContext); + ExFreePoolWithTag(ParentFileRecord, TAG_NTFS); + } + + // Read the Index Root + Status = ReadAttribute(DeviceExt, DirectoryContext, 0, (PCHAR)I30IndexRoot, I30IndexRootLength); + if (!NT_SUCCESS(Status)) + { + DPRINT1("ERROR: Couln't read index root attribute for Mft index #%I64u\n", DirectoryMftIndex); + ReleaseAttributeContext(DirectoryContext); + ExFreePoolWithTag(I30IndexRoot, TAG_NTFS); + ExFreePoolWithTag(ParentFileRecord, TAG_NTFS); + return Status; + } + + // Make sure it's empty (temporarily) + IndexNodeEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)I30IndexRoot + I30IndexRoot->Header.FirstEntryOffset + 0x10); + if (IndexNodeEntry->Data.Directory.IndexedFile != 0 || IndexNodeEntry->Flags != 2) + { + DPRINT1("FIXME: File-creation is only supported in empty directories right now! Be patient! :)\n"); + ReleaseAttributeContext(DirectoryContext); + ExFreePoolWithTag(I30IndexRoot, TAG_NTFS); + ExFreePoolWithTag(ParentFileRecord, TAG_NTFS); + return STATUS_NOT_IMPLEMENTED; + } + + // Now we need to setup a new index root attribute to replace the old one + NewIndexRoot = ExAllocatePoolWithTag(NonPagedPool, DeviceExt->NtfsInfo.BytesPerIndexRecord, TAG_NTFS); + if (!NewIndexRoot) + { + DPRINT1("ERROR: Unable to allocate memory for new index root attribute!\n"); + ReleaseAttributeContext(DirectoryContext); + ExFreePoolWithTag(I30IndexRoot, TAG_NTFS); + ExFreePoolWithTag(ParentFileRecord, TAG_NTFS); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // Setup the new index record + RtlZeroMemory(NewIndexRoot, DeviceExt->NtfsInfo.BytesPerIndexRecord); // shouldn't be necessary but aids in debugging + + NewIndexRoot->AttributeType = AttributeFileName; + NewIndexRoot->CollationRule = COLLATION_FILE_NAME; + NewIndexRoot->SizeOfEntry = DeviceExt->NtfsInfo.BytesPerIndexRecord; + // If Bytes per index record is less than cluster size, clusters per index record becomes sectors per index + if(NewIndexRoot->SizeOfEntry < DeviceExt->NtfsInfo.BytesPerCluster) + NewIndexRoot->ClustersPerIndexRecord = NewIndexRoot->SizeOfEntry / DeviceExt->NtfsInfo.BytesPerSector; + else + NewIndexRoot->ClustersPerIndexRecord = NewIndexRoot->SizeOfEntry / DeviceExt->NtfsInfo.BytesPerCluster; + + // Setup the Index node header + NewIndexRoot->Header.FirstEntryOffset = 0x10; + NewIndexRoot->Header.Flags = INDEX_ROOT_SMALL; + // still need to calculate sizes + + // The first index node entry will be for the filename we're adding + IndexNodeEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)NewIndexRoot + NewIndexRoot->Header.FirstEntryOffset + 0x10); + IndexNodeEntry->Data.Directory.IndexedFile = FileReferenceNumber; + IndexNodeEntry->Flags = INDEX_ROOT_SMALL; + IndexNodeEntry->KeyLength = FIELD_OFFSET(FILENAME_ATTRIBUTE, Name) + (2 * FilenameAttribute->NameLength); + IndexNodeEntry->Length = ALIGN_UP_BY(IndexNodeEntry->KeyLength, 8) + FIELD_OFFSET(INDEX_ENTRY_ATTRIBUTE, FileName); + + // Now we can calculate the Node length (temp logic) + NewIndexRoot->Header.TotalSizeOfEntries = NewIndexRoot->Header.FirstEntryOffset + IndexNodeEntry->Length + 0x10; + NewIndexRoot->Header.AllocatedSize = NewIndexRoot->Header.TotalSizeOfEntries; + + DPRINT1("New Index Node Entry Stream Length: %u\nNew Inde Node Entry Length: %u\n", + IndexNodeEntry->KeyLength, + IndexNodeEntry->Length); + + // copy over the attribute proper + RtlCopyMemory(&IndexNodeEntry->FileName, FilenameAttribute, IndexNodeEntry->KeyLength); + + // Now setup the dummy key + IndexNodeEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)IndexNodeEntry + IndexNodeEntry->Length); + + IndexNodeEntry->Data.Directory.IndexedFile = 0; + IndexNodeEntry->Length = 0x10; + IndexNodeEntry->KeyLength = 0; + IndexNodeEntry->Flags = NTFS_INDEX_ENTRY_END; + + // This is when we'd normally setup the length (already done above) + + // Write back the new index root attribute to the parent directory file record + + // First, we need to make sure the attribute is large enough. + // We can't set the size as we normally would, because if we extend past the file record, + // we must create an index allocation and index bitmap (TODO). Also TODO: support file records with + // $ATTRIBUTE_LIST's. + AttributeLength = NewIndexRoot->Header.AllocatedSize + FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE, Header); + DestinationAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)ParentFileRecord + IndexRootOffset); + + NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)DestinationAttribute + DestinationAttribute->Length); + if (NextAttribute->Type != AttributeEnd) + { + DPRINT1("FIXME: For now, only resizing index root at the end of a file record is supported!\n"); + ExFreePoolWithTag(NewIndexRoot, TAG_NTFS); + ReleaseAttributeContext(DirectoryContext); + ExFreePoolWithTag(I30IndexRoot, TAG_NTFS); + ExFreePoolWithTag(ParentFileRecord, TAG_NTFS); + return STATUS_NOT_IMPLEMENTED; + } + + // Update the length of the attribute in the file record of the parent directory + InternalSetResidentAttributeLength(DirectoryContext, + ParentFileRecord, + IndexRootOffset, + AttributeLength); + + Status = UpdateFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord); + if (!NT_SUCCESS(Status)) + { + DPRINT1("ERROR: Failed to update file record of directory with index: %llx\n", DirectoryMftIndex); + ExFreePoolWithTag(ParentFileRecord, TAG_NTFS); + ExFreePoolWithTag(NewIndexRoot, TAG_NTFS); + ExFreePoolWithTag(I30IndexRoot, TAG_NTFS); + return Status; + } + + // Update the parent directory with the new index root + Status = WriteAttribute(DeviceExt, + DirectoryContext, + 0, + (PUCHAR)NewIndexRoot, + AttributeLength, + &LengthWritten); + if (!NT_SUCCESS(Status) ) + { + DPRINT1("ERROR: Unable to write new index root attribute to parent directory!\n"); + ExFreePoolWithTag(NewIndexRoot, TAG_NTFS); + ReleaseAttributeContext(DirectoryContext); + ExFreePoolWithTag(I30IndexRoot, TAG_NTFS); + ExFreePoolWithTag(ParentFileRecord, TAG_NTFS); + return Status; + } + + // re-read the parent file record, so we can dump it + Status = ReadFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord); + if (!NT_SUCCESS(Status)) + { + DPRINT1("ERROR: Couldn't read parent directory after messing with it!\n"); + } + else + { + DPRINT1("Dumping new parent file record:\n"); + NtfsDumpFileRecord(DeviceExt, ParentFileRecord); + } + + // Cleanup + ExFreePoolWithTag(NewIndexRoot, TAG_NTFS); + ReleaseAttributeContext(DirectoryContext); + ExFreePoolWithTag(I30IndexRoot, TAG_NTFS); + ExFreePoolWithTag(ParentFileRecord, TAG_NTFS); + + return Status; +} ULONGLONG NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt, diff --git a/drivers/filesystems/ntfs/mft.c b/drivers/filesystems/ntfs/mft.c index 19ae0d15c17..5b9b291e4ef 100644 --- a/drivers/filesystems/ntfs/mft.c +++ b/drivers/filesystems/ntfs/mft.c @@ -1361,6 +1361,9 @@ FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb, * @param DeviceExt * Pointer to the DEVICE_EXTENSION of the target drive. * +* @param DestinationIndex +* Pointer to a ULONGLONG which will receive the MFT index where the file record was stored. +* * @return * STATUS_SUCCESS on success. * STATUS_OBJECT_NAME_NOT_FOUND if we can't find the MFT's $Bitmap or if we weren't able @@ -1371,7 +1374,8 @@ FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb, */ NTSTATUS AddNewMftEntry(PFILE_RECORD_HEADER FileRecord, - PDEVICE_EXTENSION DeviceExt) + PDEVICE_EXTENSION DeviceExt, + PULONGLONG DestinationIndex) { NTSTATUS Status = STATUS_SUCCESS; ULONGLONG MftIndex; @@ -1454,6 +1458,8 @@ AddNewMftEntry(PFILE_RECORD_HEADER FileRecord, return Status; } + *DestinationIndex = MftIndex; + ExFreePoolWithTag(BitmapData, TAG_NTFS); ReleaseAttributeContext(BitmapContext); diff --git a/drivers/filesystems/ntfs/ntfs.h b/drivers/filesystems/ntfs/ntfs.h index 2177f1db0e3..a3265db9da6 100644 --- a/drivers/filesystems/ntfs/ntfs.h +++ b/drivers/filesystems/ntfs/ntfs.h @@ -534,7 +534,8 @@ NTSTATUS AddFileName(PFILE_RECORD_HEADER FileRecord, PNTFS_ATTR_RECORD AttributeAddress, PDEVICE_EXTENSION DeviceExt, - PFILE_OBJECT FileObject); + PFILE_OBJECT FileObject, + PULONGLONG ParentMftIndex); NTSTATUS AddStandardInformation(PFILE_RECORD_HEADER FileRecord, @@ -675,6 +676,12 @@ NtfsDeviceControl(PNTFS_IRP_CONTEXT IrpContext); /* dirctl.c */ +NTSTATUS +NtfsAddFilenameToDirectory(PDEVICE_EXTENSION DeviceExt, + ULONGLONG DirectoryMftIndex, + ULONGLONG FileMftIndex, + PFILENAME_ATTRIBUTE FilenameAttribute); + ULONGLONG NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt, PFILE_RECORD_HEADER FileRecord, @@ -816,7 +823,8 @@ NtfsFileSystemControl(PNTFS_IRP_CONTEXT IrpContext); /* mft.c */ NTSTATUS AddNewMftEntry(PFILE_RECORD_HEADER FileRecord, - PDEVICE_EXTENSION DeviceExt); + PDEVICE_EXTENSION DeviceExt, + PULONGLONG DestinationIndex); PNTFS_ATTR_CONTEXT PrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord); @@ -842,6 +850,12 @@ WriteAttribute(PDEVICE_EXTENSION Vcb, ULONGLONG AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord); +VOID +InternalSetResidentAttributeLength(PNTFS_ATTR_CONTEXT AttrContext, + PFILE_RECORD_HEADER FileRecord, + ULONG AttrOffset, + ULONG DataSize); + NTSTATUS SetAttributeDataLength(PFILE_OBJECT FileObject, PNTFS_FCB Fcb,