Reworked code for handling of asynchonous i/o requests.
[reactos.git] / reactos / drivers / fs / vfat / create.c
index 2c5252d..8ca6b0e 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: create.c,v 1.10 2000/12/29 23:17:12 dwelch Exp $
+/* $Id: create.c,v 1.34 2001/11/02 22:44:34 hbirr Exp $
  *
  * COPYRIGHT:        See COPYING in the top level directory
  * PROJECT:          ReactOS kernel
 
 #include "vfat.h"
 
-/* FUNCTIONS ****************************************************************/
+/* GLOBALS *******************************************************************/
+
+#define ENTRIES_PER_PAGE   (PAGESIZE / sizeof (FATDirEntry))
+
+/* FUNCTIONS *****************************************************************/
 
 BOOLEAN
 IsLastEntry (PVOID Block, ULONG Offset)
@@ -54,85 +58,160 @@ IsDeletedEntry (PVOID Block, ULONG Offset)
          (((FATDirEntry *) Block)[Offset].Filename[0] == 0));
 }
 
-BOOLEAN
-GetEntryName (PVOID Block, PULONG _Offset, PWSTR Name, PULONG _jloop,
-             PDEVICE_EXTENSION DeviceExt, ULONG * _StartingSector)
+void  vfat8Dot3ToString (PCHAR pBasename, PCHAR pExtension, PWSTR pName)
+{
+  int  fromIndex, toIndex;
+
+  fromIndex = toIndex = 0; 
+  while (fromIndex < 8 && pBasename [fromIndex] != ' ')
+  {
+    pName [toIndex++] = pBasename [fromIndex++];
+  }
+  if (pExtension [0] != ' ')
+  {
+    pName [toIndex++] = L'.';
+    fromIndex = 0;
+    while (fromIndex < 3 && pExtension [fromIndex] != ' ')
+    {
+      pName [toIndex++] = pExtension [fromIndex++];
+    }
+  }
+  pName [toIndex] = L'\0';
+}
+
+static void  vfat8Dot3ToVolumeLabel (PCHAR pBasename, PCHAR pExtension, PWSTR pName)
+{
+  int  fromIndex, toIndex;
+
+  fromIndex = toIndex = 0;
+  while (fromIndex < 8 && pBasename [fromIndex] != ' ')
+  {
+    pName [toIndex++] = pBasename [fromIndex++];
+  }
+  if (pExtension [0] != ' ')
+  {
+    fromIndex = 0;
+    while (fromIndex < 3 && pBasename [fromIndex] != ' ')
+    {
+      pName [toIndex++] = pExtension [fromIndex++];
+    }
+  }
+  pName [toIndex] = L'\0';
+}
+
+NTSTATUS
+GetEntryName(PVOID *pContext,
+             PVOID *Block,
+             PFILE_OBJECT FileObject,
+             PWSTR Name,
+             PULONG pIndex,
+             PULONG pIndex2)
 /*
  * FUNCTION: Retrieves the file name, be it in short or long file name format
  */
 {
-  FATDirEntry *test;
-  slot *test2;
-  ULONG Offset = *_Offset;
-  ULONG StartingSector = *_StartingSector;
-  ULONG jloop = *_jloop;
+  NTSTATUS Status;
+  FATDirEntry * test;
+  slot * test2;
   ULONG cpos;
-
-  test = (FATDirEntry *) Block;
-  test2 = (slot *) Block;
+  ULONG Offset = *pIndex % ENTRIES_PER_PAGE;
+  ULONG Read;
+  LARGE_INTEGER FileOffset;
 
   *Name = 0;
-
-  if (IsDeletedEntry (Block, Offset))
+  while (TRUE)
+  {
+    test = (FATDirEntry *) *Block;
+    test2 = (slot *) *Block;
+    if (vfatIsDirEntryEndMarker(&test[Offset]))
     {
-      return (FALSE);
+      return STATUS_NO_MORE_ENTRIES;
     }
-
-  if (test2[Offset].attr == 0x0f)
+    if (test2[Offset].attr == 0x0f && !vfatIsDirEntryDeleted(&test[Offset]))
     {
-      vfat_initstr (Name, 256);
+      *Name = 0;
+      if (pIndex2)
+        *pIndex2 = *pIndex; // start of dir entry
+
+      DPRINT ("  long name entry found at %d\n", *pIndex);
+
+      DPRINT ("  name chunk1:[%.*S] chunk2:[%.*S] chunk3:[%.*S]\n",
+              5, test2 [Offset].name0_4,
+              6, test2 [Offset].name5_10,
+              2, test2 [Offset].name11_12);
+
+      vfat_initstr (Name, 255);
       vfat_wcsncpy (Name, test2[Offset].name0_4, 5);
       vfat_wcsncat (Name, test2[Offset].name5_10, 5, 6);
       vfat_wcsncat (Name, test2[Offset].name11_12, 11, 2);
 
+      DPRINT ("  longName: [%S]\n", Name);
       cpos = 0;
       while ((test2[Offset].id != 0x41) && (test2[Offset].id != 0x01) &&
-            (test2[Offset].attr > 0))
-       {
-         Offset++;
-         if (Offset == ENTRIES_PER_SECTOR)
-           {
-             Offset = 0;
-             StartingSector++; //FIXME : nor always the next sector
-             jloop++;
-             VFATReadSectors (DeviceExt->StorageDevice,
-                              StartingSector, 1, Block);
-             test2 = (slot *) Block;
-           }
-         cpos++;
-         vfat_movstr (Name, 13, 0, cpos * 13);
-         vfat_wcsncpy (Name, test2[Offset].name0_4, 5);
-         vfat_wcsncat (Name, test2[Offset].name5_10, 5, 6);
-         vfat_wcsncat (Name, test2[Offset].name11_12, 11, 2);
-
-       }
-
-      if (IsDeletedEntry (Block, Offset + 1))
-       {
-         Offset++;
-         *_Offset = Offset;
-         *_jloop = jloop;
-         *_StartingSector = StartingSector;
-         return (FALSE);
-       }
-
-      *_Offset = Offset;
-      *_jloop = jloop;
-      *_StartingSector = StartingSector;
-
-      return (TRUE);
+        (test2[Offset].attr > 0))
+         {
+           (*pIndex)++;
+        Offset++;
+
+           if (Offset == ENTRIES_PER_PAGE)
+        {
+          Offset = 0;
+          CcUnpinData(*pContext);
+          FileOffset.QuadPart = *pIndex * sizeof(FATDirEntry);
+          if(!CcMapData(FileObject, &FileOffset, PAGESIZE, TRUE, pContext, Block))
+          {
+            *pContext = NULL;
+            return STATUS_NO_MORE_ENTRIES;
+          }
+             test2 = (slot *) *Block;
+        }
+        DPRINT ("  long name entry found at %d\n", *pIndex);
+
+        DPRINT ("  name chunk1:[%.*S] chunk2:[%.*S] chunk3:[%.*S]\n",
+                 5, test2 [Offset].name0_4,
+                 6, test2 [Offset].name5_10,
+                 2, test2 [Offset].name11_12);
+
+           cpos++;
+           vfat_movstr (Name, 13, 0, cpos * 13);
+           vfat_wcsncpy (Name, test2[Offset].name0_4, 5);
+           vfat_wcsncat (Name, test2[Offset].name5_10, 5, 6);
+           vfat_wcsncat (Name, test2[Offset].name11_12, 11, 2);
+
+        DPRINT ("  longName: [%S]\n", Name);
+         }
+      (*pIndex)++;
+      Offset++;
+         if (Offset == ENTRIES_PER_PAGE)
+      {
+        Offset = 0;
+        CcUnpinData(*pContext);
+        FileOffset.QuadPart = *pIndex * sizeof(FATDirEntry);
+        if(!CcMapData(FileObject, &FileOffset, PAGESIZE, TRUE, pContext, Block))
+         {
+          *pContext = NULL;
+          return STATUS_NO_MORE_ENTRIES;
+        }
+           test2 = (slot *) *Block;
+        test = (FATDirEntry*) *Block;
+      }
     }
-
-  RtlAnsiToUnicode (Name, test[Offset].Filename, 8);
-  if (test[Offset].Ext[0] != ' ')
+    else
     {
-      RtlCatAnsiToUnicode (Name, ".", 1);
+      if (vfatIsDirEntryEndMarker(&test[Offset]))
+        return STATUS_NO_MORE_ENTRIES;
+      if (vfatIsDirEntryDeleted(&test[Offset]))
+        return STATUS_UNSUCCESSFUL;
+      if (*Name == 0)
+      {
+        vfat8Dot3ToString (test[Offset].Filename, test[Offset].Ext, Name);
+        if (pIndex2)
+          *pIndex2 = *pIndex;
+      }
+      break;
     }
-  RtlCatAnsiToUnicode (Name, test[Offset].Ext, 3);
-
-  *_Offset = Offset;
-
-  return (TRUE);
+  }
+  return STATUS_SUCCESS;
 }
 
 NTSTATUS
