[NTFS] - In the NtfsAddFilenameToDirectory() function, rename DirectoryContext parame...
[reactos.git] / drivers / filesystems / ntfs / create.c
index 88cc138..f52cd7d 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  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
@@ -20,7 +20,8 @@
  * 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 */