2005-08-24 18:29:45 +00:00
|
|
|
/*
|
2022-09-24 13:24:08 +00:00
|
|
|
* PROJECT: VFAT Filesystem
|
|
|
|
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
|
|
|
* PURPOSE: File close routines
|
|
|
|
* COPYRIGHT: Copyright 1998 Jason Filby <jasonfilby@yahoo.com>
|
|
|
|
* Copyright 2014-2018 Pierre Schweitzer <pierre@reactos.org>
|
2000-07-07 02:14:14 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
|
|
|
|
#include "vfat.h"
|
|
|
|
|
2013-12-19 16:20:28 +00:00
|
|
|
#define NDEBUG
|
|
|
|
#include <debug.h>
|
|
|
|
|
2000-07-07 02:14:14 +00:00
|
|
|
/* FUNCTIONS ****************************************************************/
|
|
|
|
|
[FASTFAT] Implement delayed close
When we're about to close a file (ie, forget everything about it
and release any associated structure), actually delay it.
This allows keep data fresh in memory for faster reuse in case
it would be required. The effective closing will only happen after some time.
For specific operations, this will produce a real speed up in ReactOS.
For instance, with that patch, Winamp starts within seconds, instead of dozen
of minutes.
In most cases, it will bring ReactOS to performances it had before fixing
the huge leak in FastFAT (commit 94ead99) without leaking the whole FS.
For now, due to regressions, this is only activated for files and not
for directories. Once it gets fixed, it will be enabled for both.
CORE-14826
CORE-14917
2018-08-18 15:04:02 +00:00
|
|
|
VOID
|
|
|
|
VfatCommonCloseFile(
|
|
|
|
PDEVICE_EXTENSION DeviceExt,
|
|
|
|
PVFATFCB pFcb)
|
|
|
|
{
|
[FASTFAT] Improvements for volume dismount + minor bugfixing.
- Cache the RootFcb so that its cleanup can be handled separately
during dismounting.
- Force volume dismount at cleanup if the VCB_DISMOUNT_PENDING flag
is set.
- Actually dismount a volume if its VCB has been flagged as not good,
or if we force dismounting.
NOTE: In their *CheckForDismount() function, our 3rd-party FS drivers
as well as MS' fastfat, perform a comparison check of the current VCB's
VPB ReferenceCount with some sort of "dangling"/"residual" open count.
It seems to be related to the fact that the volume root directory as
well as auxiliary data stream(s) are still opened, and only these are
allowed to be opened at that moment. After analysis it appears that for
the ReactOS' fastfat, this number is equal to "3".
- On dismounting, cleanup and destroy the RootFcb, VolumeFcb and the
FATFileObject. Then cleanup the SpareVPB or the IoVPB members, and
finish by removing the dismounted volume from the VolumeListEntry
and cleaning up the notify synchronization object and the resources.
- During dismounting, and on shutdown, flush the volume before
resetting its dirty bit.
- On shutdown, after volume flushing, try to unmount it without forcing.
- Release the VCB resources only when we actually dismount the volume
in VfatCheckForDismount().
- Initialize first the notify list and the synchronization object,
before sending the FSRTL_VOLUME_MOUNT notification.
- If we failed at mounting a volume but its VCB's FATFileObject was
already initialized, first call CcUninitializeCacheMap() on it
before dereferencing it.
- Send FSRTL_VOLUME_LOCK, FSRTL_VOLUME_LOCK_FAILED and
FSRTL_VOLUME_UNLOCK notifications during volume locking (and failure)
and volume unlocking.
- Flush the volume before locking it, and clean its dirty bit if needed.
NOTE: In addition to checking for VCB_CLEAR_DIRTY, we also check for the
presence of the VCB_IS_DIRTY flag before cleaning up the dirty bit: this
allows us to not re-clean the bit if it has been previously cleaned.
This is needed for instance in this scenario:
- The volume is locked (it gets flushed and the dirty bit is possibly cleared);
- The volume then gets formatted with a completely different FS, that
possibly clears up the first sector (e.g. BTRFS ignores 1st sector);
- The volume is then dismounted: if we didn't check whether VCB_IS_DIRTY
was set prior to resetting it, we could attempt clearing it again! But
now that the volume's filesystem has been completely changed, we would
then try to modify the dirty bit on an erroneous position on disk!
That's why it should not be touched in this case during dismounting.
- The volume is unlocked (same comment as above), and later can be
detected as being BTRFS.
2018-11-11 16:17:48 +00:00
|
|
|
/* Nothing to do for volumes or for the FAT file object */
|
|
|
|
if (BooleanFlagOn(pFcb->Flags, FCB_IS_FAT | FCB_IS_VOLUME))
|
[FASTFAT] Implement delayed close
When we're about to close a file (ie, forget everything about it
and release any associated structure), actually delay it.
This allows keep data fresh in memory for faster reuse in case
it would be required. The effective closing will only happen after some time.
For specific operations, this will produce a real speed up in ReactOS.
For instance, with that patch, Winamp starts within seconds, instead of dozen
of minutes.
In most cases, it will bring ReactOS to performances it had before fixing
the huge leak in FastFAT (commit 94ead99) without leaking the whole FS.
For now, due to regressions, this is only activated for files and not
for directories. Once it gets fixed, it will be enabled for both.
CORE-14826
CORE-14917
2018-08-18 15:04:02 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If cache is still initialized, release it
|
|
|
|
* This only affects directories
|
|
|
|
*/
|
|
|
|
if (pFcb->OpenHandleCount == 0 && BooleanFlagOn(pFcb->Flags, FCB_CACHE_INITIALIZED))
|
|
|
|
{
|
|
|
|
PFILE_OBJECT tmpFileObject;
|
|
|
|
tmpFileObject = pFcb->FileObject;
|
|
|
|
if (tmpFileObject != NULL)
|
|
|
|
{
|
|
|
|
pFcb->FileObject = NULL;
|
|
|
|
CcUninitializeCacheMap(tmpFileObject, NULL, NULL);
|
|
|
|
ClearFlag(pFcb->Flags, FCB_CACHE_INITIALIZED);
|
|
|
|
ObDereferenceObject(tmpFileObject);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef KDBG
|
|
|
|
pFcb->Flags |= FCB_CLOSED;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Release the FCB, we likely cause its deletion */
|
|
|
|
vfatReleaseFCB(DeviceExt, pFcb);
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
NTAPI
|
|
|
|
VfatCloseWorker(
|
|
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
|
|
IN PVOID Context)
|
|
|
|
{
|
|
|
|
PLIST_ENTRY Entry;
|
|
|
|
PVFATFCB pFcb;
|
|
|
|
PDEVICE_EXTENSION Vcb;
|
|
|
|
PVFAT_CLOSE_CONTEXT CloseContext;
|
|
|
|
BOOLEAN ConcurrentDeletion;
|
|
|
|
|
|
|
|
/* Start removing work items */
|
|
|
|
ExAcquireFastMutex(&VfatGlobalData->CloseMutex);
|
|
|
|
while (!IsListEmpty(&VfatGlobalData->CloseListHead))
|
|
|
|
{
|
|
|
|
Entry = RemoveHeadList(&VfatGlobalData->CloseListHead);
|
|
|
|
CloseContext = CONTAINING_RECORD(Entry, VFAT_CLOSE_CONTEXT, CloseListEntry);
|
|
|
|
|
|
|
|
/* One less */
|
|
|
|
--VfatGlobalData->CloseCount;
|
|
|
|
/* Reset its entry to detect concurrent deletions */
|
|
|
|
InitializeListHead(&CloseContext->CloseListEntry);
|
|
|
|
ExReleaseFastMutex(&VfatGlobalData->CloseMutex);
|
|
|
|
|
|
|
|
/* Get the elements */
|
|
|
|
Vcb = CloseContext->Vcb;
|
|
|
|
pFcb = CloseContext->Fcb;
|
|
|
|
ExAcquireResourceExclusiveLite(&Vcb->DirResource, TRUE);
|
|
|
|
/* If it didn't got deleted in between */
|
|
|
|
if (BooleanFlagOn(pFcb->Flags, FCB_DELAYED_CLOSE))
|
|
|
|
{
|
|
|
|
/* Close it! */
|
|
|
|
DPRINT("Late closing: %wZ\n", &pFcb->PathNameU);
|
|
|
|
ClearFlag(pFcb->Flags, FCB_DELAYED_CLOSE);
|
|
|
|
pFcb->CloseContext = NULL;
|
|
|
|
VfatCommonCloseFile(Vcb, pFcb);
|
|
|
|
ConcurrentDeletion = FALSE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Otherwise, mark not to delete it */
|
|
|
|
ConcurrentDeletion = TRUE;
|
|
|
|
}
|
|
|
|
ExReleaseResourceLite(&Vcb->DirResource);
|
|
|
|
|
|
|
|
/* If we were the fastest, delete the context */
|
|
|
|
if (!ConcurrentDeletion)
|
|
|
|
{
|
|
|
|
ExFreeToPagedLookasideList(&VfatGlobalData->CloseContextLookasideList, CloseContext);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Lock again the list */
|
|
|
|
ExAcquireFastMutex(&VfatGlobalData->CloseMutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We're done, bye! */
|
|
|
|
VfatGlobalData->CloseWorkerRunning = FALSE;
|
|
|
|
ExReleaseFastMutex(&VfatGlobalData->CloseMutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
VfatPostCloseFile(
|
|
|
|
PDEVICE_EXTENSION DeviceExt,
|
|
|
|
PFILE_OBJECT FileObject)
|
|
|
|
{
|
|
|
|
PVFAT_CLOSE_CONTEXT CloseContext;
|
|
|
|
|
|
|
|
/* Allocate a work item */
|
|
|
|
CloseContext = ExAllocateFromPagedLookasideList(&VfatGlobalData->CloseContextLookasideList);
|
|
|
|
if (CloseContext == NULL)
|
|
|
|
{
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set relevant fields */
|
|
|
|
CloseContext->Vcb = DeviceExt;
|
|
|
|
CloseContext->Fcb = FileObject->FsContext;
|
|
|
|
CloseContext->Fcb->CloseContext = CloseContext;
|
|
|
|
|
|
|
|
/* Acquire the lock to insert in list */
|
|
|
|
ExAcquireFastMutex(&VfatGlobalData->CloseMutex);
|
|
|
|
|
|
|
|
/* One more element */
|
|
|
|
InsertTailList(&VfatGlobalData->CloseListHead, &CloseContext->CloseListEntry);
|
|
|
|
++VfatGlobalData->CloseCount;
|
|
|
|
|
|
|
|
/* If we have more than 16 items in list, and no worker thread
|
|
|
|
* start a new one
|
|
|
|
*/
|
|
|
|
if (VfatGlobalData->CloseCount > 16 && !VfatGlobalData->CloseWorkerRunning)
|
|
|
|
{
|
|
|
|
VfatGlobalData->CloseWorkerRunning = TRUE;
|
|
|
|
IoQueueWorkItem(VfatGlobalData->CloseWorkItem, VfatCloseWorker, CriticalWorkQueue, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We're done */
|
|
|
|
ExReleaseFastMutex(&VfatGlobalData->CloseMutex);
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2000-07-07 02:14:14 +00:00
|
|
|
/*
|
|
|
|
* FUNCTION: Closes a file
|
|
|
|
*/
|
2013-12-09 10:35:15 +00:00
|
|
|
NTSTATUS
|
|
|
|
VfatCloseFile(
|
|
|
|
PDEVICE_EXTENSION DeviceExt,
|
|
|
|
PFILE_OBJECT FileObject)
|
2000-07-07 02:14:14 +00:00
|
|
|
{
|
2013-12-09 10:35:15 +00:00
|
|
|
PVFATFCB pFcb;
|
|
|
|
PVFATCCB pCcb;
|
2019-01-12 15:25:52 +00:00
|
|
|
BOOLEAN IsVolume;
|
2013-12-09 10:35:15 +00:00
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
|
|
|
|
DPRINT("VfatCloseFile(DeviceExt %p, FileObject %p)\n",
|
2014-04-18 12:07:55 +00:00
|
|
|
DeviceExt, FileObject);
|
2013-12-09 10:35:15 +00:00
|
|
|
|
|
|
|
/* FIXME : update entry in directory? */
|
|
|
|
pCcb = (PVFATCCB) (FileObject->FsContext2);
|
|
|
|
pFcb = (PVFATFCB) (FileObject->FsContext);
|
|
|
|
|
|
|
|
if (pFcb == NULL)
|
2001-08-14 20:47:30 +00:00
|
|
|
{
|
2013-12-09 10:35:15 +00:00
|
|
|
return STATUS_SUCCESS;
|
2001-08-14 20:47:30 +00:00
|
|
|
}
|
2005-05-08 02:16:32 +00:00
|
|
|
|
2019-01-12 15:25:52 +00:00
|
|
|
IsVolume = BooleanFlagOn(pFcb->Flags, FCB_IS_VOLUME);
|
|
|
|
|
[FASTFAT] Implement delayed close
When we're about to close a file (ie, forget everything about it
and release any associated structure), actually delay it.
This allows keep data fresh in memory for faster reuse in case
it would be required. The effective closing will only happen after some time.
For specific operations, this will produce a real speed up in ReactOS.
For instance, with that patch, Winamp starts within seconds, instead of dozen
of minutes.
In most cases, it will bring ReactOS to performances it had before fixing
the huge leak in FastFAT (commit 94ead99) without leaking the whole FS.
For now, due to regressions, this is only activated for files and not
for directories. Once it gets fixed, it will be enabled for both.
CORE-14826
CORE-14917
2018-08-18 15:04:02 +00:00
|
|
|
if (pCcb)
|
2013-12-09 10:35:15 +00:00
|
|
|
{
|
[FASTFAT] Implement delayed close
When we're about to close a file (ie, forget everything about it
and release any associated structure), actually delay it.
This allows keep data fresh in memory for faster reuse in case
it would be required. The effective closing will only happen after some time.
For specific operations, this will produce a real speed up in ReactOS.
For instance, with that patch, Winamp starts within seconds, instead of dozen
of minutes.
In most cases, it will bring ReactOS to performances it had before fixing
the huge leak in FastFAT (commit 94ead99) without leaking the whole FS.
For now, due to regressions, this is only activated for files and not
for directories. Once it gets fixed, it will be enabled for both.
CORE-14826
CORE-14917
2018-08-18 15:04:02 +00:00
|
|
|
vfatDestroyCCB(pCcb);
|
2013-12-09 10:35:15 +00:00
|
|
|
}
|
2018-05-18 12:51:24 +00:00
|
|
|
|
[FASTFAT] Implement delayed close
When we're about to close a file (ie, forget everything about it
and release any associated structure), actually delay it.
This allows keep data fresh in memory for faster reuse in case
it would be required. The effective closing will only happen after some time.
For specific operations, this will produce a real speed up in ReactOS.
For instance, with that patch, Winamp starts within seconds, instead of dozen
of minutes.
In most cases, it will bring ReactOS to performances it had before fixing
the huge leak in FastFAT (commit 94ead99) without leaking the whole FS.
For now, due to regressions, this is only activated for files and not
for directories. Once it gets fixed, it will be enabled for both.
CORE-14826
CORE-14917
2018-08-18 15:04:02 +00:00
|
|
|
/* If we have to close immediately, or if delaying failed, close */
|
2018-08-19 07:55:38 +00:00
|
|
|
if (VfatGlobalData->ShutdownStarted || !BooleanFlagOn(pFcb->Flags, FCB_DELAYED_CLOSE) ||
|
|
|
|
!NT_SUCCESS(VfatPostCloseFile(DeviceExt, FileObject)))
|
[FASTFAT] Implement delayed close
When we're about to close a file (ie, forget everything about it
and release any associated structure), actually delay it.
This allows keep data fresh in memory for faster reuse in case
it would be required. The effective closing will only happen after some time.
For specific operations, this will produce a real speed up in ReactOS.
For instance, with that patch, Winamp starts within seconds, instead of dozen
of minutes.
In most cases, it will bring ReactOS to performances it had before fixing
the huge leak in FastFAT (commit 94ead99) without leaking the whole FS.
For now, due to regressions, this is only activated for files and not
for directories. Once it gets fixed, it will be enabled for both.
CORE-14826
CORE-14917
2018-08-18 15:04:02 +00:00
|
|
|
{
|
|
|
|
VfatCommonCloseFile(DeviceExt, pFcb);
|
2013-12-09 10:35:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
FileObject->FsContext2 = NULL;
|
|
|
|
FileObject->FsContext = NULL;
|
|
|
|
FileObject->SectionObjectPointer = NULL;
|
2000-12-29 23:17:12 +00:00
|
|
|
|
2014-11-29 20:26:07 +00:00
|
|
|
#ifdef ENABLE_SWAPOUT
|
2019-01-12 15:25:52 +00:00
|
|
|
if (IsVolume && DeviceExt->OpenHandleCount == 0)
|
2014-11-10 22:11:36 +00:00
|
|
|
{
|
|
|
|
VfatCheckForDismount(DeviceExt, FALSE);
|
|
|
|
}
|
2014-11-29 20:26:07 +00:00
|
|
|
#endif
|
2014-11-10 22:11:36 +00:00
|
|
|
|
2013-12-09 10:35:15 +00:00
|
|
|
return Status;
|
2000-07-07 02:14:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FUNCTION: Closes a file
|
|
|
|
*/
|
2013-12-09 10:35:15 +00:00
|
|
|
NTSTATUS
|
|
|
|
VfatClose(
|
|
|
|
PVFAT_IRP_CONTEXT IrpContext)
|
2000-07-07 02:14:14 +00:00
|
|
|
{
|
2013-12-09 10:35:15 +00:00
|
|
|
NTSTATUS Status;
|
2000-12-29 23:17:12 +00:00
|
|
|
|
2013-12-09 10:35:15 +00:00
|
|
|
DPRINT("VfatClose(DeviceObject %p, Irp %p)\n", IrpContext->DeviceObject, IrpContext->Irp);
|
2000-12-29 23:17:12 +00:00
|
|
|
|
2013-12-09 10:35:15 +00:00
|
|
|
if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
|
2002-05-15 18:05:00 +00:00
|
|
|
{
|
2013-12-09 10:35:15 +00:00
|
|
|
DPRINT("Closing file system\n");
|
2015-05-15 16:03:29 +00:00
|
|
|
IrpContext->Irp->IoStatus.Information = 0;
|
|
|
|
return STATUS_SUCCESS;
|
2002-05-15 18:05:00 +00:00
|
|
|
}
|
2015-05-25 17:54:27 +00:00
|
|
|
if (!ExAcquireResourceExclusiveLite(&IrpContext->DeviceExt->DirResource, BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
|
2013-12-09 10:35:15 +00:00
|
|
|
{
|
2015-05-15 16:03:29 +00:00
|
|
|
return VfatMarkIrpContextForQueue(IrpContext);
|
2013-12-09 10:35:15 +00:00
|
|
|
}
|
2001-11-02 22:47:36 +00:00
|
|
|
|
2013-12-09 10:35:15 +00:00
|
|
|
Status = VfatCloseFile(IrpContext->DeviceExt, IrpContext->FileObject);
|
|
|
|
ExReleaseResourceLite(&IrpContext->DeviceExt->DirResource);
|
2000-12-29 23:17:12 +00:00
|
|
|
|
2013-12-09 10:35:15 +00:00
|
|
|
IrpContext->Irp->IoStatus.Information = 0;
|
2000-12-29 23:17:12 +00:00
|
|
|
|
2013-12-09 10:35:15 +00:00
|
|
|
return Status;
|
2000-07-07 02:14:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* EOF */
|