@@ -147,16 +226,25 @@ ReadVolumeLabel (PDEVICE_EXTENSION DeviceExt, PVPB Vpb)
   char *block;
   ULONG StartingSector;
   ULONG NextCluster;
+  NTSTATUS Status;
 
-  Size = DeviceExt->rootDirectorySectors;      //FIXME : in fat32, no limit
+  Size = DeviceExt->rootDirectorySectors;      /* FIXME : in fat32, no limit */
   StartingSector = DeviceExt->rootStart;
   NextCluster = 0;
 
   block = ExAllocatePool (NonPagedPool, BLOCKSIZE);
-  DPRINT ("FindFile : start at sector %lx, entry %ld\n", StartingSector, i);
+  DPRINT ("ReadVolumeLabel : start at sector %lx, entry %ld\n", StartingSector, i);
   for (j = 0; j < Size; j++)
     {
-      VFATReadSectors (DeviceExt->StorageDevice, StartingSector, 1, block);
+      /* FIXME: Check status */
+      Status = VfatReadSectors (DeviceExt->StorageDevice, StartingSector, 1, block);
+      if (!NT_SUCCESS(Status))
+       {
+         *(Vpb->VolumeLabel) = 0;
+         Vpb->VolumeLabelLength = 0;
+         ExFreePool(block);
+         return(Status);
+       }
 
       for (i = 0; i < ENTRIES_PER_SECTOR; i++)
        {
@@ -165,8 +253,7 @@ ReadVolumeLabel (PDEVICE_EXTENSION DeviceExt, PVPB Vpb)
              FATDirEntry *test = (FATDirEntry *) block;
 
              /* copy volume label */
-             RtlAnsiToUnicode (Vpb->VolumeLabel, test[i].Filename, 8);
-             RtlCatAnsiToUnicode (Vpb->VolumeLabel, test[i].Ext, 3);
+             vfat8Dot3ToVolumeLabel (test[i].Filename, test[i].Ext, Vpb->VolumeLabel);
              Vpb->VolumeLabelLength = wcslen (Vpb->VolumeLabel);
 
              ExFreePool (block);
@@ -180,16 +267,18 @@ ReadVolumeLabel (PDEVICE_EXTENSION DeviceExt, PVPB Vpb)
              return (STATUS_UNSUCCESSFUL);
            }
        }
