[fastfat_new]
[reactos.git] / reactos / drivers / filesystems / fastfat_new / create.c
index 88ccc95..85d2b5a 100644 (file)
 #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
+NTAPI
+FatiOpenRootDcb(IN PFAT_IRP_CONTEXT IrpContext,
+                IN PFILE_OBJECT FileObject,
+                IN PVCB Vcb,
+                IN PACCESS_MASK DesiredAccess,
+                IN USHORT ShareAccess,
+                IN ULONG CreateDisposition)
+{
+    IO_STATUS_BLOCK Iosb;
+
+    DPRINT1("Opening root directory\n");
+
+    Iosb.Status = STATUS_NOT_IMPLEMENTED;
+
+    return Iosb;
+}
+
+IO_STATUS_BLOCK
+NTAPI
+FatiOpenExistingFile(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 IsPagingFile,
+                     IN BOOLEAN DeleteOnClose,
+                     IN BOOLEAN IsDosName)
+{
+    IO_STATUS_BLOCK Iosb = {{0}};
+    OEM_STRING AnsiName;
+    CHAR AnsiNameBuf[512];
+    PFCB Fcb;
+    NTSTATUS Status;
+    FF_FILE *FileHandle;
+
+    /* Check for create file option and fail */
+    if (CreateDisposition == FILE_CREATE)
+    {
+        Iosb.Status = STATUS_OBJECT_NAME_COLLISION;
+        return Iosb;
+    }
+
+    // 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, NULL);
+
+    if (!FileHandle)
+    {
+        Iosb.Status = STATUS_OBJECT_NAME_NOT_FOUND; // FIXME: A shortcut for now
+        return Iosb;
+    }
+
+    /* Create a new FCB for this file */
+    Fcb = FatCreateFcb(IrpContext, Vcb, ParentDcb, FileHandle);
+
+    // TODO: Check if overwrite is needed
+
+    // This is usual file open branch, without overwriting!
+    /* Set context and section object pointers */
+    FatSetFileObject(FileObject,
+                     UserFileOpen,
+                     Fcb,
+                     FatCreateCcb());
+    FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
+
+    Iosb.Status = STATUS_SUCCESS;
+    Iosb.Information = FILE_OPENED;
+
+    return Iosb;
+}
+
 NTSTATUS
 NTAPI
 FatiCreate(IN PFAT_IRP_CONTEXT IrpContext,
@@ -34,8 +134,9 @@ FatiCreate(IN PFAT_IRP_CONTEXT IrpContext,
 
     /* Control blocks */
     PVCB Vcb, DecodedVcb;
-    PFCB Fcb;
+    PFCB Fcb, NextFcb;
     PCCB Ccb;
+    PFCB ParentDcb;
 
     /* IRP data */
     PFILE_OBJECT FileObject;
@@ -51,8 +152,13 @@ FatiCreate(IN PFAT_IRP_CONTEXT IrpContext,
 
     /* Misc */
     NTSTATUS Status;
-    //IO_STATUS_BLOCK Iosb;
+    IO_STATUS_BLOCK Iosb;
     PIO_STACK_LOCATION IrpSp;
+    BOOLEAN EndBackslash = FALSE, OpenedAsDos;
+    UNICODE_STRING RemainingPart, FirstName, NextName;
+    OEM_STRING AnsiFirstName;
+
+    Iosb.Status = STATUS_SUCCESS;
 
     /* Get current IRP stack location */
     IrpSp = IoGetCurrentIrpStackLocation(Irp);
@@ -184,10 +290,10 @@ FatiCreate(IN PFAT_IRP_CONTEXT IrpContext,
 
     // TODO: Make sure EAs aren't supported on FAT32
 
+    /* Check if it's a volume open request */
     if (FileName.Length == 0)
     {
-        /* It is a volume open request, check related FO to be sure */
-
+        /* Test related FO to be sure */
         if (!RelatedFO ||
             FatDecodeFileObject(RelatedFO, &DecodedVcb, &Fcb, &Ccb) == UserVolumeOpen)
         {
@@ -197,8 +303,265 @@ FatiCreate(IN PFAT_IRP_CONTEXT IrpContext,
         }
     }
 
-    //return Iosb.Status;
-    return STATUS_SUCCESS;
+    /* Check if this is a relative open */
+    if (RelatedFO)
+    {
+        // RelatedFO will be a parent directory
+        UNIMPLEMENTED;
+    }
+    else
+    {
+        /* Absolute open */
+        if ((FileName.Length == sizeof(WCHAR)) &&
+            (FileName.Buffer[0] == L'\\'))
+        {
+            /* Check if it's ok to open it */
+            if (NonDirectoryFile)
+            {
+                DPRINT1("Trying to open root dir as a file\n");
+
+                /* Cleanup and return */
+                FatReleaseVcb(IrpContext, Vcb);
+                return STATUS_FILE_IS_A_DIRECTORY;
+            }
+
+            /* Check delete on close on a root dir */
+            if (DeleteOnClose)
+            {
+                /* Cleanup and return */
+                FatReleaseVcb(IrpContext, Vcb);
+                return STATUS_CANNOT_DELETE;
+            }
+
+            /* Call root directory open routine */
+            Iosb = FatiOpenRootDcb(IrpContext,
+                                   FileObject,
+                                   Vcb,
+                                   DesiredAccess,
+                                   ShareAccess,
+                                   CreateDisposition);
+
+            Irp->IoStatus.Information = Iosb.Information;
+
+            /* Cleanup and return */
+            FatReleaseVcb(IrpContext, Vcb);
+            return Iosb.Status;
+        }
+        else
+        {
+            /* Not a root dir */
+            ParentDcb = Vcb->RootDcb;
+            DPRINT1("ParentDcb %p\n", ParentDcb);
+        }
+
+        /* 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)
+        {
+            DPRINT1("ParentDcb->FullFileName.Buffer is NULL\n");
+        }
+
+        /* 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)
+            {
+                FsRtlDissectName(RemainingPart, &FirstName, &NextName);
+
+                /* 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);
+                }
+
+                /* Check if we found anything */
+                if (!NextFcb && Fcb->Dcb.SplayLinksUnicode)
+                {
+                    ASSERT(FALSE);
+                }
+
+                /* 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 remaining name doesn't start from a backslash */
+            if (RemainingPart.Length &&
+                RemainingPart.Buffer[0] == L'\\')
+            {
+                /* Cut it */
+                RemainingPart.Buffer++;
+                RemainingPart.Length -= sizeof(WCHAR);
+            }
+
+            if (Fcb->Condition == FcbGood)
+            {
+                /* Good FCB, break out of the loop */
+                break;
+            }
+            else
+            {
+                ASSERT(FALSE);
+            }
+        }
+
+        /* We have a valid FCB now */
+        if (!RemainingPart.Length)
+        {
+            DPRINT1("It's possible to open an existing FCB\n");
+            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)
+        {
+            DPRINT1("Weird FCB node type %x, expected DCB or root DCB\n", FatNodeType(Fcb));
+            ASSERT(FALSE);
+        }
+
+        /* Create additional DCBs for all path items */
+        ParentDcb = Fcb;
+        while (TRUE)
+        {
+            FsRtlDissectName(RemainingPart, &FirstName, &RemainingPart);
+
+            /* Check for validity */
+            if ((RemainingPart.Length && RemainingPart.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))
+            {
+                ASSERT(FALSE);
+            }
+
+            DPRINT1("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);
+
+            /* Set its name */
+            FatSetFullNameInFcb(ParentDcb, &FirstName);
+        }
+
+        // TODO: Try to open directory
+
+        /* 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);
+
+        Irp->IoStatus.Information = Iosb.Information;
+    }
+
+    /* Unlock VCB */
+    FatReleaseVcb(IrpContext, Vcb);
+
+    /* Complete the request */
+    FatCompleteRequest(IrpContext, Irp, Iosb.Status);
+
+    return Iosb.Status;
 }
 
 NTSTATUS
@@ -207,7 +570,6 @@ FatCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp)
 {
     PFAT_IRP_CONTEXT IrpContext;
     NTSTATUS Status;
-    //PVOLUME_DEVICE_OBJECT VolumeDO = (PVOLUME_DEVICE_OBJECT)DeviceObject;
 
     DPRINT1("FatCreate()\n");