https://git.reactos.org/?p=reactos.git;a=commitdiff;h=7f762aac01103bffbbdd2…
commit 7f762aac01103bffbbdd23807cef9731fdcf9936
Author: Trevor Thompson <tmt256(a)email.vccs.edu>
AuthorDate: Sat May 27 03:20:31 2017 +0000
[NTFS] - Add support for changing a file's size via SetEndOfFile():
-Handle IRP_MJ_SET_INFORMATION IRP requests.
+NtfsSetEndOfFile() - Sets the end of file (file size) for a given file.
+NtfsSetInformation() - Sets the specified file information. At this point, only FileEndOfFileInformation is fully implemented. FileAllocationInformation is handled the same way and not truly implemented, but this works well enough for SetEndOfFile().
Overwriting a file in NTFS should now work in the majority of use cases.
svn path=/branches/GSoC_2016/NTFS/; revision=74675
---
drivers/filesystems/ntfs/dispatch.c | 4 +
drivers/filesystems/ntfs/finfo.c | 244 ++++++++++++++++++++++++++++++++++++
drivers/filesystems/ntfs/ntfs.c | 1 +
drivers/filesystems/ntfs/ntfs.h | 9 ++
4 files changed, 258 insertions(+)
diff --git a/drivers/filesystems/ntfs/dispatch.c b/drivers/filesystems/ntfs/dispatch.c
index 09140f2f32..bb67de73aa 100644
--- a/drivers/filesystems/ntfs/dispatch.c
+++ b/drivers/filesystems/ntfs/dispatch.c
@@ -81,6 +81,10 @@ NtfsDispatch(PNTFS_IRP_CONTEXT IrpContext)
Status = NtfsQueryInformation(IrpContext);
break;
+ case IRP_MJ_SET_INFORMATION:
+ Status = NtfsSetInformation(IrpContext);
+ break;
+
case IRP_MJ_DIRECTORY_CONTROL:
Status = NtfsDirectoryControl(IrpContext);
break;
diff --git a/drivers/filesystems/ntfs/finfo.c b/drivers/filesystems/ntfs/finfo.c
index 50604f847d..76cc78f4d9 100644
--- a/drivers/filesystems/ntfs/finfo.c
+++ b/drivers/filesystems/ntfs/finfo.c
@@ -396,4 +396,248 @@ NtfsQueryInformation(PNTFS_IRP_CONTEXT IrpContext)
return Status;
}
+/**
+* @name NtfsSetEndOfFile
+* @implemented
+*
+* Sets the end of file (file size) for a given file.
+*
+* @param Fcb
+* Pointer to an NTFS_FCB which describes the target file. Fcb->MainResource should have been
+* acquired with ExAcquireResourceSharedLite().
+*
+* @param FileObject
+* Pointer to a FILE_OBJECT describing the target file.
+*
+* @param DeviceExt
+* Points to the target disk's DEVICE_EXTENSION
+*
+* @param IrpFlags
+* ULONG describing the flags of the original IRP request (Irp->Flags).
+*
+* @param NewFileSize
+* Pointer to a LARGE_INTEGER which indicates the new end of file (file size).
+*
+* @return
+* STATUS_SUCCESS if successful,
+* STATUS_USER_MAPPED_FILE if trying to truncate a file but MmCanFileBeTruncated() returned false,
+* STATUS_OBJECT_NAME_NOT_FOUND if there was no $DATA attribute associated with the target file,
+* STATUS_INVALID_PARAMETER if there was no $FILENAME attribute associated with the target file,
+* STATUS_INSUFFICIENT_RESOURCES if an allocation failed,
+* STATUS_ACCESS_DENIED if target file is a volume or if paging is involved.
+*
+* @remarks As this function sets the size of a file at the file-level
+* (and not at the attribute level) it's not recommended to use this
+* function alongside functions that operate on the data attribute directly.
+*
+*/
+NTSTATUS
+NtfsSetEndOfFile(PNTFS_FCB Fcb,
+ PFILE_OBJECT FileObject,
+ PDEVICE_EXTENSION DeviceExt,
+ ULONG IrpFlags,
+ PLARGE_INTEGER NewFileSize)
+{
+ LARGE_INTEGER CurrentFileSize;
+ PFILE_RECORD_HEADER FileRecord;
+ PNTFS_ATTR_CONTEXT DataContext;
+ ULONG AttributeOffset;
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONGLONG AllocationSize;
+ PFILENAME_ATTRIBUTE fileNameAttribute;
+ ULONGLONG ParentMFTId;
+ UNICODE_STRING filename;
+
+
+ // Allocate non-paged memory for the file record
+ FileRecord = ExAllocatePoolWithTag(NonPagedPool, DeviceExt->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
+ if (FileRecord == NULL)
+ {
+ DPRINT1("Couldn't allocate memory for file record!");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ // read the file record
+ DPRINT("Reading file record...\n");
+ Status = ReadFileRecord(DeviceExt, Fcb->MFTIndex, FileRecord);
+ if (!NT_SUCCESS(Status))
+ {
+ // We couldn't get the file's record. Free the memory and return the error
+ DPRINT1("Can't find record for %wS!\n", Fcb->ObjectName);
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+ return Status;
+ }
+
+ DPRINT("Found record for %wS\n", Fcb->ObjectName);
+
+ CurrentFileSize.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, (PULONGLONG)&CurrentFileSize);
+
+ // Are we trying to decrease the file size?
+ if (NewFileSize->QuadPart < CurrentFileSize.QuadPart)
+ {
+ // Is the file mapped?
+ if (!MmCanFileBeTruncated(FileObject->SectionObjectPointer,
+ NewFileSize))
+ {
+ DPRINT1("Couldn't decrease file size!\n");
+ return STATUS_USER_MAPPED_FILE;
+ }
+ }
+
+ // Find the attribute with the data stream for our file
+ DPRINT("Finding Data Attribute...\n");
+ Status = FindAttribute(DeviceExt,
+ FileRecord,
+ AttributeData,
+ Fcb->Stream,
+ wcslen(Fcb->Stream),
+ &DataContext,
+ &AttributeOffset);
+
+ // Did we fail to find the attribute?
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("No '%S' data stream associated with file!\n", Fcb->Stream);
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+ return Status;
+ }
+
+ // Get the size of the data attribute
+ CurrentFileSize.QuadPart = AttributeDataLength(&DataContext->Record);
+
+ // Are we enlarging the attribute?
+ if (NewFileSize->QuadPart > CurrentFileSize.QuadPart)
+ {
+ // is increasing the stream size not allowed?
+ if ((Fcb->Flags & FCB_IS_VOLUME) ||
+ (IrpFlags & IRP_PAGING_IO))
+ {
+ // TODO - just fail for now
+ ReleaseAttributeContext(DataContext);
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+ return STATUS_ACCESS_DENIED;
+ }
+ }
+
+ // set the attribute data length
+ Status = SetAttributeDataLength(FileObject, Fcb, DataContext, AttributeOffset, FileRecord, NewFileSize);
+ if (!NT_SUCCESS(Status))
+ {
+ ReleaseAttributeContext(DataContext);
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+ return Status;
+ }
+
+ // now we need to update this file's size in every directory index entry that references it
+ // TODO: expand to work with every filename / hardlink stored in the file record.
+ fileNameAttribute = GetBestFileNameFromRecord(Fcb->Vcb, FileRecord);
+ if (fileNameAttribute == NULL)
+ {
+ DPRINT1("Unable to find FileName attribute associated with file!\n");
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ ParentMFTId = fileNameAttribute->DirectoryFileReferenceNumber & NTFS_MFT_MASK;
+
+ filename.Buffer = fileNameAttribute->Name;
+ filename.Length = fileNameAttribute->NameLength * sizeof(WCHAR);
+ filename.MaximumLength = filename.Length;
+
+ AllocationSize = ROUND_UP(NewFileSize->QuadPart, Fcb->Vcb->NtfsInfo.BytesPerCluster);
+
+ Status = UpdateFileNameRecord(Fcb->Vcb, ParentMFTId, &filename, FALSE, NewFileSize->QuadPart, AllocationSize);
+
+ ReleaseAttributeContext(DataContext);
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+
+ return Status;
+}
+
+/**
+* @name NtfsSetInformation
+* @implemented
+*
+* Sets the specified file information.
+*
+* @param IrpContext
+* Points to an NTFS_IRP_CONTEXT which describes the set operation
+*
+* @return
+* STATUS_SUCCESS if successful,
+* STATUS_NOT_IMPLEMENTED if trying to set an unimplemented information class,
+* STATUS_USER_MAPPED_FILE if trying to truncate a file but MmCanFileBeTruncated() returned false,
+* STATUS_OBJECT_NAME_NOT_FOUND if there was no $DATA attribute associated with the target file,
+* STATUS_INVALID_PARAMETER if there was no $FILENAME attribute associated with the target file,
+* STATUS_INSUFFICIENT_RESOURCES if an allocation failed,
+* STATUS_ACCESS_DENIED if target file is a volume or if paging is involved.
+*
+* @remarks Called by NtfsDispatch() in response to an IRP_MJ_SET_INFORMATION request.
+* Only the FileEndOfFileInformation InformationClass is fully implemented. FileAllocationInformation
+* is a hack and not a true implementation, but it's enough to make SetEndOfFile() work.
+* All other information classes are TODO.
+*
+*/
+NTSTATUS
+NtfsSetInformation(PNTFS_IRP_CONTEXT IrpContext)
+{
+ FILE_INFORMATION_CLASS FileInformationClass;
+ PIO_STACK_LOCATION Stack;
+ PDEVICE_EXTENSION DeviceExt;
+ PFILE_OBJECT FileObject;
+ PNTFS_FCB Fcb;
+ PVOID SystemBuffer;
+ ULONG BufferLength;
+ PIRP Irp;
+ PDEVICE_OBJECT DeviceObject;
+ NTSTATUS Status = STATUS_NOT_IMPLEMENTED;
+
+ DPRINT1("NtfsSetInformation(%p)\n", IrpContext);
+
+ Irp = IrpContext->Irp;
+ Stack = IrpContext->Stack;
+ DeviceObject = IrpContext->DeviceObject;
+ DeviceExt = DeviceObject->DeviceExtension;
+ FileInformationClass = Stack->Parameters.QueryFile.FileInformationClass;
+ FileObject = IrpContext->FileObject;
+ Fcb = FileObject->FsContext;
+
+ SystemBuffer = Irp->AssociatedIrp.SystemBuffer;
+ BufferLength = Stack->Parameters.QueryFile.Length;
+
+ if (!ExAcquireResourceSharedLite(&Fcb->MainResource,
+ BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
+ {
+ return NtfsMarkIrpContextForQueue(IrpContext);
+ }
+
+ switch (FileInformationClass)
+ {
+ PFILE_END_OF_FILE_INFORMATION EndOfFileInfo;
+
+ /* TODO: Allocation size is not actually the same as file end for NTFS,
+ however, few applications are likely to make the distinction. */
+ case FileAllocationInformation:
+ DPRINT1("FIXME: Using hacky method of setting FileAllocationInformation.\n");
+ case FileEndOfFileInformation:
+ EndOfFileInfo = (PFILE_END_OF_FILE_INFORMATION)SystemBuffer;
+ Status = NtfsSetEndOfFile(Fcb, FileObject, DeviceExt, Irp->Flags, &EndOfFileInfo->EndOfFile);
+ break;
+
+ // TODO: all other information classes
+
+ default:
+ DPRINT1("FIXME: Unimplemented information class %u\n", FileInformationClass);
+ Status = STATUS_NOT_IMPLEMENTED;
+ }
+
+ ExReleaseResourceLite(&Fcb->MainResource);
+
+ if (NT_SUCCESS(Status))
+ Irp->IoStatus.Information =
+ Stack->Parameters.QueryFile.Length - BufferLength;
+ else
+ Irp->IoStatus.Information = 0;
+
+ return Status;
+}
/* EOF */
diff --git a/drivers/filesystems/ntfs/ntfs.c b/drivers/filesystems/ntfs/ntfs.c
index 94ba4f8f6a..de36a65328 100644
--- a/drivers/filesystems/ntfs/ntfs.c
+++ b/drivers/filesystems/ntfs/ntfs.c
@@ -139,6 +139,7 @@ NtfsInitializeFunctionPointers(PDRIVER_OBJECT DriverObject)
DriverObject->MajorFunction[IRP_MJ_READ] = NtfsFsdDispatch;
DriverObject->MajorFunction[IRP_MJ_WRITE] = NtfsFsdDispatch;
DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] = NtfsFsdDispatch;
+ DriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = NtfsFsdDispatch;
DriverObject->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION] = NtfsFsdDispatch;
DriverObject->MajorFunction[IRP_MJ_SET_VOLUME_INFORMATION] = NtfsFsdDispatch;
DriverObject->MajorFunction[IRP_MJ_DIRECTORY_CONTROL] = NtfsFsdDispatch;
diff --git a/drivers/filesystems/ntfs/ntfs.h b/drivers/filesystems/ntfs/ntfs.h
index 462980d45d..68259177ee 100644
--- a/drivers/filesystems/ntfs/ntfs.h
+++ b/drivers/filesystems/ntfs/ntfs.h
@@ -796,6 +796,15 @@ NtfsMakeFCBFromDirEntry(PNTFS_VCB Vcb,
NTSTATUS
NtfsQueryInformation(PNTFS_IRP_CONTEXT IrpContext);
+NTSTATUS
+NtfsSetEndOfFile(PNTFS_FCB Fcb,
+ PFILE_OBJECT FileObject,
+ PDEVICE_EXTENSION DeviceExt,
+ ULONG IrpFlags,
+ PLARGE_INTEGER NewFileSize);
+
+NTSTATUS
+NtfsSetInformation(PNTFS_IRP_CONTEXT IrpContext);
/* fsctl.c */
https://git.reactos.org/?p=reactos.git;a=commitdiff;h=0409b3161e8789a4c9aa6…
commit 0409b3161e8789a4c9aa606616626e1237427766
Author: Trevor Thompson <tmt256(a)email.vccs.edu>
AuthorDate: Sun Apr 16 00:17:07 2017 +0000
[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
---
drivers/filesystems/ntfs/attrib.c | 242 ++++++++++++++++++++++++++++++++++++++
drivers/filesystems/ntfs/create.c | 96 ++++++++++++++-
drivers/filesystems/ntfs/mft.c | 189 ++++++++++++++++++++++++++---
drivers/filesystems/ntfs/ntfs.h | 38 +++++-
4 files changed, 545 insertions(+), 20 deletions(-)
diff --git a/drivers/filesystems/ntfs/attrib.c b/drivers/filesystems/ntfs/attrib.c
index b65a6b8b50..2a70e8c3c8 100644
--- a/drivers/filesystems/ntfs/attrib.c
+++ b/drivers/filesystems/ntfs/attrib.c
@@ -35,6 +35,185 @@
/* 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
* @implemented
@@ -192,6 +371,69 @@ AddRun(PNTFS_VCB Vcb,
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
* @implemented
diff --git a/drivers/filesystems/ntfs/create.c b/drivers/filesystems/ntfs/create.c
index 344c16464f..30312d6104 100644
--- a/drivers/filesystems/ntfs/create.c
+++ b/drivers/filesystems/ntfs/create.c
@@ -545,8 +545,15 @@ NtfsCreateFile(PDEVICE_OBJECT DeviceObject,
RequestedDisposition == FILE_OPEN_IF ||
RequestedDisposition == FILE_OVERWRITE_IF ||
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;
}
}
@@ -599,4 +606,89 @@ NtfsCreate(PNTFS_IRP_CONTEXT IrpContext)
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 */
diff --git a/drivers/filesystems/ntfs/mft.c b/drivers/filesystems/ntfs/mft.c
index 30ef5f0e3e..8b588d760b 100644
--- a/drivers/filesystems/ntfs/mft.c
+++ b/drivers/filesystems/ntfs/mft.c
@@ -172,7 +172,7 @@ AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord)
return AttrRecord->Resident.ValueLength;
}
-void
+VOID
InternalSetResidentAttributeLength(PNTFS_ATTR_CONTEXT AttrContext,
PFILE_RECORD_HEADER FileRecord,
ULONG AttrOffset,
@@ -201,14 +201,9 @@ InternalSetResidentAttributeLength(PNTFS_ATTR_CONTEXT AttrContext,
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->Type = AttributeEnd;
-
- // 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);
+ SetFileRecordEnd(FileRecord, Destination, FILE_RECORD_END);
}
/**
@@ -359,6 +354,41 @@ SetAttributeDataLength(PFILE_OBJECT FileObject,
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
ReadAttribute(PDEVICE_EXTENSION Vcb,
PNTFS_ATTR_CONTEXT Context,
@@ -711,7 +741,9 @@ WriteAttribute(PDEVICE_EXTENSION Vcb,
{
// We reached the last assigned cluster
// 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;
}
@@ -1070,25 +1102,39 @@ UpdateIndexEntryFileNameSize(PDEVICE_EXTENSION Vcb,
}
/**
-* UpdateFileRecord
+* @name UpdateFileRecord
* @implemented
+*
* 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
UpdateFileRecord(PDEVICE_EXTENSION Vcb,
- ULONGLONG index,
- PFILE_RECORD_HEADER file)
+ ULONGLONG MftIndex,
+ PFILE_RECORD_HEADER FileRecord)
{
ULONG BytesWritten;
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
- AddFixupArray(Vcb, &file->Ntfs);
+ AddFixupArray(Vcb, &FileRecord->Ntfs);
// 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))
{
@@ -1096,7 +1142,7 @@ UpdateFileRecord(PDEVICE_EXTENSION Vcb,
}
// remove the fixup array (so the file record pointer can still be used)
- FixupUpdateSequenceArray(Vcb, &file->Ntfs);
+ FixupUpdateSequenceArray(Vcb, &FileRecord->Ntfs);
return Status;
}
@@ -1133,6 +1179,117 @@ FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb,
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
AddFixupArray(PDEVICE_EXTENSION Vcb,
PNTFS_RECORD_HEADER Record)
diff --git a/drivers/filesystems/ntfs/ntfs.h b/drivers/filesystems/ntfs/ntfs.h
index ce8a7df2de..5a36bcc9fe 100644
--- a/drivers/filesystems/ntfs/ntfs.h
+++ b/drivers/filesystems/ntfs/ntfs.h
@@ -516,6 +516,10 @@ NtfsMarkIrpContextForQueue(PNTFS_IRP_CONTEXT IrpContext)
//VOID
//NtfsDumpAttribute(PATTRIBUTE Attribute);
+NTSTATUS
+AddData(PFILE_RECORD_HEADER FileRecord,
+ PNTFS_ATTR_RECORD AttributeAddress);
+
NTSTATUS
AddRun(PNTFS_VCB Vcb,
PNTFS_ATTR_CONTEXT AttrContext,
@@ -524,6 +528,16 @@ AddRun(PNTFS_VCB Vcb,
ULONGLONG NextAssignedCluster,
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
ConvertDataRunsToLargeMCB(PUCHAR DataRun,
PLARGE_MCB DataRunsMCB,
@@ -647,6 +661,9 @@ NtfsClose(PNTFS_IRP_CONTEXT IrpContext);
NTSTATUS
NtfsCreate(PNTFS_IRP_CONTEXT IrpContext);
+NTSTATUS
+NtfsCreateFileRecord(PDEVICE_EXTENSION DeviceExt,
+ PFILE_OBJECT FileObject);
/* devctl.c */
@@ -786,6 +803,10 @@ NtfsFileSystemControl(PNTFS_IRP_CONTEXT IrpContext);
/* mft.c */
+NTSTATUS
+AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
+ PDEVICE_EXTENSION DeviceExt);
+
PNTFS_ATTR_CONTEXT
PrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord);
@@ -818,6 +839,11 @@ SetAttributeDataLength(PFILE_OBJECT FileObject,
PFILE_RECORD_HEADER FileRecord,
PLARGE_INTEGER DataSize);
+VOID
+SetFileRecordEnd(PFILE_RECORD_HEADER FileRecord,
+ PNTFS_ATTR_RECORD AttrEnd,
+ ULONG EndMarker);
+
ULONGLONG
AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord);
@@ -855,8 +881,8 @@ UpdateFileNameRecord(PDEVICE_EXTENSION Vcb,
NTSTATUS
UpdateFileRecord(PDEVICE_EXTENSION Vcb,
- ULONGLONG index,
- PFILE_RECORD_HEADER file);
+ ULONGLONG MftIndex,
+ PFILE_RECORD_HEADER FileRecord);
NTSTATUS
FindAttribute(PDEVICE_EXTENSION Vcb,
@@ -919,6 +945,14 @@ NtfsFindFileAt(PDEVICE_EXTENSION Vcb,
PULONGLONG MFTIndex,
ULONGLONG CurrentMFTIndex);
+NTSTATUS
+NtfsFindMftRecord(PDEVICE_EXTENSION Vcb,
+ ULONGLONG MFTIndex,
+ PUNICODE_STRING FileName,
+ PULONG FirstEntry,
+ BOOLEAN DirSearch,
+ ULONGLONG *OutMFTIndex);
+
/* misc.c */
BOOLEAN
https://git.reactos.org/?p=reactos.git;a=commitdiff;h=7643831d569d693c73858…
commit 7643831d569d693c73858bfdd0f9d63cc6d1b6af
Author: Trevor Thompson <tmt256(a)email.vccs.edu>
AuthorDate: Mon Aug 22 11:08:22 2016 +0000
[NTFS]
+NtfsDumpFileRecord() - Provides diagnostic information about a file record.
svn path=/branches/GSoC_2016/NTFS/; revision=72424
---
drivers/filesystems/ntfs/mft.c | 40 ++++++++++++++++++++++++++++++++++++++++
drivers/filesystems/ntfs/ntfs.h | 4 ++++
2 files changed, 44 insertions(+)
diff --git a/drivers/filesystems/ntfs/mft.c b/drivers/filesystems/ntfs/mft.c
index 4b0ded49f3..30ef5f0e3e 100644
--- a/drivers/filesystems/ntfs/mft.c
+++ b/drivers/filesystems/ntfs/mft.c
@@ -1460,6 +1460,46 @@ NtfsLookupFile(PDEVICE_EXTENSION Vcb,
return NtfsLookupFileAt(Vcb, PathName, FileRecord, MFTIndex, NTFS_FILE_ROOT);
}
+/**
+* @name NtfsDumpFileRecord
+* @implemented
+*
+* Provides diagnostic information about a file record. Prints a hex dump
+* of the entire record (based on the size reported by FileRecord->ByesInUse),
+* then prints a dump of each attribute.
+*
+* @param Vcb
+* Pointer to a DEVICE_EXTENSION describing the volume.
+*
+* @param FileRecord
+* Pointer to the file record to be analyzed.
+*
+* @remarks
+* FileRecord must be a complete file record at least FileRecord->BytesAllocated
+* in size, and not just the header.
+*
+*/
+VOID
+NtfsDumpFileRecord(PDEVICE_EXTENSION Vcb,
+ PFILE_RECORD_HEADER FileRecord)
+{
+ ULONG i, j;
+
+ // dump binary data, 8 bytes at a time
+ for (i = 0; i < FileRecord->BytesInUse; i += 8)
+ {
+ // display current offset, in hex
+ DbgPrint("\t%03x\t", i);
+
+ // display hex value of each of the next 8 bytes
+ for (j = 0; j < 8; j++)
+ DbgPrint("%02x ", *(PUCHAR)((ULONG_PTR)FileRecord + i + j));
+ DbgPrint("\n");
+ }
+
+ NtfsDumpFileAttributes(Vcb, FileRecord);
+}
+
NTSTATUS
NtfsFindFileAt(PDEVICE_EXTENSION Vcb,
PUNICODE_STRING SearchPattern,
diff --git a/drivers/filesystems/ntfs/ntfs.h b/drivers/filesystems/ntfs/ntfs.h
index 29a6c2af00..ce8a7df2de 100644
--- a/drivers/filesystems/ntfs/ntfs.h
+++ b/drivers/filesystems/ntfs/ntfs.h
@@ -907,6 +907,10 @@ NtfsLookupFileAt(PDEVICE_EXTENSION Vcb,
PULONGLONG MFTIndex,
ULONGLONG CurrentMFTIndex);
+VOID
+NtfsDumpFileRecord(PDEVICE_EXTENSION Vcb,
+ PFILE_RECORD_HEADER FileRecord);
+
NTSTATUS
NtfsFindFileAt(PDEVICE_EXTENSION Vcb,
PUNICODE_STRING SearchPattern,