-      // not found in this sector, try next :
+      /* not found in this sector, try next : */
 
       /* directory can be fragmented although it is best to keep them
-         unfragmented */
+         unfragmented.*/
       StartingSector++;
+
       if (DeviceExt->FatType == FAT32)
        {
          if (StartingSector == ClusterToSector (DeviceExt, NextCluster + 1))
            {
-             NextCluster = GetNextCluster (DeviceExt, NextCluster);
+             Status = GetNextCluster (DeviceExt, NextCluster, &NextCluster,
+                                      FALSE);
              if (NextCluster == 0 || NextCluster == 0xffffffff)
                {
                  *(Vpb->VolumeLabel) = 0;
@@ -207,396 +296,275 @@ ReadVolumeLabel (PDEVICE_EXTENSION DeviceExt, PVPB Vpb)
   return (STATUS_UNSUCCESSFUL);
 }
 
-
 NTSTATUS
-FindFile (PDEVICE_EXTENSION DeviceExt, PVFATFCB Fcb,
-         PVFATFCB Parent, PWSTR FileToFind, ULONG * StartSector,
-         ULONG * Entry)
+FindFile (PDEVICE_EXTENSION DeviceExt,
+          PVFATFCB Fcb,
+          PVFATFCB Parent,
+          PWSTR FileToFind,
+          ULONG *pDirIndex,
+          ULONG *pDirIndex2)
 /*
  * FUNCTION: Find a file
  */
 {
-  ULONG i, j;
-  ULONG Size;
-  char *block;
   WCHAR name[256];
-  ULONG StartingSector;
-  ULONG NextCluster;
+  WCHAR name2[14];
+  char * block;
   WCHAR TempStr[2];
-
-  DPRINT ("FindFile(Parent %x, FileToFind '%S')\n", Parent, FileToFind);
-
+  NTSTATUS Status;
+  ULONG len;
+  ULONG DirIndex;
+  ULONG Offset;
+  ULONG FirstCluster;
+  ULONG Read;
+  BOOL isRoot;
+  LARGE_INTEGER FileOffset;
+  PVOID Context = NULL;
+
+  DPRINT ("FindFile(Parent %x, FileToFind '%S', DirIndex: %d)\n", Parent, FileToFind, pDirIndex ? *pDirIndex : 0);
+  DPRINT ("FindFile: old Pathname %x, old Objectname %x)\n",Fcb->PathName, Fcb->ObjectName);
+
+  isRoot = FALSE;
+  DirIndex = 0;
   if (wcslen (FileToFind) == 0)
+  {
+    CHECKPOINT;
+    TempStr[0] = (WCHAR) '.';
+    TempStr[1] = 0;
+    FileToFind = (PWSTR)&TempStr;
+  }
+  if (Parent)
+  {
+    FirstCluster = vfatDirEntryGetFirstCluster(DeviceExt, &Parent->entry);
+    if (DeviceExt->FatType == FAT32)
     {
-      CHECKPOINT;
-      TempStr[0] = (WCHAR) '.';
-      TempStr[1] = 0;
-      FileToFind = (PWSTR) & TempStr;
+      if (FirstCluster == ((struct _BootSector32*)(DeviceExt->Boot))->RootCluster)
+        isRoot = TRUE;
     }
-  if (Parent != NULL)
+    else
     {
-      DPRINT ("Parent->entry.FirstCluster %d\n", Parent->entry.FirstCluster);
+      if (FirstCluster == 1)
+        isRoot = TRUE;
     }
-
-  DPRINT ("FindFile '%S'\n", FileToFind);
-  if (Parent == NULL || Parent->entry.FirstCluster == 1)
-    {
-      CHECKPOINT;
-      Size = DeviceExt->rootDirectorySectors;  /* FIXME : in fat32, no limit */
-      StartingSector = DeviceExt->rootStart;
-      NextCluster = 0;
-      if (FileToFind[0] == 0 || (FileToFind[0] == '\\' && FileToFind[1] == 0)
-         || (FileToFind[0] == '.' && FileToFind[1] == 0))
+  }
+  else
+    isRoot = TRUE;
+  if (isRoot)
+  {
+    if (DeviceExt->FatType == FAT32)
+      FirstCluster = ((struct _BootSector32*)(DeviceExt->Boot))->RootCluster;
+    else
+      FirstCluster = 1;
+
+    if (FileToFind[0] == 0 || (FileToFind[0] == '\\' && FileToFind[1] == 0)
+           || (FileToFind[0] == '.' && FileToFind[1] == 0))
        {
          /* it's root : complete essentials fields then return ok */
          CHECKPOINT;
          memset (Fcb, 0, sizeof (VFATFCB));
          memset (Fcb->entry.Filename, ' ', 11);
+         CHECKPOINT;
+         Fcb->PathName[0]='\\';
+         Fcb->ObjectName = &Fcb->PathName[1];
          Fcb->entry.FileSize = DeviceExt->rootDirectorySectors * BLOCKSIZE;
          Fcb->entry.Attrib = FILE_ATTRIBUTE_DIRECTORY;
          if (DeviceExt->FatType == FAT32)
-           Fcb->entry.FirstCluster = 2;
+      {
+           Fcb->entry.FirstCluster = ((PUSHORT)FirstCluster)[0];
+        Fcb->entry.FirstClusterHigh = ((PUSHORT)FirstCluster)[1];
+      }
          else
-           Fcb->entry.FirstCluster = 1;        /* FIXME : is 1 the good value for mark root? */
-         if (StartSector)
-           *StartSector = StartingSector;
-         if (Entry)
-           *Entry = 0;
+           Fcb->entry.FirstCluster = 1;
+         if (pDirIndex)
+           *pDirIndex = 0;
+      if (pDirIndex2)
+        *pDirIndex2 = 0;
+         DPRINT("FindFile: new Pathname %S, new Objectname %S)\n",Fcb->PathName, Fcb->ObjectName);
          return (STATUS_SUCCESS);
        }
-    }
+  }
   else
+  {
+    DPRINT ("Parent->entry.FileSize %x\n", Parent->entry.FileSize);
+    FirstCluster = vfatDirEntryGetFirstCluster (DeviceExt, &Parent->entry);
+  }
+  if (pDirIndex && (*pDirIndex))
+    DirIndex = *pDirIndex;
+
+  Offset = DirIndex % ENTRIES_PER_PAGE;
+  while(TRUE)
+  {
+    if (Context == NULL || Offset == ENTRIES_PER_PAGE)
     {
-      DPRINT ("Parent->entry.FileSize %x\n", Parent->entry.FileSize);
-
-      Size = ULONG_MAX;
-      if (DeviceExt->FatType == FAT32)
-       NextCluster = Parent->entry.FirstCluster
-         + Parent->entry.FirstClusterHigh * 65536;
-      else
-       NextCluster = Parent->entry.FirstCluster;
-      StartingSector = ClusterToSector (DeviceExt, NextCluster);
-      if (Parent->entry.FirstCluster == 1 && DeviceExt->FatType != FAT32)
-       {
-         /* read of root directory in FAT16 or FAT12 */
-         StartingSector = DeviceExt->rootStart;
-       }
+      if (Offset == ENTRIES_PER_PAGE)
+        Offset = 0;
+      if (Context)
+      {
+        CcUnpinData(Context);
+      }
+      FileOffset.QuadPart = (DirIndex - Offset) * sizeof(FATDirEntry);
+      if (!CcMapData(Parent->FileObject, &FileOffset, PAGESIZE, TRUE,
+             &Context, (PVOID*)&block))
+      {
+         Context = NULL;
+         break;
+      }
     }
-  CHECKPOINT;
-  block = ExAllocatePool (NonPagedPool, BLOCKSIZE);
-  CHECKPOINT;
-  if (StartSector && (*StartSector))
-    StartingSector = *StartSector;
-  i = (Entry) ? (*Entry) : 0;
-  DPRINT ("FindFile : start at sector %lx, entry %ld\n", StartingSector, i);
-  for (j = 0; j < Size; j++)
+       if (vfatIsDirEntryVolume(&((FATDirEntry*)block)[Offset]))
     {
-      VFATReadSectors (DeviceExt->StorageDevice, StartingSector, 1, block);
-
-      for (i = (Entry) ? (*Entry) : 0; i < ENTRIES_PER_SECTOR; i++)
+      Offset++;
+      DirIndex++;
+         continue;
+    }
+    Status = GetEntryName (&Context, (PVOID*)&block, Parent->FileObject, name,
+                           &DirIndex, pDirIndex2);
+    if (Status == STATUS_NO_MORE_ENTRIES)
+      break;
+    Offset = DirIndex % ENTRIES_PER_PAGE;
+    if (NT_SUCCESS(Status))
        {
-         if (IsVolEntry ((PVOID) block, i))
-           continue;
-         if (IsLastEntry ((PVOID) block, i))
-           {
-             if (StartSector)
-               *StartSector = StartingSector;
-             if (Entry)
-               *Entry = i;
-             ExFreePool (block);
-             return (STATUS_UNSUCCESSFUL);
-           }
-         if (GetEntryName
-             ((PVOID) block, &i, name, &j, DeviceExt, &StartingSector))
-           {
-             if (wstrcmpjoki (name, FileToFind))
+      vfat8Dot3ToString(((FATDirEntry *) block)[Offset].Filename,((FATDirEntry *) block)[Offset].Ext, name2);
+         if (wstrcmpjoki (name, FileToFind) || wstrcmpjoki (name2, FileToFind))
+         {
+           if (Parent && Parent->PathName)
                {
-                 /* In the case of a long filename, the firstcluster is stored in
-                    the next record -- where it's short name is */
-                 if (((FATDirEntry *) block)[i].Attrib == 0x0f)
-                   i++;
-                 if (i == (ENTRIES_PER_SECTOR))
-                   {
-                     /* entry is in next sector */
-                     StartingSector++;
-                     /* FIXME : treat case of next sector fragmented */
-                     VFATReadSectors (DeviceExt->StorageDevice,
-                                      StartingSector, 1, block);
-                     i = 0;
-                   }
-                 memcpy (&Fcb->entry, &((FATDirEntry *) block)[i],
-                         sizeof (FATDirEntry));
-                 vfat_wcsncpy (Fcb->ObjectName, name, MAX_PATH);
-                 if (StartSector)
-                   *StartSector = StartingSector;
-                 if (Entry)
-                   *Entry = i;
-                 ExFreePool (block);
-                 return (STATUS_SUCCESS);
+                 len = wcslen(Parent->PathName);
+                 CHECKPOINT;
+                 memcpy(Fcb->PathName, Parent->PathName, len*sizeof(WCHAR));
+                 Fcb->ObjectName=&Fcb->PathName[len];
+                 if (len != 1 || Fcb->PathName[0] != '\\')
+                 {
+                   Fcb->ObjectName[0] = '\\';
+                       Fcb->ObjectName = &Fcb->ObjectName[1];
+                 }
                }
-           }
-       }
-      /* not found in this sector, try next : */
-
-      /* directory can be fragmented although it is best to keep them
-         unfragmented */
-      if (Entry)
-       *Entry = 0;
-      StartingSector++;
-      if ((Parent != NULL && Parent->entry.FirstCluster != 1)
-         || DeviceExt->FatType == FAT32)
-       {
-         if (StartingSector == ClusterToSector (DeviceExt, NextCluster + 1))
-           {
-             NextCluster = GetNextCluster (DeviceExt, NextCluster);
-             if (NextCluster == 0 || NextCluster == 0xffffffff)
+               else
                {
-                 if (StartSector)
-                   *StartSector = StartingSector;
-                 if (Entry)
-                   *Entry = i;
-                 ExFreePool (block);
-                 return (STATUS_UNSUCCESSFUL);
+                 Fcb->ObjectName=Fcb->PathName;
+                 Fcb->ObjectName[0]='\\';
+                 Fcb->ObjectName=&Fcb->ObjectName[1];
                }
-             StartingSector = ClusterToSector (DeviceExt, NextCluster);
-           }
-       }
+
+               memcpy (&Fcb->entry, &((FATDirEntry *) block)[Offset],
+                            sizeof (FATDirEntry));
+               vfat_wcsncpy (Fcb->ObjectName, name, MAX_PATH);
+               if (pDirIndex)
+                 *pDirIndex = DirIndex;
+         DPRINT("FindFile: new Pathname %S, new Objectname %S, DirIndex %d\n",Fcb->PathName, Fcb->ObjectName, DirIndex);
+               if (Context)
+                 CcUnpinData(Context);
+               return STATUS_SUCCESS;
+         }
     }
-  if (StartSector)
-    *StartSector = StartingSector;
-  if (Entry)
-    *Entry = i;
-  ExFreePool (block);
+    Offset++;
+    DirIndex++;
+  }
+  if (pDirIndex)
+       *pDirIndex = DirIndex;
+  if (Context)
+    CcUnpinData(Context);
   return (STATUS_UNSUCCESSFUL);
 }
 
-
+NTSTATUS
+vfatMakeAbsoluteFilename (PFILE_OBJECT pFileObject,
+                          PWSTR pRelativeFileName,
+                          PWSTR *pAbsoluteFilename)
+{
+  PWSTR  rcName;
+  PVFATFCB  fcb;
+  PVFATCCB  ccb;
+
+  DbgPrint ("try related for %S\n", pRelativeFileName);
+  ccb = pFileObject->FsContext2;
+  assert (ccb);
+  fcb = ccb->pFcb;
+  assert (fcb);
+
+  /* verify related object is a directory and target name
+     don't start with \. */
+  if (!(fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY)
+      || (pRelativeFileName[0] != '\\'))
+  {
+    return  STATUS_INVALID_PARAMETER;
+  }
+
+  /* construct absolute path name */
+  assert (wcslen (fcb->PathName) + 1 + wcslen (pRelativeFileName) + 1
+          <= MAX_PATH);
+  rcName = ExAllocatePool (NonPagedPool, MAX_PATH);
+  wcscpy (rcName, fcb->PathName);
+  wcscat (rcName, L"\\");
+  wcscat (rcName, pRelativeFileName);
+  *pAbsoluteFilename = rcName;
+
+  return  STATUS_SUCCESS;
+}
 
 NTSTATUS
-FsdOpenFile (PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject,
+VfatOpenFile (PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject,
             PWSTR FileName)
 /*
  * FUNCTION: Opens a file
  */
 {
-  PWSTR current = NULL;
-  PWSTR next;
-  PWSTR string;
   PVFATFCB ParentFcb;
-  PVFATFCB Fcb, pRelFcb;
-  PVFATFCB Temp;
-  PVFATCCB newCCB, pRelCcb;
+  PVFATFCB Fcb;
   NTSTATUS Status;
-  PFILE_OBJECT pRelFileObject;
   PWSTR AbsFileName = NULL;
-  short i, j;
-  PLIST_ENTRY current_entry;
-  KIRQL oldIrql;
 
-  DPRINT ("FsdOpenFile(%08lx, %08lx, %S)\n", DeviceExt, FileObject, FileName);
+  DPRINT ("VfatOpenFile(%08lx, %08lx, %S)\n", DeviceExt, FileObject, FileName);
 
-  /* FIXME : treat relative name */
   if (FileObject->RelatedFileObject)
     {
-      DbgPrint ("try related for %S\n", FileName);
-      pRelFileObject = FileObject->RelatedFileObject;
-      pRelCcb = pRelFileObject->FsContext2;
-      assert (pRelCcb);
-      pRelFcb = pRelCcb->pFcb;
-      assert (pRelFcb);
-      /*
-       * verify related object is a directory and target name don't start with
-       *  \.
-       */
-      if (!(pRelFcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY)
-         || (FileName[0] != '\\'))
-       {
-         Status = STATUS_INVALID_PARAMETER;
-         return Status;
-       }
-      /* construct absolute path name */
-      AbsFileName = ExAllocatePool (NonPagedPool, MAX_PATH);
-      for (i = 0; pRelFcb->PathName[i]; i++)
-       AbsFileName[i] = pRelFcb->PathName[i];
-      AbsFileName[i++] = '\\';
-      for (j = 0; FileName[j] && i < MAX_PATH; j++)
-       AbsFileName[i++] = FileName[j];
-      assert (i < MAX_PATH);
-      AbsFileName[i] = 0;
+      DPRINT ("Converting relative filename to absolute filename\n");
+      Status = vfatMakeAbsoluteFilename (FileObject->RelatedFileObject,
+                                         FileName,
+                                         &AbsFileName);
       FileName = AbsFileName;
     }
 
-  /*
-   * try first to find an existing FCB in memory
-   */
-  CHECKPOINT;
-
-  KeAcquireSpinLock (&DeviceExt->FcbListLock, &oldIrql);
-  current_entry = DeviceExt->FcbListHead.Flink;
-  while (current_entry != &DeviceExt->FcbListHead)
-    {
-      Fcb = CONTAINING_RECORD (current_entry, VFATFCB, FcbListEntry);
-
-      DPRINT ("Scanning %x\n", Fcb);
-      DPRINT ("Scanning %S\n", Fcb->PathName);
-
-      if (DeviceExt == Fcb->pDevExt && wstrcmpi (FileName, Fcb->PathName))
-       {
-         Fcb->RefCount++;
-         KeReleaseSpinLock (&DeviceExt->FcbListLock, oldIrql);
-         FileObject->FsContext = (PVOID)&Fcb->RFCB;
-         newCCB = ExAllocatePool (NonPagedPool, sizeof (VFATCCB));
-         memset (newCCB, 0, sizeof (VFATCCB));
-         FileObject->FsContext2 = newCCB;
-         newCCB->pFcb = Fcb;
-         newCCB->PtrFileObject = FileObject;
-         if (AbsFileName)
-           ExFreePool (AbsFileName);
-         return (STATUS_SUCCESS);
-       }
-
-      current_entry = current_entry->Flink;
-    }
-  KeReleaseSpinLock (&DeviceExt->FcbListLock, oldIrql);
-
-  CHECKPOINT;
-  DPRINT ("FileName %S\n", FileName);
+  //FIXME: Get cannonical path name (remove .'s, ..'s and extra separators)
 
-  string = FileName;
-  ParentFcb = NULL;
-  Fcb = ExAllocatePool (NonPagedPool, sizeof (VFATFCB));
-  memset (Fcb, 0, sizeof (VFATFCB));
-  Fcb->ObjectName = Fcb->PathName;
-  next = &string[0];
+  DPRINT ("PathName to open: %S\n", FileName);
 
-  CHECKPOINT;
-  if (*next == 0)              // root
+  /*  try first to find an existing FCB in memory  */
+  DPRINT ("Checking for existing FCB in memory\n");
+  Fcb = vfatGrabFCBFromTable (DeviceExt, FileName);
+  if (Fcb == NULL)
+  {
+    DPRINT ("No existing FCB found, making a new one if file exists.\n");
+    Status = vfatGetFCBForFile (DeviceExt, &ParentFcb, &Fcb, FileName);
+    if (ParentFcb != NULL)
     {
-      memset (Fcb->entry.Filename, ' ', 11);
-      Fcb->entry.FileSize = DeviceExt->rootDirectorySectors * BLOCKSIZE;
-      Fcb->entry.Attrib = FILE_ATTRIBUTE_DIRECTORY;
-      if (DeviceExt->FatType == FAT32)
-       Fcb->entry.FirstCluster = 2;
-      else
-       Fcb->entry.FirstCluster = 1;
-      /* FIXME : is 1 the good value for mark root? */
-      ParentFcb = Fcb;
-      Fcb = NULL;
+      vfatReleaseFCB (DeviceExt, ParentFcb);
     }
-  else
+    if (!NT_SUCCESS (Status))
     {
-      while (TRUE)
-       {
-         CHECKPOINT;
-         *next = '\\';
-         current = next + 1;
-         next = wcschr (next + 1, '\\');
-         if (next != NULL)
-           {
-             *next = 0;
-           }
-         else
-           {
-             /* reached the last path component */
-             DPRINT ("exiting: current '%S'\n", current);
-             break;
-           }
+      DPRINT ("Could not make a new FCB, status: %x\n", Status);
 
-         DPRINT ("current '%S'\n", current);
-         Status = FindFile (DeviceExt, Fcb, ParentFcb, current, NULL, NULL);
-         if (Status != STATUS_SUCCESS)
-           {
-             CHECKPOINT;
-             if (Fcb != NULL)
-               ExFreePool (Fcb);
-             if (ParentFcb != NULL)
-               ExFreePool (ParentFcb);
-             if (AbsFileName)
-               ExFreePool (AbsFileName);
-
-             DPRINT ("error STATUS_OBJECT_PATH_NOT_FOUND\n");
-             return STATUS_OBJECT_PATH_NOT_FOUND;
-           }
-         Temp = Fcb;
-         CHECKPOINT;
-         if (ParentFcb == NULL)
-           {
-             CHECKPOINT;
-             Fcb = ExAllocatePool (NonPagedPool, sizeof (VFATFCB));
-             memset (Fcb, 0, sizeof (VFATFCB));
-             Fcb->ObjectName = Fcb->PathName;
-           }
-         else
-           Fcb = ParentFcb;
-         CHECKPOINT;
-         ParentFcb = Temp;
-       }
-
-      /* searching for last path component */
-      DPRINT ("current '%S'\n", current);
-      Status = FindFile (DeviceExt, Fcb, ParentFcb, current, NULL, NULL);
-      if (Status != STATUS_SUCCESS)
-       {
-         /* file does not exist */
-         CHECKPOINT;
-         if (Fcb != NULL)
-           ExFreePool (Fcb);
-         if (ParentFcb != NULL)
-           ExFreePool (ParentFcb);
-         if (AbsFileName)
-           ExFreePool (AbsFileName);
-
-         return STATUS_OBJECT_NAME_NOT_FOUND;
-       }
+      if (AbsFileName)
+        ExFreePool (AbsFileName);
 
-      Temp = Fcb;
-      if (ParentFcb == NULL)
-       {
-         CHECKPOINT;
-         Fcb = ExAllocatePool (NonPagedPool, sizeof (VFATFCB));
-         memset (Fcb, 0, sizeof (VFATFCB));
-         Fcb->ObjectName = Fcb->PathName;
-       }
-      else
-       Fcb = ParentFcb;
-      ParentFcb = Temp;
+      return  Status;
     }
+  }
+  if (Fcb->Flags & FCB_DELETE_PENDING)
+  {
+    vfatReleaseFCB (DeviceExt, Fcb);
+    if (AbsFileName)
+      ExFreePool (AbsFileName);
+    return STATUS_DELETE_PENDING;
+  }
+  DPRINT ("Attaching FCB to fileObject\n");
+  Status = vfatAttachFCBToFileObject (DeviceExt, Fcb, FileObject);
 
-  
-  FileObject->FsContext = (PVOID)&ParentFcb->RFCB;
-  newCCB = ExAllocatePool (NonPagedPool, sizeof (VFATCCB));
-  memset (newCCB, 0, sizeof (VFATCCB));
-  FileObject->FsContext2 = newCCB;
-  newCCB->pFcb = ParentFcb;
-  newCCB->PtrFileObject = FileObject;
-  ParentFcb->RefCount++;
-  /* FIXME : initialize all fields in FCB and CCB */
-
-  KeAcquireSpinLock (&DeviceExt->FcbListLock, &oldIrql);
-  InsertTailList (&DeviceExt->FcbListHead, &ParentFcb->FcbListEntry);
-  KeReleaseSpinLock (&DeviceExt->FcbListLock, oldIrql);
-
-  vfat_wcsncpy (ParentFcb->PathName, FileName, MAX_PATH);
-  ParentFcb->ObjectName = ParentFcb->PathName + (current - FileName);
-  ParentFcb->pDevExt = DeviceExt;
-  Status = CcInitializeFileCache(FileObject, &ParentFcb->RFCB.Bcb);
-  if (!NT_SUCCESS(Status))
-    {
-      DbgPrint("CcInitializeFileCache failed\n");
-      KeBugCheck(0);
-    }
-  DPRINT ("file open, fcb=%x\n", ParentFcb);
-  DPRINT ("FileSize %d\n", ParentFcb->entry.FileSize);
-  if (Fcb)
-    ExFreePool (Fcb);
   if (AbsFileName)
     ExFreePool (AbsFileName);
-  CHECKPOINT;
 
-  return (STATUS_SUCCESS);
+  return  Status;
 }
 
-
 NTSTATUS
 VfatCreateFile (PDEVICE_OBJECT DeviceObject, PIRP Irp)
 /*
@@ -610,6 +578,7 @@ VfatCreateFile (PDEVICE_OBJECT DeviceObject, PIRP Irp)
   ULONG RequestedDisposition, RequestedOptions;
   PVFATCCB pCcb;
   PVFATFCB pFcb;
+  PWCHAR c;
 
   Stack = IoGetCurrentIrpStackLocation (Irp);
   assert (Stack);
@@ -623,9 +592,27 @@ VfatCreateFile (PDEVICE_OBJECT DeviceObject, PIRP Irp)
   DeviceExt = DeviceObject->DeviceExtension;
   assert (DeviceExt);
 
-  Status = FsdOpenFile (DeviceExt, FileObject, FileObject->FileName.Buffer);
+  /*
+   * Check for illegal characters in the file name
+   */
+  c = FileObject->FileName.Buffer;
+  while (*c != 0)
+    {
+      if (*c == L'*' || *c == L'?' || (*c == L'\\' && c[1] == L'\\'))
+       {
+         Irp->IoStatus.Information = 0;
+         Irp->IoStatus.Status = STATUS_OBJECT_NAME_INVALID;
+         return(STATUS_OBJECT_NAME_INVALID);
+       }
+      c++;
+    }
+
+  Status = VfatOpenFile (DeviceExt, FileObject, FileObject->FileName.Buffer);
 
-  CHECKPOINT;
+  /*
+   * If the directory containing the file to open doesn't exist then
+   * fail immediately
+   */
   Irp->IoStatus.Information = 0;
   if (Status == STATUS_OBJECT_PATH_NOT_FOUND)
     {
@@ -633,13 +620,20 @@ VfatCreateFile (PDEVICE_OBJECT DeviceObject, PIRP Irp)
       return Status;
     }
 
-  CHECKPOINT;
+  if (Status == STATUS_DELETE_PENDING)
+  {
+    Irp->IoStatus.Status = Status;
+    return Status;
+  }
   if (!NT_SUCCESS (Status))
     {
-      if (RequestedDisposition == FILE_CREATE
-         || RequestedDisposition == FILE_OPEN_IF
-         || RequestedDisposition == FILE_OVERWRITE_IF
-         || RequestedDisposition == FILE_SUPERSEDE)
+      /*
+       * If the file open failed then create the required file
+       */
+      if (RequestedDisposition == FILE_CREATE ||
+         RequestedDisposition == FILE_OPEN_IF ||
+         RequestedDisposition == FILE_OVERWRITE_IF ||
+         RequestedDisposition == FILE_SUPERSEDE)
        {
          CHECKPOINT;
          Status =
@@ -657,52 +651,70 @@ VfatCreateFile (PDEVICE_OBJECT DeviceObject, PIRP Irp)
        }
     }
   else
-    {
-      if (RequestedDisposition == FILE_CREATE)
+  {
+    /*
+     * Otherwise fail if the caller wanted to create a new file
+     */
+    if (RequestedDisposition == FILE_CREATE)
        {
          Irp->IoStatus.Information = FILE_EXISTS;
          Status = STATUS_OBJECT_NAME_COLLISION;
        }
-      pCcb = FileObject->FsContext2;
-      pFcb = pCcb->pFcb;
-      if (RequestedDisposition == FILE_SUPERSEDE)
-       {
-         ULONG Cluster, NextCluster;
-         /* FIXME set size to 0 and free clusters */
-         pFcb->entry.FileSize = 0;
-         if (DeviceExt->FatType == FAT32)
+    pCcb = FileObject->FsContext2;
+    pFcb = pCcb->pFcb;
+    /*
+     * If requested then delete the file and create a new one with the
+     * same name
+     */
+    if (RequestedDisposition == FILE_SUPERSEDE)
+    {
+       ULONG Cluster, NextCluster;
+       /* FIXME set size to 0 and free clusters */
+       pFcb->entry.FileSize = 0;
+       if (DeviceExt->FatType == FAT32)
            Cluster = pFcb->entry.FirstCluster
              + pFcb->entry.FirstClusterHigh * 65536;
-         else
-           Cluster = pFcb->entry.FirstCluster;
-         pFcb->entry.FirstCluster = 0;
-         pFcb->entry.FirstClusterHigh = 0;
-         updEntry (DeviceExt, FileObject);
-         while (Cluster != 0xffffffff && Cluster > 1)
-           {
-             NextCluster = GetNextCluster (DeviceExt, Cluster);
-             WriteCluster (DeviceExt, Cluster, 0);
-             Cluster = NextCluster;
-           }
-       }
-      if ((RequestedOptions & FILE_NON_DIRECTORY_FILE)
+       else
+         Cluster = pFcb->entry.FirstCluster;
+       pFcb->entry.FirstCluster = 0;
+       pFcb->entry.FirstClusterHigh = 0;
+       updEntry (DeviceExt, FileObject);
+       if ((ULONG)pFcb->RFCB.FileSize.QuadPart > 0)
+       {
+         pFcb->RFCB.AllocationSize.QuadPart = 0;
+        pFcb->RFCB.FileSize.QuadPart = 0;
+        pFcb->RFCB.ValidDataLength.QuadPart = 0;
+        CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&pFcb->RFCB.AllocationSize);
+       }
+       while (Cluster != 0xffffffff && Cluster > 1)
+       {
+        Status = GetNextCluster (DeviceExt, Cluster, &NextCluster, FALSE);
+        WriteCluster (DeviceExt, Cluster, 0);
+        Cluster = NextCluster;
+       }
+    }
+
+    /*
+     * Check the file has the requested attributes
+     */
+    if ((RequestedOptions & FILE_NON_DIRECTORY_FILE)
          && (pFcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY))
        {
          Status = STATUS_FILE_IS_A_DIRECTORY;
        }
-      if ((RequestedOptions & FILE_DIRECTORY_FILE)
+    if ((RequestedOptions & FILE_DIRECTORY_FILE)
          && !(pFcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY))
        {
          Status = STATUS_NOT_A_DIRECTORY;
        }
-      /* FIXME : test share access */
+    /* FIXME : test share access */
       /* FIXME : test write access if requested */
-      if (!NT_SUCCESS (Status))
-       VfatCloseFile (DeviceExt, FileObject);
-      else
-       Irp->IoStatus.Information = FILE_OPENED;
+    if (!NT_SUCCESS (Status))
+         VfatCloseFile (DeviceExt, FileObject);
+    else
+         Irp->IoStatus.Information = FILE_OPENED;
       /* FIXME : make supersed or overwrite if requested */
-    }
+  }
 
   Irp->IoStatus.Status = Status;
 
@@ -710,39 +722,37 @@ VfatCreateFile (PDEVICE_OBJECT DeviceObject, PIRP Irp)
 }
 
 
-NTSTATUS STDCALL
-VfatCreate (PDEVICE_OBJECT DeviceObject, PIRP Irp)
+NTSTATUS VfatCreate (PVFAT_IRP_CONTEXT IrpContext)
 /*
  * FUNCTION: Create or open a file
  */
 {
-  NTSTATUS Status = STATUS_SUCCESS;
-  PDEVICE_EXTENSION DeviceExt;
-
-  assert (DeviceObject);
-  assert (Irp);
-  
-  if (DeviceObject->Size == sizeof (DEVICE_OBJECT))
-    {
-      /* DeviceObject represents FileSystem instead of logical volume */
-      DbgPrint ("FsdCreate called with file system\n");
-      Irp->IoStatus.Status = Status;
-      Irp->IoStatus.Information = FILE_OPENED;
-      IoCompleteRequest (Irp, IO_NO_INCREMENT);
-      return (Status);
-    }
-
-  DeviceExt = DeviceObject->DeviceExtension;
-  assert (DeviceExt);
-  ExAcquireResourceExclusiveLite (&DeviceExt->DirResource, TRUE);
-
-  Status = VfatCreateFile (DeviceObject, Irp);
-  
-  ExReleaseResourceLite (&DeviceExt->DirResource);
-  
-  Irp->IoStatus.Status = Status;
-  IoCompleteRequest (Irp, IO_NO_INCREMENT);
+  NTSTATUS Status;
 
+  assert (IrpContext);
+
+  if (IrpContext->DeviceObject->Size == sizeof (DEVICE_OBJECT))
+  {
+     /* DeviceObject represents FileSystem instead of logical volume */
+     DbgPrint ("FsdCreate called with file system\n");
+     IrpContext->Irp->IoStatus.Information = FILE_OPENED;
+     Status = STATUS_SUCCESS;
+     goto ByeBye;
+  }
+
+  if (!(IrpContext->Flags & IRPCONTEXT_CANWAIT))
+  {
+     return VfatQueueRequest (IrpContext);
+  }
+
+  ExAcquireResourceExclusiveLite (&IrpContext->DeviceExt->DirResource, TRUE);
+  Status = VfatCreateFile (IrpContext->DeviceObject, IrpContext->Irp);
+  ExReleaseResourceLite (&IrpContext->DeviceExt->DirResource);
+
+ByeBye:
+  IrpContext->Irp->IoStatus.Status = Status;
+  IoCompleteRequest (IrpContext->Irp, NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT);
+  VfatFreeIrpContext(IrpContext);
   return Status;
 }