Added some fixes for accessing the page file.
[reactos.git] / reactos / drivers / fs / vfat / create.c
index 3241882..aa67ad5 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: create.c,v 1.24 2001/05/02 03:18:03 rex Exp $
+/* $Id: create.c,v 1.36 2002/01/15 21:54:51 hbirr Exp $
  *
  * COPYRIGHT:        See COPYING in the top level directory
  * PROJECT:          ReactOS kernel
@@ -21,9 +21,7 @@
 
 /* GLOBALS *******************************************************************/
 
-#define TAG(A, B, C, D) (ULONG)(((A)<<0) + ((B)<<8) + ((C)<<16) + ((D)<<24))
-
-#define TAG_CCB TAG('V', 'C', 'C', 'B')
+#define ENTRIES_PER_PAGE   (PAGESIZE / sizeof (FATDirEntry))
 
 /* FUNCTIONS *****************************************************************/
 
@@ -60,7 +58,7 @@ IsDeletedEntry (PVOID Block, ULONG Offset)
          (((FATDirEntry *) Block)[Offset].Filename[0] == 0));
 }
 
-static void  vfat8Dot3ToString (PCHAR pBasename, PCHAR pExtension, PWSTR pName)
+void  vfat8Dot3ToString (PCHAR pBasename, PCHAR pExtension, PWSTR pName)
 {
   int  fromIndex, toIndex;
 
@@ -73,7 +71,7 @@ static void  vfat8Dot3ToString (PCHAR pBasename, PCHAR pExtension, PWSTR pName)
   {
     pName [toIndex++] = L'.';
     fromIndex = 0;
-    while (fromIndex < 3 && pBasename [fromIndex] != ' ')
+    while (fromIndex < 3 && pExtension [fromIndex] != ' ')
     {
       pName [toIndex++] = pExtension [fromIndex++];
     }
@@ -85,7 +83,7 @@ static void  vfat8Dot3ToVolumeLabel (PCHAR pBasename, PCHAR pExtension, PWSTR pN
 {
   int  fromIndex, toIndex;
 
-  fromIndex = toIndex = 0; 
+  fromIndex = toIndex = 0;
   while (fromIndex < 8 && pBasename [fromIndex] != ' ')
   {
     pName [toIndex++] = pBasename [fromIndex++];
@@ -101,82 +99,119 @@ static void  vfat8Dot3ToVolumeLabel (PCHAR pBasename, PCHAR pExtension, PWSTR pN
   pName [toIndex] = L'\0';
 }
 
-BOOLEAN
-GetEntryName (PVOID Block, PULONG _Offset, PWSTR Name, PULONG _jloop,
-             PDEVICE_EXTENSION DeviceExt, ULONG * _StartingSector)
+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;
-              /* FIXME: Check status */
-              GetNextSector (DeviceExt, StartingSector, &StartingSector, FALSE);
-             jloop++;
-             /* FIXME: Check status */
-             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;
+        (test2[Offset].attr > 0))
+         {
+           (*pIndex)++;
+        Offset++;
 
-      return (TRUE);
+           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;
+      }
     }
-
-  vfat8Dot3ToString (test[Offset].Filename, test[Offset].Ext, Name);
-
-  *_Offset = Offset;
-
-  return (TRUE);
+    else
+    {
+      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;
+    }
+  }
+  return STATUS_SUCCESS;
 }
 
 NTSTATUS
@@ -198,11 +233,18 @@ ReadVolumeLabel (PDEVICE_EXTENSION DeviceExt, PVPB Vpb)
   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++)
     {
       /* FIXME: Check status */
-      VfatReadSectors (DeviceExt->StorageDevice, StartingSector, 1, block);
+      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++)
        {
@@ -211,7 +253,7 @@ ReadVolumeLabel (PDEVICE_EXTENSION DeviceExt, PVPB Vpb)
              FATDirEntry *test = (FATDirEntry *) block;
 
              /* copy volume label */
-              vfat8Dot3ToVolumeLabel (test[i].Filename, test[i].Ext, Vpb->VolumeLabel);
+             vfat8Dot3ToVolumeLabel (test[i].Filename, test[i].Ext, Vpb->VolumeLabel);
              Vpb->VolumeLabelLength = wcslen (Vpb->VolumeLabel);
 
              ExFreePool (block);
@@ -254,43 +296,68 @@ 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];
   NTSTATUS Status;
   ULONG len;
