[FASTFAT] Fix use after free when volume is unmounted
[reactos.git] / drivers / filesystems / fastfat / cleanup.c
index 4887f9d..ad5f120 100644 (file)
@@ -4,6 +4,7 @@
  * FILE:             drivers/fs/vfat/cleanup.c
  * PURPOSE:          VFAT Filesystem
  * PROGRAMMER:       Jason Filby (jasonfilby@yahoo.com)
+ *                   Pierre Schweitzer (pierre@reactos.org)
  */
 
 /* INCLUDES *****************************************************************/
 
 /*
  * FUNCTION: Cleans up after a file has been closed.
+ * Returns whether the device was deleted
  */
 static
-NTSTATUS
+BOOLEAN
 VfatCleanupFile(
     PVFAT_IRP_CONTEXT IrpContext)
 {
     PVFATFCB pFcb;
+    PVFATCCB pCcb;
+    BOOLEAN IsVolume;
+    PDEVICE_EXTENSION DeviceExt = IrpContext->DeviceExt;
     PFILE_OBJECT FileObject = IrpContext->FileObject;
+    BOOLEAN Deleted = FALSE;
 
     DPRINT("VfatCleanupFile(DeviceExt %p, FileObject %p)\n",
            IrpContext->DeviceExt, FileObject);
@@ -32,11 +38,13 @@ VfatCleanupFile(
     /* FIXME: handle file/directory deletion here */
     pFcb = (PVFATFCB)FileObject->FsContext;
     if (!pFcb)
-        return STATUS_SUCCESS;
+        return FALSE;
 
-    if (pFcb->Flags & FCB_IS_VOLUME)
+    IsVolume = BooleanFlagOn(pFcb->Flags, FCB_IS_VOLUME);
+    if (IsVolume)
     {
         pFcb->OpenHandleCount--;
+        DeviceExt->OpenHandleCount--;
 
         if (pFcb->OpenHandleCount != 0)
         {
@@ -45,21 +53,24 @@ VfatCleanupFile(
     }
     else
     {
-        if(!ExAcquireResourceExclusiveLite(&pFcb->MainResource,
-                                           (BOOLEAN)(IrpContext->Flags & IRPCONTEXT_CANWAIT)))
-        {
-            return STATUS_PENDING;
-        }
-        if(!ExAcquireResourceExclusiveLite(&pFcb->PagingIoResource,
-                                           (BOOLEAN)(IrpContext->Flags & IRPCONTEXT_CANWAIT)))
+        ExAcquireResourceExclusiveLite(&pFcb->MainResource, TRUE);
+        ExAcquireResourceExclusiveLite(&pFcb->PagingIoResource, TRUE);
+
+        pCcb = FileObject->FsContext2;
+        if (BooleanFlagOn(pCcb->Flags, CCB_DELETE_ON_CLOSE))
         {
-            ExReleaseResourceLite(&pFcb->MainResource);
-            return STATUS_PENDING;
+            pFcb->Flags |= FCB_DELETE_PENDING;
         }
 
+        /* Notify about the cleanup */
+        FsRtlNotifyCleanup(IrpContext->DeviceExt->NotifySync,
+                           &(IrpContext->DeviceExt->NotifyList),
+                           FileObject->FsContext2);
+
         pFcb->OpenHandleCount--;
+        DeviceExt->OpenHandleCount--;
 
-        if (!(*pFcb->Attributes & FILE_ATTRIBUTE_DIRECTORY) &&
+        if (!vfatFCBIsDirectory(pFcb) &&
             FsRtlAreThereCurrentFileLocks(&pFcb->FileLock))
         {
             /* remove all locks this process have on this file */
@@ -69,41 +80,94 @@ VfatCleanupFile(
                                NULL);
         }
 
-        if (pFcb->Flags & FCB_IS_DIRTY)
+        if (BooleanFlagOn(pFcb->Flags, FCB_IS_DIRTY))
         {
-            VfatUpdateEntry (pFcb);
+            VfatUpdateEntry (DeviceExt, pFcb);
         }
 
-        if (pFcb->Flags & FCB_DELETE_PENDING &&
+        if (BooleanFlagOn(pFcb->Flags, FCB_DELETE_PENDING) &&
             pFcb->OpenHandleCount == 0)
         {
-            PFILE_OBJECT tmpFileObject;
-            tmpFileObject = pFcb->FileObject;
-            if (tmpFileObject != NULL)
+            if (vfatFCBIsDirectory(pFcb) &&
+                !VfatIsDirectoryEmpty(DeviceExt, pFcb))
             {
-                pFcb->FileObject = NULL;
-                CcUninitializeCacheMap(tmpFileObject, NULL, NULL);
-                ObDereferenceObject(tmpFileObject);
+                pFcb->Flags &= ~FCB_DELETE_PENDING;
+            }
+            else
+            {
+                PFILE_OBJECT tmpFileObject;
+                tmpFileObject = pFcb->FileObject;
+                if (tmpFileObject != NULL)
+                {
+                    pFcb->FileObject = NULL;
+                    CcUninitializeCacheMap(tmpFileObject, NULL, NULL);
+                    ClearFlag(pFcb->Flags, FCB_CACHE_INITIALIZED);
+                    ObDereferenceObject(tmpFileObject);
+                }
+
+                pFcb->RFCB.ValidDataLength.QuadPart = 0;
+                pFcb->RFCB.FileSize.QuadPart = 0;
+                pFcb->RFCB.AllocationSize.QuadPart = 0;
             }
-
-            CcPurgeCacheSection(FileObject->SectionObjectPointer, NULL, 0, FALSE);
         }
 
         /* Uninitialize the cache (should be done even if caching was never initialized) */
         CcUninitializeCacheMap(FileObject, &pFcb->RFCB.FileSize, NULL);
 
+        if (BooleanFlagOn(pFcb->Flags, FCB_DELETE_PENDING) &&
+            pFcb->OpenHandleCount == 0)
+        {
+            VfatDelEntry(DeviceExt, pFcb, NULL);
+
+            vfatReportChange(DeviceExt,
+                             pFcb,
+                             (vfatFCBIsDirectory(pFcb) ?
+                              FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
+                             FILE_ACTION_REMOVED);
+        }
+
         if (pFcb->OpenHandleCount != 0)
         {
             IoRemoveShareAccess(FileObject, &pFcb->FCBShareAccess);
         }
+/* FIXME: causes FS corruption and breaks selfhosting/testbots and so on */
+#if 0
+        /* If that's the last open handle we just closed, try to see whether
+         * we can delay close operation
+         */
+        else if (!BooleanFlagOn(pFcb->Flags, FCB_DELETE_PENDING) && !BooleanFlagOn(pFcb->Flags, FCB_IS_PAGE_FILE) &&
+                 !BooleanFlagOn(pFcb->Flags, FCB_IS_FAT) && !BooleanFlagOn(pFcb->Flags, FCB_IS_VOLUME))
+        {
+            /* This is only allowed if that's a directory with no open files
+             * OR if it's a file with no section opened
+             */
+            if ((vfatFCBIsDirectory(pFcb) && IsListEmpty(&pFcb->ParentListHead)) ||
+                (!vfatFCBIsDirectory(pFcb) && FileObject->SectionObjectPointer->DataSectionObject == NULL &&
+                 FileObject->SectionObjectPointer->ImageSectionObject == NULL))
+            {
+                DPRINT("Delaying close of: %wZ\n", &pFcb->PathNameU);
+                SetFlag(pFcb->Flags, FCB_DELAYED_CLOSE);
+            }
+        }
+#endif
 
         FileObject->Flags |= FO_CLEANUP_COMPLETE;
+#ifdef KDBG
+        pFcb->Flags |= FCB_CLEANED_UP;
+#endif
 
         ExReleaseResourceLite(&pFcb->PagingIoResource);
         ExReleaseResourceLite(&pFcb->MainResource);
     }
 
-    return STATUS_SUCCESS;
+#ifdef ENABLE_SWAPOUT
+    if (IsVolume && BooleanFlagOn(DeviceExt->Flags, VCB_DISMOUNT_PENDING))
+    {
+        Deleted = VfatCheckForDismount(DeviceExt, TRUE);
+    }
+#endif
+
+    return Deleted;
 }
 
 /*
@@ -113,38 +177,22 @@ NTSTATUS
 VfatCleanup(
     PVFAT_IRP_CONTEXT IrpContext)
 {
-    NTSTATUS Status;
+    BOOLEAN Deleted;
 
     DPRINT("VfatCleanup(DeviceObject %p, Irp %p)\n", IrpContext->DeviceObject, IrpContext->Irp);
 
     if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
     {
-        Status = STATUS_SUCCESS;
-        goto ByeBye;
-    }
-
-    if (!ExAcquireResourceExclusiveLite(&IrpContext->DeviceExt->DirResource,
-                                        (BOOLEAN)(IrpContext->Flags & IRPCONTEXT_CANWAIT)))
-    {
-        return VfatQueueRequest(IrpContext);
+        IrpContext->Irp->IoStatus.Information = 0;
+        return STATUS_SUCCESS;
     }
 
-    Status = VfatCleanupFile(IrpContext);
-
-    ExReleaseResourceLite(&IrpContext->DeviceExt->DirResource);
-
-    if (Status == STATUS_PENDING)
-    {
-        return VfatQueueRequest(IrpContext);
-    }
+    ExAcquireResourceExclusiveLite(&IrpContext->DeviceExt->DirResource, TRUE);
+    Deleted = VfatCleanupFile(IrpContext);
+    if (!Deleted) ExReleaseResourceLite(&IrpContext->DeviceExt->DirResource);
 
-ByeBye:
-    IrpContext->Irp->IoStatus.Status = Status;
     IrpContext->Irp->IoStatus.Information = 0;
-
-    IoCompleteRequest(IrpContext->Irp, IO_NO_INCREMENT);
-    VfatFreeIrpContext(IrpContext);
-    return Status;
+    return STATUS_SUCCESS;
 }
 
 /* EOF */