DPRINT1("NtfsOpenFileById(%p, %p, %I64x, %p)\n", DeviceExt, FileObject, MftId, FoundFCB);
- ASSERT(MftId < 0x10);
+ ASSERT(MftId < NTFS_FILE_FIRST_USER_FILE);
if (MftId > 0xb) /* No entries are used yet beyond this */
{
return STATUS_OBJECT_NAME_NOT_FOUND;
NtfsOpenFile(PDEVICE_EXTENSION DeviceExt,
PFILE_OBJECT FileObject,
PWSTR FileName,
+ BOOLEAN CaseSensitive,
PNTFS_FCB * FoundFCB)
{
PNTFS_FCB ParentFcb;
NTSTATUS Status;
PWSTR AbsFileName = NULL;
- DPRINT1("NtfsOpenFile(%p, %p, %S, %p)\n", DeviceExt, FileObject, FileName, FoundFCB);
+ DPRINT1("NtfsOpenFile(%p, %p, %S, %s, %p)\n",
+ DeviceExt,
+ FileObject,
+ FileName,
+ CaseSensitive ? "TRUE" : "FALSE",
+ FoundFCB);
*FoundFCB = NULL;
Status = NtfsGetFCBForFile(DeviceExt,
&ParentFcb,
&Fcb,
- FileName);
+ FileName,
+ CaseSensitive);
if (ParentFcb != NULL)
{
NtfsReleaseFCB(DeviceExt,
ParentFcb);
}
- if (!NT_SUCCESS (Status))
+ if (!NT_SUCCESS(Status))
{
DPRINT("Could not make a new FCB, status: %x\n", Status);
if (AbsFileName)
- ExFreePool(AbsFileName);
+ ExFreePoolWithTag(AbsFileName, TAG_NTFS);
return Status;
}
static
NTSTATUS
NtfsCreateFile(PDEVICE_OBJECT DeviceObject,
- PIRP Irp)
+ PNTFS_IRP_CONTEXT IrpContext)
{
PDEVICE_EXTENSION DeviceExt;
PIO_STACK_LOCATION Stack;
// PWSTR FileName;
NTSTATUS Status;
UNICODE_STRING FullPath;
+ PIRP Irp = IrpContext->Irp;
- DPRINT1("NtfsCreateFile(%p, %p) called\n", DeviceObject, Irp);
+ DPRINT1("NtfsCreateFile(%p, %p) called\n", DeviceObject, IrpContext);
DeviceExt = DeviceObject->DeviceExtension;
ASSERT(DeviceExt);
- Stack = IoGetCurrentIrpStackLocation (Irp);
+ Stack = IoGetCurrentIrpStackLocation(Irp);
ASSERT(Stack);
RequestedDisposition = ((Stack->Parameters.Create.Options >> 24) & 0xff);
return STATUS_INVALID_PARAMETER;
MFTId = (*(PULONGLONG)FileObject->FileName.Buffer) & NTFS_MFT_MASK;
- if (MFTId < 0x10)
+ if (MFTId < NTFS_FILE_FIRST_USER_FILE)
{
Status = NtfsOpenFileById(DeviceExt, FileObject, MFTId, &Fcb);
}
Status = NtfsOpenFile(DeviceExt,
FileObject,
((RequestedOptions & FILE_OPEN_BY_FILE_ID) ? FullPath.Buffer : FileObject->FileName.Buffer),
+ BooleanFlagOn(Stack->Flags, SL_CASE_SENSITIVE),
&Fcb);
if (RequestedOptions & FILE_OPEN_BY_FILE_ID)
DoneOverwriting:
if (fileRecord)
- ExFreePool(fileRecord);
+ ExFreePoolWithTag(fileRecord, TAG_NTFS);
if (dataContext)
ReleaseAttributeContext(dataContext);
return STATUS_ACCESS_DENIED;
}
- // Create the file record on disk
- Status = NtfsCreateFileRecord(DeviceExt, FileObject);
+ // Was the user trying to create a directory?
+ if (RequestedOptions & FILE_DIRECTORY_FILE)
+ {
+ // Create the directory on disk
+ Status = NtfsCreateDirectory(DeviceExt,
+ FileObject,
+ BooleanFlagOn(Stack->Flags, SL_CASE_SENSITIVE),
+ BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT));
+ }
+ else
+ {
+ // Create the file record on disk
+ Status = NtfsCreateFileRecord(DeviceExt,
+ FileObject,
+ BooleanFlagOn(Stack->Flags, SL_CASE_SENSITIVE),
+ BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT));
+ }
- // Update the parent directory index
- // Still TODO
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("ERROR: Couldn't create file record!\n");
+ return Status;
+ }
- // Call NtfsOpenFile()
+ // Before we open the file/directory we just created, we need to change the disposition (upper 8 bits of ULONG)
+ // from create to open, since we already created the file
+ Stack->Parameters.Create.Options = (ULONG)FILE_OPEN << 24 | RequestedOptions;
- return STATUS_CANNOT_MAKE;
+ // Now we should be able to open the file using NtfsCreateFile()
+ Status = NtfsCreateFile(DeviceObject, IrpContext);
+ if (NT_SUCCESS(Status))
+ {
+ // We need to change Irp->IoStatus.Information to reflect creation
+ Irp->IoStatus.Information = FILE_CREATED;
+ }
+ return Status;
}
}
ExAcquireResourceExclusiveLite(&DeviceExt->DirResource,
TRUE);
Status = NtfsCreateFile(DeviceObject,
- IrpContext->Irp);
+ IrpContext);
ExReleaseResourceLite(&DeviceExt->DirResource);
return Status;
}
/**
-* @name NtfsCreateFileRecord()
+* @name NtfsCreateDirectory()
* @implemented
*
-* Creates a file record and saves it to the MFT.
+* Creates a file record for a new directory and saves it to the MFT. Adds the filename attribute of the
+* created directory to the parent directory's index.
*
* @param DeviceExt
* Points to the target disk's DEVICE_EXTENSION
*
* @param FileObject
-* Pointer to a FILE_OBJECT describing the file to be created
+* Pointer to a FILE_OBJECT describing the directory to be created
+*
+* @param CaseSensitive
+* Boolean indicating if the function should operate in case-sensitive mode. This will be TRUE
+* if an application created the folder with the FILE_FLAG_POSIX_SEMANTICS flag.
+*
+* @param CanWait
+* Boolean indicating if the function is allowed to wait for exclusive access to the master file table.
+* This will only be relevant if the MFT doesn't have any free file records and needs to be enlarged.
*
* @return
-* STATUS_SUCCESS on success.
+* STATUS_SUCCESS on success.
* STATUS_INSUFFICIENT_RESOURCES if unable to allocate memory for the file record.
+* STATUS_CANT_WAIT if CanWait was FALSE and the function needed to resize the MFT but
+* couldn't get immediate, exclusive access to it.
*/
NTSTATUS
-NtfsCreateFileRecord(PDEVICE_EXTENSION DeviceExt,
- PFILE_OBJECT FileObject)
+NtfsCreateDirectory(PDEVICE_EXTENSION DeviceExt,
+ PFILE_OBJECT FileObject,
+ BOOLEAN CaseSensitive,
+ BOOLEAN CanWait)
{
+
NTSTATUS Status = STATUS_SUCCESS;
PFILE_RECORD_HEADER FileRecord;
PNTFS_ATTR_RECORD NextAttribute;
+ PFILENAME_ATTRIBUTE FilenameAttribute;
+ ULONGLONG ParentMftIndex;
+ ULONGLONG FileMftIndex;
+ PB_TREE Tree;
+ PINDEX_ROOT_ATTRIBUTE NewIndexRoot;
+ ULONG MaxIndexRootSize;
+ ULONG RootLength;
+
+ DPRINT1("NtfsCreateFileRecord(%p, %p, %s, %s)\n",
+ DeviceExt,
+ FileObject,
+ CaseSensitive ? "TRUE" : "FALSE",
+ CanWait ? "TRUE" : "FALSE");
+
+ // Start with an empty file record
+ FileRecord = NtfsCreateEmptyFileRecord(DeviceExt);
+ if (!FileRecord)
+ {
+ DPRINT1("ERROR: Unable to allocate memory for file record!\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ // Set the directory flag
+ FileRecord->Flags |= FRH_DIRECTORY;
+
+ // find where the first attribute will be added
+ NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset);
+
+ // 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, CaseSensitive, &ParentMftIndex);
+
+ // save a pointer to the filename attribute
+ FilenameAttribute = (PFILENAME_ATTRIBUTE)((ULONG_PTR)NextAttribute + NextAttribute->Resident.ValueOffset);
+
+ // advance NextAttribute pointer to the next attribute
+ NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length);
+
+ // Create an empty b-tree to represent our new index
+ Status = CreateEmptyBTree(&Tree);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("ERROR: Failed to create empty B-Tree!\n");
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+ return Status;
+ }
+
+ // Calculate maximum size of index root
+ MaxIndexRootSize = DeviceExt->NtfsInfo.BytesPerFileRecord
+ - ((ULONG_PTR)NextAttribute - (ULONG_PTR)FileRecord)
+ - sizeof(ULONG) * 2;
+
+ // Create a new index record from the tree
+ Status = CreateIndexRootFromBTree(DeviceExt,
+ Tree,
+ MaxIndexRootSize,
+ &NewIndexRoot,
+ &RootLength);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("ERROR: Unable to create empty index root!\n");
+ DestroyBTree(Tree);
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+ return Status;
+ }
+
+ // We're done with the B-Tree
+ DestroyBTree(Tree);
+
+ // add the $INDEX_ROOT attribute
+ Status = AddIndexRoot(DeviceExt, FileRecord, NextAttribute, NewIndexRoot, RootLength, L"$I30", 4);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("ERROR: Failed to add index root to new file record!\n");
+ ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+ return Status;
+ }
+
+
+#ifndef NDEBUG
+ NtfsDumpFileRecord(DeviceExt, FileRecord);
+#endif
+
+ // Now that we've built the file record in memory, we need to store it in the MFT.
+ Status = AddNewMftEntry(FileRecord, DeviceExt, &FileMftIndex, CanWait);
+ if (NT_SUCCESS(Status))
+ {
+ // The highest 2 bytes should be the sequence number, unless the parent happens to be root
+ if (FileMftIndex == NTFS_FILE_ROOT)
+ FileMftIndex = FileMftIndex + ((ULONGLONG)NTFS_FILE_ROOT << 48);
+ else
+ FileMftIndex = FileMftIndex + ((ULONGLONG)FileRecord->SequenceNumber << 48);
+
+ DPRINT1("New File Reference: 0x%016I64x\n", FileMftIndex);
+
+ // Add the filename attribute to the filename-index of the parent directory
+ Status = NtfsAddFilenameToDirectory(DeviceExt,
+ ParentMftIndex,
+ FileMftIndex,
+ FilenameAttribute,
+ CaseSensitive);
+ }
+
+ ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+
+ return Status;
+}
+
+/**
+* @name NtfsCreateEmptyFileRecord
+* @implemented
+*
+* Creates a new, empty file record, with no attributes.
+*
+* @param DeviceExt
+* Pointer to the DEVICE_EXTENSION of the target volume the file record will be stored on.
+*
+* @return
+* A pointer to the newly-created FILE_RECORD_HEADER if the function succeeds, NULL otherwise.
+*/
+PFILE_RECORD_HEADER
+NtfsCreateEmptyFileRecord(PDEVICE_EXTENSION DeviceExt)
+{
+ PFILE_RECORD_HEADER FileRecord;
+ PNTFS_ATTR_RECORD NextAttribute;
+
+ DPRINT1("NtfsCreateEmptyFileRecord(%p)\n", DeviceExt);
// allocate memory for file record
FileRecord = ExAllocatePoolWithTag(NonPagedPool,
if (!FileRecord)
{
DPRINT1("ERROR: Unable to allocate memory for file record!\n");
- return STATUS_INSUFFICIENT_RESOURCES;
+ return NULL;
}
RtlZeroMemory(FileRecord, DeviceExt->NtfsInfo.BytesPerFileRecord);
// 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->AttributeOffset = ALIGN_UP_BY(FileRecord->AttributeOffset, ATTR_RECORD_ALIGNMENT);
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);
NextAttribute->Type = AttributeEnd;
NextAttribute->Length = FILE_RECORD_END;
+ return FileRecord;
+}
+
+
+/**
+* @name NtfsCreateFileRecord()
+* @implemented
+*
+* Creates a file record and saves it to the MFT. Adds the filename attribute of the
+* created file to the parent directory's index.
+*
+* @param DeviceExt
+* Points to the target disk's DEVICE_EXTENSION
+*
+* @param FileObject
+* Pointer to a FILE_OBJECT describing the file to be created
+*
+* @param CanWait
+* Boolean indicating if the function is allowed to wait for exclusive access to the master file table.
+* This will only be relevant if the MFT doesn't have any free file records and needs to be enlarged.
+*
+* @return
+* STATUS_SUCCESS on success.
+* STATUS_INSUFFICIENT_RESOURCES if unable to allocate memory for the file record.
+* STATUS_CANT_WAIT if CanWait was FALSE and the function needed to resize the MFT but
+* couldn't get immediate, exclusive access to it.
+*/
+NTSTATUS
+NtfsCreateFileRecord(PDEVICE_EXTENSION DeviceExt,
+ PFILE_OBJECT FileObject,
+ BOOLEAN CaseSensitive,
+ BOOLEAN CanWait)
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PFILE_RECORD_HEADER FileRecord;
+ PNTFS_ATTR_RECORD NextAttribute;
+ PFILENAME_ATTRIBUTE FilenameAttribute;
+ ULONGLONG ParentMftIndex;
+ ULONGLONG FileMftIndex;
+
+ DPRINT1("NtfsCreateFileRecord(%p, %p, %s, %s)\n",
+ DeviceExt,
+ FileObject,
+ CaseSensitive ? "TRUE" : "FALSE",
+ CanWait ? "TRUE" : "FALSE");
+
+ // allocate memory for file record
+ FileRecord = NtfsCreateEmptyFileRecord(DeviceExt);
+ if (!FileRecord)
+ {
+ DPRINT1("ERROR: Unable to allocate memory for file record!\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ // find where the first attribute will be added
+ NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset);
+
// 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);
+ AddFileName(FileRecord, NextAttribute, DeviceExt, FileObject, CaseSensitive, &ParentMftIndex);
+
+ // save a pointer to the filename attribute
+ FilenameAttribute = (PFILENAME_ATTRIBUTE)((ULONG_PTR)NextAttribute + NextAttribute->Resident.ValueOffset);
// 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);
+#ifndef NDEBUG
// dump file record in memory (for debugging)
NtfsDumpFileRecord(DeviceExt, FileRecord);
+#endif
// Now that we've built the file record in memory, we need to store it in the MFT.
- Status = AddNewMftEntry(FileRecord, DeviceExt);
+ Status = AddNewMftEntry(FileRecord, DeviceExt, &FileMftIndex, CanWait);
+ if (NT_SUCCESS(Status))
+ {
+ // The highest 2 bytes should be the sequence number, unless the parent happens to be root
+ if (FileMftIndex == NTFS_FILE_ROOT)
+ FileMftIndex = FileMftIndex + ((ULONGLONG)NTFS_FILE_ROOT << 48);
+ else
+ FileMftIndex = FileMftIndex + ((ULONGLONG)FileRecord->SequenceNumber << 48);
+
+ DPRINT1("New File Reference: 0x%016I64x\n", FileMftIndex);
+
+ // Add the filename attribute to the filename-index of the parent directory
+ Status = NtfsAddFilenameToDirectory(DeviceExt,
+ ParentMftIndex,
+ FileMftIndex,
+ FilenameAttribute,
+ CaseSensitive);
+ }
ExFreePoolWithTag(FileRecord, TAG_NTFS);