Author: cfinck
Date: Sun Apr 16 10:36:16 2017
New Revision: 74323
URL: http://svn.reactos.org/svn/reactos?rev=74323&view=rev
Log:
[SPOOLSS]
Add ASSERTs, improve documentation and the variety of tests for AlignRpcPtr/UndoAlignRpcPtr.
Based on comments by Serge Gautherie.
Modified:
trunk/reactos/win32ss/printing/base/spoolss/memory.c
trunk/rostests/apitests/spoolss/AlignRpcPtr.c
Modified: trunk/reactos/win32ss/printing/base/spoolss/memory.c
URL: http://svn.reactos.org/svn/reactos/trunk/reactos/win32ss/printing/base/spoo…
==============================================================================
--- trunk/reactos/win32ss/printing/base/spoolss/memory.c [iso-8859-1] (original)
+++ trunk/reactos/win32ss/printing/base/spoolss/memory.c [iso-8859-1] Sun Apr 16 10:36:16 2017
@@ -28,6 +28,8 @@
PVOID WINAPI
AlignRpcPtr(PVOID pBuffer, PDWORD pcbBuffer)
{
+ ASSERT(pcbBuffer);
+
// Align down the buffer size in pcbBuffer to a 4-byte boundary.
*pcbBuffer -= *pcbBuffer % sizeof(DWORD);
@@ -209,7 +211,7 @@
* The original unaligned buffer, which you input as pBuffer to AlignRpcPtr.
* The data from pSourceBuffer is copied into this buffer before pSourceBuffer is freed.
* If AlignRpcPtr did not allocate a buffer, pDestinationBuffer equals pSourceBuffer and no memory is copied or freed.
- * This parameter may be NULL if pSourceBuffer is NULL.
+ * This parameter may be NULL if pSourceBuffer is NULL or cbBuffer is 0.
*
* @param pSourceBuffer
* The aligned buffer, which is returned by AlignRpcPtr.
@@ -233,6 +235,9 @@
PDWORD WINAPI
UndoAlignRpcPtr(PVOID pDestinationBuffer, PVOID pSourceBuffer, DWORD cbBuffer, PDWORD pcbNeeded)
{
+ // pDestinationBuffer is accessed unless pSourceBuffer equals pDestinationBuffer or cbBuffer is 0.
+ ASSERT(pDestinationBuffer || pSourceBuffer == pDestinationBuffer || cbBuffer == 0);
+
// If pSourceBuffer is given, and source and destination pointers don't match,
// we assume that pSourceBuffer is the buffer allocated by AlignRpcPtr.
if (pSourceBuffer && pSourceBuffer != pDestinationBuffer)
Modified: trunk/rostests/apitests/spoolss/AlignRpcPtr.c
URL: http://svn.reactos.org/svn/reactos/trunk/rostests/apitests/spoolss/AlignRpc…
==============================================================================
--- trunk/rostests/apitests/spoolss/AlignRpcPtr.c [iso-8859-1] (original)
+++ trunk/rostests/apitests/spoolss/AlignRpcPtr.c [iso-8859-1] Sun Apr 16 10:36:16 2017
@@ -54,13 +54,18 @@
ok(pOutputBuffer != pInputBuffer, "pOutputBuffer == pInputBuffer\n");
ok(cbBuffer == 4, "cbBuffer is %lu\n", cbBuffer);
+ // Prove that AlignRpcPtr also works with a NULL buffer. The size should be aligned down.
+ cbBuffer = 6;
+ ok(!AlignRpcPtr(NULL, &cbBuffer), "AlignRpcPtr returns something\n");
+ ok(cbBuffer == 4, "cbBuffer is %lu\n", cbBuffer);
+
// We can also test all parameters of UndoAlignRpcPtr here.
// Because pOutputBuffer != pInputBuffer, it copies the given 4 bytes from (aligned) pOutputBuffer to (unaligned) pInputBuffer
// while aligning up the given 7 bytes in our passed &cbBuffer.
// &cbBuffer is also returned.
strcpy(pOutputBuffer, "abc");
strcpy(pInputBuffer, "XXXXXXXXX");
- cbBuffer = 7;
+ cbBuffer = 5;
pcbBuffer = UndoAlignRpcPtr(pInputBuffer, pOutputBuffer, 4, &cbBuffer);
ok(strcmp(pInputBuffer, "abc") == 0, "pInputBuffer is %s\n", pInputBuffer);
ok(pcbBuffer == &cbBuffer, "pcbBuffer != &cbBuffer\n");
@@ -68,14 +73,14 @@
// Prove that UndoAlignRpcPtr works without any parameters and doesn't try to copy data from NULL pointers.
ok(!UndoAlignRpcPtr(NULL, NULL, 0, NULL), "UndoAlignRpcPtr returns something\n");
- ok(!UndoAlignRpcPtr(NULL, NULL, 4, NULL), "UndoAlignRpcPtr returns something\n");
+ ok(!UndoAlignRpcPtr(NULL, NULL, 6, NULL), "UndoAlignRpcPtr returns something\n");
// Prove that UndoAlignRpcPtr doesn't access source and destination memory at all when they are equal.
// If it did, it should crash here, because I'm giving invalid memory addresses.
ok(!UndoAlignRpcPtr((PVOID)1, (PVOID)1, 4, NULL), "UndoAlignRpcPtr returns something\n");
- // Prove that the pcbNeeded parameter of UndoAlignRpcPtr works independently and aligns up everything up to a DWORD.
- cbBuffer = 0xFFFFFFFF;
+ // Prove that the pcbNeeded parameter of UndoAlignRpcPtr works independently and aligns up to a DWORD.
+ cbBuffer = 0xFFFFFFFD;
pcbBuffer = UndoAlignRpcPtr(NULL, NULL, 0, &cbBuffer);
ok(pcbBuffer == &cbBuffer, "pcbBuffer != &cbBuffer\n");
ok(cbBuffer == 0, "cbBuffer is %lu\n", cbBuffer);
Author: tthompson
Date: Sun Apr 16 00:17:07 2017
New Revision: 74321
URL: http://svn.reactos.org/svn/reactos?rev=74321&view=rev
Log:
[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().
Modified:
branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/attrib.c
branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/create.c
branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/mft.c
branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/ntfs.h
Modified: branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/attrib.c
URL: http://svn.reactos.org/svn/reactos/branches/GSoC_2016/NTFS/drivers/filesyst…
==============================================================================
--- branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/attrib.c [iso-8859-1] (original)
+++ branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/attrib.c [iso-8859-1] Sun Apr 16 00:17:07 2017
@@ -36,6 +36,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
*
@@ -190,6 +369,69 @@
NtfsDumpDataRuns((PUCHAR)((ULONG_PTR)DestinationAttribute + DestinationAttribute->NonResident.MappingPairsOffset), 0);
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;
}
/**
Modified: branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/create.c
URL: http://svn.reactos.org/svn/reactos/branches/GSoC_2016/NTFS/drivers/filesyst…
==============================================================================
--- branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/create.c [iso-8859-1] (original)
+++ branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/create.c [iso-8859-1] Sun Apr 16 00:17:07 2017
@@ -545,8 +545,15 @@
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 @@
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 */
Modified: branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/mft.c
URL: http://svn.reactos.org/svn/reactos/branches/GSoC_2016/NTFS/drivers/filesyst…
==============================================================================
--- branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/mft.c [iso-8859-1] (original)
+++ branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/mft.c [iso-8859-1] Sun Apr 16 00:17:07 2017
@@ -172,7 +172,7 @@
return AttrRecord->Resident.ValueLength;
}
-void
+VOID
InternalSetResidentAttributeLength(PNTFS_ATTR_CONTEXT AttrContext,
PFILE_RECORD_HEADER FileRecord,
ULONG AttrOffset,
@@ -201,14 +201,9 @@
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 @@
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 @@
{
// 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 @@
}
/**
-* 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 @@
}
// remove the fixup array (so the file record pointer can still be used)
- FixupUpdateSequenceArray(Vcb, &file->Ntfs);
+ FixupUpdateSequenceArray(Vcb, &FileRecord->Ntfs);
return Status;
}
@@ -1131,6 +1177,117 @@
}
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
Modified: branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/ntfs.h
URL: http://svn.reactos.org/svn/reactos/branches/GSoC_2016/NTFS/drivers/filesyst…
==============================================================================
--- branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/ntfs.h [iso-8859-1] (original)
+++ branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/ntfs.h [iso-8859-1] Sun Apr 16 00:17:07 2017
@@ -517,12 +517,26 @@
//NtfsDumpAttribute(PATTRIBUTE Attribute);
NTSTATUS
+AddData(PFILE_RECORD_HEADER FileRecord,
+ PNTFS_ATTR_RECORD AttributeAddress);
+
+NTSTATUS
AddRun(PNTFS_VCB Vcb,
PNTFS_ATTR_CONTEXT AttrContext,
ULONG AttrOffset,
PFILE_RECORD_HEADER FileRecord,
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,
@@ -647,6 +661,9 @@
NTSTATUS
NtfsCreate(PNTFS_IRP_CONTEXT IrpContext);
+NTSTATUS
+NtfsCreateFileRecord(PDEVICE_EXTENSION DeviceExt,
+ PFILE_OBJECT FileObject);
/* devctl.c */
@@ -786,6 +803,10 @@
/* mft.c */
+NTSTATUS
+AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
+ PDEVICE_EXTENSION DeviceExt);
+
PNTFS_ATTR_CONTEXT
PrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord);
@@ -817,6 +838,11 @@
ULONG AttrOffset,
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 @@
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 @@
PULONGLONG MFTIndex,
ULONGLONG CurrentMFTIndex);
+NTSTATUS
+NtfsFindMftRecord(PDEVICE_EXTENSION Vcb,
+ ULONGLONG MFTIndex,
+ PUNICODE_STRING FileName,
+ PULONG FirstEntry,
+ BOOLEAN DirSearch,
+ ULONGLONG *OutMFTIndex);
+
/* misc.c */
BOOLEAN