[FASTFAT_NEW]
[reactos.git] / reactos / drivers / filesystems / fastfat_new / create.c
index 846577a..de84dc9 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * PROJECT:         ReactOS FAT file system driver
- * LICENSE:         GPL - See COPYING in the top level directory
+ * LICENSE:         GNU GPLv3 as published by the Free Software Foundation
  * FILE:            drivers/filesystems/fastfat/create.c
  * PURPOSE:         Create routines
  * PROGRAMMERS:     Aleksey Bragin (aleksey@reactos.org)
 #define NDEBUG
 #include "fastfat.h"
 
-NTSYSAPI
-NTSTATUS
-NTAPI
-RtlUpcaseUnicodeStringToCountedOemString(
-    IN OUT POEM_STRING DestinationString,
-    IN PCUNICODE_STRING SourceString,
-    IN BOOLEAN AllocateDestinationString
-);
-
-
 /* FUNCTIONS *****************************************************************/
 
 IO_STATUS_BLOCK
@@ -33,10 +23,283 @@ FatiOpenRootDcb(IN PFAT_IRP_CONTEXT IrpContext,
                 IN ULONG CreateDisposition)
 {
     IO_STATUS_BLOCK Iosb;
+    PFCB Dcb;
+    NTSTATUS Status;
+    PCCB Ccb;
+
+    /* Reference our DCB */
+    Dcb = Vcb->RootDcb;
+
+    DPRINT("Opening root directory\n");
+
+    /* Exclusively lock this DCB */
+    (VOID)FatAcquireExclusiveFcb(IrpContext, Dcb);
+
+    do
+    {
+        /* Validate parameters */
+        if (CreateDisposition != FILE_OPEN &&
+            CreateDisposition != FILE_OPEN_IF)
+        {
+            Iosb.Status = STATUS_ACCESS_DENIED;
+            break;
+        }
+
+        // TODO: Check file access
+
+        /* Is it a first time open? */
+        if (Dcb->OpenCount == 0)
+        {
+            /* Set share access */
+            IoSetShareAccess(*DesiredAccess,
+                             ShareAccess,
+                             FileObject,
+                             &Dcb->ShareAccess);
+        }
+        else
+        {
+            /* Check share access */
+            Status = IoCheckShareAccess(*DesiredAccess,
+                                        ShareAccess,
+                                        FileObject,
+                                        &Dcb->ShareAccess,
+                                        TRUE);
+        }
+
+        /* Set file object pointers */
+        Ccb = FatCreateCcb();
+        FatSetFileObject(FileObject, UserDirectoryOpen, Dcb, Ccb);
+
+        /* Increment counters */
+        Dcb->OpenCount++;
+        Dcb->UncleanCount++;
+        Vcb->OpenFileCount++;
+        if (IsFileObjectReadOnly(FileObject)) Vcb->ReadOnlyCount++;
+
+        /* Set success statuses */
+        Iosb.Status = STATUS_SUCCESS;
+        Iosb.Information = FILE_OPENED;
+    } while (FALSE);
+
+    /* Release the DCB lock */
+    FatReleaseFcb(IrpContext, Dcb);
+
+    return Iosb;
+}
+
+FF_ERROR
+NTAPI
+FatiTryToOpen(IN PFILE_OBJECT FileObject,
+              IN PVCB Vcb)
+{
+    OEM_STRING AnsiName;
+    CHAR AnsiNameBuf[512];
+    FF_ERROR Error;
+    NTSTATUS Status;
+    FF_FILE *FileHandle;
+
+    /* Convert the name to ANSI */
+    AnsiName.Buffer = AnsiNameBuf;
+    AnsiName.Length = 0;
+    AnsiName.MaximumLength = sizeof(AnsiNameBuf);
+    RtlZeroMemory(AnsiNameBuf, sizeof(AnsiNameBuf));
+    Status = RtlUpcaseUnicodeStringToCountedOemString(&AnsiName, &FileObject->FileName, FALSE);
+    if (!NT_SUCCESS(Status))
+    {
+        ASSERT(FALSE);
+    }
+
+    /* Open the file with FullFAT */
+    FileHandle = FF_Open(Vcb->Ioman, AnsiName.Buffer, FF_MODE_READ, &Error);
+
+    /* Close the handle */
+    if (FileHandle) FF_Close(FileHandle);
+
+    /* Return status */
+    return Error;
+}
+
+IO_STATUS_BLOCK
+NTAPI
+FatiOverwriteFile(PFAT_IRP_CONTEXT IrpContext,
+                  PFILE_OBJECT FileObject,
+                  PFCB Fcb,
+                  ULONG AllocationSize,
+                  PFILE_FULL_EA_INFORMATION EaBuffer,
+                  ULONG EaLength,
+                  UCHAR FileAttributes,
+                  ULONG CreateDisposition,
+                  BOOLEAN NoEaKnowledge)
+{
+    IO_STATUS_BLOCK Iosb = {{0}};
+    PCCB Ccb;
+    LARGE_INTEGER Zero;
+    ULONG NotifyFilter;
+
+    Zero.QuadPart = 0;
+
+    /* Check Ea mismatch first */
+    if (NoEaKnowledge && EaLength > 0)
+    {
+        Iosb.Status = STATUS_ACCESS_DENIED;
+        return Iosb;
+    }
 
-    DPRINT1("Opening root directory\n");
+    do
+    {
+        /* Check if it's not still mapped */
+        if (!MmCanFileBeTruncated(&Fcb->SectionObjectPointers,
+                                  &Zero))
+        {
+            /* Fail */
+            Iosb.Status = STATUS_USER_MAPPED_FILE;
+            break;
+        }
+
+        /* Set file object pointers */
+        Ccb = FatCreateCcb();
+        FatSetFileObject(FileObject,
+                         UserFileOpen,
+                         Fcb,
+                         Ccb);
+
+        FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
+
+        /* Indicate that create is in progress */
+        Fcb->Vcb->State |= VCB_STATE_CREATE_IN_PROGRESS;
+
+        /* Purge the cache section */
+        CcPurgeCacheSection(&Fcb->SectionObjectPointers, NULL, 0, FALSE);
+
+        /* Add Eas */
+        if (EaLength > 0)
+        {
+            ASSERT(FALSE);
+        }
+
+        /* Acquire the paging resource */
+        (VOID)ExAcquireResourceExclusiveLite(Fcb->Header.PagingIoResource, TRUE);
+
+        /* Initialize FCB header */
+        Fcb->Header.FileSize.QuadPart = 0;
+        Fcb->Header.ValidDataLength.QuadPart = 0;
 
-    Iosb.Status = STATUS_NOT_IMPLEMENTED;
+        /* Let CC know about changed file size */
+        CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&Fcb->Header.AllocationSize);
+
+        // TODO: Actually truncate the file
+        DPRINT1("TODO: Actually truncate file '%wZ' with a fullfat handle %x\n", &Fcb->FullFileName, Fcb->FatHandle);
+
+        /* Release the paging resource */
+        ExReleaseResourceLite(Fcb->Header.PagingIoResource);
+
+        /* Specify truncate on close */
+        Fcb->State |= FCB_STATE_TRUNCATE_ON_CLOSE;
+
+        // TODO: Delete previous EA if needed
+
+        /* Send notification about changes */
+        NotifyFilter = FILE_NOTIFY_CHANGE_LAST_WRITE |
+                       FILE_NOTIFY_CHANGE_ATTRIBUTES |
+                       FILE_NOTIFY_CHANGE_SIZE;
+
+        FsRtlNotifyFullReportChange(Fcb->Vcb->NotifySync,
+                                    &Fcb->Vcb->NotifyList,
+                                    (PSTRING)&Fcb->FullFileName,
+                                    Fcb->FullFileName.Length - Fcb->FileNameLength,
+                                    NULL,
+                                    NULL,
+                                    NotifyFilter,
+                                    FILE_ACTION_MODIFIED,
+                                    NULL);
+
+        /* Set success status */
+        Iosb.Status = STATUS_SUCCESS;
+
+        /* Set correct information code */
+        Iosb.Information = (CreateDisposition == FILE_SUPERSEDE) ? FILE_SUPERSEDED : FILE_OVERWRITTEN;
+    } while (0);
+
+    /* Remove the create in progress flag */
+    ClearFlag(Fcb->Vcb->State, VCB_STATE_CREATE_IN_PROGRESS);
+
+    return Iosb;
+}
+
+IO_STATUS_BLOCK
+NTAPI
+FatiOpenExistingDir(IN PFAT_IRP_CONTEXT IrpContext,
+                     IN PFILE_OBJECT FileObject,
+                     IN PVCB Vcb,
+                     IN PFCB ParentDcb,
+                     IN PACCESS_MASK DesiredAccess,
+                     IN USHORT ShareAccess,
+                     IN ULONG AllocationSize,
+                     IN PFILE_FULL_EA_INFORMATION EaBuffer,
+                     IN ULONG EaLength,
+                     IN UCHAR FileAttributes,
+                     IN ULONG CreateDisposition,
+                     IN BOOLEAN DeleteOnClose)
+{
+    IO_STATUS_BLOCK Iosb = {{0}};
+    OEM_STRING AnsiName;
+    CHAR AnsiNameBuf[512];
+    PFCB Fcb;
+    NTSTATUS Status;
+    FF_FILE *FileHandle;
+
+    /* Only open is permitted */
+    if (CreateDisposition != FILE_OPEN &&
+        CreateDisposition != FILE_OPEN_IF)
+    {
+        Iosb.Status = STATUS_OBJECT_NAME_COLLISION;
+        return Iosb;
+    }
+
+    // TODO: Check dir access
+
+    /* Convert the name to ANSI */
+    AnsiName.Buffer = AnsiNameBuf;
+    AnsiName.Length = 0;
+    AnsiName.MaximumLength = sizeof(AnsiNameBuf);
+    RtlZeroMemory(AnsiNameBuf, sizeof(AnsiNameBuf));
+    Status = RtlUpcaseUnicodeStringToCountedOemString(&AnsiName, &FileObject->FileName, FALSE);
+    if (!NT_SUCCESS(Status))
+    {
+        ASSERT(FALSE);
+    }
+
+    /* Open the dir with FullFAT */
+    FileHandle = FF_Open(Vcb->Ioman, AnsiName.Buffer, FF_MODE_DIR, NULL);
+
+    if (!FileHandle)
+    {
+        Iosb.Status = STATUS_OBJECT_NAME_NOT_FOUND; // FIXME: A shortcut for now
+        return Iosb;
+    }
+
+    /* Create a new DCB for this directory */
+    Fcb = FatCreateDcb(IrpContext, Vcb, ParentDcb, FileHandle);
+
+    /* Set share access */
+    IoSetShareAccess(*DesiredAccess, ShareAccess, FileObject, &Fcb->ShareAccess);
+
+    /* Set context and section object pointers */
+    FatSetFileObject(FileObject,
+                     UserDirectoryOpen,
+                     Fcb,
+                     FatCreateCcb());
+
+    /* Increase counters */
+    Fcb->UncleanCount++;
+    Fcb->OpenCount++;
+    Vcb->OpenFileCount++;
+    if (IsFileObjectReadOnly(FileObject)) Vcb->ReadOnlyCount++;
+
+    Iosb.Status = STATUS_SUCCESS;
+    Iosb.Information = FILE_OPENED;
+
+    DPRINT1("Successfully opened dir %s\n", AnsiNameBuf);
 
     return Iosb;
 }
