/*
* ReactOS kernel
- * Copyright (C) 2002 ReactOS Team
+ * Copyright (C) 2002, 2014 ReactOS Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* PROJECT: ReactOS kernel
* FILE: drivers/filesystem/ntfs/create.c
* PURPOSE: NTFS filesystem driver
- * PROGRAMMER: Eric Kohl
+ * PROGRAMMERS: Eric Kohl
+ * Pierre Schweitzer (pierre@reactos.org)
*/
/* INCLUDES *****************************************************************/
#define NDEBUG
#include <debug.h>
-/* GLOBALS *****************************************************************/
-
+static PCWSTR MftIdToName[] = {
+ L"$MFT",
+ L"$MFTMirr",
+ L"$LogFile",
+ L"$Volume",
+ L"AttrDef",
+ L".",
+ L"$Bitmap",
+ L"$Boot",
+ L"$BadClus",
+ L"$Quota",
+ L"$UpCase",
+ L"$Extended",
+};
/* FUNCTIONS ****************************************************************/
-static NTSTATUS
+static
+NTSTATUS
NtfsMakeAbsoluteFilename(PFILE_OBJECT pFileObject,
- PWSTR pRelativeFileName,
- PWSTR *pAbsoluteFilename)
+ PWSTR pRelativeFileName,
+ PWSTR *pAbsoluteFilename)
{
- PWSTR rcName;
- PNTFS_FCB Fcb;
+ PWSTR rcName;
+ PNTFS_FCB Fcb;
+
+ DPRINT("try related for %S\n", pRelativeFileName);
+ Fcb = pFileObject->FsContext;
+ ASSERT(Fcb);
- DPRINT("try related for %S\n", pRelativeFileName);
- Fcb = pFileObject->FsContext;
- ASSERT(Fcb);
+ if (Fcb->Flags & FCB_IS_VOLUME)
+ {
+ /* This is likely to be an opening by ID, return ourselves */
+ if (pRelativeFileName[0] == L'\\')
+ {
+ *pAbsoluteFilename = NULL;
+ return STATUS_SUCCESS;
+ }
+
+ return STATUS_INVALID_PARAMETER;
+ }
- /* verify related object is a directory and target name
- don't start with \. */
- if (NtfsFCBIsDirectory(Fcb) == FALSE ||
- pRelativeFileName[0] == L'\\')
+ /* verify related object is a directory and target name
+ don't start with \. */
+ if (NtfsFCBIsDirectory(Fcb) == FALSE ||
+ pRelativeFileName[0] == L'\\')
{
- return(STATUS_INVALID_PARAMETER);
+ return STATUS_INVALID_PARAMETER;
}
- /* construct absolute path name */
- ASSERT(wcslen (Fcb->PathName) + 1 + wcslen (pRelativeFileName) + 1
- <= MAX_PATH);
- rcName = ExAllocatePoolWithTag(NonPagedPool, MAX_PATH * sizeof(WCHAR), TAG_NTFS);
- if (!rcName)
+ /* construct absolute path name */
+ ASSERT(wcslen (Fcb->PathName) + 1 + wcslen (pRelativeFileName) + 1 <= MAX_PATH);
+ rcName = ExAllocatePoolWithTag(NonPagedPool, MAX_PATH * sizeof(WCHAR), TAG_NTFS);
+ if (!rcName)
{
- return(STATUS_INSUFFICIENT_RESOURCES);
+ return STATUS_INSUFFICIENT_RESOURCES;
}
- wcscpy(rcName, Fcb->PathName);
- if (!NtfsFCBIsRoot(Fcb))
- wcscat (rcName, L"\\");
- wcscat (rcName, pRelativeFileName);
- *pAbsoluteFilename = rcName;
+ wcscpy(rcName, Fcb->PathName);
+ if (!NtfsFCBIsRoot(Fcb))
+ wcscat (rcName, L"\\");
+ wcscat (rcName, pRelativeFileName);
+ *pAbsoluteFilename = rcName;
- return(STATUS_SUCCESS);
+ return STATUS_SUCCESS;
}
-static NTSTATUS
-NtfsOpenFile(PDEVICE_EXTENSION DeviceExt,
- PFILE_OBJECT FileObject,
- PWSTR FileName)
-/*
- * FUNCTION: Opens a file
- */
+static
+NTSTATUS
+NtfsMoonWalkID(PDEVICE_EXTENSION DeviceExt,
+ ULONGLONG Id,
+ PUNICODE_STRING OutPath)
{
- PNTFS_FCB ParentFcb;
- PNTFS_FCB Fcb;
- NTSTATUS Status;
- PWSTR AbsFileName = NULL;
+ NTSTATUS Status;
+ PFILE_RECORD_HEADER MftRecord;
+ PFILENAME_ATTRIBUTE FileName;
+ WCHAR FullPath[MAX_PATH];
+ ULONG WritePosition = MAX_PATH - 1;
+
+ DPRINT1("NtfsMoonWalkID(%p, %I64x, %p)\n", DeviceExt, Id, OutPath);
+
+ RtlZeroMemory(FullPath, sizeof(FullPath));
+ MftRecord = ExAllocatePoolWithTag(NonPagedPool,
+ DeviceExt->NtfsInfo.BytesPerFileRecord,
+ TAG_NTFS);
+ if (MftRecord == NULL)
+ {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ while (TRUE)
+ {
+ Status = ReadFileRecord(DeviceExt, Id, MftRecord);
+ if (!NT_SUCCESS(Status))
+ break;
+
+ ASSERT(MftRecord->Ntfs.Type == NRH_FILE_TYPE);
+ if (!(MftRecord->Flags & FRH_IN_USE))
+ {
+ Status = STATUS_OBJECT_PATH_NOT_FOUND;
+ break;
+ }
+
+ FileName = GetBestFileNameFromRecord(DeviceExt, MftRecord);
+ if (FileName == NULL)
+ {
+ DPRINT1("$FILE_NAME attribute not found for %I64x\n", Id);
+ Status = STATUS_OBJECT_PATH_NOT_FOUND;
+ break;
+ }
+
+ WritePosition -= FileName->NameLength;
+ ASSERT(WritePosition < MAX_PATH);
+ RtlCopyMemory(FullPath + WritePosition, FileName->Name, FileName->NameLength * sizeof(WCHAR));
+ WritePosition -= 1;
+ ASSERT(WritePosition < MAX_PATH);
+ FullPath[WritePosition] = L'\\';
+
+ Id = FileName->DirectoryFileReferenceNumber & NTFS_MFT_MASK;
+ if (Id == NTFS_FILE_ROOT)
+ break;
+ }
+
+ ExFreePoolWithTag(MftRecord, TAG_NTFS);
+
+ if (!NT_SUCCESS(Status))
+ return Status;
+
+ OutPath->Length = (MAX_PATH - WritePosition - 1) * sizeof(WCHAR);
+ OutPath->MaximumLength = (MAX_PATH - WritePosition) * sizeof(WCHAR);
+ OutPath->Buffer = ExAllocatePoolWithTag(NonPagedPool, OutPath->MaximumLength, TAG_NTFS);
+ if (OutPath->Buffer == NULL)
+ {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+ RtlCopyMemory(OutPath->Buffer, FullPath + WritePosition, OutPath->MaximumLength);
+
+ return Status;
+}
+
+static
+NTSTATUS
+NtfsOpenFileById(PDEVICE_EXTENSION DeviceExt,
+ PFILE_OBJECT FileObject,
+ ULONGLONG MftId,
+ PNTFS_FCB * FoundFCB)
+{
+ NTSTATUS Status;
+ PNTFS_FCB FCB;
+ PFILE_RECORD_HEADER MftRecord;
+
+ DPRINT1("NtfsOpenFileById(%p, %p, %I64x, %p)\n", DeviceExt, FileObject, MftId, FoundFCB);
- DPRINT("NtfsOpenFile(%p, %p, %S)\n", DeviceExt, FileObject, FileName);
+ ASSERT(MftId < 0x10);
+ if (MftId > 0xb) /* No entries are used yet beyond this */
+ {
+ return STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ MftRecord = ExAllocatePoolWithTag(NonPagedPool,
+ DeviceExt->NtfsInfo.BytesPerFileRecord,
+ TAG_NTFS);
+ if (MftRecord == NULL)
+ {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = ReadFileRecord(DeviceExt, MftId, MftRecord);
+ if (!NT_SUCCESS(Status))
+ {
+ ExFreePoolWithTag(MftRecord, TAG_NTFS);
+ return Status;
+ }
- if (FileObject->RelatedFileObject)
+ if (!(MftRecord->Flags & FRH_IN_USE))
{
- DPRINT("Converting relative filename to absolute filename\n");
+ ExFreePoolWithTag(MftRecord, TAG_NTFS);
+ return STATUS_OBJECT_PATH_NOT_FOUND;
+ }
- Status = NtfsMakeAbsoluteFilename(FileObject->RelatedFileObject,
- FileName,
- &AbsFileName);
- FileName = AbsFileName;
- if (!NT_SUCCESS(Status))
- {
- return(Status);
- }
- return(STATUS_UNSUCCESSFUL);
+ FCB = NtfsGrabFCBFromTable(DeviceExt, MftIdToName[MftId]);
+ if (FCB == NULL)
+ {
+ UNICODE_STRING Name;
+
+ RtlInitUnicodeString(&Name, MftIdToName[MftId]);
+ Status = NtfsMakeFCBFromDirEntry(DeviceExt, NULL, &Name, NULL, MftRecord, MftId, &FCB);
+ if (!NT_SUCCESS(Status))
+ {
+ ExFreePoolWithTag(MftRecord, TAG_NTFS);
+ return Status;
+ }
}
- //FIXME: Get cannonical path name (remove .'s, ..'s and extra separators)
+ ASSERT(FCB != NULL);
+
+ ExFreePoolWithTag(MftRecord, TAG_NTFS);
- DPRINT("PathName to open: %S\n", FileName);
+ Status = NtfsAttachFCBToFileObject(DeviceExt,
+ FCB,
+ FileObject);
+ *FoundFCB = FCB;
- /* try first to find an existing FCB in memory */
- DPRINT("Checking for existing FCB in memory\n");
- Fcb = NtfsGrabFCBFromTable(DeviceExt,
- FileName);
- if (Fcb == NULL)
+ return Status;
+}
+
+/*
+ * FUNCTION: Opens a file
+ */
+static
+NTSTATUS
+NtfsOpenFile(PDEVICE_EXTENSION DeviceExt,
+ PFILE_OBJECT FileObject,
+ PWSTR FileName,
+ BOOLEAN CaseSensitive,
+ PNTFS_FCB * FoundFCB)
+{
+ PNTFS_FCB ParentFcb;
+ PNTFS_FCB Fcb;
+ NTSTATUS Status;
+ PWSTR AbsFileName = NULL;
+
+ DPRINT1("NtfsOpenFile(%p, %p, %S, %s, %p)\n",
+ DeviceExt,
+ FileObject,
+ FileName,
+ CaseSensitive ? "TRUE" : "FALSE",
+ FoundFCB);
+
+ *FoundFCB = NULL;
+
+ if (FileObject->RelatedFileObject)
{
- DPRINT("No existing FCB found, making a new one if file exists.\n");
- Status = NtfsGetFCBForFile(DeviceExt,
- &ParentFcb,
- &Fcb,
- FileName);
- if (ParentFcb != NULL)
- {
- NtfsReleaseFCB(DeviceExt,
- ParentFcb);
- }
+ DPRINT("Converting relative filename to absolute filename\n");
+
+ Status = NtfsMakeAbsoluteFilename(FileObject->RelatedFileObject,
+ FileName,
+ &AbsFileName);
+ if (AbsFileName) FileName = AbsFileName;
+ if (!NT_SUCCESS(Status))
+ {
+ return Status;
+ }
+ }
- if (!NT_SUCCESS (Status))
- {
- DPRINT("Could not make a new FCB, status: %x\n", Status);
+ //FIXME: Get canonical path name (remove .'s, ..'s and extra separators)
- if (AbsFileName)
- ExFreePool(AbsFileName);
+ DPRINT("PathName to open: %S\n", FileName);
- return(Status);
- }
+ /* try first to find an existing FCB in memory */
+ DPRINT("Checking for existing FCB in memory\n");
+ Fcb = NtfsGrabFCBFromTable(DeviceExt,
+ FileName);
+ if (Fcb == NULL)
+ {
+ DPRINT("No existing FCB found, making a new one if file exists.\n");
+ Status = NtfsGetFCBForFile(DeviceExt,
+ &ParentFcb,
+ &Fcb,
+ FileName,
+ CaseSensitive);
+ if (ParentFcb != NULL)
+ {
+ NtfsReleaseFCB(DeviceExt,
+ ParentFcb);
+ }
+
+ if (!NT_SUCCESS (Status))
+ {
+ DPRINT("Could not make a new FCB, status: %x\n", Status);
+
+ if (AbsFileName)
+ ExFreePool(AbsFileName);
+
+ return Status;
+ }
}
- DPRINT("Attaching FCB to fileObject\n");
- Status = NtfsAttachFCBToFileObject(DeviceExt,
- Fcb,
- FileObject);
+ DPRINT("Attaching FCB to fileObject\n");
+ Status = NtfsAttachFCBToFileObject(DeviceExt,
+ Fcb,
+ FileObject);
- if (AbsFileName)
- ExFreePool (AbsFileName);
+ if (AbsFileName)
+ ExFreePool(AbsFileName);
- return(Status);
+ *FoundFCB = Fcb;
+
+ return Status;
}
-static NTSTATUS
-NtfsCreateFile(PDEVICE_OBJECT DeviceObject,
- PIRP Irp)
/*
* FUNCTION: Opens a file
*/
+static
+NTSTATUS
+NtfsCreateFile(PDEVICE_OBJECT DeviceObject,
+ PNTFS_IRP_CONTEXT IrpContext)
{
- PDEVICE_EXTENSION DeviceExt;
- PIO_STACK_LOCATION Stack;
- PFILE_OBJECT FileObject;
- ULONG RequestedDisposition;
-// ULONG RequestedOptions;
-// PFCB Fcb;
-// PWSTR FileName;
- NTSTATUS Status;
-
- DPRINT("NtfsCreateFile() called\n");
-
- DeviceExt = DeviceObject->DeviceExtension;
- ASSERT(DeviceExt);
- Stack = IoGetCurrentIrpStackLocation (Irp);
- ASSERT(Stack);
-
- RequestedDisposition = ((Stack->Parameters.Create.Options >> 24) & 0xff);
-// RequestedOptions =
-// Stack->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
+ PDEVICE_EXTENSION DeviceExt;
+ PIO_STACK_LOCATION Stack;
+ PFILE_OBJECT FileObject;
+ ULONG RequestedDisposition;
+ ULONG RequestedOptions;
+ PNTFS_FCB Fcb = NULL;
+// PWSTR FileName;
+ NTSTATUS Status;
+ UNICODE_STRING FullPath;
+ PIRP Irp = IrpContext->Irp;
+
+ DPRINT1("NtfsCreateFile(%p, %p) called\n", DeviceObject, IrpContext);
+
+ DeviceExt = DeviceObject->DeviceExtension;
+ ASSERT(DeviceExt);
+ Stack = IoGetCurrentIrpStackLocation (Irp);
+ ASSERT(Stack);
+
+ RequestedDisposition = ((Stack->Parameters.Create.Options >> 24) & 0xff);
+ RequestedOptions = Stack->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
// PagingFileCreate = (Stack->Flags & SL_OPEN_PAGING_FILE) ? TRUE : FALSE;
-// if ((RequestedOptions & FILE_DIRECTORY_FILE)
-// && RequestedDisposition == FILE_SUPERSEDE)
-// return STATUS_INVALID_PARAMETER;
+ if (RequestedOptions & FILE_DIRECTORY_FILE &&
+ RequestedDisposition == FILE_SUPERSEDE)
+ {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ /* Deny create if the volume is locked */
+ if (DeviceExt->Flags & VCB_VOLUME_LOCKED)
+ {
+ return STATUS_ACCESS_DENIED;
+ }
- FileObject = Stack->FileObject;
+ FileObject = Stack->FileObject;
- if (RequestedDisposition == FILE_CREATE ||
- RequestedDisposition == FILE_OVERWRITE_IF ||
- RequestedDisposition == FILE_SUPERSEDE)
+ if ((RequestedOptions & FILE_OPEN_BY_FILE_ID) == FILE_OPEN_BY_FILE_ID)
{
- return(STATUS_ACCESS_DENIED);
+ ULONGLONG MFTId;
+
+ if (FileObject->FileName.Length != sizeof(ULONGLONG))
+ return STATUS_INVALID_PARAMETER;
+
+ MFTId = (*(PULONGLONG)FileObject->FileName.Buffer) & NTFS_MFT_MASK;
+ if (MFTId < 0x10)
+ {
+ Status = NtfsOpenFileById(DeviceExt, FileObject, MFTId, &Fcb);
+ }
+ else
+ {
+ Status = NtfsMoonWalkID(DeviceExt, MFTId, &FullPath);
+ }
+
+ if (!NT_SUCCESS(Status))
+ {
+ return Status;
+ }
+
+ DPRINT1("Open by ID: %I64x -> %wZ\n", (*(PULONGLONG)FileObject->FileName.Buffer) & NTFS_MFT_MASK, &FullPath);
}
- Status = NtfsOpenFile(DeviceExt,
- FileObject,
- FileObject->FileName.Buffer);
+ /* This a open operation for the volume itself */
+ if (FileObject->FileName.Length == 0 &&
+ (FileObject->RelatedFileObject == NULL || FileObject->RelatedFileObject->FsContext2 != NULL))
+ {
+ if (RequestedDisposition != FILE_OPEN &&
+ RequestedDisposition != FILE_OPEN_IF)
+ {
+ return STATUS_ACCESS_DENIED;
+ }
+
+ if (RequestedOptions & FILE_DIRECTORY_FILE)
+ {
+ return STATUS_NOT_A_DIRECTORY;
+ }
+
+ NtfsAttachFCBToFileObject(DeviceExt, DeviceExt->VolumeFcb, FileObject);
+ DeviceExt->VolumeFcb->RefCount++;
+
+ Irp->IoStatus.Information = FILE_OPENED;
+ return STATUS_SUCCESS;
+ }
- /*
- * If the directory containing the file to open doesn't exist then
- * fail immediately
- */
- Irp->IoStatus.Information = (NT_SUCCESS(Status)) ? FILE_OPENED : 0;
- Irp->IoStatus.Status = Status;
+ if (Fcb == NULL)
+ {
+ Status = NtfsOpenFile(DeviceExt,
+ FileObject,
+ ((RequestedOptions & FILE_OPEN_BY_FILE_ID) ? FullPath.Buffer : FileObject->FileName.Buffer),
+ (Stack->Flags & SL_CASE_SENSITIVE),
+ &Fcb);
+
+ if (RequestedOptions & FILE_OPEN_BY_FILE_ID)
+ {
+ ExFreePoolWithTag(FullPath.Buffer, TAG_NTFS);
+ }
+ }
- return(Status);
+ if (NT_SUCCESS(Status))
+ {
+ if (RequestedDisposition == FILE_CREATE)
+ {
+ Irp->IoStatus.Information = FILE_EXISTS;
+ NtfsCloseFile(DeviceExt, FileObject);
+ return STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ if (RequestedOptions & FILE_NON_DIRECTORY_FILE &&
+ NtfsFCBIsDirectory(Fcb))
+ {
+ NtfsCloseFile(DeviceExt, FileObject);
+ return STATUS_FILE_IS_A_DIRECTORY;
+ }
+
+ if (RequestedOptions & FILE_DIRECTORY_FILE &&
+ !NtfsFCBIsDirectory(Fcb))
+ {
+ NtfsCloseFile(DeviceExt, FileObject);
+ return STATUS_NOT_A_DIRECTORY;
+ }
+
+ /*
+ * If it is a reparse point & FILE_OPEN_REPARSE_POINT, then allow opening it
+ * as a normal file.
+ * Otherwise, attempt to read reparse data and hand them to the Io manager
+ * with status reparse to force a reparse.
+ */
+ if (NtfsFCBIsReparsePoint(Fcb) &&
+ ((RequestedOptions & FILE_OPEN_REPARSE_POINT) != FILE_OPEN_REPARSE_POINT))
+ {
+ PREPARSE_DATA_BUFFER ReparseData = NULL;
+
+ Status = NtfsReadFCBAttribute(DeviceExt, Fcb,
+ AttributeReparsePoint, L"", 0,
+ (PVOID *)&Irp->Tail.Overlay.AuxiliaryBuffer);
+ if (NT_SUCCESS(Status))
+ {
+ ReparseData = (PREPARSE_DATA_BUFFER)Irp->Tail.Overlay.AuxiliaryBuffer;
+ if (ReparseData->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
+ {
+ Status = STATUS_REPARSE;
+ }
+ else
+ {
+ Status = STATUS_NOT_IMPLEMENTED;
+ ExFreePoolWithTag(ReparseData, TAG_NTFS);
+ }
+ }
+
+ Irp->IoStatus.Information = ((Status == STATUS_REPARSE) ? ReparseData->ReparseTag : 0);
+
+ NtfsCloseFile(DeviceExt, FileObject);
+ return Status;
+ }
+
+ if (RequestedDisposition == FILE_OVERWRITE ||
+ RequestedDisposition == FILE_OVERWRITE_IF ||
+ RequestedDisposition == FILE_SUPERSEDE)
+ {
+ PFILE_RECORD_HEADER fileRecord = NULL;
+ PNTFS_ATTR_CONTEXT dataContext = NULL;
+ ULONG DataAttributeOffset;
+ LARGE_INTEGER Zero;
+ Zero.QuadPart = 0;
+
+ if (!NtfsGlobalData->EnableWriteSupport)
+ {
+ DPRINT1("NTFS write-support is EXPERIMENTAL and is disabled by default!\n");
+ NtfsCloseFile(DeviceExt, FileObject);
+ return STATUS_ACCESS_DENIED;
+ }
+
+ // TODO: check for appropriate access
+
+ ExAcquireResourceExclusiveLite(&(Fcb->MainResource), TRUE);
+
+ fileRecord = ExAllocatePoolWithTag(NonPagedPool,
+ Fcb->Vcb->NtfsInfo.BytesPerFileRecord,
+ TAG_NTFS);
+ if (fileRecord)
+ {
+
+ Status = ReadFileRecord(Fcb->Vcb,
+ Fcb->MFTIndex,
+ fileRecord);
+ if (!NT_SUCCESS(Status))
+ goto DoneOverwriting;
+
+ // find the data attribute and set it's length to 0 (TODO: Handle Alternate Data Streams)
+ Status = FindAttribute(Fcb->Vcb, fileRecord, AttributeData, L"", 0, &dataContext, &DataAttributeOffset);
+ if (!NT_SUCCESS(Status))
+ goto DoneOverwriting;
+
+ Status = SetAttributeDataLength(FileObject, Fcb, dataContext, DataAttributeOffset, fileRecord, &Zero);
+ }
+ else
+ {
+ Status = STATUS_NO_MEMORY;
+ }
+
+ DoneOverwriting:
+ if (fileRecord)
+ ExFreePoolWithTag(fileRecord, TAG_NTFS);
+ if (dataContext)
+ ReleaseAttributeContext(dataContext);
+
+ ExReleaseResourceLite(&(Fcb->MainResource));
+
+ if (!NT_SUCCESS(Status))
+ {
+ NtfsCloseFile(DeviceExt, FileObject);
+ return Status;
+ }
+
+ if (RequestedDisposition == FILE_SUPERSEDE)
+ {
+ Irp->IoStatus.Information = FILE_SUPERSEDED;
+ }
+ else
+ {
+ Irp->IoStatus.Information = FILE_OVERWRITTEN;
+ }
+ }
+ }
+ else
+ {
+ /* HUGLY HACK: Can't create new files yet... */
+ if (RequestedDisposition == FILE_CREATE ||
+ RequestedDisposition == FILE_OPEN_IF ||
+ RequestedDisposition == FILE_OVERWRITE_IF ||
+ RequestedDisposition == FILE_SUPERSEDE)
+ {
+ if (!NtfsGlobalData->EnableWriteSupport)
+ {
+ DPRINT1("NTFS write-support is EXPERIMENTAL and is disabled by default!\n");
+ NtfsCloseFile(DeviceExt, FileObject);
+ return STATUS_ACCESS_DENIED;
+ }
+
+ // Create the file record on disk
+ Status = NtfsCreateFileRecord(DeviceExt,
+ FileObject,
+ (Stack->Flags & SL_CASE_SENSITIVE),
+ BooleanFlagOn(IrpContext->Flags,IRPCONTEXT_CANWAIT));
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("ERROR: Couldn't create file record!\n");
+ return Status;
+ }
+
+ // Now we should be able to open the file
+ return NtfsCreateFile(DeviceObject, IrpContext);
+ }
+ }
+
+ if (NT_SUCCESS(Status))
+ {
+ Fcb->OpenHandleCount++;
+ DeviceExt->OpenHandleCount++;
+ }
+
+ /*
+ * If the directory containing the file to open doesn't exist then
+ * fail immediately
+ */
+ Irp->IoStatus.Information = (NT_SUCCESS(Status)) ? FILE_OPENED : 0;
+
+ return Status;
}
-NTSTATUS NTAPI
-NtfsFsdCreate(PDEVICE_OBJECT DeviceObject,
- PIRP Irp)
+NTSTATUS
+NtfsCreate(PNTFS_IRP_CONTEXT IrpContext)
{
- PDEVICE_EXTENSION DeviceExt;
- NTSTATUS Status;
+ PDEVICE_EXTENSION DeviceExt;
+ NTSTATUS Status;
+ PDEVICE_OBJECT DeviceObject;
- if (DeviceObject == NtfsGlobalData->DeviceObject)
+ DeviceObject = IrpContext->DeviceObject;
+ if (DeviceObject == NtfsGlobalData->DeviceObject)
{
- /* DeviceObject represents FileSystem instead of logical volume */
- DPRINT("Opening file system\n");
- Irp->IoStatus.Information = FILE_OPENED;
- Status = STATUS_SUCCESS;
- goto ByeBye;
+ /* DeviceObject represents FileSystem instead of logical volume */
+ DPRINT("Opening file system\n");
+ IrpContext->Irp->IoStatus.Information = FILE_OPENED;
+ return STATUS_SUCCESS;
}
- DeviceExt = DeviceObject->DeviceExtension;
+ DeviceExt = DeviceObject->DeviceExtension;
- FsRtlEnterFileSystem();
- ExAcquireResourceExclusiveLite(&DeviceExt->DirResource,
- TRUE);
- Status = NtfsCreateFile(DeviceObject,
- Irp);
- ExReleaseResourceLite(&DeviceExt->DirResource);
- FsRtlExitFileSystem();
+ if (!(IrpContext->Flags & IRPCONTEXT_CANWAIT))
+ {
+ return NtfsMarkIrpContextForQueue(IrpContext);
+ }
+
+ ExAcquireResourceExclusiveLite(&DeviceExt->DirResource,
+ TRUE);
+ Status = NtfsCreateFile(DeviceObject,
+ IrpContext);
+ ExReleaseResourceLite(&DeviceExt->DirResource);
+
+ return Status;
+}
+
+/**
+* @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 = 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, 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);
+
+ // 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, &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);
+ }
-ByeBye:
- Irp->IoStatus.Status = Status;
- IoCompleteRequest(Irp,
- NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT);
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
- return(Status);
+ return Status;
}
/* EOF */