-
-//  DPRINT ("FindFile(Parent %x, FileToFind '%S')\n", Parent, FileToFind);
-  DPRINT("FindFile: old Pathname %x, old Objectname %x)\n",Fcb->PathName, Fcb->ObjectName);
-
+  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 || Parent->entry.FirstCluster == 1)
+    else
     {
-      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))
+      if (FirstCluster == 1)
+        isRoot = TRUE;
+    }
+  }
+  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;
@@ -302,139 +369,105 @@ FindFile (PDEVICE_EXTENSION DeviceExt, PVFATFCB Fcb,
          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;    
-         if (StartSector)
-           *StartSector = StartingSector;
-         if (Entry)
-           *Entry = 0;
-           DPRINT("FindFile: new Pathname %S, new Objectname %S)\n",Fcb->PathName, Fcb->ObjectName);
+           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);
+  {
+    DPRINT ("Parent->entry.FileSize %x\n", Parent->entry.FileSize);
+    FirstCluster = vfatDirEntryGetFirstCluster (DeviceExt, &Parent->entry);
+  }
+  if (pDirIndex && (*pDirIndex))
+    DirIndex = *pDirIndex;
 
-      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;
-       }
+  Offset = DirIndex % ENTRIES_PER_PAGE;
+  while(TRUE)
+  {
+    if (Context == NULL || Offset == ENTRIES_PER_PAGE)
+    {
+      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;
+      }
     }
-  block = ExAllocatePool (NonPagedPool, BLOCKSIZE);
-  if (StartSector && (*StartSector))
-    StartingSector = *StartSector;
-  i = (Entry) ? (*Entry) : 0;
-  for (j = 0; j < Size; j++)
+       if (vfatIsDirEntryVolume(&((FATDirEntry*)block)[Offset]))
     {
-      /* FIXME: Check status */
-      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))
-                   {
-                      /* FIXME: Check status */
-                     GetNextSector (DeviceExt, StartingSector, &StartingSector, FALSE);
-
-                     /* FIXME: Check status */
-                     VfatReadSectors (DeviceExt->StorageDevice,
-                                      StartingSector, 1, block);
-                     i = 0;
-                   }
-                 if (Parent && Parent->PathName)
+                 len = wcslen(Parent->PathName);
+                 CHECKPOINT;
+                 memcpy(Fcb->PathName, Parent->PathName, len*sizeof(WCHAR));
+                 Fcb->ObjectName=&Fcb->PathName[len];
+                 if (len != 1 || Fcb->PathName[0] != '\\')
                  {
-                   len = wcslen(Parent->PathName);
-                   CHECKPOINT;
-                   memcpy(Fcb->PathName, Parent->PathName, len*sizeof(WCHAR));
-                   Fcb->ObjectName=&Fcb->PathName[len];
+                   Fcb->ObjectName[0] = '\\';
+                       Fcb->ObjectName = &Fcb->ObjectName[1];
                  }
-                 else
-                       Fcb->ObjectName=Fcb->PathName;
-
+               }
+               else
+               {
+                 Fcb->ObjectName=Fcb->PathName;
                  Fcb->ObjectName[0]='\\';
                  Fcb->ObjectName=&Fcb->ObjectName[1];
-
-                 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);
-           DPRINT("FindFile: new Pathname %S, new Objectname %S)\n",Fcb->PathName, Fcb->ObjectName);
-                 return (STATUS_SUCCESS);
                }
-           }
-       }
-      /* not found in this sector, try next : */
 
