mirror of
https://github.com/reactos/reactos.git
synced 2025-08-06 17:33:18 +00:00
[NTFS] Add support for creating new MFT entries:
+AddStandardInformation(), +AddData(), +AddFileName() - Add attributes to a file record +NtfsCreateFileRecord() - Creates a new file record and saves it to the MFT. +AddNewMftEntry() - Adds a file record to the MFT. NtfsCreateFile() - Modified to create a file record on a file-creation request (file creation is still unsupported; the created file needs to be added to the parent's directory index). +SetFileRecordEnd() - Establishes a new file record size UpdateFileRecord() - Improved documentation InternalSetResidentAttributeLength() - Updated to use SetFileRecordEnd(). svn path=/branches/GSoC_2016/NTFS/; revision=74321
This commit is contained in:
parent
7643831d56
commit
0409b3161e
4 changed files with 545 additions and 20 deletions
|
@ -35,6 +35,185 @@
|
||||||
|
|
||||||
/* FUNCTIONS ****************************************************************/
|
/* FUNCTIONS ****************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name AddData
|
||||||
|
* @implemented
|
||||||
|
*
|
||||||
|
* Adds a $DATA attribute to a given FileRecord.
|
||||||
|
*
|
||||||
|
* @param FileRecord
|
||||||
|
* Pointer to a complete file record to add the attribute to. Caller is responsible for
|
||||||
|
* ensuring FileRecord is large enough to contain $DATA.
|
||||||
|
*
|
||||||
|
* @param AttributeAddress
|
||||||
|
* Pointer to the region of memory that will receive the $DATA attribute.
|
||||||
|
* This address must reside within FileRecord. Must be aligned to an 8-byte boundary (relative to FileRecord).
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* STATUS_SUCCESS on success. STATUS_NOT_IMPLEMENTED if target address isn't at the end
|
||||||
|
* of the given file record.
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* Only adding the attribute to the end of the file record is supported; AttributeAddress must
|
||||||
|
* be of type AttributeEnd.
|
||||||
|
* As it's implemented, this function is only intended to assist in creating new file records. It
|
||||||
|
* could be made more general-purpose by considering file records with an $ATTRIBUTE_LIST.
|
||||||
|
* It's the caller's responsibility to ensure the given file record has enough memory allocated
|
||||||
|
* for the attribute.
|
||||||
|
*/
|
||||||
|
NTSTATUS
|
||||||
|
AddData(PFILE_RECORD_HEADER FileRecord,
|
||||||
|
PNTFS_ATTR_RECORD AttributeAddress)
|
||||||
|
{
|
||||||
|
ULONG ResidentHeaderLength = FIELD_OFFSET(NTFS_ATTR_RECORD, Resident.Reserved) + sizeof(UCHAR);
|
||||||
|
ULONG FileRecordEnd = AttributeAddress->Length;
|
||||||
|
|
||||||
|
if (AttributeAddress->Type != AttributeEnd)
|
||||||
|
{
|
||||||
|
DPRINT1("FIXME: Can only add $DATA attribute to the end of a file record.\n");
|
||||||
|
return STATUS_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
AttributeAddress->Type = AttributeData;
|
||||||
|
AttributeAddress->Length = ResidentHeaderLength;
|
||||||
|
AttributeAddress->Length = ALIGN_UP_BY(AttributeAddress->Length, 8);
|
||||||
|
AttributeAddress->Resident.ValueLength = 0;
|
||||||
|
AttributeAddress->Resident.ValueOffset = ResidentHeaderLength;
|
||||||
|
|
||||||
|
// for unnamed $DATA attributes, NameOffset equals header length
|
||||||
|
AttributeAddress->NameOffset = ResidentHeaderLength;
|
||||||
|
AttributeAddress->Instance = FileRecord->NextAttributeNumber++;
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
SetFileRecordEnd(FileRecord, AttributeAddress, FileRecordEnd);
|
||||||
|
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name AddFileName
|
||||||
|
* @implemented
|
||||||
|
*
|
||||||
|
* Adds a $FILE_NAME attribute to a given FileRecord.
|
||||||
|
*
|
||||||
|
* @param FileRecord
|
||||||
|
* Pointer to a complete file record to add the attribute to. Caller is responsible for
|
||||||
|
* ensuring FileRecord is large enough to contain $FILE_NAME.
|
||||||
|
*
|
||||||
|
* @param AttributeAddress
|
||||||
|
* Pointer to the region of memory that will receive the $FILE_NAME attribute.
|
||||||
|
* This address must reside within FileRecord. Must be aligned to an 8-byte boundary (relative to FileRecord).
|
||||||
|
*
|
||||||
|
* @param DeviceExt
|
||||||
|
* Points to the target disk's DEVICE_EXTENSION.
|
||||||
|
*
|
||||||
|
* @param FileObject
|
||||||
|
* Pointer to the FILE_OBJECT which represents the new name.
|
||||||
|
* This parameter is used to determine the filename and parent directory.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* STATUS_SUCCESS on success. STATUS_NOT_IMPLEMENTED if target address isn't at the end
|
||||||
|
* of the given file record.
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* Only adding the attribute to the end of the file record is supported; AttributeAddress must
|
||||||
|
* be of type AttributeEnd.
|
||||||
|
* As it's implemented, this function is only intended to assist in creating new file records. It
|
||||||
|
* could be made more general-purpose by considering file records with an $ATTRIBUTE_LIST.
|
||||||
|
* It's the caller's responsibility to ensure the given file record has enough memory allocated
|
||||||
|
* for the attribute.
|
||||||
|
*/
|
||||||
|
NTSTATUS
|
||||||
|
AddFileName(PFILE_RECORD_HEADER FileRecord,
|
||||||
|
PNTFS_ATTR_RECORD AttributeAddress,
|
||||||
|
PDEVICE_EXTENSION DeviceExt,
|
||||||
|
PFILE_OBJECT FileObject)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
NTSTATUS Status = STATUS_SUCCESS;
|
||||||
|
ULONG FirstEntry = 0;
|
||||||
|
|
||||||
|
if (AttributeAddress->Type != AttributeEnd)
|
||||||
|
{
|
||||||
|
DPRINT1("FIXME: Can only add $FILE_NAME attribute to the end of a file record.\n");
|
||||||
|
return STATUS_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
AttributeAddress->Type = AttributeFileName;
|
||||||
|
AttributeAddress->Instance = FileRecord->NextAttributeNumber++;
|
||||||
|
|
||||||
|
FileNameAttribute = (PFILENAME_ATTRIBUTE)((LONG_PTR)AttributeAddress + ResidentHeaderLength);
|
||||||
|
|
||||||
|
// set timestamps
|
||||||
|
KeQuerySystemTime(&SystemTime);
|
||||||
|
FileNameAttribute->CreationTime = SystemTime.QuadPart;
|
||||||
|
FileNameAttribute->ChangeTime = SystemTime.QuadPart;
|
||||||
|
FileNameAttribute->LastWriteTime = SystemTime.QuadPart;
|
||||||
|
FileNameAttribute->LastAccessTime = SystemTime.QuadPart;
|
||||||
|
|
||||||
|
FileNameAttribute->FileAttributes = NTFS_FILE_TYPE_ARCHIVE;
|
||||||
|
|
||||||
|
// we need to extract the filename from the path
|
||||||
|
DPRINT1("Pathname: %wZ\n", &FileObject->FileName);
|
||||||
|
|
||||||
|
FsRtlDissectName(FileObject->FileName, &Current, &Remaining);
|
||||||
|
|
||||||
|
while (Current.Length != 0)
|
||||||
|
{
|
||||||
|
DPRINT1("Current: %wZ\n", &Current);
|
||||||
|
|
||||||
|
Status = NtfsFindMftRecord(DeviceExt, CurrentMFTIndex, &Current, &FirstEntry, FALSE, &CurrentMFTIndex);
|
||||||
|
if (!NT_SUCCESS(Status))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Remaining.Length == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
FsRtlDissectName(Current, &Current, &Remaining);
|
||||||
|
}
|
||||||
|
|
||||||
|
DPRINT1("MFT Index of parent: %I64u\n", CurrentMFTIndex);
|
||||||
|
|
||||||
|
// set reference to parent directory
|
||||||
|
FileNameAttribute->DirectoryFileReferenceNumber = CurrentMFTIndex;
|
||||||
|
|
||||||
|
// The highest 2 bytes should be the sequence number, unless the parent happens to be root
|
||||||
|
if (CurrentMFTIndex == NTFS_FILE_ROOT)
|
||||||
|
FileNameAttribute->DirectoryFileReferenceNumber |= (ULONGLONG)NTFS_FILE_ROOT << 48;
|
||||||
|
else
|
||||||
|
FileNameAttribute->DirectoryFileReferenceNumber |= (ULONGLONG)FileRecord->SequenceNumber << 48;
|
||||||
|
|
||||||
|
DPRINT1("FileNameAttribute->DirectoryFileReferenceNumber: 0x%I64x\n", FileNameAttribute->DirectoryFileReferenceNumber);
|
||||||
|
|
||||||
|
FileNameAttribute->NameLength = Current.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);
|
||||||
|
FileRecord->LinkCount++;
|
||||||
|
|
||||||
|
AttributeAddress->Length = ResidentHeaderLength +
|
||||||
|
FIELD_OFFSET(FILENAME_ATTRIBUTE, Name) + Current.Length;
|
||||||
|
AttributeAddress->Length = ALIGN_UP_BY(AttributeAddress->Length, 8);
|
||||||
|
|
||||||
|
AttributeAddress->Resident.ValueLength = FIELD_OFFSET(FILENAME_ATTRIBUTE, Name) + Current.Length;
|
||||||
|
AttributeAddress->Resident.ValueOffset = ResidentHeaderLength;
|
||||||
|
AttributeAddress->Resident.Flags = 1; // 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);
|
||||||
|
SetFileRecordEnd(FileRecord, AttributeAddress, FileRecordEnd);
|
||||||
|
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name AddRun
|
* @name AddRun
|
||||||
* @implemented
|
* @implemented
|
||||||
|
@ -192,6 +371,69 @@ AddRun(PNTFS_VCB Vcb,
|
||||||
return Status;
|
return Status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name AddStandardInformation
|
||||||
|
* @implemented
|
||||||
|
*
|
||||||
|
* Adds a $STANDARD_INFORMATION attribute to a given FileRecord.
|
||||||
|
*
|
||||||
|
* @param FileRecord
|
||||||
|
* Pointer to a complete file record to add the attribute to. Caller is responsible for
|
||||||
|
* ensuring FileRecord is large enough to contain $STANDARD_INFORMATION.
|
||||||
|
*
|
||||||
|
* @param AttributeAddress
|
||||||
|
* Pointer to the region of memory that will receive the $STANDARD_INFORMATION attribute.
|
||||||
|
* This address must reside within FileRecord. Must be aligned to an 8-byte boundary (relative to FileRecord).
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* STATUS_SUCCESS on success. STATUS_NOT_IMPLEMENTED if target address isn't at the end
|
||||||
|
* of the given file record.
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* Only adding the attribute to the end of the file record is supported; AttributeAddress must
|
||||||
|
* be of type AttributeEnd.
|
||||||
|
* As it's implemented, this function is only intended to assist in creating new file records. It
|
||||||
|
* could be made more general-purpose by considering file records with an $ATTRIBUTE_LIST.
|
||||||
|
* It's the caller's responsibility to ensure the given file record has enough memory allocated
|
||||||
|
* for the attribute.
|
||||||
|
*/
|
||||||
|
NTSTATUS
|
||||||
|
AddStandardInformation(PFILE_RECORD_HEADER FileRecord,
|
||||||
|
PNTFS_ATTR_RECORD AttributeAddress)
|
||||||
|
{
|
||||||
|
ULONG ResidentHeaderLength = FIELD_OFFSET(NTFS_ATTR_RECORD, Resident.Reserved) + sizeof(UCHAR);
|
||||||
|
PSTANDARD_INFORMATION StandardInfo = (PSTANDARD_INFORMATION)((LONG_PTR)AttributeAddress + ResidentHeaderLength);
|
||||||
|
LARGE_INTEGER SystemTime;
|
||||||
|
ULONG FileRecordEnd = AttributeAddress->Length;
|
||||||
|
|
||||||
|
if (AttributeAddress->Type != AttributeEnd)
|
||||||
|
{
|
||||||
|
DPRINT1("FIXME: Can only add $STANDARD_INFORMATION attribute to the end of a file record.\n");
|
||||||
|
return STATUS_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
AttributeAddress->Type = AttributeStandardInformation;
|
||||||
|
AttributeAddress->Length = sizeof(STANDARD_INFORMATION) + ResidentHeaderLength;
|
||||||
|
AttributeAddress->Length = ALIGN_UP_BY(AttributeAddress->Length, 8);
|
||||||
|
AttributeAddress->Resident.ValueLength = sizeof(STANDARD_INFORMATION);
|
||||||
|
AttributeAddress->Resident.ValueOffset = ResidentHeaderLength;
|
||||||
|
AttributeAddress->Instance = FileRecord->NextAttributeNumber++;
|
||||||
|
|
||||||
|
// set dates and times
|
||||||
|
KeQuerySystemTime(&SystemTime);
|
||||||
|
StandardInfo->CreationTime = SystemTime.QuadPart;
|
||||||
|
StandardInfo->ChangeTime = SystemTime.QuadPart;
|
||||||
|
StandardInfo->LastWriteTime = SystemTime.QuadPart;
|
||||||
|
StandardInfo->LastAccessTime = SystemTime.QuadPart;
|
||||||
|
StandardInfo->FileAttribute = NTFS_FILE_TYPE_ARCHIVE;
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
SetFileRecordEnd(FileRecord, AttributeAddress, FileRecordEnd);
|
||||||
|
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name ConvertDataRunsToLargeMCB
|
* @name ConvertDataRunsToLargeMCB
|
||||||
* @implemented
|
* @implemented
|
||||||
|
|
|
@ -545,8 +545,15 @@ NtfsCreateFile(PDEVICE_OBJECT DeviceObject,
|
||||||
RequestedDisposition == FILE_OPEN_IF ||
|
RequestedDisposition == FILE_OPEN_IF ||
|
||||||
RequestedDisposition == FILE_OVERWRITE_IF ||
|
RequestedDisposition == FILE_OVERWRITE_IF ||
|
||||||
RequestedDisposition == FILE_SUPERSEDE)
|
RequestedDisposition == FILE_SUPERSEDE)
|
||||||
{
|
{
|
||||||
DPRINT1("Denying file creation request on NTFS volume\n");
|
// Create the file record on disk
|
||||||
|
Status = NtfsCreateFileRecord(DeviceExt, FileObject);
|
||||||
|
|
||||||
|
// Update the parent directory index
|
||||||
|
// Still TODO
|
||||||
|
|
||||||
|
// Call NtfsOpenFile()
|
||||||
|
|
||||||
return STATUS_CANNOT_MAKE;
|
return STATUS_CANNOT_MAKE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -599,4 +606,89 @@ NtfsCreate(PNTFS_IRP_CONTEXT IrpContext)
|
||||||
return Status;
|
return Status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name NtfsCreateFileRecord()
|
||||||
|
* @implemented
|
||||||
|
*
|
||||||
|
* Creates a file record and saves it to the MFT.
|
||||||
|
*
|
||||||
|
* @param DeviceExt
|
||||||
|
* Points to the target disk's DEVICE_EXTENSION
|
||||||
|
*
|
||||||
|
* @param FileObject
|
||||||
|
* Pointer to a FILE_OBJECT describing the file to be created
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* STATUS_SUCCESS on success.
|
||||||
|
* STATUS_INSUFFICIENT_RESOURCES if unable to allocate memory for the file record.
|
||||||
|
*/
|
||||||
|
NTSTATUS
|
||||||
|
NtfsCreateFileRecord(PDEVICE_EXTENSION DeviceExt,
|
||||||
|
PFILE_OBJECT FileObject)
|
||||||
|
{
|
||||||
|
NTSTATUS Status = STATUS_SUCCESS;
|
||||||
|
PFILE_RECORD_HEADER FileRecord;
|
||||||
|
PNTFS_ATTR_RECORD NextAttribute;
|
||||||
|
|
||||||
|
// allocate memory for file record
|
||||||
|
FileRecord = ExAllocatePoolWithTag(NonPagedPool,
|
||||||
|
DeviceExt->NtfsInfo.BytesPerFileRecord,
|
||||||
|
TAG_NTFS);
|
||||||
|
if (!FileRecord)
|
||||||
|
{
|
||||||
|
DPRINT1("ERROR: Unable to allocate memory for file record!\n");
|
||||||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
RtlZeroMemory(FileRecord, DeviceExt->NtfsInfo.BytesPerFileRecord);
|
||||||
|
|
||||||
|
FileRecord->Ntfs.Type = NRH_FILE_TYPE;
|
||||||
|
|
||||||
|
// calculate USA offset and count
|
||||||
|
FileRecord->Ntfs.UsaOffset = FIELD_OFFSET(FILE_RECORD_HEADER, MFTRecordNumber) + sizeof(ULONG);
|
||||||
|
|
||||||
|
// size of USA (in ULONG's) will be 1 (for USA number) + 1 for every sector the file record uses
|
||||||
|
FileRecord->BytesAllocated = DeviceExt->NtfsInfo.BytesPerFileRecord;
|
||||||
|
FileRecord->Ntfs.UsaCount = (FileRecord->BytesAllocated / DeviceExt->NtfsInfo.BytesPerSector) + 1;
|
||||||
|
|
||||||
|
// setup other file record fields
|
||||||
|
FileRecord->SequenceNumber = 1;
|
||||||
|
FileRecord->AttributeOffset = FileRecord->Ntfs.UsaOffset + (2 * FileRecord->Ntfs.UsaCount);
|
||||||
|
FileRecord->AttributeOffset = ALIGN_UP_BY(FileRecord->AttributeOffset, 8);
|
||||||
|
FileRecord->Flags = FRH_IN_USE;
|
||||||
|
FileRecord->BytesInUse = FileRecord->AttributeOffset + sizeof(ULONG) * 2;
|
||||||
|
|
||||||
|
// find where the first attribute will be added
|
||||||
|
NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset);
|
||||||
|
|
||||||
|
// mark the (temporary) end of the file-record
|
||||||
|
NextAttribute->Type = AttributeEnd;
|
||||||
|
NextAttribute->Length = FILE_RECORD_END;
|
||||||
|
|
||||||
|
// add first attribute, $STANDARD_INFORMATION
|
||||||
|
AddStandardInformation(FileRecord, NextAttribute);
|
||||||
|
|
||||||
|
// advance NextAttribute pointer to the next attribute
|
||||||
|
NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length);
|
||||||
|
|
||||||
|
// Add the $FILE_NAME attribute
|
||||||
|
AddFileName(FileRecord, NextAttribute, DeviceExt, FileObject);
|
||||||
|
|
||||||
|
// advance NextAttribute pointer to the next attribute
|
||||||
|
NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length);
|
||||||
|
|
||||||
|
// add the $DATA attribute
|
||||||
|
AddData(FileRecord, NextAttribute);
|
||||||
|
|
||||||
|
// dump file record in memory (for debugging)
|
||||||
|
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);
|
||||||
|
|
||||||
|
ExFreePoolWithTag(FileRecord, TAG_NTFS);
|
||||||
|
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
/* EOF */
|
/* EOF */
|
||||||
|
|
|
@ -172,7 +172,7 @@ AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord)
|
||||||
return AttrRecord->Resident.ValueLength;
|
return AttrRecord->Resident.ValueLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
VOID
|
||||||
InternalSetResidentAttributeLength(PNTFS_ATTR_CONTEXT AttrContext,
|
InternalSetResidentAttributeLength(PNTFS_ATTR_CONTEXT AttrContext,
|
||||||
PFILE_RECORD_HEADER FileRecord,
|
PFILE_RECORD_HEADER FileRecord,
|
||||||
ULONG AttrOffset,
|
ULONG AttrOffset,
|
||||||
|
@ -201,14 +201,9 @@ InternalSetResidentAttributeLength(PNTFS_ATTR_CONTEXT AttrContext,
|
||||||
Destination->Length += Padding;
|
Destination->Length += Padding;
|
||||||
}
|
}
|
||||||
|
|
||||||
// advance Destination to the final "attribute" and write the end type
|
// advance Destination to the final "attribute" and set the file record end
|
||||||
Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)Destination + Destination->Length);
|
Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)Destination + Destination->Length);
|
||||||
Destination->Type = AttributeEnd;
|
SetFileRecordEnd(FileRecord, Destination, FILE_RECORD_END);
|
||||||
|
|
||||||
// write the final marker (which shares the same offset and type as the Length field)
|
|
||||||
Destination->Length = FILE_RECORD_END;
|
|
||||||
|
|
||||||
FileRecord->BytesInUse = NextAttributeOffset + (sizeof(ULONG) * 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -359,6 +354,41 @@ SetAttributeDataLength(PFILE_OBJECT FileObject,
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name SetFileRecordEnd
|
||||||
|
* @implemented
|
||||||
|
*
|
||||||
|
* This small function sets a new endpoint for the file record. It set's the final
|
||||||
|
* AttrEnd->Type to AttributeEnd and recalculates the bytes used by the file record.
|
||||||
|
*
|
||||||
|
* @param FileRecord
|
||||||
|
* Pointer to the file record whose endpoint (length) will be set.
|
||||||
|
*
|
||||||
|
* @param AttrEnd
|
||||||
|
* Pointer to section of memory that will receive the AttributeEnd marker. This must point
|
||||||
|
* to memory allocated for the FileRecord. Must be aligned to an 8-byte boundary (relative to FileRecord).
|
||||||
|
*
|
||||||
|
* @param EndMarker
|
||||||
|
* This value will be written after AttributeEnd but isn't critical at all. When Windows resizes
|
||||||
|
* a file record, it preserves the final ULONG that previously ended the record, even though this
|
||||||
|
* value is (to my knowledge) never used. We emulate this behavior.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
VOID
|
||||||
|
SetFileRecordEnd(PFILE_RECORD_HEADER FileRecord,
|
||||||
|
PNTFS_ATTR_RECORD AttrEnd,
|
||||||
|
ULONG EndMarker)
|
||||||
|
{
|
||||||
|
// mark the end of attributes
|
||||||
|
AttrEnd->Type = AttributeEnd;
|
||||||
|
|
||||||
|
// Restore the "file-record-end marker." The value is never checked but this behavior is consistent with Win2k3.
|
||||||
|
AttrEnd->Length = EndMarker;
|
||||||
|
|
||||||
|
// recalculate bytes in use
|
||||||
|
FileRecord->BytesInUse = (ULONG_PTR)AttrEnd - (ULONG_PTR)FileRecord + sizeof(ULONG) * 2;
|
||||||
|
}
|
||||||
|
|
||||||
ULONG
|
ULONG
|
||||||
ReadAttribute(PDEVICE_EXTENSION Vcb,
|
ReadAttribute(PDEVICE_EXTENSION Vcb,
|
||||||
PNTFS_ATTR_CONTEXT Context,
|
PNTFS_ATTR_CONTEXT Context,
|
||||||
|
@ -711,7 +741,9 @@ WriteAttribute(PDEVICE_EXTENSION Vcb,
|
||||||
{
|
{
|
||||||
// We reached the last assigned cluster
|
// We reached the last assigned cluster
|
||||||
// TODO: assign new clusters to the end of the file.
|
// TODO: assign new clusters to the end of the file.
|
||||||
// (Presently, this code will never be reached, the write should have already failed by now)
|
// (Presently, this code will rarely be reached, the write will usually have already failed by now)
|
||||||
|
// [We can reach here by creating a new file record when the MFT isn't large enough]
|
||||||
|
DPRINT1("FIXME: Master File Table needs to be enlarged.\n");
|
||||||
return STATUS_END_OF_FILE;
|
return STATUS_END_OF_FILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1070,25 +1102,39 @@ UpdateIndexEntryFileNameSize(PDEVICE_EXTENSION Vcb,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UpdateFileRecord
|
* @name UpdateFileRecord
|
||||||
* @implemented
|
* @implemented
|
||||||
|
*
|
||||||
* Writes a file record to the master file table, at a given index.
|
* Writes a file record to the master file table, at a given index.
|
||||||
|
*
|
||||||
|
* @param Vcb
|
||||||
|
* Pointer to the DEVICE_EXTENSION of the target drive being written to.
|
||||||
|
*
|
||||||
|
* @param MftIndex
|
||||||
|
* Target index in the master file table to store the file record.
|
||||||
|
*
|
||||||
|
* @param FileRecord
|
||||||
|
* Pointer to the complete file record which will be written to the master file table.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* STATUS_SUCCESSFUL on success. An error passed from WriteAttribute() otherwise.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
UpdateFileRecord(PDEVICE_EXTENSION Vcb,
|
UpdateFileRecord(PDEVICE_EXTENSION Vcb,
|
||||||
ULONGLONG index,
|
ULONGLONG MftIndex,
|
||||||
PFILE_RECORD_HEADER file)
|
PFILE_RECORD_HEADER FileRecord)
|
||||||
{
|
{
|
||||||
ULONG BytesWritten;
|
ULONG BytesWritten;
|
||||||
NTSTATUS Status = STATUS_SUCCESS;
|
NTSTATUS Status = STATUS_SUCCESS;
|
||||||
|
|
||||||
DPRINT("UpdateFileRecord(%p, %I64x, %p)\n", Vcb, index, file);
|
DPRINT("UpdateFileRecord(%p, 0x%I64x, %p)\n", Vcb, MftIndex, FileRecord);
|
||||||
|
|
||||||
// Add the fixup array to prepare the data for writing to disk
|
// Add the fixup array to prepare the data for writing to disk
|
||||||
AddFixupArray(Vcb, &file->Ntfs);
|
AddFixupArray(Vcb, &FileRecord->Ntfs);
|
||||||
|
|
||||||
// write the file record to the master file table
|
// 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);
|
Status = WriteAttribute(Vcb, Vcb->MFTContext, MftIndex * Vcb->NtfsInfo.BytesPerFileRecord, (const PUCHAR)FileRecord, Vcb->NtfsInfo.BytesPerFileRecord, &BytesWritten);
|
||||||
|
|
||||||
if (!NT_SUCCESS(Status))
|
if (!NT_SUCCESS(Status))
|
||||||
{
|
{
|
||||||
|
@ -1096,7 +1142,7 @@ UpdateFileRecord(PDEVICE_EXTENSION Vcb,
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove the fixup array (so the file record pointer can still be used)
|
// remove the fixup array (so the file record pointer can still be used)
|
||||||
FixupUpdateSequenceArray(Vcb, &file->Ntfs);
|
FixupUpdateSequenceArray(Vcb, &FileRecord->Ntfs);
|
||||||
|
|
||||||
return Status;
|
return Status;
|
||||||
}
|
}
|
||||||
|
@ -1133,6 +1179,117 @@ FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb,
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name AddNewMftEntry
|
||||||
|
* @implemented
|
||||||
|
*
|
||||||
|
* Adds a file record to the master file table of a given device.
|
||||||
|
*
|
||||||
|
* @param FileRecord
|
||||||
|
* Pointer to a complete file record which will be saved to disk.
|
||||||
|
*
|
||||||
|
* @param DeviceExt
|
||||||
|
* Pointer to the DEVICE_EXTENSION of the target drive.
|
||||||
|
*
|
||||||
|
* @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
|
||||||
|
* to read the attribute.
|
||||||
|
* STATUS_INSUFFICIENT_RESOURCES if we can't allocate enough memory for a copy of $Bitmap.
|
||||||
|
* STATUS_NOT_IMPLEMENTED if we need to increase the size of the MFT.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
NTSTATUS
|
||||||
|
AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
|
||||||
|
PDEVICE_EXTENSION DeviceExt)
|
||||||
|
{
|
||||||
|
NTSTATUS Status = STATUS_SUCCESS;
|
||||||
|
ULONGLONG MftIndex;
|
||||||
|
RTL_BITMAP Bitmap;
|
||||||
|
ULONGLONG BitmapDataSize;
|
||||||
|
ULONGLONG AttrBytesRead;
|
||||||
|
PVOID BitmapData;
|
||||||
|
ULONG LengthWritten;
|
||||||
|
|
||||||
|
// First, we have to read the mft's $Bitmap attribute
|
||||||
|
PNTFS_ATTR_CONTEXT BitmapContext;
|
||||||
|
Status = FindAttribute(DeviceExt, DeviceExt->MasterFileTable, AttributeBitmap, L"", 0, &BitmapContext, NULL);
|
||||||
|
if (!NT_SUCCESS(Status))
|
||||||
|
{
|
||||||
|
DPRINT1("ERROR: Couldn't find $Bitmap attribute of master file table!\n");
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate a buffer for the $Bitmap attribute
|
||||||
|
BitmapDataSize = AttributeDataLength(&BitmapContext->Record);
|
||||||
|
BitmapData = ExAllocatePoolWithTag(NonPagedPool, BitmapDataSize, TAG_NTFS);
|
||||||
|
if (!BitmapData)
|
||||||
|
{
|
||||||
|
ReleaseAttributeContext(BitmapContext);
|
||||||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read $Bitmap attribute
|
||||||
|
AttrBytesRead = ReadAttribute(DeviceExt, BitmapContext, 0, BitmapData, BitmapDataSize);
|
||||||
|
|
||||||
|
if (AttrBytesRead == 0)
|
||||||
|
{
|
||||||
|
DPRINT1("ERROR: Unable to read $Bitmap attribute of master file table!\n");
|
||||||
|
ExFreePoolWithTag(BitmapData, TAG_NTFS);
|
||||||
|
ReleaseAttributeContext(BitmapContext);
|
||||||
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert buffer into bitmap
|
||||||
|
RtlInitializeBitMap(&Bitmap, (PULONG)BitmapData, BitmapDataSize * 8);
|
||||||
|
|
||||||
|
// set next available bit, preferrably after 23rd bit
|
||||||
|
MftIndex = RtlFindClearBitsAndSet(&Bitmap, 1, 24);
|
||||||
|
if ((LONG)MftIndex == -1)
|
||||||
|
{
|
||||||
|
DPRINT1("ERROR: Couldn't find free space in MFT for file record!\n");
|
||||||
|
|
||||||
|
ExFreePoolWithTag(BitmapData, TAG_NTFS);
|
||||||
|
ReleaseAttributeContext(BitmapContext);
|
||||||
|
|
||||||
|
// TODO: increase mft size
|
||||||
|
return STATUS_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
DPRINT1("Creating file record at MFT index: %I64u\n", MftIndex);
|
||||||
|
|
||||||
|
// update file record with index
|
||||||
|
FileRecord->MFTRecordNumber = MftIndex;
|
||||||
|
|
||||||
|
// [BitmapData should have been updated via RtlFindClearBitsAndSet()]
|
||||||
|
|
||||||
|
// write the bitmap back to the MFT's $Bitmap attribute
|
||||||
|
Status = WriteAttribute(DeviceExt, BitmapContext, 0, BitmapData, BitmapDataSize, &LengthWritten);
|
||||||
|
if (!NT_SUCCESS(Status))
|
||||||
|
{
|
||||||
|
DPRINT1("ERROR encountered when writing $Bitmap attribute!\n");
|
||||||
|
ExFreePoolWithTag(BitmapData, TAG_NTFS);
|
||||||
|
ReleaseAttributeContext(BitmapContext);
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the file record (write it to disk)
|
||||||
|
Status = UpdateFileRecord(DeviceExt, MftIndex, FileRecord);
|
||||||
|
|
||||||
|
if (!NT_SUCCESS(Status))
|
||||||
|
{
|
||||||
|
DPRINT1("ERROR: Unable to write file record!\n");
|
||||||
|
ExFreePoolWithTag(BitmapData, TAG_NTFS);
|
||||||
|
ReleaseAttributeContext(BitmapContext);
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExFreePoolWithTag(BitmapData, TAG_NTFS);
|
||||||
|
ReleaseAttributeContext(BitmapContext);
|
||||||
|
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
AddFixupArray(PDEVICE_EXTENSION Vcb,
|
AddFixupArray(PDEVICE_EXTENSION Vcb,
|
||||||
PNTFS_RECORD_HEADER Record)
|
PNTFS_RECORD_HEADER Record)
|
||||||
|
|
|
@ -516,6 +516,10 @@ NtfsMarkIrpContextForQueue(PNTFS_IRP_CONTEXT IrpContext)
|
||||||
//VOID
|
//VOID
|
||||||
//NtfsDumpAttribute(PATTRIBUTE Attribute);
|
//NtfsDumpAttribute(PATTRIBUTE Attribute);
|
||||||
|
|
||||||
|
NTSTATUS
|
||||||
|
AddData(PFILE_RECORD_HEADER FileRecord,
|
||||||
|
PNTFS_ATTR_RECORD AttributeAddress);
|
||||||
|
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
AddRun(PNTFS_VCB Vcb,
|
AddRun(PNTFS_VCB Vcb,
|
||||||
PNTFS_ATTR_CONTEXT AttrContext,
|
PNTFS_ATTR_CONTEXT AttrContext,
|
||||||
|
@ -524,6 +528,16 @@ AddRun(PNTFS_VCB Vcb,
|
||||||
ULONGLONG NextAssignedCluster,
|
ULONGLONG NextAssignedCluster,
|
||||||
ULONG RunLength);
|
ULONG RunLength);
|
||||||
|
|
||||||
|
NTSTATUS
|
||||||
|
AddFileName(PFILE_RECORD_HEADER FileRecord,
|
||||||
|
PNTFS_ATTR_RECORD AttributeAddress,
|
||||||
|
PDEVICE_EXTENSION DeviceExt,
|
||||||
|
PFILE_OBJECT FileObject);
|
||||||
|
|
||||||
|
NTSTATUS
|
||||||
|
AddStandardInformation(PFILE_RECORD_HEADER FileRecord,
|
||||||
|
PNTFS_ATTR_RECORD AttributeAddress);
|
||||||
|
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
ConvertDataRunsToLargeMCB(PUCHAR DataRun,
|
ConvertDataRunsToLargeMCB(PUCHAR DataRun,
|
||||||
PLARGE_MCB DataRunsMCB,
|
PLARGE_MCB DataRunsMCB,
|
||||||
|
@ -647,6 +661,9 @@ NtfsClose(PNTFS_IRP_CONTEXT IrpContext);
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
NtfsCreate(PNTFS_IRP_CONTEXT IrpContext);
|
NtfsCreate(PNTFS_IRP_CONTEXT IrpContext);
|
||||||
|
|
||||||
|
NTSTATUS
|
||||||
|
NtfsCreateFileRecord(PDEVICE_EXTENSION DeviceExt,
|
||||||
|
PFILE_OBJECT FileObject);
|
||||||
|
|
||||||
/* devctl.c */
|
/* devctl.c */
|
||||||
|
|
||||||
|
@ -786,6 +803,10 @@ NtfsFileSystemControl(PNTFS_IRP_CONTEXT IrpContext);
|
||||||
|
|
||||||
|
|
||||||
/* mft.c */
|
/* mft.c */
|
||||||
|
NTSTATUS
|
||||||
|
AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
|
||||||
|
PDEVICE_EXTENSION DeviceExt);
|
||||||
|
|
||||||
PNTFS_ATTR_CONTEXT
|
PNTFS_ATTR_CONTEXT
|
||||||
PrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord);
|
PrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord);
|
||||||
|
|
||||||
|
@ -818,6 +839,11 @@ SetAttributeDataLength(PFILE_OBJECT FileObject,
|
||||||
PFILE_RECORD_HEADER FileRecord,
|
PFILE_RECORD_HEADER FileRecord,
|
||||||
PLARGE_INTEGER DataSize);
|
PLARGE_INTEGER DataSize);
|
||||||
|
|
||||||
|
VOID
|
||||||
|
SetFileRecordEnd(PFILE_RECORD_HEADER FileRecord,
|
||||||
|
PNTFS_ATTR_RECORD AttrEnd,
|
||||||
|
ULONG EndMarker);
|
||||||
|
|
||||||
ULONGLONG
|
ULONGLONG
|
||||||
AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord);
|
AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord);
|
||||||
|
|
||||||
|
@ -855,8 +881,8 @@ UpdateFileNameRecord(PDEVICE_EXTENSION Vcb,
|
||||||
|
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
UpdateFileRecord(PDEVICE_EXTENSION Vcb,
|
UpdateFileRecord(PDEVICE_EXTENSION Vcb,
|
||||||
ULONGLONG index,
|
ULONGLONG MftIndex,
|
||||||
PFILE_RECORD_HEADER file);
|
PFILE_RECORD_HEADER FileRecord);
|
||||||
|
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
FindAttribute(PDEVICE_EXTENSION Vcb,
|
FindAttribute(PDEVICE_EXTENSION Vcb,
|
||||||
|
@ -919,6 +945,14 @@ NtfsFindFileAt(PDEVICE_EXTENSION Vcb,
|
||||||
PULONGLONG MFTIndex,
|
PULONGLONG MFTIndex,
|
||||||
ULONGLONG CurrentMFTIndex);
|
ULONGLONG CurrentMFTIndex);
|
||||||
|
|
||||||
|
NTSTATUS
|
||||||
|
NtfsFindMftRecord(PDEVICE_EXTENSION Vcb,
|
||||||
|
ULONGLONG MFTIndex,
|
||||||
|
PUNICODE_STRING FileName,
|
||||||
|
PULONG FirstEntry,
|
||||||
|
BOOLEAN DirSearch,
|
||||||
|
ULONGLONG *OutMFTIndex);
|
||||||
|
|
||||||
/* misc.c */
|
/* misc.c */
|
||||||
|
|
||||||
BOOLEAN
|
BOOLEAN
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue