mirror of
https://github.com/reactos/reactos.git
synced 2024-08-26 05:08:39 +00:00
![Pierre Schweitzer](/assets/img/avatar_default.png)
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
519 lines
15 KiB
C
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;
|
|
}
|