From: Trevor Thompson Date: Tue, 18 Jul 2017 19:59:36 +0000 (+0000) Subject: [NTFS] - Fix some more issues, including remaining issues marked as "unresolved"... X-Git-Tag: 0.4.9-dev~693^2~16 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=935fcd1b353a65d3af01e3f9061d3784f759cdbc [NTFS] - Fix some more issues, including remaining issues marked as "unresolved" from CR-123: -Add define for indexed flag for resident attributes, RA_INDEXED. -CreateBTreeFromIndex() - Don't try to read index entries beyond attribute length. -Move NtfsAddFileNameToDirectory() from dirctl.c to mft.c (no changes to function). -SetResidentAttributeDataLength() - Don't try to free AttribData if it hasn't been allocated. Cast IndexRecord to PUCHAR for WriteAttribute(), and cast BitmapData to PCHAR for ReadAttribute() (make gcc happy). -Replace magic number 16 with a define, NTFS_FILE_FIRST_USER_FILE. svn path=/branches/GSoC_2016/NTFS/; revision=75371 --- diff --git a/drivers/filesystems/ntfs/attrib.c b/drivers/filesystems/ntfs/attrib.c index bd070c97ad9..a6bc9a8aca1 100644 --- a/drivers/filesystems/ntfs/attrib.c +++ b/drivers/filesystems/ntfs/attrib.c @@ -245,7 +245,7 @@ AddFileName(PFILE_RECORD_HEADER FileRecord, AttributeAddress->Resident.ValueLength = FIELD_OFFSET(FILENAME_ATTRIBUTE, Name) + FilenameNoPath.Length; AttributeAddress->Resident.ValueOffset = ResidentHeaderLength; - AttributeAddress->Resident.Flags = 1; // indexed + AttributeAddress->Resident.Flags = RA_INDEXED; // move the attribute-end and file-record-end markers to the end of the file record AttributeAddress = (PNTFS_ATTR_RECORD)((ULONG_PTR)AttributeAddress + AttributeAddress->Length); diff --git a/drivers/filesystems/ntfs/btree.c b/drivers/filesystems/ntfs/btree.c index 179107272bc..a7eacb3173c 100644 --- a/drivers/filesystems/ntfs/btree.c +++ b/drivers/filesystems/ntfs/btree.c @@ -138,6 +138,7 @@ CreateBTreeFromIndex(PNTFS_ATTR_CONTEXT IndexRootContext, PB_TREE Tree = ExAllocatePoolWithTag(NonPagedPool, sizeof(B_TREE), TAG_NTFS); PB_TREE_FILENAME_NODE RootNode = ExAllocatePoolWithTag(NonPagedPool, sizeof(B_TREE_FILENAME_NODE), TAG_NTFS); PB_TREE_KEY CurrentKey = ExAllocatePoolWithTag(NonPagedPool, sizeof(B_TREE_KEY), TAG_NTFS); + ULONG CurrentOffset = IndexRoot->Header.FirstEntryOffset; DPRINT1("CreateBTreeFromIndex(%p, %p, %p)\n", IndexRootContext, IndexRoot, NewTree); @@ -161,11 +162,21 @@ CreateBTreeFromIndex(PNTFS_ATTR_CONTEXT IndexRootContext, RootNode->FirstKey = CurrentKey; Tree->RootNode = RootNode; - // Create a key for each entry in the node + // Make sure we won't try reading past the attribute-end + if (FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE, Header) + IndexRoot->Header.TotalSizeOfEntries > IndexRootContext->Record.Resident.ValueLength) + { + DPRINT1("Filesystem corruption detected!\n"); + DestroyBTree(Tree); + return STATUS_FILE_CORRUPT_ERROR; + } + + // Start at the first node entry CurrentNodeEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)IndexRoot + FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE, Header) + IndexRoot->Header.FirstEntryOffset); - while (TRUE) + + // Create a key for each entry in the node + while (CurrentOffset < IndexRoot->Header.TotalSizeOfEntries) { // Allocate memory for the current entry CurrentKey->IndexEntry = ExAllocatePoolWithTag(NonPagedPool, CurrentNodeEntry->Length, TAG_NTFS); @@ -207,6 +218,7 @@ CreateBTreeFromIndex(PNTFS_ATTR_CONTEXT IndexRootContext, } // Advance to the next entry + CurrentOffset += CurrentNodeEntry->Length; CurrentNodeEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)CurrentNodeEntry + CurrentNodeEntry->Length); CurrentKey = NextKey; } @@ -336,7 +348,7 @@ CreateIndexRootFromBTree(PDEVICE_EXTENSION DeviceExt, // Add Length of Current Entry to Total Size of Entries NewIndexRoot->Header.TotalSizeOfEntries += CurrentNodeEntry->Length; - // Go to the next node + // Go to the next node entry CurrentNodeEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)CurrentNodeEntry + CurrentNodeEntry->Length); CurrentKey = CurrentKey->NextKey; @@ -533,16 +545,12 @@ NtfsInsertKey(ULONGLONG FileReference, { // Should the New Key go before the current key? LONG Comparison = CompareTreeKeys(NewKey, CurrentKey, CaseSensitive); - if (Comparison == 0) - { - DPRINT1("DRIVER ERROR: Asked to insert key into tree that already has it!\n"); - ExFreePoolWithTag(NewKey, TAG_NTFS); - ExFreePoolWithTag(NewEntry, TAG_NTFS); - return STATUS_INVALID_PARAMETER; - } + + ASSERT(Comparison != 0); + + // Is NewKey < CurrentKey? if (Comparison < 0) { - // NewKey is < CurrentKey // Insert New Key before Current Key NewKey->NextKey = CurrentKey; diff --git a/drivers/filesystems/ntfs/create.c b/drivers/filesystems/ntfs/create.c index 30cc9210959..f5ca1027fc0 100644 --- a/drivers/filesystems/ntfs/create.c +++ b/drivers/filesystems/ntfs/create.c @@ -185,7 +185,7 @@ NtfsOpenFileById(PDEVICE_EXTENSION DeviceExt, DPRINT1("NtfsOpenFileById(%p, %p, %I64x, %p)\n", DeviceExt, FileObject, MftId, FoundFCB); - ASSERT(MftId < 0x10); + ASSERT(MftId < NTFS_FILE_FIRST_USER_FILE); if (MftId > 0xb) /* No entries are used yet beyond this */ { return STATUS_OBJECT_NAME_NOT_FOUND; @@ -375,7 +375,7 @@ NtfsCreateFile(PDEVICE_OBJECT DeviceObject, return STATUS_INVALID_PARAMETER; MFTId = (*(PULONGLONG)FileObject->FileName.Buffer) & NTFS_MFT_MASK; - if (MFTId < 0x10) + if (MFTId < NTFS_FILE_FIRST_USER_FILE) { Status = NtfsOpenFileById(DeviceExt, FileObject, MFTId, &Fcb); } diff --git a/drivers/filesystems/ntfs/dirctl.c b/drivers/filesystems/ntfs/dirctl.c index c41688ea2b3..98294b47913 100644 --- a/drivers/filesystems/ntfs/dirctl.c +++ b/drivers/filesystems/ntfs/dirctl.c @@ -34,251 +34,6 @@ /* 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. -* -* @param CaseSensitive -* Boolean indicating if the function should operate in case-sensitive mode. This will be TRUE -* if an application created the file with the FILE_FLAG_POSIX_SEMANTICS flag. -* -* @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 a few files in a 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, - BOOLEAN CaseSensitive) -{ - NTSTATUS Status = STATUS_SUCCESS; - PFILE_RECORD_HEADER ParentFileRecord; - PNTFS_ATTR_CONTEXT IndexRootContext; - PINDEX_ROOT_ATTRIBUTE I30IndexRoot; - ULONG IndexRootOffset; - ULONGLONG I30IndexRootLength; - ULONG LengthWritten; - PNTFS_ATTR_RECORD DestinationAttribute; - PINDEX_ROOT_ATTRIBUTE NewIndexRoot; - ULONG AttributeLength; - PNTFS_ATTR_RECORD NextAttribute; - PB_TREE NewTree; - ULONG BtreeIndexLength; - ULONG MaxIndexSize; - - // 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, - &IndexRootContext, - &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; - } - - // Find the maximum index size given what the file record can hold - MaxIndexSize = DeviceExt->NtfsInfo.BytesPerFileRecord - - IndexRootOffset - - IndexRootContext->Record.Resident.ValueOffset - - FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE, Header) - - (sizeof(ULONG) * 2); - - // Allocate memory for the index root data - I30IndexRootLength = AttributeDataLength(&IndexRootContext->Record); - I30IndexRoot = ExAllocatePoolWithTag(NonPagedPool, I30IndexRootLength, TAG_NTFS); - if (!I30IndexRoot) - { - DPRINT1("ERROR: Couldn't allocate memory for index root attribute!\n"); - ReleaseAttributeContext(IndexRootContext); - ExFreePoolWithTag(ParentFileRecord, TAG_NTFS); - return STATUS_INSUFFICIENT_RESOURCES; - } - - // Read the Index Root - Status = ReadAttribute(DeviceExt, IndexRootContext, 0, (PCHAR)I30IndexRoot, I30IndexRootLength); - if (!NT_SUCCESS(Status)) - { - DPRINT1("ERROR: Couln't read index root attribute for Mft index #%I64u\n", DirectoryMftIndex); - ReleaseAttributeContext(IndexRootContext); - ExFreePoolWithTag(I30IndexRoot, TAG_NTFS); - ExFreePoolWithTag(ParentFileRecord, TAG_NTFS); - return Status; - } - - // Convert the index to a B*Tree - Status = CreateBTreeFromIndex(IndexRootContext, I30IndexRoot, &NewTree); - if (!NT_SUCCESS(Status)) - { - DPRINT1("ERROR: Failed to create B-Tree from Index!\n"); - ReleaseAttributeContext(IndexRootContext); - ExFreePoolWithTag(I30IndexRoot, TAG_NTFS); - ExFreePoolWithTag(ParentFileRecord, TAG_NTFS); - return Status; - } - - DumpBTree(NewTree); - - // Insert the key for the file we're adding - Status = NtfsInsertKey(FileReferenceNumber, FilenameAttribute, NewTree->RootNode, CaseSensitive); - if (!NT_SUCCESS(Status)) - { - DPRINT1("ERROR: Failed to insert key into B-Tree!\n"); - DestroyBTree(NewTree); - ReleaseAttributeContext(IndexRootContext); - ExFreePoolWithTag(I30IndexRoot, TAG_NTFS); - ExFreePoolWithTag(ParentFileRecord, TAG_NTFS); - return Status; - } - - DumpBTree(NewTree); - - // Convert B*Tree back to Index Root - Status = CreateIndexRootFromBTree(DeviceExt, NewTree, MaxIndexSize, &NewIndexRoot, &BtreeIndexLength); - if (!NT_SUCCESS(Status)) - { - DPRINT1("ERROR: Failed to create Index root from B-Tree!\n"); - DestroyBTree(NewTree); - ReleaseAttributeContext(IndexRootContext); - ExFreePoolWithTag(I30IndexRoot, TAG_NTFS); - ExFreePoolWithTag(ParentFileRecord, TAG_NTFS); - return Status; - } - - // We're done with the B-Tree now - DestroyBTree(NewTree); - - // Write back the new index root attribute to the parent directory file record - - // First, we need to resize the attribute. - // CreateIndexRootFromBTree() should have verified that the index root fits within MaxIndexSize. - // 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); - - // Find the attribute (or attribute-end marker) after the index root - 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(IndexRootContext); - 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(IndexRootContext, - ParentFileRecord, - IndexRootOffset, - AttributeLength); - - NT_ASSERT(ParentFileRecord->BytesInUse <= DeviceExt->NtfsInfo.BytesPerFileRecord); - - 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); - ReleaseAttributeContext(IndexRootContext); - ExFreePoolWithTag(I30IndexRoot, TAG_NTFS); - return Status; - } - - // Write the new index root to disk - Status = WriteAttribute(DeviceExt, - IndexRootContext, - 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(IndexRootContext); - 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(IndexRootContext); - ExFreePoolWithTag(I30IndexRoot, TAG_NTFS); - ExFreePoolWithTag(ParentFileRecord, TAG_NTFS); - - return Status; -} - ULONGLONG NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt, PFILE_RECORD_HEADER FileRecord, diff --git a/drivers/filesystems/ntfs/mft.c b/drivers/filesystems/ntfs/mft.c index ed531df173e..72953c00868 100644 --- a/drivers/filesystems/ntfs/mft.c +++ b/drivers/filesystems/ntfs/mft.c @@ -750,7 +750,8 @@ SetResidentAttributeDataLength(PDEVICE_EXTENSION Vcb, _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { DPRINT1("Unable to create LargeMcb!\n"); - ExFreePoolWithTag(AttribData, TAG_NTFS); + if (AttribDataSize.QuadPart > 0) + ExFreePoolWithTag(AttribData, TAG_NTFS); _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; @@ -1452,7 +1453,7 @@ UpdateFileNameRecord(PDEVICE_EXTENSION Vcb, { // we need to write the index root attribute back to disk ULONG LengthWritten; - Status = WriteAttribute(Vcb, IndexRootCtx, 0, IndexRecord, AttributeDataLength(&IndexRootCtx->Record), &LengthWritten); + Status = WriteAttribute(Vcb, IndexRootCtx, 0, (PUCHAR)IndexRecord, AttributeDataLength(&IndexRootCtx->Record), &LengthWritten); if (!NT_SUCCESS(Status)) { DPRINT1("ERROR: Couldn't update Index Root!\n"); @@ -1514,7 +1515,7 @@ UpdateIndexEntryFileNameSize(PDEVICE_EXTENSION Vcb, while (IndexEntry < LastEntry && !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END)) { - if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) > 0x10 && + if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) > NTFS_FILE_FIRST_USER_FILE && *CurrentEntry >= *StartEntry && IndexEntry->FileName.NameType != NTFS_FILE_NAME_DOS && CompareFileName(FileName, IndexEntry, DirSearch, CaseSensitive)) @@ -1755,7 +1756,7 @@ AddNewMftEntry(PFILE_RECORD_HEADER FileRecord, } // read $Bitmap attribute - AttrBytesRead = ReadAttribute(DeviceExt, BitmapContext, 0, BitmapData, BitmapDataSize); + AttrBytesRead = ReadAttribute(DeviceExt, BitmapContext, 0, (PCHAR)BitmapData, BitmapDataSize); if (AttrBytesRead == 0) { @@ -1843,6 +1844,251 @@ AddNewMftEntry(PFILE_RECORD_HEADER FileRecord, return Status; } +/** +* @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. +* +* @param CaseSensitive +* Boolean indicating if the function should operate in case-sensitive mode. This will be TRUE +* if an application created the file with the FILE_FLAG_POSIX_SEMANTICS flag. +* +* @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 a few files in a 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, + BOOLEAN CaseSensitive) +{ + NTSTATUS Status = STATUS_SUCCESS; + PFILE_RECORD_HEADER ParentFileRecord; + PNTFS_ATTR_CONTEXT IndexRootContext; + PINDEX_ROOT_ATTRIBUTE I30IndexRoot; + ULONG IndexRootOffset; + ULONGLONG I30IndexRootLength; + ULONG LengthWritten; + PNTFS_ATTR_RECORD DestinationAttribute; + PINDEX_ROOT_ATTRIBUTE NewIndexRoot; + ULONG AttributeLength; + PNTFS_ATTR_RECORD NextAttribute; + PB_TREE NewTree; + ULONG BtreeIndexLength; + ULONG MaxIndexSize; + + // 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, + &IndexRootContext, + &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; + } + + // Find the maximum index size given what the file record can hold + MaxIndexSize = DeviceExt->NtfsInfo.BytesPerFileRecord + - IndexRootOffset + - IndexRootContext->Record.Resident.ValueOffset + - FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE, Header) + - (sizeof(ULONG) * 2); + + // Allocate memory for the index root data + I30IndexRootLength = AttributeDataLength(&IndexRootContext->Record); + I30IndexRoot = ExAllocatePoolWithTag(NonPagedPool, I30IndexRootLength, TAG_NTFS); + if (!I30IndexRoot) + { + DPRINT1("ERROR: Couldn't allocate memory for index root attribute!\n"); + ReleaseAttributeContext(IndexRootContext); + ExFreePoolWithTag(ParentFileRecord, TAG_NTFS); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // Read the Index Root + Status = ReadAttribute(DeviceExt, IndexRootContext, 0, (PCHAR)I30IndexRoot, I30IndexRootLength); + if (!NT_SUCCESS(Status)) + { + DPRINT1("ERROR: Couln't read index root attribute for Mft index #%I64u\n", DirectoryMftIndex); + ReleaseAttributeContext(IndexRootContext); + ExFreePoolWithTag(I30IndexRoot, TAG_NTFS); + ExFreePoolWithTag(ParentFileRecord, TAG_NTFS); + return Status; + } + + // Convert the index to a B*Tree + Status = CreateBTreeFromIndex(IndexRootContext, I30IndexRoot, &NewTree); + if (!NT_SUCCESS(Status)) + { + DPRINT1("ERROR: Failed to create B-Tree from Index!\n"); + ReleaseAttributeContext(IndexRootContext); + ExFreePoolWithTag(I30IndexRoot, TAG_NTFS); + ExFreePoolWithTag(ParentFileRecord, TAG_NTFS); + return Status; + } + + DumpBTree(NewTree); + + // Insert the key for the file we're adding + Status = NtfsInsertKey(FileReferenceNumber, FilenameAttribute, NewTree->RootNode, CaseSensitive); + if (!NT_SUCCESS(Status)) + { + DPRINT1("ERROR: Failed to insert key into B-Tree!\n"); + DestroyBTree(NewTree); + ReleaseAttributeContext(IndexRootContext); + ExFreePoolWithTag(I30IndexRoot, TAG_NTFS); + ExFreePoolWithTag(ParentFileRecord, TAG_NTFS); + return Status; + } + + DumpBTree(NewTree); + + // Convert B*Tree back to Index Root + Status = CreateIndexRootFromBTree(DeviceExt, NewTree, MaxIndexSize, &NewIndexRoot, &BtreeIndexLength); + if (!NT_SUCCESS(Status)) + { + DPRINT1("ERROR: Failed to create Index root from B-Tree!\n"); + DestroyBTree(NewTree); + ReleaseAttributeContext(IndexRootContext); + ExFreePoolWithTag(I30IndexRoot, TAG_NTFS); + ExFreePoolWithTag(ParentFileRecord, TAG_NTFS); + return Status; + } + + // We're done with the B-Tree now + DestroyBTree(NewTree); + + // Write back the new index root attribute to the parent directory file record + + // First, we need to resize the attribute. + // CreateIndexRootFromBTree() should have verified that the index root fits within MaxIndexSize. + // 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); + + // Find the attribute (or attribute-end marker) after the index root + 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(IndexRootContext); + 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(IndexRootContext, + ParentFileRecord, + IndexRootOffset, + AttributeLength); + + NT_ASSERT(ParentFileRecord->BytesInUse <= DeviceExt->NtfsInfo.BytesPerFileRecord); + + 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); + ReleaseAttributeContext(IndexRootContext); + ExFreePoolWithTag(I30IndexRoot, TAG_NTFS); + return Status; + } + + // Write the new index root to disk + Status = WriteAttribute(DeviceExt, + IndexRootContext, + 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(IndexRootContext); + 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(IndexRootContext); + ExFreePoolWithTag(I30IndexRoot, TAG_NTFS); + ExFreePoolWithTag(ParentFileRecord, TAG_NTFS); + + return Status; +} + NTSTATUS AddFixupArray(PDEVICE_EXTENSION Vcb, PNTFS_RECORD_HEADER Record) @@ -1995,7 +2241,7 @@ BrowseIndexEntries(PDEVICE_EXTENSION Vcb, while (IndexEntry < LastEntry && !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END)) { - if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) >= 0x10 && + if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) >= NTFS_FILE_FIRST_USER_FILE && *CurrentEntry >= *StartEntry && IndexEntry->FileName.NameType != NTFS_FILE_NAME_DOS && CompareFileName(FileName, IndexEntry, DirSearch, CaseSensitive)) diff --git a/drivers/filesystems/ntfs/ntfs.h b/drivers/filesystems/ntfs/ntfs.h index ddb876daf9c..9cbc4d54858 100644 --- a/drivers/filesystems/ntfs/ntfs.h +++ b/drivers/filesystems/ntfs/ntfs.h @@ -193,6 +193,7 @@ typedef enum #define NTFS_FILE_QUOTA 9 #define NTFS_FILE_UPCASE 10 #define NTFS_FILE_EXTEND 11 +#define NTFS_FILE_FIRST_USER_FILE 16 #define NTFS_MFT_MASK 0x0000FFFFFFFFFFFFULL @@ -223,6 +224,9 @@ typedef enum #define NTFS_FILE_TYPE_COMPRESSED 0x800 #define NTFS_FILE_TYPE_DIRECTORY 0x10000000 +/* Indexed Flag in Resident attributes - still somewhat speculative */ +#define RA_INDEXED 0x01 + typedef struct { ULONG Type; /* Magic number 'FILE' */ @@ -740,13 +744,6 @@ NtfsDeviceControl(PNTFS_IRP_CONTEXT IrpContext); /* dirctl.c */ -NTSTATUS -NtfsAddFilenameToDirectory(PDEVICE_EXTENSION DeviceExt, - ULONGLONG DirectoryMftIndex, - ULONGLONG FileReferenceNumber, - PFILENAME_ATTRIBUTE FilenameAttribute, - BOOLEAN CaseSensitive); - ULONGLONG NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt, PFILE_RECORD_HEADER FileRecord, @@ -888,6 +885,13 @@ NtfsFileSystemControl(PNTFS_IRP_CONTEXT IrpContext); /* mft.c */ +NTSTATUS +NtfsAddFilenameToDirectory(PDEVICE_EXTENSION DeviceExt, + ULONGLONG DirectoryMftIndex, + ULONGLONG FileReferenceNumber, + PFILENAME_ATTRIBUTE FilenameAttribute, + BOOLEAN CaseSensitive); + NTSTATUS AddNewMftEntry(PFILE_RECORD_HEADER FileRecord, PDEVICE_EXTENSION DeviceExt,