[FASTFAT]
[reactos.git] / reactos / drivers / filesystems / fastfat / create.c
index 04d0c0d..0859762 100644 (file)
  */
 /*
  * PROJECT:          ReactOS kernel
- * FILE:             drivers/fs/vfat/create.c
+ * FILE:             drivers/filesystems/fastfat/create.c
  * PURPOSE:          VFAT Filesystem
  * PROGRAMMER:       Jason Filby (jasonfilby@yahoo.com)
+ *                   Pierre Schweitzer (pierre@reactos.org)
  */
 
 /* INCLUDES *****************************************************************/
 
-#define NDEBUG
 #include "vfat.h"
 
+#define NDEBUG
+#include <debug.h>
+
 /* FUNCTIONS *****************************************************************/
 
 VOID
@@ -272,7 +275,7 @@ FindFile(
         return Status;
     }
 
-    while(TRUE)
+    while (TRUE)
     {
         Status = DeviceExt->GetNextDirEntry(&Context, &Page, Parent, DirContext, First);
         First = FALSE;
@@ -362,7 +365,6 @@ VfatOpenFile(
         DPRINT("'%wZ'\n", &FileObject->RelatedFileObject->FileName);
 
         *ParentFcb = FileObject->RelatedFileObject->FsContext;
-        (*ParentFcb)->RefCount++;
     }
     else
     {
@@ -380,7 +382,7 @@ VfatOpenFile(
                                           FALSE);
         if (!NT_SUCCESS(Status))
         {
-            DPRINT ("Status %lx\n", Status);
+            DPRINT("Status %lx\n", Status);
             *ParentFcb = NULL;
             return Status;
         }
@@ -388,21 +390,22 @@ VfatOpenFile(
 
     if (*ParentFcb)
     {
-        (*ParentFcb)->RefCount++;
+        vfatGrabFCB(DeviceExt, *ParentFcb);
     }
 
     /*  try first to find an existing FCB in memory  */
     DPRINT("Checking for existing FCB in memory\n");
 
     Status = vfatGetFCBForFile(DeviceExt, ParentFcb, &Fcb, PathNameU);
-    if (!NT_SUCCESS (Status))
+    if (!NT_SUCCESS(Status))
     {
         DPRINT ("Could not make a new FCB, status: %x\n", Status);
         return  Status;
     }
+
     if (Fcb->Flags & FCB_DELETE_PENDING)
     {
-        vfatReleaseFCB (DeviceExt, Fcb);
+        vfatReleaseFCB(DeviceExt, Fcb);
         return STATUS_DELETE_PENDING;
     }
 
@@ -418,7 +421,7 @@ VfatOpenFile(
     Status = vfatAttachFCBToFileObject(DeviceExt, Fcb, FileObject);
     if (!NT_SUCCESS(Status))
     {
-        vfatReleaseFCB (DeviceExt, Fcb);
+        vfatReleaseFCB(DeviceExt, Fcb);
     }
     return  Status;
 }
@@ -439,17 +442,20 @@ VfatCreateFile(
     PVFATFCB pFcb = NULL;
     PVFATFCB ParentFcb = NULL;
     PWCHAR c, last;
-    BOOLEAN PagingFileCreate = FALSE;
+    BOOLEAN PagingFileCreate;
     BOOLEAN Dots;
+    BOOLEAN OpenTargetDir;
     UNICODE_STRING FileNameU;
     UNICODE_STRING PathNameU;
     ULONG Attributes;
 
     /* Unpack the various parameters. */
-    Stack = IoGetCurrentIrpStackLocation (Irp);
+    Stack = IoGetCurrentIrpStackLocation(Irp);
     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;
+    OpenTargetDir = (Stack->Flags & SL_OPEN_TARGET_DIRECTORY) ? TRUE : FALSE;
+
     FileObject = Stack->FileObject;
     DeviceExt = DeviceObject->DeviceExtension;
 
@@ -466,10 +472,18 @@ VfatCreateFile(
         return STATUS_INVALID_PARAMETER;
     }
 
+    /* Deny create if the volume is locked */
+    if (DeviceExt->Flags & VCB_VOLUME_LOCKED)
+    {
+        return STATUS_ACCESS_DENIED;
+    }
+
     /* This a open operation for the volume itself */
     if (FileObject->FileName.Length == 0 &&
         (FileObject->RelatedFileObject == NULL || FileObject->RelatedFileObject->FsContext2 != NULL))
     {
+        DPRINT("Volume opening\n");
+
         if (RequestedDisposition != FILE_OPEN &&
             RequestedDisposition != FILE_OPEN_IF)
         {
@@ -483,9 +497,36 @@ VfatCreateFile(
         }
 #endif
 
+        if (OpenTargetDir)
+        {
+            return STATUS_INVALID_PARAMETER;
+        }
+
         pFcb = DeviceExt->VolumeFcb;
+
+        if (pFcb->OpenHandleCount == 0)
+        {
+            IoSetShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
+                             Stack->Parameters.Create.ShareAccess,
+                             FileObject,
+                             &pFcb->FCBShareAccess);
+        }
+        else
+        {
+            Status = IoCheckShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
+                                        Stack->Parameters.Create.ShareAccess,
+                                        FileObject,
+                                        &pFcb->FCBShareAccess,
+                                        FALSE);
+            if (!NT_SUCCESS(Status))
+            {
+                return Status;
+            }
+        }
+
         vfatAttachFCBToFileObject(DeviceExt, pFcb, FileObject);
-        pFcb->RefCount++;
+        DeviceExt->OpenHandleCount++;
+        pFcb->OpenHandleCount++;
 
         Irp->IoStatus.Information = FILE_OPENED;
         return STATUS_SUCCESS;
@@ -518,6 +559,13 @@ VfatCreateFile(
         }
     }
 
+    /* Check if we try to open target directory of root dir */
+    if (OpenTargetDir && FileObject->RelatedFileObject == NULL && PathNameU.Length == sizeof(WCHAR) &&
+        PathNameU.Buffer[0] == L'\\')
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
     if (FileObject->RelatedFileObject && PathNameU.Length >= sizeof(WCHAR) && PathNameU.Buffer[0] == L'\\')
     {
         return STATUS_OBJECT_NAME_INVALID;
@@ -529,7 +577,110 @@ VfatCreateFile(
     }
 
     /* Try opening the file. */
-    Status = VfatOpenFile(DeviceExt, &PathNameU, FileObject, RequestedDisposition, &ParentFcb);
+    if (!OpenTargetDir)
+    {
+        Status = VfatOpenFile(DeviceExt, &PathNameU, FileObject, RequestedDisposition, &ParentFcb);
+    }
+    else
+    {
+        PVFATFCB TargetFcb;
+        LONG idx, FileNameLen;
+
+        ParentFcb = (FileObject->RelatedFileObject != NULL) ? FileObject->RelatedFileObject->FsContext : NULL;
+        if (ParentFcb)
+        {
+            vfatGrabFCB(DeviceExt, ParentFcb);
+        }
+        Status = vfatGetFCBForFile(DeviceExt, &ParentFcb, &TargetFcb, &PathNameU);
+
+        if (NT_SUCCESS(Status))
+        {
+            vfatReleaseFCB(DeviceExt, TargetFcb);
+            Irp->IoStatus.Information = FILE_EXISTS;
+        }
+        else
+        {
+            Irp->IoStatus.Information = FILE_DOES_NOT_EXIST;
+        }
+
+        idx = FileObject->FileName.Length / sizeof(WCHAR) - 1;
+
+        /* Skip trailing \ - if any */
+        if (PathNameU.Buffer[idx] == L'\\')
+        {
+            --idx;
+            PathNameU.Length -= sizeof(WCHAR);
+        }
+
+        /* Get file name */
+        while (idx >= 0 && PathNameU.Buffer[idx] != L'\\')
+        {
+            --idx;
+        }
+
+        if (idx > 0 || PathNameU.Buffer[0] == L'\\')
+        {
+            /* We don't want to include / in the name */
+            FileNameLen = PathNameU.Length - ((idx + 1) * sizeof(WCHAR));
+
+            /* Update FO just to keep file name */
+            /* Skip first slash */
+            ++idx;
+            FileObject->FileName.Length = FileNameLen;
+            RtlMoveMemory(&PathNameU.Buffer[0], &PathNameU.Buffer[idx], FileObject->FileName.Length);
+        }
+        else
+        {
+            /* This is a relative open and we have only the filename, so open the parent directory
+             * It is in RelatedFileObject
+             */
+            ASSERT(FileObject->RelatedFileObject != NULL);
+
+            /* No need to modify the FO, it already has the name */
+        }
+
+        /* We're done with opening! */
+        if (ParentFcb != NULL)
+        {
+            Status = vfatAttachFCBToFileObject(DeviceExt, ParentFcb, FileObject);
+        }
+
+        if (NT_SUCCESS(Status))
+        {
+            pFcb = FileObject->FsContext;
+            ASSERT(pFcb == ParentFcb);
+
+            if (pFcb->OpenHandleCount == 0)
+            {
+                IoSetShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
+                                 Stack->Parameters.Create.ShareAccess,
+                                 FileObject,
+                                 &pFcb->FCBShareAccess);
+            }
+            else
+            {
+                Status = IoCheckShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
+                                            Stack->Parameters.Create.ShareAccess,
+                                            FileObject,
+                                            &pFcb->FCBShareAccess,
+                                            FALSE);
+                if (!NT_SUCCESS(Status))
+                {
+                    VfatCloseFile(DeviceExt, FileObject);
+                    return Status;
+                }
+            }
+
+            pFcb->OpenHandleCount++;
+            DeviceExt->OpenHandleCount++;
+        }
+        else if (ParentFcb != NULL)
+        {
+            vfatReleaseFCB(DeviceExt, ParentFcb);
+        }
+
+        return Status;
+    }
 
     /*
      * If the directory containing the file to open doesn't exist then
@@ -548,7 +699,7 @@ VfatCreateFile(
 
     if (!NT_SUCCESS(Status) && ParentFcb == NULL)
     {
-        DPRINT1("VfatOpenFile faild for '%wZ', status %x\n", &PathNameU, Status);
+        DPRINT1("VfatOpenFile failed for '%wZ', status %x\n", &PathNameU, Status);
         return Status;
     }
 
@@ -565,7 +716,7 @@ VfatCreateFile(
                 Attributes |= FILE_ATTRIBUTE_ARCHIVE;
             vfatSplitPathName(&PathNameU, NULL, &FileNameU);
             Status = VfatAddEntry(DeviceExt, &FileNameU, &pFcb, ParentFcb, RequestedOptions,
-                                  (UCHAR)(Attributes & FILE_ATTRIBUTE_VALID_FLAGS));
+                                  (UCHAR)(Attributes & FILE_ATTRIBUTE_VALID_FLAGS), NULL);
             vfatReleaseFCB(DeviceExt, ParentFcb);
             if (NT_SUCCESS(Status))
             {
@@ -752,7 +903,22 @@ VfatCreateFile(
                             &pFcb->FCBShareAccess);
     }
 
+    if (Irp->IoStatus.Information == FILE_CREATED)
+    {
+        FsRtlNotifyFullReportChange(DeviceExt->NotifySync,
+                                    &(DeviceExt->NotifyList),
+                                    (PSTRING)&pFcb->PathNameU,
+                                    pFcb->PathNameU.Length - pFcb->LongNameU.Length,
+                                    NULL,
+                                    NULL,
+                                    ((*pFcb->Attributes & FILE_ATTRIBUTE_DIRECTORY) ?
+                                    FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
+                                    FILE_ACTION_ADDED,
+                                    NULL);
+    }
+
     pFcb->OpenHandleCount++;
+    DeviceExt->OpenHandleCount++;
 
     /* FIXME : test write access if requested */