@@ -59,7 +322,12 @@ FatiOpenExistingFile(IN PFAT_IRP_CONTEXT IrpContext,
                      IN BOOLEAN IsDosName)
 {
     IO_STATUS_BLOCK Iosb = {{0}};
+    OEM_STRING AnsiName;
+    CHAR AnsiNameBuf[512];
     PFCB Fcb;
+    NTSTATUS Status;
+    FF_FILE *FileHandle;
+    FF_ERROR FfError;
 
     /* Check for create file option and fail */
     if (CreateDisposition == FILE_CREATE)
@@ -70,12 +338,34 @@ FatiOpenExistingFile(IN PFAT_IRP_CONTEXT IrpContext,
 
     // TODO: Check more params
 
+    /* Convert the name to ANSI */
+    AnsiName.Buffer = AnsiNameBuf;
+    AnsiName.Length = 0;
+    AnsiName.MaximumLength = sizeof(AnsiNameBuf);
+    RtlZeroMemory(AnsiNameBuf, sizeof(AnsiNameBuf));
+    Status = RtlUpcaseUnicodeStringToCountedOemString(&AnsiName, &FileObject->FileName, FALSE);
+    if (!NT_SUCCESS(Status))
+    {
+        ASSERT(FALSE);
+    }
+
+    /* Open the file with FullFAT */
+    FileHandle = FF_Open(Vcb->Ioman, AnsiName.Buffer, FF_MODE_READ, &FfError);
+
+    if (!FileHandle)
+    {
+        DPRINT1("Failed to open file '%s', error %ld\n", AnsiName.Buffer, FfError);
+        Iosb.Status = STATUS_OBJECT_NAME_NOT_FOUND; // FIXME: A shortcut for now
+        return Iosb;
+    }
+    DPRINT1("Succeeded opening file '%s'\n", AnsiName.Buffer);
+
     /* Create a new FCB for this file */
-    Fcb = FatCreateFcb(IrpContext, Vcb, ParentDcb);
+    Fcb = FatCreateFcb(IrpContext, Vcb, ParentDcb, FileHandle);
 
     // TODO: Check if overwrite is needed
 
-    // This is usual file open branch, without overwriting!
+    // TODO: This is usual file open branch, without overwriting!
     /* Set context and section object pointers */
     FatSetFileObject(FileObject,
                      UserFileOpen,
@@ -86,6 +376,108 @@ FatiOpenExistingFile(IN PFAT_IRP_CONTEXT IrpContext,
     Iosb.Status = STATUS_SUCCESS;
     Iosb.Information = FILE_OPENED;
 
+
+    /* Increase counters */
+    Fcb->UncleanCount++;
+    Fcb->OpenCount++;
+    if (FlagOn(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING)) Fcb->NonCachedUncleanCount++;
+    if (IsFileObjectReadOnly(FileObject)) Vcb->ReadOnlyCount++;
+
+    return Iosb;
+}
+
+IO_STATUS_BLOCK
+NTAPI
+FatiOpenVolume(IN PFAT_IRP_CONTEXT IrpContext,
+               IN PFILE_OBJECT FileObject,
+               IN PVCB Vcb,
+               IN PACCESS_MASK DesiredAccess,
+               IN USHORT ShareAccess,
+               IN ULONG CreateDisposition)
+{
+    PCCB Ccb;
+    IO_STATUS_BLOCK Iosb = {{0}};
+    BOOLEAN VolumeFlushed = FALSE;
+
+    /* Check parameters */
+    if (CreateDisposition != FILE_OPEN &&
+        CreateDisposition != FILE_OPEN_IF)
+    {
+        /* Deny access */
+        Iosb.Status = STATUS_ACCESS_DENIED;
+    }
+
+    /* Check if it's exclusive open */
+    if (!FlagOn(ShareAccess, FILE_SHARE_WRITE) &&
+        !FlagOn(ShareAccess, FILE_SHARE_DELETE))
+    {
+        // TODO: Check if exclusive read access requested
+        // and opened handles count is not 0
+        //if (!FlagOn(ShareAccess, FILE_SHARE_READ)
+
+        DPRINT1("Exclusive volume open\n");
+
+        // TODO: Flush the volume
+        VolumeFlushed = TRUE;
+    }
+    else if (FlagOn(*DesiredAccess, FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA))
+    {
+        DPRINT1("Shared open\n");
+
+        // TODO: Flush the volume
+        VolumeFlushed = TRUE;
+    }
+
+    if (VolumeFlushed &&
+        !FlagOn(Vcb->State, VCB_STATE_MOUNTED_DIRTY) &&
+        FlagOn(Vcb->State, VCB_STATE_FLAG_DIRTY) &&
+        CcIsThereDirtyData(Vcb->Vpb))
+    {
+        UNIMPLEMENTED;
+    }
+
+    /* Set share access */
+    if (Vcb->DirectOpenCount > 0)
+    {
+        /* This volume has already been opened */
+        Iosb.Status = IoCheckShareAccess(*DesiredAccess,
+                                         ShareAccess,
+                                         FileObject,
+                                         &Vcb->ShareAccess,
+                                         TRUE);
+
+        if (!NT_SUCCESS(Iosb.Status))
+        {
+            ASSERT(FALSE);
+        }
+    }
+    else
+    {
+        /* This is the first time open */
+        IoSetShareAccess(*DesiredAccess,
+                         ShareAccess,
+                         FileObject,
+                         &Vcb->ShareAccess);
+    }
+
+    /* Set file object pointers */
+    Ccb = FatCreateCcb();
+    FatSetFileObject(FileObject, UserVolumeOpen, Vcb, Ccb);
+    FileObject->SectionObjectPointer = &Vcb->SectionObjectPointers;
+
+    /* Increase direct open count */
+    Vcb->DirectOpenCount++;
+    Vcb->OpenFileCount++;
+    if (IsFileObjectReadOnly(FileObject)) Vcb->ReadOnlyCount++;
+
+    /* Set no buffering flag */
+    FileObject->Flags |= FO_NO_INTERMEDIATE_BUFFERING;
+
+    // TODO: User's access check
+
+    Iosb.Status = STATUS_SUCCESS;
+    Iosb.Information = FILE_OPENED;
+
     return Iosb;
 }
 
@@ -109,9 +501,9 @@ FatiCreate(IN PFAT_IRP_CONTEXT IrpContext,
     ULONG CreateDisposition;
 
     /* Control blocks */
-    PVCB Vcb, DecodedVcb;
-    PFCB Fcb, NextFcb;
-    PCCB Ccb;
+    PVCB Vcb, DecodedVcb, RelatedVcb;
+    PFCB Fcb, NextFcb, RelatedDcb;
+    PCCB Ccb, RelatedCcb;
     PFCB ParentDcb;
 
     /* IRP data */
@@ -130,29 +522,32 @@ FatiCreate(IN PFAT_IRP_CONTEXT IrpContext,
     NTSTATUS Status;
     IO_STATUS_BLOCK Iosb;
     PIO_STACK_LOCATION IrpSp;
-    BOOLEAN EndBackslash = FALSE, OpenedAsDos;
-    UNICODE_STRING RemainingPart, FirstName, NextName;
+    BOOLEAN EndBackslash = FALSE, OpenedAsDos, FirstRun = TRUE;
+    UNICODE_STRING RemainingPart, FirstName, NextName, FileNameUpcased;
     OEM_STRING AnsiFirstName;
+    FF_ERROR FfError;
+    TYPE_OF_OPEN TypeOfOpen;
+    BOOLEAN OplockPostIrp = FALSE;
 
     Iosb.Status = STATUS_SUCCESS;
 
     /* Get current IRP stack location */
     IrpSp = IoGetCurrentIrpStackLocation(Irp);
 
-    DPRINT1("FatCommonCreate\n", 0 );
-    DPRINT1("Irp                       = %08lx\n",   Irp );
-    DPRINT1("\t->Flags                   = %08lx\n", Irp->Flags );
-    DPRINT1("\t->FileObject              = %08lx\n", IrpSp->FileObject );
-    DPRINT1("\t->RelatedFileObject       = %08lx\n", IrpSp->FileObject->RelatedFileObject );
-    DPRINT1("\t->FileName                = %wZ\n",   &IrpSp->FileObject->FileName );
-    DPRINT1("\t->AllocationSize.LowPart  = %08lx\n", Irp->Overlay.AllocationSize.LowPart );
-    DPRINT1("\t->AllocationSize.HighPart = %08lx\n", Irp->Overlay.AllocationSize.HighPart );
-    DPRINT1("\t->SystemBuffer            = %08lx\n", Irp->AssociatedIrp.SystemBuffer );
-    DPRINT1("\t->DesiredAccess           = %08lx\n", IrpSp->Parameters.Create.SecurityContext->DesiredAccess );
-    DPRINT1("\t->Options                 = %08lx\n", IrpSp->Parameters.Create.Options );
-    DPRINT1("\t->FileAttributes          = %04x\n",  IrpSp->Parameters.Create.FileAttributes );
-    DPRINT1("\t->ShareAccess             = %04x\n",  IrpSp->Parameters.Create.ShareAccess );
-    DPRINT1("\t->EaLength                = %08lx\n", IrpSp->Parameters.Create.EaLength );
+    DPRINT("FatCommonCreate\n", 0 );
+    DPRINT("Irp                       = %08lx\n",   Irp );
+    DPRINT("\t->Flags                   = %08lx\n", Irp->Flags );
+    DPRINT("\t->FileObject              = %08lx\n", IrpSp->FileObject );
+    DPRINT("\t->RelatedFileObject       = %08lx\n", IrpSp->FileObject->RelatedFileObject );
+    DPRINT("\t->FileName                = %wZ\n",   &IrpSp->FileObject->FileName );
+    DPRINT("\t->AllocationSize.LowPart  = %08lx\n", Irp->Overlay.AllocationSize.LowPart );
+    DPRINT("\t->AllocationSize.HighPart = %08lx\n", Irp->Overlay.AllocationSize.HighPart );
+    DPRINT("\t->SystemBuffer            = %08lx\n", Irp->AssociatedIrp.SystemBuffer );
+    DPRINT("\t->DesiredAccess           = %08lx\n", IrpSp->Parameters.Create.SecurityContext->DesiredAccess );
+    DPRINT("\t->Options                 = %08lx\n", IrpSp->Parameters.Create.Options );
+    DPRINT("\t->FileAttributes          = %04x\n",  IrpSp->Parameters.Create.FileAttributes );
+    DPRINT("\t->ShareAccess             = %04x\n",  IrpSp->Parameters.Create.ShareAccess );
+    DPRINT("\t->EaLength                = %08lx\n", IrpSp->Parameters.Create.EaLength );
 
     /* Apply a special hack for Win32, idea taken from FASTFAT reference driver from WDK */
     if ((IrpSp->FileObject->FileName.Length > sizeof(WCHAR)) &&
@@ -197,6 +592,13 @@ FatiCreate(IN PFAT_IRP_CONTEXT IrpContext,
     if (RelatedFO)
         FileObject->Vpb = RelatedFO->Vpb;
 
+    /* Reject open by id */
+    if (Options & FILE_OPEN_BY_FILE_ID)
+    {
+        FatCompleteRequest(IrpContext, Irp, STATUS_INVALID_PARAMETER);
+        return STATUS_INVALID_PARAMETER;
+    }
+
     /* Prepare file attributes mask */
     FileAttributes &= (FILE_ATTRIBUTE_READONLY |
                        FILE_ATTRIBUTE_HIDDEN   |
@@ -257,12 +659,22 @@ FatiCreate(IN PFAT_IRP_CONTEXT IrpContext,
         DPRINT1("This volume is locked\n");
         Status = STATUS_ACCESS_DENIED;
 
+        /* Set volume dismount status */
+        if (Vcb->Condition != VcbGood)
+            Status = STATUS_VOLUME_DISMOUNTED;
+
         /* Cleanup and return */
         FatReleaseVcb(IrpContext, Vcb);
+        FatCompleteRequest(IrpContext, Irp, Status);
         return Status;
     }
 
-    // TODO: Check if the volume is write protected and disallow DELETE_ON_CLOSE
+    /* Check if the volume is write protected and disallow DELETE_ON_CLOSE */
+    if (DeleteOnClose & FlagOn(Vcb->State, VCB_STATE_FLAG_WRITE_PROTECTED))
+    {
+        ASSERT(FALSE);
+        return STATUS_NOT_IMPLEMENTED;
+    }
 
     // TODO: Make sure EAs aren't supported on FAT32
 
@@ -273,17 +685,84 @@ FatiCreate(IN PFAT_IRP_CONTEXT IrpContext,
         if (!RelatedFO ||
             FatDecodeFileObject(RelatedFO, &DecodedVcb, &Fcb, &Ccb) == UserVolumeOpen)
         {
+            /* Check parameters */
+            if (DirectoryFile || OpenTargetDirectory)
+            {
+                Status = DirectoryFile ? STATUS_NOT_A_DIRECTORY : STATUS_INVALID_PARAMETER;
+
+                /* Unlock VCB */
+                FatReleaseVcb(IrpContext, Vcb);
+
+                /* Complete the request and return */
+                FatCompleteRequest(IrpContext, Irp, Status);
+                return Status;
+            }
+
             /* It is indeed a volume open request */
-            DPRINT1("Volume open request, not implemented now!\n");
-            UNIMPLEMENTED;
+            Iosb = FatiOpenVolume(IrpContext,
+                                  FileObject,
+                                  Vcb,
+                                  DesiredAccess,
+                                  ShareAccess,
+                                  CreateDisposition);
+
+            /* Set resulting information */
+            Irp->IoStatus.Information = Iosb.Information;
+
+            /* Unlock VCB */
+            FatReleaseVcb(IrpContext, Vcb);
+
+            /* Complete the request and return */
+            FatCompleteRequest(IrpContext, Irp, Iosb.Status);
+            return Iosb.Status;
         }
     }
 
     /* Check if this is a relative open */
     if (RelatedFO)
     {
-        // RelatedFO will be a parent directory
-        UNIMPLEMENTED;
+        /* Decode the file object */
+        TypeOfOpen = FatDecodeFileObject(RelatedFO,
+                                         &RelatedVcb,
+                                         &RelatedDcb,
+                                         &RelatedCcb);
+
+        /* Check open type */
+        if (TypeOfOpen != UserFileOpen &&
+            TypeOfOpen != UserDirectoryOpen)
+        {
+            DPRINT1("Invalid file object!\n");
+
+            Status = STATUS_OBJECT_PATH_NOT_FOUND;
+
+            /* Cleanup and return */
+            FatReleaseVcb(IrpContext, Vcb);
+            FatCompleteRequest(IrpContext, Irp, Status);
+            return Status;
+        }
+
+        /* File path must be relative */
+        if (FileName.Length != 0 &&
+            FileName.Buffer[0] == L'\\')
+        {
+            Status = STATUS_OBJECT_NAME_INVALID;
+
+            /* The name is absolute, fail */
+            FatReleaseVcb(IrpContext, Vcb);
+            FatCompleteRequest(IrpContext, Irp, Status);
+            return Status;
+        }
+
+        /* Make sure volume is the same */
+        ASSERT(RelatedVcb == Vcb);
+
+        /* Save VPB */
+        FileObject->Vpb = RelatedFO->Vpb;
+
+        /* Set parent DCB */
+        ParentDcb = RelatedDcb;
+
+        DPRINT("Opening file '%wZ' relatively to '%wZ'\n", &FileName, &ParentDcb->FullFileName);
     }
     else
     {
@@ -295,18 +774,34 @@ FatiCreate(IN PFAT_IRP_CONTEXT IrpContext,
             if (NonDirectoryFile)
             {
                 DPRINT1("Trying to open root dir as a file\n");
+                Status = STATUS_FILE_IS_A_DIRECTORY;
+
+                /* Cleanup and return */
+                FatReleaseVcb(IrpContext, Vcb);
+                FatCompleteRequest(IrpContext, Irp, Status);
+                return Status;
+            }
+
+            /* Check for target directory on a root dir */
+            if (OpenTargetDirectory)
+            {
+                Status = STATUS_INVALID_PARAMETER;
 
                 /* Cleanup and return */
                 FatReleaseVcb(IrpContext, Vcb);
-                return STATUS_FILE_IS_A_DIRECTORY;
+                FatCompleteRequest(IrpContext, Irp, Status);
+                return Status;
             }
 
             /* Check delete on close on a root dir */
             if (DeleteOnClose)
             {
+                Status = STATUS_CANNOT_DELETE;
+
                 /* Cleanup and return */
                 FatReleaseVcb(IrpContext, Vcb);
-                return STATUS_CANNOT_DELETE;
+                FatCompleteRequest(IrpContext, Irp, Status);
+                return Status;
             }
 
             /* Call root directory open routine */
@@ -321,144 +816,298 @@ FatiCreate(IN PFAT_IRP_CONTEXT IrpContext,
 
             /* Cleanup and return */
             FatReleaseVcb(IrpContext, Vcb);
+            FatCompleteRequest(IrpContext, Irp, Iosb.Status);
             return Iosb.Status;
         }
         else
         {
             /* Not a root dir */
             ParentDcb = Vcb->RootDcb;
-            DPRINT1("ParentDcb %p\n", ParentDcb);
+            DPRINT("ParentDcb %p\n", ParentDcb);
         }
+    }
 
-        /* Check for backslash at the end */
-        if (FileName.Length &&
-            FileName.Buffer[FileName.Length / sizeof(WCHAR) - 1] == L'\\')
+    /* Check for backslash at the end */
+    if (FileName.Length &&
+        FileName.Buffer[FileName.Length / sizeof(WCHAR) - 1] == L'\\')
+    {
+        /* Cut it out */
+        FileName.Length -= sizeof(WCHAR);
+
+        /* Remember we cut it */
+        EndBackslash = TRUE;
+    }
+
+    /* Ensure the name is set */
+    if (!ParentDcb->FullFileName.Buffer)
+    {
+        /* Set it if it's missing */
+        FatSetFullFileNameInFcb(IrpContext, ParentDcb);
+    }
+
+    /* Check max path length */
+    if (ParentDcb->FullFileName.Length + FileName.Length + sizeof(WCHAR) <= FileName.Length)
+    {
+        DPRINT1("Max length is way off\n");
+        Iosb.Status = STATUS_OBJECT_NAME_INVALID;
+        ASSERT(FALSE);
+    }
+
+    /* Loop through FCBs to find a good one */
+    while (TRUE)
+    {
+        Fcb = ParentDcb;
+
+        /* Dissect the name */
+        RemainingPart = FileName;
+        while (RemainingPart.Length)
         {
-            /* Cut it out */
-            FileName.Length -= sizeof(WCHAR);
+            FsRtlDissectName(RemainingPart, &FirstName, &NextName);
 
-            /* Remember we cut it */
-            EndBackslash = TRUE;
+            /* Check for validity */
+            if ((NextName.Length && NextName.Buffer[0] == L'\\') ||
+                (NextName.Length > 255 * sizeof(WCHAR)))
+            {
+                /* The name is invalid */
+                DPRINT1("Invalid name found\n");
+                Iosb.Status = STATUS_OBJECT_NAME_INVALID;
+                ASSERT(FALSE);
+            }
+
+            /* Convert the name to ANSI */
+            AnsiFirstName.Buffer = ExAllocatePool(PagedPool, FirstName.Length);
+            AnsiFirstName.Length = 0;
+            AnsiFirstName.MaximumLength = FirstName.Length;
+            Status = RtlUpcaseUnicodeStringToCountedOemString(&AnsiFirstName, &FirstName, FALSE);
+
+            if (!NT_SUCCESS(Status))
+            {
+                DPRINT1("RtlUpcaseUnicodeStringToCountedOemString() failed with 0x%08x\n", Status);
+                ASSERT(FALSE);
+                NextFcb = NULL;
+                AnsiFirstName.Length = 0;
+            }
+            else
+            {
+                /* Find the coresponding FCB */
+                NextFcb = FatFindFcb(IrpContext,
+                                     &Fcb->Dcb.SplayLinksAnsi,
+                                     (PSTRING)&AnsiFirstName,
+                                     &OpenedAsDos);
+            }
+
+            /* If nothing found - try with unicode */
+            if (!NextFcb && Fcb->Dcb.SplayLinksUnicode)
+            {
+                FileNameUpcased.Buffer = FsRtlAllocatePool(PagedPool, FirstName.Length);
+                FileNameUpcased.Length = 0;
+                FileNameUpcased.MaximumLength = FirstName.Length;
+
+                /* Downcase and then upcase to normalize it */
+                Status = RtlDowncaseUnicodeString(&FileNameUpcased, &FirstName, FALSE);
+                Status = RtlUpcaseUnicodeString(&FileNameUpcased, &FileNameUpcased, FALSE);
+
+                /* Try to find FCB again using unicode name */
+                NextFcb = FatFindFcb(IrpContext,
+                                     &Fcb->Dcb.SplayLinksUnicode,
+                                     (PSTRING)&FileNameUpcased,
+                                     &OpenedAsDos);
+            }
+
+            /* Move to the next FCB */
+            if (NextFcb)
+            {
+                Fcb = NextFcb;
+                RemainingPart = NextName;
+            }
+
+            /* Break out of this loop if nothing can be found */
+            if (!NextFcb ||
+                NextName.Length == 0 ||
+                FatNodeType(NextFcb) == FAT_NTC_FCB)
+            {
+                break;
+            }
         }
 
-        /* Ensure the name is set */
-        if (!ParentDcb->FullFileName.Buffer)
+        /* Ensure remaining name doesn't start from a backslash */
+        if (RemainingPart.Length &&
+            RemainingPart.Buffer[0] == L'\\')
         {
-            DPRINT1("ParentDcb->FullFileName.Buffer is NULL\n");
+            /* Cut it */
+            RemainingPart.Buffer++;
+            RemainingPart.Length -= sizeof(WCHAR);
         }
 
-        /* Check max path length */
-        if (ParentDcb->FullFileName.Length + FileName.Length + sizeof(WCHAR) <= FileName.Length)
+        if (Fcb->Condition == FcbGood)
+        {
+            /* Good FCB, break out of the loop */
+            break;
+        }
+        else
         {
-            DPRINT1("Max length is way off\n");
-            Iosb.Status = STATUS_OBJECT_NAME_INVALID;
             ASSERT(FALSE);
         }
+    }
 
-        /* Loop through FCBs to find a good one */
-        while (TRUE)
-        {
-            Fcb = ParentDcb;
+    /* Treat page file in a special way */
+    if (IsPagingFile)
+    {
+        UNIMPLEMENTED;
+        // FIXME: System file too
+    }
 
-            /* Dissect the name */
-            RemainingPart = FileName;
-            while (RemainingPart.Length)
-            {
-                FsRtlDissectName(RemainingPart, &FirstName, &NextName);
+    /* Make sure there is no pending delete on a higher-level FCB */
+    if (Fcb->State & FCB_STATE_DELETE_ON_CLOSE)
+    {
+        Iosb.Status = STATUS_DELETE_PENDING;
 
-                /* Check for validity */
-                if ((NextName.Length && NextName.Buffer[0] == L'\\') ||
-                    (NextName.Length > 255 * sizeof(WCHAR)))
-                {
-                    /* The name is invalid */
-                    DPRINT1("Invalid name found\n");
-                    Iosb.Status = STATUS_OBJECT_NAME_INVALID;
-                    ASSERT(FALSE);
-                }
+        /* Cleanup and return */
+        FatReleaseVcb(IrpContext, Vcb);
 
-                /* Convert the name to ANSI */
-                AnsiFirstName.Buffer = ExAllocatePool(PagedPool, FirstName.Length);
-                AnsiFirstName.Length = 0;
-                AnsiFirstName.MaximumLength = FirstName.Length;
-                Status = RtlUpcaseUnicodeStringToCountedOemString(&AnsiFirstName, &FirstName, FALSE);
+        /* Complete the request */
+        FatCompleteRequest(IrpContext, Irp, Iosb.Status);
 
-                if (!NT_SUCCESS(Status))
-                {
-                    DPRINT1("RtlUpcaseUnicodeStringToCountedOemString() failed with 0x%08x\n", Status);
-                    ASSERT(FALSE);
-                    NextFcb = NULL;
-                    AnsiFirstName.Length = 0;
-                }
-                else
-                {
-                    /* Find the coresponding FCB */
-                    NextFcb = FatFindFcb(IrpContext,
-                                         &Fcb->Dcb.SplayLinksAnsi,
-                                        (PSTRING)&AnsiFirstName,
-                                         &OpenedAsDos);
-                }
+        return Iosb.Status;
+    }
 
-                /* Check if we found anything */
-                if (!NextFcb && Fcb->Dcb.SplayLinksUnicode)
-                {
-                    ASSERT(FALSE);
-                }
+    /* We have a valid FCB now */
+    if (!RemainingPart.Length)
+    {
+        /* Check for target dir open */
+        if (OpenTargetDirectory)
+        {
+            DPRINT1("Opening target dir is missing\n");
+            ASSERT(FALSE);
+        }
 
-                /* Move to the next FCB */
-                if (NextFcb)
-                {
-                    Fcb = NextFcb;
-                    RemainingPart = NextName;
-                }
+        /* Check this FCB's type */
+        if (FatNodeType(Fcb) == FAT_NTC_ROOT_DCB ||
+            FatNodeType(Fcb) == FAT_NTC_DCB)
+        {
+            /* Open a directory */
+            if (NonDirectoryFile)
+            {
+                /* Forbidden */
+                Iosb.Status = STATUS_FILE_IS_A_DIRECTORY;
+                ASSERT(FALSE);
+                return Iosb.Status;
+            }
 
-                /* Break out of this loop if nothing can be found */
-                if (!NextFcb ||
-                    NextName.Length == 0 ||
-                    FatNodeType(NextFcb) == FAT_NTC_FCB)
-                {
-                    break;
-                }
+            /* Open existing DCB */
+            Iosb = FatiOpenExistingDcb(IrpContext,
+                                       FileObject,
+                                       Vcb,
+                                       Fcb,
+                                       DesiredAccess,
+                                       ShareAccess,
+                                       CreateDisposition,
+                                       NoEaKnowledge,
+                                       DeleteOnClose);
+
+            /* Save information */
+            Irp->IoStatus.Information = Iosb.Information;
+
+            /* Unlock VCB */
+            FatReleaseVcb(IrpContext, Vcb);
+
+            /* Complete the request */
+            FatCompleteRequest(IrpContext, Irp, Iosb.Status);
+
+            return Iosb.Status;
+        }
+        else if (FatNodeType(Fcb) == FAT_NTC_FCB)
+        {
+            /* Open a file */
+            if (OpenDirectory)
+            {
+                /* Forbidden */
+                Iosb.Status = STATUS_NOT_A_DIRECTORY;
+                ASSERT(FALSE);
+                return Iosb.Status;
             }
 
-            /* Ensure remaining name doesn't start from a backslash */
-            if (RemainingPart.Length &&
-                RemainingPart.Buffer[0] == L'\\')
+            /* Check for trailing backslash */
+            if (EndBackslash)
             {
-                /* Cut it */
-                RemainingPart.Buffer++;
-                RemainingPart.Length -= sizeof(WCHAR);
+                /* Forbidden */
+                Iosb.Status = STATUS_OBJECT_NAME_INVALID;
+                ASSERT(FALSE);
+                return Iosb.Status;
             }
 
-            if (Fcb->Condition == FcbGood)
+            Iosb = FatiOpenExistingFcb(IrpContext,
+                                       FileObject,
+                                       Vcb,
+                                       Fcb,
+                                       DesiredAccess,
+                                       ShareAccess,
+                                       AllocationSize,
+                                       EaBuffer,
+                                       EaLength,
+                                       FileAttributes,
+                                       CreateDisposition,
+                                       NoEaKnowledge,
+                                       DeleteOnClose,
+                                       OpenedAsDos,
+                                       &OplockPostIrp);
+
+            /* Check if it's pending */
+            if (Iosb.Status != STATUS_PENDING)
             {
-                /* Good FCB, break out of the loop */
-                break;
+                /* In case of success set cache supported flag */
+                if (NT_SUCCESS(Iosb.Status) && !NoIntermediateBuffering)
+                {
+                    SetFlag(FileObject->Flags, FO_CACHE_SUPPORTED);
+                }
+
+                /* Save information */
+                Irp->IoStatus.Information = Iosb.Information;
+
+                /* Unlock VCB */
+                FatReleaseVcb(IrpContext, Vcb);
+
+                /* Complete the request */
+                FatCompleteRequest(IrpContext, Irp, Iosb.Status);
+
+                return Iosb.Status;
             }
             else
             {
+                /* Queue this IRP */
+                UNIMPLEMENTED;
                 ASSERT(FALSE);
             }
         }
-
-        /* We have a valid FCB now */
-        if (!RemainingPart.Length)
+        else
         {
-            DPRINT1("It's possible to open an existing FCB\n");
-            ASSERT(FALSE);
+            /* Unexpected FCB type */
+            KeBugCheckEx(FAT_FILE_SYSTEM, __LINE__, (ULONG_PTR)Fcb, 0, 0);
         }
+    }
+
+    /* During parsing we encountered a part which has no attached FCB/DCB.
+    Check that the parent is really DCB and not FCB */
+    if (FatNodeType(Fcb) != FAT_NTC_ROOT_DCB &&
+        FatNodeType(Fcb) != FAT_NTC_DCB)
+    {
+        DPRINT1("Weird FCB node type %x, expected DCB or root DCB\n", FatNodeType(Fcb));
+        ASSERT(FALSE);
+    }
 
-        /* During parsing we encountered a part which has no attached FCB/DCB.
-           Check that the parent is really DCB and not FCB */
-        if (FatNodeType(Fcb) != FAT_NTC_ROOT_DCB &&
-            FatNodeType(Fcb) != FAT_NTC_DCB)
+    /* Create additional DCBs for all path items */
+    ParentDcb = Fcb;
+    while (TRUE)
+    {
+        if (FirstRun)
         {
-            DPRINT1("Weird FCB node type %x, expected DCB or root DCB\n", FatNodeType(Fcb));
-            ASSERT(FALSE);
+            RemainingPart = NextName;
+            if (AnsiFirstName.Length)
+                Status = STATUS_SUCCESS;
+            else
+                Status = STATUS_UNMAPPABLE_CHARACTER;
         }
-
-        /* Create additional DCBs for all path items */
-        ParentDcb = Fcb;
-        while (TRUE)
+        else
         {
             FsRtlDissectName(RemainingPart, &FirstName, &RemainingPart);
 
@@ -477,55 +1126,147 @@ FatiCreate(IN PFAT_IRP_CONTEXT IrpContext,
             AnsiFirstName.Length = 0;
             AnsiFirstName.MaximumLength = FirstName.Length;
             Status = RtlUpcaseUnicodeStringToCountedOemString(&AnsiFirstName, &FirstName, FALSE);
+        }
 
-            if (!NT_SUCCESS(Status))
+        if (!NT_SUCCESS(Status))
+        {
+            ASSERT(FALSE);
+        }
+
+        DPRINT("FirstName %wZ, RemainingPart %wZ\n", &FirstName, &RemainingPart);
+
+        /* Break if came to the end */
+        if (!RemainingPart.Length) break;
+
+        /* Create a DCB for this entry */
+        ParentDcb = FatCreateDcb(IrpContext,
+                                 Vcb,
+                                 ParentDcb,
+                                 NULL);
+
+        /* Set its name */
+        FatSetFullNameInFcb(ParentDcb, &FirstName);
+    }
+
+    /* Try to open it and get a result, saying if this is a dir or a file */
+    FfError = FatiTryToOpen(FileObject, Vcb);
+
+    /* Check if we need to open target directory */
+    if (OpenTargetDirectory)
+    {
+        // TODO: Open target directory
+        UNIMPLEMENTED;
+    }
+
+    /* Check, if path is a directory or a file */
+    if (FfError == FF_ERR_FILE_OBJECT_IS_A_DIR ||
+        FfError == FF_ERR_NONE)
+    {
+        if (FfError == FF_ERR_FILE_OBJECT_IS_A_DIR)
+        {
+            if (NonDirectoryFile)
             {
-                ASSERT(FALSE);
+                DPRINT1("Can't open dir as a file\n");
+
+                /* Unlock VCB */
+                FatReleaseVcb(IrpContext, Vcb);
+
+                /* Complete the request */
+                Iosb.Status = STATUS_FILE_IS_A_DIRECTORY;
+                FatCompleteRequest(IrpContext, Irp, Iosb.Status);
+                return Iosb.Status;
             }
 
-            DPRINT1("FirstName %wZ, RemainingPart %wZ\n", &FirstName, &RemainingPart);
+            /* Open this directory */
+            Iosb = FatiOpenExistingDir(IrpContext,
+                FileObject,
+                Vcb,
+                ParentDcb,
+                DesiredAccess,
+                ShareAccess,
+                AllocationSize,
+                EaBuffer,
+                EaLength,
+                FileAttributes,
+                CreateDisposition,
+                DeleteOnClose);
 
-            /* Break if came to the end */
-            if (!RemainingPart.Length) break;
+            Irp->IoStatus.Information = Iosb.Information;
 
-            // TODO: Create a DCB for this entry
-        }
+            /* Unlock VCB */
+            FatReleaseVcb(IrpContext, Vcb);
 
-        // TODO: Try to open directory
+            /* Complete the request */
+            FatCompleteRequest(IrpContext, Irp, Iosb.Status);
 
-        /* If end backslash here, then it's definately not permitted,
-           since we're opening files here */
-        if (EndBackslash)
+            return Iosb.Status;
+        }
+        else
         {
+            /* This is opening an existing file */
+            if (OpenDirectory)
+            {
+                /* But caller wanted a dir */
+                Status = STATUS_NOT_A_DIRECTORY;
+
+                /* Unlock VCB */
+                FatReleaseVcb(IrpContext, Vcb);
+
+                /* Complete the request */
+                FatCompleteRequest(IrpContext, Irp, Status);
+
+                return Status;
+            }
+
+            /* If end backslash here, then it's definately not permitted,
+            since we're opening files here */
+            if (EndBackslash)
+            {
+                /* Unlock VCB */
+                FatReleaseVcb(IrpContext, Vcb);
+
+                /* Complete the request */
+                Iosb.Status = STATUS_OBJECT_NAME_INVALID;
+                FatCompleteRequest(IrpContext, Irp, Iosb.Status);
+                return Iosb.Status;
+            }
+
+            /* Try to open the file */
+            Iosb = FatiOpenExistingFile(IrpContext,
+                                        FileObject,
+                                        Vcb,
+                                        ParentDcb,
+                                        DesiredAccess,
+                                        ShareAccess,
+                                        AllocationSize,
+                                        EaBuffer,
+                                        EaLength,
+                                        FileAttributes,
+                                        CreateDisposition,
+                                        FALSE,
+                                        DeleteOnClose,
+                                        OpenedAsDos);
+
+            /* In case of success set cache supported flag */
+            if (NT_SUCCESS(Iosb.Status) && !NoIntermediateBuffering)
+            {
+                SetFlag(FileObject->Flags, FO_CACHE_SUPPORTED);
+            }
+
+            Irp->IoStatus.Information = Iosb.Information;
+
+            /* Unlock VCB */
+            FatReleaseVcb(IrpContext, Vcb);
+
             /* Complete the request */
-            Iosb.Status = STATUS_OBJECT_NAME_INVALID;
             FatCompleteRequest(IrpContext, Irp, Iosb.Status);
+
             return Iosb.Status;
         }
+    }
 
-        /* Try to open the file */
-        Iosb = FatiOpenExistingFile(IrpContext,
-                                    FileObject,
-                                    Vcb,
-                                    ParentDcb,
-                                    DesiredAccess,
-                                    ShareAccess,
-                                    AllocationSize,
-                                    EaBuffer,
-                                    EaLength,
-                                    FileAttributes,
-                                    CreateDisposition,
-                                    FALSE,
-                                    DeleteOnClose,
-                                    OpenedAsDos);
-
-        Irp->IoStatus.Information = Iosb.Information;
-    }
-
-    /* Complete the request */
-    FatCompleteRequest(IrpContext, Irp, Iosb.Status);
-
-    return Iosb.Status;
+    /* We come here only in the case when a new file is created */
+    ASSERT(FALSE);
 }
 
 NTSTATUS
@@ -534,9 +1275,6 @@ FatCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp)
 {
     PFAT_IRP_CONTEXT IrpContext;
     NTSTATUS Status;
-    //PVOLUME_DEVICE_OBJECT VolumeDO = (PVOLUME_DEVICE_OBJECT)DeviceObject;
-
-    DPRINT1("FatCreate()\n");
 
     /* If it's called with our Disk FS device object - it's always open */
     // TODO: Add check for CDROM FS device object