mirror of
https://github.com/reactos/reactos.git
synced 2024-08-26 05:08:39 +00:00
![Hermès Bélusca-Maïto](/assets/img/avatar_default.png)
- 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.
183 lines
5.2 KiB
C
183 lines
5.2 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS kernel
|
|
* FILE: drivers/fs/vfat/flush.c
|
|
* PURPOSE: VFAT Filesystem
|
|
* PROGRAMMER:
|
|
*/
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
#include "vfat.h"
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* FUNCTIONS ****************************************************************/
|
|
|
|
static
|
|
NTSTATUS
|
|
VfatFlushFile(
|
|
PDEVICE_EXTENSION DeviceExt,
|
|
PVFATFCB Fcb)
|
|
{
|
|
IO_STATUS_BLOCK IoStatus;
|
|
NTSTATUS Status;
|
|
|
|
DPRINT("VfatFlushFile(DeviceExt %p, Fcb %p) for '%wZ'\n", DeviceExt, Fcb, &Fcb->PathNameU);
|
|
|
|
CcFlushCache(&Fcb->SectionObjectPointers, NULL, 0, &IoStatus);
|
|
if (IoStatus.Status == STATUS_INVALID_PARAMETER)
|
|
{
|
|
/* FIXME: Caching was possible not initialized */
|
|
IoStatus.Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
ExAcquireResourceExclusiveLite(&DeviceExt->DirResource, TRUE);
|
|
if (BooleanFlagOn(Fcb->Flags, FCB_IS_DIRTY))
|
|
{
|
|
Status = VfatUpdateEntry(DeviceExt, Fcb);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
IoStatus.Status = Status;
|
|
}
|
|
}
|
|
ExReleaseResourceLite(&DeviceExt->DirResource);
|
|
|
|
return IoStatus.Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
VfatFlushVolume(
|
|
PDEVICE_EXTENSION DeviceExt,
|
|
PVFATFCB VolumeFcb)
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
PVFATFCB Fcb;
|
|
NTSTATUS Status, ReturnStatus = STATUS_SUCCESS;
|
|
PIRP Irp;
|
|
KEVENT Event;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
|
|
DPRINT("VfatFlushVolume(DeviceExt %p, VolumeFcb %p)\n", DeviceExt, VolumeFcb);
|
|
|
|
ASSERT(VolumeFcb == DeviceExt->VolumeFcb);
|
|
|
|
ListEntry = DeviceExt->FcbListHead.Flink;
|
|
while (ListEntry != &DeviceExt->FcbListHead)
|
|
{
|
|
Fcb = CONTAINING_RECORD(ListEntry, VFATFCB, FcbListEntry);
|
|
ListEntry = ListEntry->Flink;
|
|
if (!vfatFCBIsDirectory(Fcb))
|
|
{
|
|
ExAcquireResourceExclusiveLite(&Fcb->MainResource, TRUE);
|
|
Status = VfatFlushFile(DeviceExt, Fcb);
|
|
ExReleaseResourceLite (&Fcb->MainResource);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("VfatFlushFile failed, status = %x\n", Status);
|
|
ReturnStatus = Status;
|
|
}
|
|
}
|
|
/* FIXME: Stop flushing if this is a removable media and the media was removed */
|
|
}
|
|
|
|
ListEntry = DeviceExt->FcbListHead.Flink;
|
|
while (ListEntry != &DeviceExt->FcbListHead)
|
|
{
|
|
Fcb = CONTAINING_RECORD(ListEntry, VFATFCB, FcbListEntry);
|
|
ListEntry = ListEntry->Flink;
|
|
if (vfatFCBIsDirectory(Fcb))
|
|
{
|
|
ExAcquireResourceExclusiveLite(&Fcb->MainResource, TRUE);
|
|
Status = VfatFlushFile(DeviceExt, Fcb);
|
|
ExReleaseResourceLite (&Fcb->MainResource);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("VfatFlushFile failed, status = %x\n", Status);
|
|
ReturnStatus = Status;
|
|
}
|
|
}
|
|
/* FIXME: Stop flushing if this is a removable media and the media was removed */
|
|
}
|
|
|
|
Fcb = (PVFATFCB) DeviceExt->FATFileObject->FsContext;
|
|
|
|
ExAcquireResourceExclusiveLite(&DeviceExt->FatResource, TRUE);
|
|
Status = VfatFlushFile(DeviceExt, Fcb);
|
|
ExReleaseResourceLite(&DeviceExt->FatResource);
|
|
|
|
/* Prepare an IRP to flush device buffers */
|
|
Irp = IoBuildSynchronousFsdRequest(IRP_MJ_FLUSH_BUFFERS,
|
|
DeviceExt->StorageDevice,
|
|
NULL, 0, NULL, &Event,
|
|
&IoStatusBlock);
|
|
if (Irp != NULL)
|
|
{
|
|
KeInitializeEvent(&Event, NotificationEvent, FALSE);
|
|
|
|
Status = IoCallDriver(DeviceExt->StorageDevice, Irp);
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
|
|
Status = IoStatusBlock.Status;
|
|
}
|
|
|
|
/* Ignore device not supporting flush operation */
|
|
if (Status == STATUS_INVALID_DEVICE_REQUEST)
|
|
{
|
|
DPRINT1("Flush not supported, ignored\n");
|
|
Status = STATUS_SUCCESS;
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("VfatFlushFile failed, status = %x\n", Status);
|
|
ReturnStatus = Status;
|
|
}
|
|
|
|
return ReturnStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
VfatFlush(
|
|
PVFAT_IRP_CONTEXT IrpContext)
|
|
{
|
|
NTSTATUS Status;
|
|
PVFATFCB Fcb;
|
|
|
|
/* This request is not allowed on the main device object. */
|
|
if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
|
|
{
|
|
IrpContext->Irp->IoStatus.Information = 0;
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
Fcb = (PVFATFCB)IrpContext->FileObject->FsContext;
|
|
ASSERT(Fcb);
|
|
|
|
if (BooleanFlagOn(Fcb->Flags, FCB_IS_VOLUME))
|
|
{
|
|
ExAcquireResourceExclusiveLite(&IrpContext->DeviceExt->DirResource, TRUE);
|
|
Status = VfatFlushVolume(IrpContext->DeviceExt, Fcb);
|
|
ExReleaseResourceLite(&IrpContext->DeviceExt->DirResource);
|
|
}
|
|
else
|
|
{
|
|
ExAcquireResourceExclusiveLite(&Fcb->MainResource, TRUE);
|
|
Status = VfatFlushFile(IrpContext->DeviceExt, Fcb);
|
|
ExReleaseResourceLite (&Fcb->MainResource);
|
|
}
|
|
|
|
IrpContext->Irp->IoStatus.Information = 0;
|
|
return Status;
|
|
}
|
|
|
|
/* EOF */
|