mirror of
https://github.com/reactos/reactos.git
synced 2025-07-25 04:23:48 +00:00
[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
This commit is contained in:
parent
4ca9151300
commit
e0048b1362
5 changed files with 388 additions and 26 deletions
|
@ -112,6 +112,9 @@ AddData(PFILE_RECORD_HEADER FileRecord,
|
||||||
* Pointer to the FILE_OBJECT which represents the new name.
|
* Pointer to the FILE_OBJECT which represents the new name.
|
||||||
* This parameter is used to determine the filename and parent directory.
|
* 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
|
* @return
|
||||||
* STATUS_SUCCESS on success. STATUS_NOT_IMPLEMENTED if target address isn't at the end
|
* STATUS_SUCCESS on success. STATUS_NOT_IMPLEMENTED if target address isn't at the end
|
||||||
* of the given file record.
|
* of the given file record.
|
||||||
|
@ -128,16 +131,18 @@ NTSTATUS
|
||||||
AddFileName(PFILE_RECORD_HEADER FileRecord,
|
AddFileName(PFILE_RECORD_HEADER FileRecord,
|
||||||
PNTFS_ATTR_RECORD AttributeAddress,
|
PNTFS_ATTR_RECORD AttributeAddress,
|
||||||
PDEVICE_EXTENSION DeviceExt,
|
PDEVICE_EXTENSION DeviceExt,
|
||||||
PFILE_OBJECT FileObject)
|
PFILE_OBJECT FileObject,
|
||||||
|
PULONGLONG ParentMftIndex)
|
||||||
{
|
{
|
||||||
ULONG ResidentHeaderLength = FIELD_OFFSET(NTFS_ATTR_RECORD, Resident.Reserved) + sizeof(UCHAR);
|
ULONG ResidentHeaderLength = FIELD_OFFSET(NTFS_ATTR_RECORD, Resident.Reserved) + sizeof(UCHAR);
|
||||||
PFILENAME_ATTRIBUTE FileNameAttribute;
|
PFILENAME_ATTRIBUTE FileNameAttribute;
|
||||||
LARGE_INTEGER SystemTime;
|
LARGE_INTEGER SystemTime;
|
||||||
ULONG FileRecordEnd = AttributeAddress->Length;
|
ULONG FileRecordEnd = AttributeAddress->Length;
|
||||||
ULONGLONG CurrentMFTIndex = NTFS_FILE_ROOT;
|
ULONGLONG CurrentMFTIndex = NTFS_FILE_ROOT;
|
||||||
UNICODE_STRING Current, Remaining;
|
UNICODE_STRING Current, Remaining, FilenameNoPath;
|
||||||
NTSTATUS Status = STATUS_SUCCESS;
|
NTSTATUS Status = STATUS_SUCCESS;
|
||||||
ULONG FirstEntry = 0;
|
ULONG FirstEntry = 0;
|
||||||
|
WCHAR Buffer[MAX_PATH];
|
||||||
|
|
||||||
if (AttributeAddress->Type != AttributeEnd)
|
if (AttributeAddress->Type != AttributeEnd)
|
||||||
{
|
{
|
||||||
|
@ -162,21 +167,30 @@ AddFileName(PFILE_RECORD_HEADER FileRecord,
|
||||||
// we need to extract the filename from the path
|
// we need to extract the filename from the path
|
||||||
DPRINT1("Pathname: %wZ\n", &FileObject->FileName);
|
DPRINT1("Pathname: %wZ\n", &FileObject->FileName);
|
||||||
|
|
||||||
|
RtlZeroMemory(&FilenameNoPath, sizeof(UNICODE_STRING));
|
||||||
|
FilenameNoPath.Buffer = Buffer;
|
||||||
|
FilenameNoPath.MaximumLength = MAX_PATH;
|
||||||
|
|
||||||
FsRtlDissectName(FileObject->FileName, &Current, &Remaining);
|
FsRtlDissectName(FileObject->FileName, &Current, &Remaining);
|
||||||
|
|
||||||
while (Current.Length != 0)
|
while (Current.Length != 0)
|
||||||
{
|
{
|
||||||
DPRINT1("Current: %wZ\n", &Current);
|
DPRINT1("Current: %wZ\n", &Current);
|
||||||
|
|
||||||
|
if(Remaining.Length != 0)
|
||||||
|
RtlCopyUnicodeString(&FilenameNoPath, &Remaining);
|
||||||
|
|
||||||
Status = NtfsFindMftRecord(DeviceExt, CurrentMFTIndex, &Current, &FirstEntry, FALSE, &CurrentMFTIndex);
|
Status = NtfsFindMftRecord(DeviceExt, CurrentMFTIndex, &Current, &FirstEntry, FALSE, &CurrentMFTIndex);
|
||||||
if (!NT_SUCCESS(Status))
|
if (!NT_SUCCESS(Status))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (Remaining.Length == 0 )
|
||||||
{
|
{
|
||||||
|
if(Current.Length != 0)
|
||||||
|
RtlCopyUnicodeString(&FilenameNoPath, &Current);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Remaining.Length == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
FsRtlDissectName(Current, &Current, &Remaining);
|
FsRtlDissectName(Current, &Current, &Remaining);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,6 +198,9 @@ AddFileName(PFILE_RECORD_HEADER FileRecord,
|
||||||
|
|
||||||
// set reference to parent directory
|
// set reference to parent directory
|
||||||
FileNameAttribute->DirectoryFileReferenceNumber = CurrentMFTIndex;
|
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
|
// The highest 2 bytes should be the sequence number, unless the parent happens to be root
|
||||||
if (CurrentMFTIndex == NTFS_FILE_ROOT)
|
if (CurrentMFTIndex == NTFS_FILE_ROOT)
|
||||||
|
@ -191,19 +208,19 @@ AddFileName(PFILE_RECORD_HEADER FileRecord,
|
||||||
else
|
else
|
||||||
FileNameAttribute->DirectoryFileReferenceNumber |= (ULONGLONG)FileRecord->SequenceNumber << 48;
|
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
|
// TODO: Get proper nametype, add DOS links as needed
|
||||||
FileNameAttribute->NameType = NTFS_FILE_NAME_WIN32_AND_DOS;
|
FileNameAttribute->NameType = NTFS_FILE_NAME_WIN32_AND_DOS;
|
||||||
RtlCopyMemory(FileNameAttribute->Name, Current.Buffer, Current.Length);
|
RtlCopyMemory(FileNameAttribute->Name, FilenameNoPath.Buffer, FilenameNoPath.Length);
|
||||||
FileRecord->LinkCount++;
|
FileRecord->LinkCount++;
|
||||||
|
|
||||||
AttributeAddress->Length = ResidentHeaderLength +
|
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->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.ValueOffset = ResidentHeaderLength;
|
||||||
AttributeAddress->Resident.Flags = 1; // indexed
|
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) '%.*S' ", FileNameAttr->NameType, FileNameAttr->NameLength, FileNameAttr->Name);
|
||||||
DbgPrint(" '%x' \n", FileNameAttr->FileAttributes);
|
DbgPrint(" '%x' \n", FileNameAttr->FileAttributes);
|
||||||
DbgPrint(" AllocatedSize: %I64u\nDataSize: %I64u\n", FileNameAttr->AllocatedSize, FileNameAttr->DataSize);
|
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)
|
NtfsDumpIndexRootAttribute(PNTFS_ATTR_RECORD Attribute)
|
||||||
{
|
{
|
||||||
PINDEX_ROOT_ATTRIBUTE IndexRootAttr;
|
PINDEX_ROOT_ATTRIBUTE IndexRootAttr;
|
||||||
|
ULONG currentOffset;
|
||||||
|
ULONG currentNode;
|
||||||
|
|
||||||
IndexRootAttr = (PINDEX_ROOT_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
|
IndexRootAttr = (PINDEX_ROOT_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
|
||||||
|
|
||||||
if (IndexRootAttr->AttributeType == AttributeFileName)
|
if (IndexRootAttr->AttributeType == AttributeFileName)
|
||||||
ASSERT(IndexRootAttr->CollationRule == COLLATION_FILE_NAME);
|
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)
|
if (IndexRootAttr->Header.Flags == INDEX_ROOT_SMALL)
|
||||||
{
|
{
|
||||||
DbgPrint(" (small) ");
|
DbgPrint(" (small)\n");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ASSERT(IndexRootAttr->Header.Flags == INDEX_ROOT_LARGE);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -523,7 +523,7 @@ NtfsCreateFile(PDEVICE_OBJECT DeviceObject,
|
||||||
|
|
||||||
DoneOverwriting:
|
DoneOverwriting:
|
||||||
if (fileRecord)
|
if (fileRecord)
|
||||||
ExFreePool(fileRecord);
|
ExFreePoolWithTag(fileRecord, TAG_NTFS);
|
||||||
if (dataContext)
|
if (dataContext)
|
||||||
ReleaseAttributeContext(dataContext);
|
ReleaseAttributeContext(dataContext);
|
||||||
|
|
||||||
|
@ -562,13 +562,14 @@ NtfsCreateFile(PDEVICE_OBJECT DeviceObject,
|
||||||
|
|
||||||
// Create the file record on disk
|
// Create the file record on disk
|
||||||
Status = NtfsCreateFileRecord(DeviceExt, FileObject);
|
Status = NtfsCreateFileRecord(DeviceExt, FileObject);
|
||||||
|
if (!NT_SUCCESS(Status))
|
||||||
|
{
|
||||||
|
DPRINT1("ERROR: Couldn't create file record!\n");
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
// Update the parent directory index
|
// Now we should be able to open the file
|
||||||
// Still TODO
|
return NtfsCreateFile(DeviceObject, Irp);
|
||||||
|
|
||||||
// Call NtfsOpenFile()
|
|
||||||
|
|
||||||
return STATUS_CANNOT_MAKE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -624,7 +625,8 @@ NtfsCreate(PNTFS_IRP_CONTEXT IrpContext)
|
||||||
* @name NtfsCreateFileRecord()
|
* @name NtfsCreateFileRecord()
|
||||||
* @implemented
|
* @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
|
* @param DeviceExt
|
||||||
* Points to the target disk's DEVICE_EXTENSION
|
* Points to the target disk's DEVICE_EXTENSION
|
||||||
|
@ -643,6 +645,9 @@ NtfsCreateFileRecord(PDEVICE_EXTENSION DeviceExt,
|
||||||
NTSTATUS Status = STATUS_SUCCESS;
|
NTSTATUS Status = STATUS_SUCCESS;
|
||||||
PFILE_RECORD_HEADER FileRecord;
|
PFILE_RECORD_HEADER FileRecord;
|
||||||
PNTFS_ATTR_RECORD NextAttribute;
|
PNTFS_ATTR_RECORD NextAttribute;
|
||||||
|
PFILENAME_ATTRIBUTE FilenameAttribute;
|
||||||
|
ULONGLONG ParentMftIndex;
|
||||||
|
ULONGLONG FileMftIndex;
|
||||||
|
|
||||||
// allocate memory for file record
|
// allocate memory for file record
|
||||||
FileRecord = ExAllocatePoolWithTag(NonPagedPool,
|
FileRecord = ExAllocatePoolWithTag(NonPagedPool,
|
||||||
|
@ -686,7 +691,10 @@ NtfsCreateFileRecord(PDEVICE_EXTENSION DeviceExt,
|
||||||
NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length);
|
NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length);
|
||||||
|
|
||||||
// Add the $FILE_NAME attribute
|
// 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
|
// advance NextAttribute pointer to the next attribute
|
||||||
NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length);
|
NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length);
|
||||||
|
@ -698,7 +706,23 @@ NtfsCreateFileRecord(PDEVICE_EXTENSION DeviceExt,
|
||||||
NtfsDumpFileRecord(DeviceExt, FileRecord);
|
NtfsDumpFileRecord(DeviceExt, FileRecord);
|
||||||
|
|
||||||
// Now that we've built the file record in memory, we need to store it in the MFT.
|
// 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);
|
ExFreePoolWithTag(FileRecord, TAG_NTFS);
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,256 @@
|
||||||
|
|
||||||
/* FUNCTIONS ****************************************************************/
|
/* 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
|
ULONGLONG
|
||||||
NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt,
|
NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt,
|
||||||
|
|
|
@ -1361,6 +1361,9 @@ FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb,
|
||||||
* @param DeviceExt
|
* @param DeviceExt
|
||||||
* Pointer to the DEVICE_EXTENSION of the target drive.
|
* 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
|
* @return
|
||||||
* STATUS_SUCCESS on success.
|
* STATUS_SUCCESS on success.
|
||||||
* STATUS_OBJECT_NAME_NOT_FOUND if we can't find the MFT's $Bitmap or if we weren't able
|
* 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
|
NTSTATUS
|
||||||
AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
|
AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
|
||||||
PDEVICE_EXTENSION DeviceExt)
|
PDEVICE_EXTENSION DeviceExt,
|
||||||
|
PULONGLONG DestinationIndex)
|
||||||
{
|
{
|
||||||
NTSTATUS Status = STATUS_SUCCESS;
|
NTSTATUS Status = STATUS_SUCCESS;
|
||||||
ULONGLONG MftIndex;
|
ULONGLONG MftIndex;
|
||||||
|
@ -1454,6 +1458,8 @@ AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
|
||||||
return Status;
|
return Status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*DestinationIndex = MftIndex;
|
||||||
|
|
||||||
ExFreePoolWithTag(BitmapData, TAG_NTFS);
|
ExFreePoolWithTag(BitmapData, TAG_NTFS);
|
||||||
ReleaseAttributeContext(BitmapContext);
|
ReleaseAttributeContext(BitmapContext);
|
||||||
|
|
||||||
|
|
|
@ -534,7 +534,8 @@ NTSTATUS
|
||||||
AddFileName(PFILE_RECORD_HEADER FileRecord,
|
AddFileName(PFILE_RECORD_HEADER FileRecord,
|
||||||
PNTFS_ATTR_RECORD AttributeAddress,
|
PNTFS_ATTR_RECORD AttributeAddress,
|
||||||
PDEVICE_EXTENSION DeviceExt,
|
PDEVICE_EXTENSION DeviceExt,
|
||||||
PFILE_OBJECT FileObject);
|
PFILE_OBJECT FileObject,
|
||||||
|
PULONGLONG ParentMftIndex);
|
||||||
|
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
AddStandardInformation(PFILE_RECORD_HEADER FileRecord,
|
AddStandardInformation(PFILE_RECORD_HEADER FileRecord,
|
||||||
|
@ -675,6 +676,12 @@ NtfsDeviceControl(PNTFS_IRP_CONTEXT IrpContext);
|
||||||
|
|
||||||
/* dirctl.c */
|
/* dirctl.c */
|
||||||
|
|
||||||
|
NTSTATUS
|
||||||
|
NtfsAddFilenameToDirectory(PDEVICE_EXTENSION DeviceExt,
|
||||||
|
ULONGLONG DirectoryMftIndex,
|
||||||
|
ULONGLONG FileMftIndex,
|
||||||
|
PFILENAME_ATTRIBUTE FilenameAttribute);
|
||||||
|
|
||||||
ULONGLONG
|
ULONGLONG
|
||||||
NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt,
|
NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt,
|
||||||
PFILE_RECORD_HEADER FileRecord,
|
PFILE_RECORD_HEADER FileRecord,
|
||||||
|
@ -816,7 +823,8 @@ NtfsFileSystemControl(PNTFS_IRP_CONTEXT IrpContext);
|
||||||
/* mft.c */
|
/* mft.c */
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
|
AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
|
||||||
PDEVICE_EXTENSION DeviceExt);
|
PDEVICE_EXTENSION DeviceExt,
|
||||||
|
PULONGLONG DestinationIndex);
|
||||||
|
|
||||||
PNTFS_ATTR_CONTEXT
|
PNTFS_ATTR_CONTEXT
|
||||||
PrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord);
|
PrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord);
|
||||||
|
@ -842,6 +850,12 @@ WriteAttribute(PDEVICE_EXTENSION Vcb,
|
||||||
ULONGLONG
|
ULONGLONG
|
||||||
AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord);
|
AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord);
|
||||||
|
|
||||||
|
VOID
|
||||||
|
InternalSetResidentAttributeLength(PNTFS_ATTR_CONTEXT AttrContext,
|
||||||
|
PFILE_RECORD_HEADER FileRecord,
|
||||||
|
ULONG AttrOffset,
|
||||||
|
ULONG DataSize);
|
||||||
|
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
SetAttributeDataLength(PFILE_OBJECT FileObject,
|
SetAttributeDataLength(PFILE_OBJECT FileObject,
|
||||||
PNTFS_FCB Fcb,
|
PNTFS_FCB Fcb,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue