reactos/drivers/filesystems/fastfat/blockdev.c
Pierre Schweitzer 7c01587680
[FASTFAT] Completely rewrite support for dirty volumes.
Until now, our support for dirty volumes was totally broken
to a point where, on FAT32 volume, the dirty couldn't even
be written nor read from the disk.

This commit totally rewrites its handling, for both FAT16 and FAT32
so that it's now fully functionnal. Furthermore, it also gets
totally compatible with our vfatlib, and thus, autochk.
Now, on mount, FastFAT will check if the volume is dirty or not, and
autochk will be able to ask for a repair if dirty. vfatlib will
repair the volume and remove the dirty bit. So that, on next
reboot, the volume will be mounted clean.

As a reminder, the dirty bit is set immediately after mounting
the volume, so that, if you crash or have a powercut, autochk
will always attempt to repair your volume (with more or less,
that's FAT!).

If you want to experience without breaking your FAT volume,
just boot, open a cmd prompt and type: fsutil dirty set c:
and reboot!

CORE-13758
CORE-13760
CORE-13759
2018-05-18 23:05:05 +02:00

519 lines
15 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: drivers/fs/vfat/blockdev.c
* PURPOSE: Temporary sector reading support
* PROGRAMMER: David Welch (welch@cwcom.net)
* UPDATE HISTORY:
*/
/* INCLUDES *****************************************************************/
#include "vfat.h"
#define NDEBUG
#include <debug.h>
/* FUNCTIONS ***************************************************************/
static IO_COMPLETION_ROUTINE VfatReadWritePartialCompletion;
static
NTSTATUS
NTAPI
VfatReadWritePartialCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context)
{
PVFAT_IRP_CONTEXT IrpContext;
PMDL Mdl;
UNREFERENCED_PARAMETER(DeviceObject);
DPRINT("VfatReadWritePartialCompletion() called\n");
IrpContext = (PVFAT_IRP_CONTEXT)Context;
while ((Mdl = Irp->MdlAddress))
{
Irp->MdlAddress = Mdl->Next;
IoFreeMdl(Mdl);
}
if (Irp->PendingReturned)
{
IrpContext->Flags |= IRPCONTEXT_PENDINGRETURNED;
}
else
{
IrpContext->Flags &= ~IRPCONTEXT_PENDINGRETURNED;
}
if (!NT_SUCCESS(Irp->IoStatus.Status))
{
IrpContext->Irp->IoStatus.Status = Irp->IoStatus.Status;
}
if (0 == InterlockedDecrement((PLONG)&IrpContext->RefCount) &&
BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_PENDINGRETURNED))
{
KeSetEvent(&IrpContext->Event, IO_NO_INCREMENT, FALSE);
}
IoFreeIrp(Irp);
DPRINT("VfatReadWritePartialCompletion() done\n");
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
VfatReadDisk(
IN PDEVICE_OBJECT pDeviceObject,
IN PLARGE_INTEGER ReadOffset,
IN ULONG ReadLength,
IN OUT PUCHAR Buffer,
IN BOOLEAN Override)
{
PIO_STACK_LOCATION Stack;
PIRP Irp;
IO_STATUS_BLOCK IoStatus;
KEVENT Event;
NTSTATUS Status;
again:
KeInitializeEvent(&Event, NotificationEvent, FALSE);
DPRINT("VfatReadDisk(pDeviceObject %p, Offset %I64x, Length %u, Buffer %p)\n",
pDeviceObject, ReadOffset->QuadPart, ReadLength, Buffer);
DPRINT ("Building synchronous FSD Request...\n");
Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
pDeviceObject,
Buffer,
ReadLength,
ReadOffset,
&Event,
&IoStatus);
if (Irp == NULL)
{
DPRINT("IoBuildSynchronousFsdRequest failed\n");
return STATUS_UNSUCCESSFUL;
}
if (Override)
{
Stack = IoGetNextIrpStackLocation(Irp);
Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
}
DPRINT("Calling IO Driver... with irp %p\n", Irp);
Status = IoCallDriver (pDeviceObject, Irp);
DPRINT("Waiting for IO Operation for %p\n", Irp);
if (Status == STATUS_PENDING)
{
DPRINT("Operation pending\n");
KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
DPRINT("Getting IO Status... for %p\n", Irp);
Status = IoStatus.Status;
}
if (Status == STATUS_VERIFY_REQUIRED)
{
PDEVICE_OBJECT DeviceToVerify;
DPRINT1 ("Media change detected!\n");
/* Find the device to verify and reset the thread field to empty value again. */
DeviceToVerify = IoGetDeviceToVerify(PsGetCurrentThread());
IoSetDeviceToVerify(PsGetCurrentThread(), NULL);
Status = IoVerifyVolume(DeviceToVerify,
FALSE);
if (NT_SUCCESS(Status))
{
DPRINT1("Volume verification successful; Reissuing read request\n");
goto again;
}
}
if (!NT_SUCCESS(Status))
{
DPRINT("IO failed!!! VfatReadDisk : Error code: %x\n", Status);
DPRINT("(pDeviceObject %p, Offset %I64x, Size %u, Buffer %p\n",
pDeviceObject, ReadOffset->QuadPart, ReadLength, Buffer);
return Status;
}
DPRINT("Block request succeeded for %p\n", Irp);
return STATUS_SUCCESS;
}
NTSTATUS
VfatReadDiskPartial(
IN PVFAT_IRP_CONTEXT IrpContext,
IN PLARGE_INTEGER ReadOffset,
IN ULONG ReadLength,
ULONG BufferOffset,
IN BOOLEAN Wait)
{
PIRP Irp;
PIO_STACK_LOCATION StackPtr;
NTSTATUS Status;
PVOID Buffer;
DPRINT("VfatReadDiskPartial(IrpContext %p, ReadOffset %I64x, ReadLength %u, BufferOffset %u, Wait %u)\n",
IrpContext, ReadOffset->QuadPart, ReadLength, BufferOffset, Wait);
DPRINT("Building asynchronous FSD Request...\n");
Buffer = (PCHAR)MmGetMdlVirtualAddress(IrpContext->Irp->MdlAddress) + BufferOffset;
again:
Irp = IoAllocateIrp(IrpContext->DeviceExt->StorageDevice->StackSize, TRUE);
if (Irp == NULL)
{
DPRINT("IoAllocateIrp failed\n");
return STATUS_UNSUCCESSFUL;
}
Irp->UserIosb = NULL;
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
StackPtr = IoGetNextIrpStackLocation(Irp);
StackPtr->MajorFunction = IRP_MJ_READ;
StackPtr->MinorFunction = 0;
StackPtr->Flags = 0;
StackPtr->Control = 0;
StackPtr->DeviceObject = IrpContext->DeviceExt->StorageDevice;
StackPtr->FileObject = NULL;
StackPtr->CompletionRoutine = NULL;
StackPtr->Parameters.Read.Length = ReadLength;
StackPtr->Parameters.Read.ByteOffset = *ReadOffset;
if (!IoAllocateMdl(Buffer, ReadLength, FALSE, FALSE, Irp))
{
DPRINT("IoAllocateMdl failed\n");
IoFreeIrp(Irp);
return STATUS_UNSUCCESSFUL;
}
IoBuildPartialMdl(IrpContext->Irp->MdlAddress, Irp->MdlAddress, Buffer, ReadLength);
IoSetCompletionRoutine(Irp,
VfatReadWritePartialCompletion,
IrpContext,
TRUE,
TRUE,
TRUE);
if (Wait)
{
KeInitializeEvent(&IrpContext->Event, NotificationEvent, FALSE);
IrpContext->RefCount = 1;
}
else
{
InterlockedIncrement((PLONG)&IrpContext->RefCount);
}
DPRINT("Calling IO Driver... with irp %p\n", Irp);
Status = IoCallDriver(IrpContext->DeviceExt->StorageDevice, Irp);
if (Wait && Status == STATUS_PENDING)
{
KeWaitForSingleObject(&IrpContext->Event, Executive, KernelMode, FALSE, NULL);
Status = IrpContext->Irp->IoStatus.Status;
}
if (Status == STATUS_VERIFY_REQUIRED)
{
PDEVICE_OBJECT DeviceToVerify;
DPRINT1("Media change detected!\n");
/* Find the device to verify and reset the thread field to empty value again. */
DeviceToVerify = IoGetDeviceToVerify(PsGetCurrentThread());
IoSetDeviceToVerify(PsGetCurrentThread(), NULL);
Status = IoVerifyVolume(DeviceToVerify,
FALSE);
if (NT_SUCCESS(Status))
{
DPRINT1("Volume verification successful; Reissuing read request\n");
goto again;
}
}
DPRINT("%x\n", Status);
return Status;
}
/* Used by dirty bit code, likely to be killed the day it's properly handle
* This is just a copy paste from VfatReadDisk()
*/
NTSTATUS
VfatWriteDisk(
IN PDEVICE_OBJECT pDeviceObject,
IN PLARGE_INTEGER WriteOffset,
IN ULONG WriteLength,
IN OUT PUCHAR Buffer,
IN BOOLEAN Override)
{
PIO_STACK_LOCATION Stack;
PIRP Irp;
IO_STATUS_BLOCK IoStatus;
KEVENT Event;
NTSTATUS Status;
again:
KeInitializeEvent(&Event, NotificationEvent, FALSE);
DPRINT("VfatWriteDisk(pDeviceObject %p, Offset %I64x, Length %u, Buffer %p)\n",
pDeviceObject, WriteOffset->QuadPart, WriteLength, Buffer);
DPRINT ("Building synchronous FSD Request...\n");
Irp = IoBuildSynchronousFsdRequest(IRP_MJ_WRITE,
pDeviceObject,
Buffer,
WriteLength,
WriteOffset,
&Event,
&IoStatus);
if (Irp == NULL)
{
DPRINT("IoBuildSynchronousFsdRequest failed\n");
return STATUS_UNSUCCESSFUL;
}
if (Override)
{
Stack = IoGetNextIrpStackLocation(Irp);
Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
}
DPRINT("Calling IO Driver... with irp %p\n", Irp);
Status = IoCallDriver (pDeviceObject, Irp);
DPRINT("Waiting for IO Operation for %p\n", Irp);
if (Status == STATUS_PENDING)
{
DPRINT("Operation pending\n");
KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
DPRINT("Getting IO Status... for %p\n", Irp);
Status = IoStatus.Status;
}
if (Status == STATUS_VERIFY_REQUIRED)
{
PDEVICE_OBJECT DeviceToVerify;
DPRINT1 ("Media change detected!\n");
/* Find the device to verify and reset the thread field to empty value again. */
DeviceToVerify = IoGetDeviceToVerify(PsGetCurrentThread());
IoSetDeviceToVerify(PsGetCurrentThread(), NULL);
Status = IoVerifyVolume(DeviceToVerify,
FALSE);
if (NT_SUCCESS(Status))
{
DPRINT1("Volume verification successful; Reissuing write request\n");
goto again;
}
}
if (!NT_SUCCESS(Status))
{
DPRINT("IO failed!!! VfatWriteDisk : Error code: %x\n", Status);
DPRINT("(pDeviceObject %p, Offset %I64x, Size %u, Buffer %p\n",
pDeviceObject, WriteOffset->QuadPart, WriteLength, Buffer);
return Status;
}
DPRINT("Block request succeeded for %p\n", Irp);
return STATUS_SUCCESS;
}
NTSTATUS
VfatWriteDiskPartial(
IN PVFAT_IRP_CONTEXT IrpContext,
IN PLARGE_INTEGER WriteOffset,
IN ULONG WriteLength,
IN ULONG BufferOffset,
IN BOOLEAN Wait)
{
PIRP Irp;
PIO_STACK_LOCATION StackPtr;
NTSTATUS Status;
PVOID Buffer;
DPRINT("VfatWriteDiskPartial(IrpContext %p, WriteOffset %I64x, WriteLength %u, BufferOffset %x, Wait %u)\n",
IrpContext, WriteOffset->QuadPart, WriteLength, BufferOffset, Wait);
Buffer = (PCHAR)MmGetMdlVirtualAddress(IrpContext->Irp->MdlAddress) + BufferOffset;
again:
DPRINT("Building asynchronous FSD Request...\n");
Irp = IoAllocateIrp(IrpContext->DeviceExt->StorageDevice->StackSize, TRUE);
if (Irp == NULL)
{
DPRINT("IoAllocateIrp failed\n");
return STATUS_UNSUCCESSFUL;
}
Irp->UserIosb = NULL;
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
StackPtr = IoGetNextIrpStackLocation(Irp);
StackPtr->MajorFunction = IRP_MJ_WRITE;
StackPtr->MinorFunction = 0;
StackPtr->Flags = 0;
StackPtr->Control = 0;
StackPtr->DeviceObject = IrpContext->DeviceExt->StorageDevice;
StackPtr->FileObject = NULL;
StackPtr->CompletionRoutine = NULL;
StackPtr->Parameters.Read.Length = WriteLength;
StackPtr->Parameters.Read.ByteOffset = *WriteOffset;
if (!IoAllocateMdl(Buffer, WriteLength, FALSE, FALSE, Irp))
{
DPRINT("IoAllocateMdl failed\n");
IoFreeIrp(Irp);
return STATUS_UNSUCCESSFUL;
}
IoBuildPartialMdl(IrpContext->Irp->MdlAddress, Irp->MdlAddress, Buffer, WriteLength);
IoSetCompletionRoutine(Irp,
VfatReadWritePartialCompletion,
IrpContext,
TRUE,
TRUE,
TRUE);
if (Wait)
{
KeInitializeEvent(&IrpContext->Event, NotificationEvent, FALSE);
IrpContext->RefCount = 1;
}
else
{
InterlockedIncrement((PLONG)&IrpContext->RefCount);
}
DPRINT("Calling IO Driver...\n");
Status = IoCallDriver(IrpContext->DeviceExt->StorageDevice, Irp);
if (Wait && Status == STATUS_PENDING)
{
KeWaitForSingleObject(&IrpContext->Event, Executive, KernelMode, FALSE, NULL);
Status = IrpContext->Irp->IoStatus.Status;
}
if (Status == STATUS_VERIFY_REQUIRED)
{
PDEVICE_OBJECT DeviceToVerify;
DPRINT1("Media change detected!\n");
/* Find the device to verify and reset the thread field to empty value again. */
DeviceToVerify = IoGetDeviceToVerify(PsGetCurrentThread());
IoSetDeviceToVerify(PsGetCurrentThread(), NULL);
Status = IoVerifyVolume(DeviceToVerify,
FALSE);
if (NT_SUCCESS(Status))
{
DPRINT1("Volume verification successful; Reissuing write request\n");
goto again;
}
}
return Status;
}
NTSTATUS
VfatBlockDeviceIoControl(
IN PDEVICE_OBJECT DeviceObject,
IN ULONG CtlCode,
IN PVOID InputBuffer OPTIONAL,
IN ULONG InputBufferSize,
IN OUT PVOID OutputBuffer OPTIONAL,
IN OUT PULONG OutputBufferSize,
IN BOOLEAN Override)
{
PIO_STACK_LOCATION Stack;
KEVENT Event;
PIRP Irp;
IO_STATUS_BLOCK IoStatus;
NTSTATUS Status;
DPRINT("VfatBlockDeviceIoControl(DeviceObject %p, CtlCode %x, "
"InputBuffer %p, InputBufferSize %x, OutputBuffer %p, "
"OutputBufferSize %p (%x)\n", DeviceObject, CtlCode,
InputBuffer, InputBufferSize, OutputBuffer, OutputBufferSize,
OutputBufferSize ? *OutputBufferSize : 0);
again:
KeInitializeEvent(&Event, NotificationEvent, FALSE);
DPRINT("Building device I/O control request ...\n");
Irp = IoBuildDeviceIoControlRequest(CtlCode,
DeviceObject,
InputBuffer,
InputBufferSize,
OutputBuffer,
(OutputBufferSize) ? *OutputBufferSize : 0,
FALSE,
&Event,
&IoStatus);
if (Irp == NULL)
{
DPRINT("IoBuildDeviceIoControlRequest failed\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
if (Override)
{
Stack = IoGetNextIrpStackLocation(Irp);
Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
}
DPRINT("Calling IO Driver... with irp %p\n", Irp);
Status = IoCallDriver(DeviceObject, Irp);
DPRINT("Waiting for IO Operation for %p\n", Irp);
if (Status == STATUS_PENDING)
{
DPRINT("Operation pending\n");
KeWaitForSingleObject (&Event, Suspended, KernelMode, FALSE, NULL);
DPRINT("Getting IO Status... for %p\n", Irp);
Status = IoStatus.Status;
}
if (Status == STATUS_VERIFY_REQUIRED)
{
PDEVICE_OBJECT DeviceToVerify;
DPRINT1("Media change detected!\n");
/* Find the device to verify and reset the thread field to empty value again. */
DeviceToVerify = IoGetDeviceToVerify(PsGetCurrentThread());
IoSetDeviceToVerify(PsGetCurrentThread(), NULL);
Status = IoVerifyVolume(DeviceToVerify,
FALSE);
if (NT_SUCCESS(Status))
{
DPRINT1("Volume verification successful; Reissuing IOCTL request\n");
goto again;
}
}
if (OutputBufferSize)
{
*OutputBufferSize = IoStatus.Information;
}
DPRINT("Returning Status %x\n", Status);
return Status;
}