-      /* directory can be fragmented although it is best to keep them
-         unfragmented. Should we change this to also use GetNextSector?
-         GetNextSector was originally implemented to handle the case above */
-      if (Entry)
-       *Entry = 0;
-      StartingSector++;
-      if ((Parent != NULL && Parent->entry.FirstCluster != 1)
-         || DeviceExt->FatType == FAT32)
-       {
-         if (StartingSector == ClusterToSector (DeviceExt, NextCluster + 1))
-           {
-             Status = GetNextCluster (DeviceExt, NextCluster, &NextCluster,
-                                      FALSE);
-             if (NextCluster == 0 || NextCluster == 0xffffffff)
-               {
-                 if (StartSector)
-                   *StartSector = StartingSector;
-                 if (Entry)
-                   *Entry = i;
-                 ExFreePool (block);
-                 return (STATUS_UNSUCCESSFUL);
-               }
-             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, 
+NTSTATUS
+vfatMakeAbsoluteFilename (PFILE_OBJECT pFileObject,
                           PWSTR pRelativeFileName,
                           PWSTR *pAbsoluteFilename)
 {
@@ -448,16 +481,16 @@ vfatMakeAbsoluteFilename (PFILE_OBJECT pFileObject,
   fcb = ccb->pFcb;
   assert (fcb);
 
-  /* verify related object is a directory and target name 
+  /* verify related object is a directory and target name
      don't start with \. */
-  if (!(fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY) 
+  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 
+  assert (wcslen (fcb->PathName) + 1 + wcslen (pRelativeFileName) + 1
           <= MAX_PATH);
   rcName = ExAllocatePool (NonPagedPool, MAX_PATH);
   wcscpy (rcName, fcb->PathName);
@@ -475,201 +508,63 @@ VfatOpenFile (PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject,
  * FUNCTION: Opens a file
  */
 {
-  PWSTR current = NULL;
-  PWSTR next;
-  PWSTR string;
-//  PWSTR buffer; // used to store a pointer while checking MAX_PATH conformance
   PVFATFCB ParentFcb;
   PVFATFCB Fcb;
-  PVFATFCB Temp;
-  PVFATCCB newCCB;
   NTSTATUS Status;
   PWSTR AbsFileName = NULL;
-  ULONG BytesPerCluster;
 
   DPRINT ("VfatOpenFile(%08lx, %08lx, %S)\n", DeviceExt, FileObject, FileName);
 
-  /* FIXME : treat relative name */
   if (FileObject->RelatedFileObject)
     {
+      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;
+  //FIXME: Get cannonical path name (remove .'s, ..'s and extra separators)
+
+  DPRINT ("PathName to open: %S\n", FileName);
 
+  /*  try first to find an existing FCB in memory  */
+  DPRINT ("Checking for existing FCB in memory\n");
   Fcb = vfatGrabFCBFromTable (DeviceExt, FileName);
-  if (Fcb != NULL)
+  if (Fcb == NULL)
   {
-    FileObject->FsContext = (PVOID)&Fcb->RFCB;
-    newCCB = ExAllocatePoolWithTag (NonPagedPool, sizeof (VFATCCB), TAG_CCB);
-    memset (newCCB, 0, sizeof (VFATCCB));
-    FileObject->Flags = FileObject->Flags | 
-        FO_FCB_IS_VALID | FO_DIRECT_CACHE_PAGING_READ;
-    FileObject->SectionObjectPointers = &Fcb->SectionObjectPointers;
-    FileObject->FsContext2 = newCCB;
-    newCCB->pFcb = Fcb;
-    newCCB->PtrFileObject = FileObject;
-    if (AbsFileName)
-      ExFreePool (AbsFileName);
-    return  STATUS_SUCCESS;
-  }
-
-  DPRINT ("FileName %S\n", FileName);
-
-  string = FileName;
-  ParentFcb = NULL;
-  Fcb = vfatNewFCB (L"\\");
-  next = &string[0];
-
-  CHECKPOINT;
-  if (*next == 0 || *(next+1) == 0)            // root
+    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;
-      DPRINT("%S filename, PathName: %S\n",FileName, ParentFcb->PathName);
-      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 ("search for (%S) in (%S)\n", current, ParentFcb ? ParentFcb->PathName : L"");
-         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 = vfatNewFCB (L"\\");
-           }
-         else
-             Fcb = ParentFcb;
-
-         if (*(Temp->ObjectName))
-         {
-           vfat_wcsncpy(Fcb->PathName+(Fcb->ObjectName-Fcb->PathName),Temp->PathName+(Fcb->ObjectName-Fcb->PathName), MAX_PATH);
+      DPRINT ("Could not make a new FCB, status: %x\n", Status);
 
-           Fcb->ObjectName = &Fcb->PathName[wcslen(Fcb->PathName)];
-           Fcb->ObjectName[0]='\\';
-           Fcb->ObjectName=&Fcb->ObjectName[1];
-           Fcb->ObjectName[0]=0;
-         }
-         CHECKPOINT;
-         ParentFcb = Temp;
-       }
+      if (AbsFileName)
+        ExFreePool (AbsFileName);
 
-      /* searching for last path component */
-      DPRINT ("search for (%S) in (%S)\n", current, Fcb ? Fcb->PathName : L"");
-      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;
-       }
-
-      Temp = Fcb;
-
-      Fcb = ParentFcb;
-      ParentFcb = Temp;
-      ParentFcb->ObjectName = &(wcschr (ParentFcb->ObjectName, '\\'))[1];
+      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->Flags = FileObject->Flags | 
-    FO_FCB_IS_VALID | FO_DIRECT_CACHE_PAGING_READ;
-  FileObject->SectionObjectPointers = &ParentFcb->SectionObjectPointers;
-  memset(FileObject->SectionObjectPointers, 0, 
-        sizeof(SECTION_OBJECT_POINTERS));
-  FileObject->FsContext = (PVOID)&ParentFcb->RFCB;
-  newCCB = ExAllocatePoolWithTag (NonPagedPool, sizeof (VFATCCB), TAG_CCB);
-  memset (newCCB, 0, sizeof (VFATCCB));
-  FileObject->FsContext2 = newCCB;
-  newCCB->pFcb = ParentFcb;
-  newCCB->PtrFileObject = FileObject;
-  ParentFcb->RefCount++;
-  /* FIXME : initialize all fields in FCB and CCB */
-
-  vfatAddFCBToTable (DeviceExt, ParentFcb);
-
-/*  vfat_wcsncpy (ParentFcb->PathName, FileName, MAX_PATH);
-  ParentFcb->ObjectName = ParentFcb->PathName + (current - FileName); */
-  ParentFcb->pDevExt = DeviceExt;
-  BytesPerCluster = DeviceExt->Boot->SectorsPerCluster * BLOCKSIZE;
-  if (BytesPerCluster >= PAGESIZE)
-    {
-      Status = CcInitializeFileCache(FileObject, &ParentFcb->RFCB.Bcb,
-                                    BytesPerCluster);
-    }
-  else
-    {
-      Status = CcInitializeFileCache(FileObject, &ParentFcb->RFCB.Bcb, 
-                                    PAGESIZE);
-    }
-  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)
 /*
@@ -684,26 +579,28 @@ VfatCreateFile (PDEVICE_OBJECT DeviceObject, PIRP Irp)
   PVFATCCB pCcb;
   PVFATFCB pFcb;
   PWCHAR c;
+  BOOLEAN PagingFileCreate = FALSE;
 
   Stack = IoGetCurrentIrpStackLocation (Irp);
   assert (Stack);
   RequestedDisposition = ((Stack->Parameters.Create.Options >> 24) & 0xff);
   RequestedOptions =
     Stack->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
+  PagingFileCreate = (Stack->Flags & SL_OPEN_PAGING_FILE) ? TRUE : FALSE;
   if ((RequestedOptions & FILE_DIRECTORY_FILE)
       && RequestedDisposition == FILE_SUPERSEDE)
     return STATUS_INVALID_PARAMETER;
   FileObject = Stack->FileObject;
   DeviceExt = DeviceObject->DeviceExtension;
   assert (DeviceExt);
-  
+
   /*
    * Check for illegal characters in the file name
    */
   c = FileObject->FileName.Buffer;
   while (*c != 0)
     {
-      if (*c == L'*' || *c == L'?')
+      if (*c == L'*' || *c == L'?' || (*c == L'\\' && c[1] == L'\\'))
        {
          Irp->IoStatus.Information = 0;
          Irp->IoStatus.Status = STATUS_OBJECT_NAME_INVALID;
@@ -725,23 +622,40 @@ VfatCreateFile (PDEVICE_OBJECT DeviceObject, PIRP Irp)
       return Status;
     }
 
+  if (Status == STATUS_DELETE_PENDING)
+  {
+    Irp->IoStatus.Status = Status;
+    return Status;
+  }
   if (!NT_SUCCESS (Status))
     {
       /*
        * If the file open failed then create the required file
        */
-      if (RequestedDisposition == FILE_CREATE || 
-         RequestedDisposition == FILE_OPEN_IF || 
-         RequestedDisposition == FILE_OVERWRITE_IF || 
+      if (RequestedDisposition == FILE_CREATE ||
+         RequestedDisposition == FILE_OPEN_IF ||
+         RequestedDisposition == FILE_OVERWRITE_IF ||
          RequestedDisposition == FILE_SUPERSEDE)
        {
          CHECKPOINT;
-         Status = 
+         Status =
            addEntry (DeviceExt, FileObject, RequestedOptions,
                      (Stack->Parameters.
                       Create.FileAttributes & FILE_ATTRIBUTE_VALID_FLAGS));
          if (NT_SUCCESS (Status))
+         {
+               if (PagingFileCreate)
+               {
+                 DPRINT("Creating a new paging file.\n");
+                 pCcb = FileObject->FsContext2;
+                 pFcb = pCcb->pFcb;
+             pFcb->Flags |= FCB_IS_PAGE_FILE;
+             pFcb->FatChainSize = 0;
+                 pFcb->FatChain = NULL;
+               }
+
            Irp->IoStatus.Information = FILE_CREATED;
+         }
          /* FIXME set size if AllocationSize requested */
          /* FIXME set extended attributes? */
          /* FIXME set share access */
@@ -751,63 +665,111 @@ VfatCreateFile (PDEVICE_OBJECT DeviceObject, PIRP Irp)
        }
     }
   else
-    {
-      /*
-       * Otherwise fail if the caller wanted to create a new file
-       */
-      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 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)
+       }
+    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)
-           {
-             Status = GetNextCluster (DeviceExt, Cluster, &NextCluster, TRUE);
-             WriteCluster (DeviceExt, Cluster, 0);
-             Cluster = NextCluster;
-           }
+       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;
+       }
+    }
+
+    /*
+     * If this create was for a paging file then make sure all the
+     * information needed to manipulate it is locked in memory.
+     */
+    if (PagingFileCreate)
+      {
+       ULONG CurrentCluster, NextCluster, i;
+       DPRINT("Open an existing paging file\n");
+       pFcb->Flags |= FCB_IS_PAGE_FILE;
+       pFcb->FatChainSize = 
+         ((pFcb->entry.FileSize + DeviceExt->BytesPerCluster - 1) / DeviceExt->BytesPerCluster);
+       if (pFcb->FatChainSize)
+       {
+         pFcb->FatChain = ExAllocatePool(NonPagedPool, 
+                                       pFcb->FatChainSize * sizeof(ULONG));
        }
 
-      /*
-       * Check the file has the requested attributes
-       */
-      if ((RequestedOptions & FILE_NON_DIRECTORY_FILE)
+       if (DeviceExt->FatType == FAT32)
+         {
+           CurrentCluster = pFcb->entry.FirstCluster + 
+             pFcb->entry.FirstClusterHigh * 65536;
+         }
+       else
+         {
+           CurrentCluster = pFcb->entry.FirstCluster;
+         }
+
+       i = 0;
+       if (pFcb->FatChainSize)
+       {
+               while (CurrentCluster != 0xffffffff)
+               {
+                 pFcb->FatChain[i] = CurrentCluster;   
+             Status = GetNextCluster (DeviceExt, CurrentCluster, &NextCluster, 
+                                    FALSE);
+             i++;
+             CurrentCluster = 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;
 
@@ -815,39 +777,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;
+  NTSTATUS Status;
 
-  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);
-    }
+  assert (IrpContext);
 
-  DeviceExt = DeviceObject->DeviceExtension;
-  assert (DeviceExt);
-  ExAcquireResourceExclusiveLite (&DeviceExt->DirResource, TRUE);
+  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;
+  }
 
-  Status = VfatCreateFile (DeviceObject, Irp);
-  
-  ExReleaseResourceLite (&DeviceExt->DirResource);
-  
-  Irp->IoStatus.Status = Status;
-  IoCompleteRequest (Irp, IO_NO_INCREMENT);
+  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;
 }