1831 lines
43 KiB
C
1831 lines
43 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1991 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
ftutil.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module contains support routines called by other FtDisk components.
|
|||
|
The prototypes for these functions are in FtDisk.H.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Bob Rinne (bobri) 2-Feb-1992
|
|||
|
Mike Glass (mglass)
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
kernel mode only
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "ntddk.h"
|
|||
|
#include "ftdisk.h"
|
|||
|
#include <ntiologc.h>
|
|||
|
#include <stdarg.h>
|
|||
|
|
|||
|
#ifdef POOL_TAGGING
|
|||
|
#ifdef ExAllocatePool
|
|||
|
#undef ExAllocatePool
|
|||
|
#endif
|
|||
|
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,' UtF')
|
|||
|
#endif
|
|||
|
|
|||
|
#if DBG
|
|||
|
|
|||
|
//
|
|||
|
// FT disk debug level global variable
|
|||
|
//
|
|||
|
|
|||
|
ULONG FtDebug = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Flags for turning on/off debugging.
|
|||
|
//
|
|||
|
|
|||
|
ULONG FtBreakOnMissingLog = 0;
|
|||
|
ULONG FtRecordIrps = 0;
|
|||
|
ULONG FtWatchMdlFree = 0;
|
|||
|
|
|||
|
#endif // DBG
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Constant data declarations.
|
|||
|
//
|
|||
|
|
|||
|
PCHAR FtDeviceName = "\\Device\\Ft";
|
|||
|
|
|||
|
//
|
|||
|
// Global Sequence number for error log.
|
|||
|
//
|
|||
|
|
|||
|
ULONG FtErrorLogSequence = 0;
|
|||
|
|
|||
|
|
|||
|
#if DBG
|
|||
|
|
|||
|
VOID
|
|||
|
FtDebugPrint(
|
|||
|
ULONG DebugPrintLevel,
|
|||
|
PCCHAR DebugMessage,
|
|||
|
...
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Debug print for the Fault Tolerance Driver.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Debug print level between 0 and N, with N being the most verbose.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
va_list ap;
|
|||
|
char buffer[256];
|
|||
|
|
|||
|
va_start( ap, DebugMessage );
|
|||
|
|
|||
|
if (DebugPrintLevel <= FtDebug) {
|
|||
|
vsprintf(buffer, DebugMessage, ap);
|
|||
|
DbgPrint(buffer);
|
|||
|
}
|
|||
|
|
|||
|
va_end(ap);
|
|||
|
|
|||
|
} // end FtDebugPrint()
|
|||
|
|
|||
|
KSPIN_LOCK FtIrpLogSpinLock;
|
|||
|
IRP_LOG FtIrpLog[FT_NUMBER_OF_IRP_LOG_ENTRIES];
|
|||
|
ULONG FtIrpLogIndex = 0;
|
|||
|
|
|||
|
#define FT_HISTORY_ENTRIES 40
|
|||
|
PIRP FtIrpCompletionHistory[FT_HISTORY_ENTRIES];
|
|||
|
ULONG FtIrpHistoryIndex = 0;
|
|||
|
|
|||
|
PIRP FtMonitoredAssociatedIrp = NULL;
|
|||
|
ULONG FtMonitoredAssociatedCount = 0;
|
|||
|
|
|||
|
VOID
|
|||
|
FtpInitializeIrpLog()
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Initialize the Irp log structure for debugging purposes. The Irp logging
|
|||
|
feature will log every irp that enters and exists FT and is headed for
|
|||
|
an FT set.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
ULONG index;
|
|||
|
|
|||
|
KeInitializeSpinLock(&FtIrpLogSpinLock);
|
|||
|
for (index = 0; index < FT_NUMBER_OF_IRP_LOG_ENTRIES; index++) {
|
|||
|
|
|||
|
FtIrpLog[index].InUse = 0;
|
|||
|
FtIrpLog[index].Context = NULL;
|
|||
|
FtIrpLog[index].Irp = NULL;
|
|||
|
FtIrpLog[index].AssociatedIrp = NULL;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
FtpRecordIrp(
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Record the Irp in the log. Also make several sanity checks on the Irp
|
|||
|
and Irp duplicates.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Irp - the irp to log
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
KIRQL irql;
|
|||
|
ULONG index;
|
|||
|
|
|||
|
KeAcquireSpinLock(&FtIrpLogSpinLock, &irql);
|
|||
|
for (index = 0; index < FT_NUMBER_OF_IRP_LOG_ENTRIES; index++) {
|
|||
|
if (FtIrpLog[index].InUse) {
|
|||
|
if ((irpSp->Context == FtIrpLog[index].Context) &&
|
|||
|
(Irp->AssociatedIrp.MasterIrp != FtIrpLog[index].AssociatedIrp)) {
|
|||
|
DebugPrint((0,
|
|||
|
"FtpRecordIrp: %x and %x have same context %x (%x-%x)\n",
|
|||
|
Irp,
|
|||
|
FtIrpLog[index].Irp,
|
|||
|
FtIrpLog[index].Context,
|
|||
|
Irp->AssociatedIrp.MasterIrp,
|
|||
|
FtIrpLog[index].AssociatedIrp));
|
|||
|
ASSERT(irpSp->Context != FtIrpLog[index].Context);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
FtIrpLog[FtIrpLogIndex].InUse = 1;
|
|||
|
FtIrpLog[FtIrpLogIndex].Irp = Irp;
|
|||
|
FtIrpLog[FtIrpLogIndex].Context = irpSp->Context;
|
|||
|
|
|||
|
if (Irp->Flags & IRP_ASSOCIATED_IRP) {
|
|||
|
ASSERT(Irp->AssociatedIrp.MasterIrp->Type == 0x0006);
|
|||
|
FtIrpLog[FtIrpLogIndex].AssociatedIrp = Irp->AssociatedIrp.MasterIrp;
|
|||
|
|
|||
|
if (!FtMonitoredAssociatedIrp) {
|
|||
|
FtMonitoredAssociatedCount = 1;
|
|||
|
FtMonitoredAssociatedIrp = Irp->AssociatedIrp.MasterIrp;
|
|||
|
// watch(((ULONG)Irp->AssociatedIrp.MasterIrp & 0x3fffffff) | 0x00000001);
|
|||
|
} else {
|
|||
|
if (FtMonitoredAssociatedIrp == Irp->AssociatedIrp.MasterIrp) {
|
|||
|
FtMonitoredAssociatedCount++;
|
|||
|
}
|
|||
|
}
|
|||
|
} else {
|
|||
|
FtIrpLog[FtIrpLogIndex].AssociatedIrp = NULL;
|
|||
|
}
|
|||
|
|
|||
|
index = FtIrpLogIndex + 1;
|
|||
|
if (index == FT_NUMBER_OF_IRP_LOG_ENTRIES) {
|
|||
|
index = 0;
|
|||
|
}
|
|||
|
|
|||
|
while (FtIrpLog[index].InUse) {
|
|||
|
|
|||
|
index++;
|
|||
|
|
|||
|
if (index == FT_NUMBER_OF_IRP_LOG_ENTRIES) {
|
|||
|
index = 0;
|
|||
|
}
|
|||
|
|
|||
|
if (index == FtIrpLogIndex) {
|
|||
|
|
|||
|
//
|
|||
|
// Wrapped around through the whole table.
|
|||
|
//
|
|||
|
|
|||
|
DebugPrint((0, "FtpRecordIrp: ran out of entries\n"));
|
|||
|
KeReleaseSpinLock(&FtIrpLogSpinLock, irql);
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
FtIrpLogIndex = index;
|
|||
|
KeReleaseSpinLock(&FtIrpLogSpinLock, irql);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
FtpCompleteRequest(
|
|||
|
IN PIRP Irp,
|
|||
|
IN CCHAR Boost
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is used for debugging to keep track of Irps inside and
|
|||
|
below FT. It is a macro in free builds.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Irp - pointer to Irp to complete.
|
|||
|
Boost - priority boost
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
ULONG index;
|
|||
|
KIRQL irql;
|
|||
|
|
|||
|
KeAcquireSpinLock(&FtIrpLogSpinLock, &irql);
|
|||
|
|
|||
|
if (FtRecordIrps) {
|
|||
|
|
|||
|
if (Irp->Flags & IRP_ASSOCIATED_IRP) {
|
|||
|
|
|||
|
ASSERT(Irp->AssociatedIrp.MasterIrp->Type == 0x0006);
|
|||
|
if (Irp->AssociatedIrp.MasterIrp == FtMonitoredAssociatedIrp) {
|
|||
|
FtMonitoredAssociatedCount--;
|
|||
|
|
|||
|
if (FtMonitoredAssociatedCount == 0) {
|
|||
|
FtMonitoredAssociatedIrp = NULL;
|
|||
|
// watch(0);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
for (index = 0; index < FT_NUMBER_OF_IRP_LOG_ENTRIES; index++) {
|
|||
|
|
|||
|
if (FtIrpLog[index].InUse) {
|
|||
|
if (FtIrpLog[index].Irp == Irp) {
|
|||
|
FtIrpLog[index].InUse = 0;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
if (index == FT_NUMBER_OF_IRP_LOG_ENTRIES) {
|
|||
|
if ((irpSp->MajorFunction == IRP_MJ_READ) ||
|
|||
|
(irpSp->MajorFunction == IRP_MJ_WRITE)) {
|
|||
|
DebugPrint((0, "FtpCompleteRequest: %x (%d) not logged!\n",
|
|||
|
Irp,
|
|||
|
FtIrpLogIndex));
|
|||
|
ASSERT(FtBreakOnMissingLog == 0);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Keep addresses of last 40 IRPs completed.
|
|||
|
//
|
|||
|
|
|||
|
FtIrpCompletionHistory[FtIrpHistoryIndex] = Irp;
|
|||
|
FtIrpHistoryIndex++;
|
|||
|
if (FtIrpHistoryIndex == FT_HISTORY_ENTRIES) {
|
|||
|
FtIrpHistoryIndex = 0;
|
|||
|
}
|
|||
|
FtIrpCompletionHistory[FtIrpHistoryIndex] = (PIRP)0xffffffff;
|
|||
|
|
|||
|
KeReleaseSpinLock(&FtIrpLogSpinLock, irql);
|
|||
|
|
|||
|
IoCompleteRequest(Irp, Boost);
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
PIRP
|
|||
|
FtpBuildRequest(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PLARGE_INTEGER StartingOffset,
|
|||
|
IN ULONG TransferLength,
|
|||
|
IN PVOID BufferAddress,
|
|||
|
IN UCHAR Flags,
|
|||
|
IN PETHREAD Thread,
|
|||
|
IN UCHAR Function
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine builds an IRP to for the target device.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - target device object.
|
|||
|
StartingOffset - byte offset from beginning of partition.
|
|||
|
TransferLength - number of bytes to transfer.
|
|||
|
BufferAddress - virtual address of data buffer.
|
|||
|
Flags - the irp stack flags (could be an override for verify)
|
|||
|
Function - major IRP function.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
pointer to target request
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PIRP newIrp;
|
|||
|
PIO_STACK_LOCATION newIrpStack;
|
|||
|
|
|||
|
//
|
|||
|
// Allocate new IRP. Extra stack is for current stack in
|
|||
|
// error recovery.
|
|||
|
//
|
|||
|
|
|||
|
newIrp = IoAllocateIrp((UCHAR)(DeviceObject->StackSize+1), FALSE);
|
|||
|
|
|||
|
if (newIrp == NULL) {
|
|||
|
return (PIRP)NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set thread context for filesystems.
|
|||
|
//
|
|||
|
|
|||
|
newIrp->Tail.Overlay.Thread = Thread;
|
|||
|
|
|||
|
//
|
|||
|
// Bump stack pointer to reserve current stack for
|
|||
|
// error recovery.
|
|||
|
//
|
|||
|
|
|||
|
IoSetNextIrpStackLocation(newIrp);
|
|||
|
|
|||
|
//
|
|||
|
// Get next stack to set request parameters.
|
|||
|
//
|
|||
|
|
|||
|
newIrpStack = IoGetNextIrpStackLocation(newIrp);
|
|||
|
|
|||
|
newIrpStack->MajorFunction = Function;
|
|||
|
newIrpStack->Parameters.Read.Length = TransferLength;
|
|||
|
newIrpStack->Parameters.Read.ByteOffset = *StartingOffset;
|
|||
|
newIrpStack->DeviceObject = DeviceObject;
|
|||
|
newIrpStack->Flags = Flags;
|
|||
|
|
|||
|
//
|
|||
|
// Allocate MDL.
|
|||
|
//
|
|||
|
|
|||
|
newIrp->MdlAddress = IoAllocateMdl(BufferAddress,
|
|||
|
TransferLength,
|
|||
|
FALSE,
|
|||
|
FALSE,
|
|||
|
(PIRP)NULL);
|
|||
|
if (!newIrp->MdlAddress) {
|
|||
|
IoFreeIrp(newIrp);
|
|||
|
return (PIRP)NULL;
|
|||
|
} else {
|
|||
|
return newIrp;
|
|||
|
}
|
|||
|
|
|||
|
} // end FtpBuildRequest()
|
|||
|
|
|||
|
|
|||
|
PIRP
|
|||
|
FtpDuplicateIrp(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP InIrp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine allocates a new IRP to be given to the device
|
|||
|
represented by the device object parameter. It then copies
|
|||
|
the contents of the input IRP to the newly allocated IRP and
|
|||
|
returns a pointer for the new IRP.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - for the device the IRP will be given.
|
|||
|
InIrp - IRP to duplicate.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
PIRP for the new IRP.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PIO_STACK_LOCATION sourceIrpStack;
|
|||
|
PIO_STACK_LOCATION newIrpStack;
|
|||
|
PIRP newIrp;
|
|||
|
|
|||
|
DebugPrint((4,
|
|||
|
"FtpDuplicateIrp: Entered DeviceObject = %x, Irp = %x\n",
|
|||
|
DeviceObject,
|
|||
|
InIrp));
|
|||
|
|
|||
|
//
|
|||
|
// Allocate new IRP.
|
|||
|
//
|
|||
|
|
|||
|
newIrp = IoAllocateIrp((CCHAR) (DeviceObject->StackSize + (CCHAR) 1),
|
|||
|
(BOOLEAN) FALSE);
|
|||
|
ASSERT(newIrp != NULL);
|
|||
|
newIrp->Tail.Overlay.Thread = InIrp->Tail.Overlay.Thread;
|
|||
|
|
|||
|
//
|
|||
|
// Set the next stack to reserve a stack location in the new Irp for
|
|||
|
// FT driver use.
|
|||
|
//
|
|||
|
|
|||
|
IoSetNextIrpStackLocation(newIrp);
|
|||
|
|
|||
|
//
|
|||
|
// Write MDL address to new IRP.
|
|||
|
//
|
|||
|
|
|||
|
newIrp->MdlAddress = InIrp->MdlAddress;
|
|||
|
sourceIrpStack = IoGetCurrentIrpStackLocation(InIrp);
|
|||
|
|
|||
|
//
|
|||
|
// CURRENT STACK (belongs to FT)
|
|||
|
// Put the major function into the irp stack location for the FT
|
|||
|
// driver and associate the input irp with this irp.
|
|||
|
//
|
|||
|
|
|||
|
newIrpStack = IoGetCurrentIrpStackLocation(newIrp);
|
|||
|
newIrpStack->MajorFunction = sourceIrpStack->MajorFunction;
|
|||
|
newIrpStack->FtLowIrpMasterIrp = InIrp;
|
|||
|
newIrpStack->FtLowIrpAllocatedMdl = (PVOID) 0;
|
|||
|
|
|||
|
//
|
|||
|
// NEXT STACK (belongs to device object below)
|
|||
|
// Copy the IO stack from the input Irp into the next stack
|
|||
|
// for the duplicate Irp.
|
|||
|
//
|
|||
|
|
|||
|
newIrpStack = IoGetNextIrpStackLocation(newIrp);
|
|||
|
newIrpStack->MajorFunction = sourceIrpStack->MajorFunction;
|
|||
|
newIrpStack->Parameters = sourceIrpStack->Parameters;
|
|||
|
newIrpStack->DeviceObject = DeviceObject;
|
|||
|
newIrpStack->Flags = sourceIrpStack->Flags;
|
|||
|
DebugPrint((4,
|
|||
|
"FtpDuplicateIrp: returning Irp = %x\n",
|
|||
|
newIrp));
|
|||
|
return newIrp;
|
|||
|
} // FtpDuplicateIrp
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
FtpGetPartitionInformation(
|
|||
|
IN PUCHAR DeviceName,
|
|||
|
IN OUT PDRIVE_LAYOUT_INFORMATION *DriveLayout,
|
|||
|
OUT PDISK_GEOMETRY DiskGeometryPtr
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine returns the partition information. Since this routine
|
|||
|
uses IoReadPartitionTable() it is the callers responsibility to free
|
|||
|
the memory area allocated for the drive layout.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceName - pointer to the character string for the device wanted.
|
|||
|
DriveLayout - pointer to a pointer to the drive layout.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
Note: it is the callers responsibility to free the memory allocated
|
|||
|
by IoReadPartitionTable() for the drive layout information.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
STRING ntNameString;
|
|||
|
UNICODE_STRING ntUnicodeString;
|
|||
|
PFILE_OBJECT fileObject;
|
|||
|
NTSTATUS status;
|
|||
|
PDEVICE_OBJECT deviceObject;
|
|||
|
PDISK_GEOMETRY diskGeometry;
|
|||
|
IO_STATUS_BLOCK ioStatusBlock;
|
|||
|
PIRP irp;
|
|||
|
KEVENT event;
|
|||
|
|
|||
|
DebugPrint((4, "FtpGetPartitionInformation: Entered \n"));
|
|||
|
|
|||
|
//
|
|||
|
// Get target device object.
|
|||
|
//
|
|||
|
|
|||
|
RtlInitString(&ntNameString,
|
|||
|
DeviceName);
|
|||
|
status = RtlAnsiStringToUnicodeString(&ntUnicodeString,
|
|||
|
&ntNameString,
|
|||
|
TRUE);
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
status = IoGetDeviceObjectPointer(&ntUnicodeString,
|
|||
|
FILE_READ_ATTRIBUTES,
|
|||
|
&fileObject,
|
|||
|
&deviceObject);
|
|||
|
|
|||
|
RtlFreeUnicodeString(&ntUnicodeString);
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
ObDereferenceObject(fileObject);
|
|||
|
|
|||
|
//
|
|||
|
// Allocate buffer for drive geometry.
|
|||
|
//
|
|||
|
|
|||
|
diskGeometry = ExAllocatePool(NonPagedPool,
|
|||
|
sizeof(DISK_GEOMETRY));
|
|||
|
|
|||
|
if (diskGeometry == NULL) {
|
|||
|
return STATUS_UNSUCCESSFUL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Create IRP for get drive geometry device control.
|
|||
|
//
|
|||
|
|
|||
|
irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY,
|
|||
|
deviceObject,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
diskGeometry,
|
|||
|
sizeof(DISK_GEOMETRY),
|
|||
|
FALSE,
|
|||
|
&event,
|
|||
|
&ioStatusBlock);
|
|||
|
|
|||
|
if (irp == NULL) {
|
|||
|
ExFreePool(diskGeometry);
|
|||
|
return STATUS_UNSUCCESSFUL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the event object to the unsignaled state.
|
|||
|
// It will be used to signal request completion.
|
|||
|
//
|
|||
|
|
|||
|
KeInitializeEvent(&event,
|
|||
|
NotificationEvent,
|
|||
|
FALSE);
|
|||
|
|
|||
|
//
|
|||
|
// No need to check the following two returned statuses as
|
|||
|
// ioBlockStatus will have ending status.
|
|||
|
//
|
|||
|
|
|||
|
status = IoCallDriver(deviceObject,
|
|||
|
irp);
|
|||
|
|
|||
|
if (status == STATUS_PENDING) {
|
|||
|
KeWaitForSingleObject(&event,
|
|||
|
Suspended,
|
|||
|
KernelMode,
|
|||
|
FALSE,
|
|||
|
NULL);
|
|||
|
|
|||
|
status = ioStatusBlock.Status;
|
|||
|
}
|
|||
|
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
|
|||
|
//
|
|||
|
// Read the partition information for the device.
|
|||
|
//
|
|||
|
|
|||
|
status = IoReadPartitionTable(deviceObject,
|
|||
|
diskGeometry->BytesPerSector,
|
|||
|
TRUE,
|
|||
|
DriveLayout);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Return the disk geometry for this drive.
|
|||
|
//
|
|||
|
|
|||
|
*DiskGeometryPtr = *diskGeometry;
|
|||
|
|
|||
|
//
|
|||
|
// Free diskGeometry information.
|
|||
|
//
|
|||
|
|
|||
|
ExFreePool(diskGeometry);
|
|||
|
return status;
|
|||
|
|
|||
|
} // FtpGetPartitionInformation
|
|||
|
|
|||
|
|
|||
|
PDEVICE_EXTENSION
|
|||
|
FtpFindDeviceExtension(
|
|||
|
IN PDEVICE_EXTENSION FtRootExtension,
|
|||
|
IN ULONG Signature,
|
|||
|
IN LARGE_INTEGER StartingOffset,
|
|||
|
IN LARGE_INTEGER Length
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine finds the the device extension that matches the
|
|||
|
signature, offset and length parameters.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
FtRootExtension - Pointer to the root of the FT device extension chain.
|
|||
|
Signature - for the disk.
|
|||
|
StartingOffset - to locate the partition.
|
|||
|
Length - to validate the correct partition was found.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
A pointer to the device extension for the requested partition (if found).
|
|||
|
NULL if the item cannot be found.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION currentExtension;
|
|||
|
LARGE_INTEGER offset;
|
|||
|
LARGE_INTEGER size;
|
|||
|
|
|||
|
currentExtension = FtRootExtension->DiskChain;
|
|||
|
while (currentExtension != NULL) {
|
|||
|
if (currentExtension->FtUnion.Identity.Signature == Signature) {
|
|||
|
|
|||
|
//
|
|||
|
// This is the correct disk.
|
|||
|
//
|
|||
|
|
|||
|
while (currentExtension != NULL) {
|
|||
|
|
|||
|
offset = currentExtension->FtUnion.Identity.PartitionOffset;
|
|||
|
size = currentExtension->FtUnion.Identity.PartitionLength;
|
|||
|
|
|||
|
if (StartingOffset.QuadPart == offset.QuadPart &&
|
|||
|
Length.QuadPart == size.QuadPart) {
|
|||
|
|
|||
|
//
|
|||
|
// This is the partition desired.
|
|||
|
//
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
currentExtension = currentExtension->ProtectChain;
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
currentExtension = currentExtension->DiskChain;
|
|||
|
}
|
|||
|
|
|||
|
return currentExtension;
|
|||
|
} // FtpFindDeviceExtension
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
FtpAttach(
|
|||
|
IN PDRIVER_OBJECT DriverObject,
|
|||
|
IN PUCHAR AttachName,
|
|||
|
IN PUCHAR DeviceName,
|
|||
|
IN OUT PDEVICE_EXTENSION *DeviceExtension
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine creates an FT device object using the name provided by
|
|||
|
the caller and attaches it to the name of an existing device provide
|
|||
|
by the caller. Some initialization for the FT device extension
|
|||
|
occurs here.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DriverObject - for setup.
|
|||
|
AttachName - the name of the target device object for the attach.
|
|||
|
DeviceName - the name for the FT device object to create.
|
|||
|
DeviceExtension - a pointer to a location to store the device extension.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension;
|
|||
|
ANSI_STRING ansiDeviceName;
|
|||
|
UNICODE_STRING unicodeDeviceName;
|
|||
|
PDEVICE_OBJECT newObject;
|
|||
|
NTSTATUS status;
|
|||
|
STRING deviceNameString;
|
|||
|
OBJECT_ATTRIBUTES objectAttributes;
|
|||
|
PFILE_OBJECT fileObject;
|
|||
|
|
|||
|
RtlInitString(&deviceNameString,
|
|||
|
AttachName);
|
|||
|
status = RtlAnsiStringToUnicodeString(&unicodeDeviceName,
|
|||
|
&deviceNameString,
|
|||
|
TRUE);
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
InitializeObjectAttributes(&objectAttributes,
|
|||
|
&unicodeDeviceName,
|
|||
|
OBJ_CASE_INSENSITIVE,
|
|||
|
NULL,
|
|||
|
NULL);
|
|||
|
|
|||
|
//
|
|||
|
// Check if this object exists.
|
|||
|
//
|
|||
|
|
|||
|
status = IoGetDeviceObjectPointer(&unicodeDeviceName,
|
|||
|
FILE_READ_ATTRIBUTES,
|
|||
|
&fileObject,
|
|||
|
&newObject);
|
|||
|
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
|
|||
|
//
|
|||
|
// FTDISK has already attached to this disk or partition.
|
|||
|
//
|
|||
|
|
|||
|
*DeviceExtension = (PDEVICE_EXTENSION)newObject->DeviceExtension;
|
|||
|
ObDereferenceObject(fileObject);
|
|||
|
status = STATUS_OBJECT_NAME_EXISTS;
|
|||
|
RtlFreeUnicodeString(&unicodeDeviceName);
|
|||
|
|
|||
|
} else if (status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
|||
|
|
|||
|
//
|
|||
|
// This must be a new disk or partition. Create attach name.
|
|||
|
//
|
|||
|
|
|||
|
status = IoCreateDevice(DriverObject,
|
|||
|
sizeof(DEVICE_EXTENSION),
|
|||
|
&unicodeDeviceName,
|
|||
|
FILE_DEVICE_DISK,
|
|||
|
0,
|
|||
|
FALSE,
|
|||
|
&newObject);
|
|||
|
RtlFreeUnicodeString(&unicodeDeviceName);
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
DebugPrint((1,
|
|||
|
"FtpAttach: IoCreateDevice failed (%x)\n",
|
|||
|
status));
|
|||
|
|
|||
|
return(status);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Point device extension back at device object.
|
|||
|
//
|
|||
|
|
|||
|
deviceExtension = newObject->DeviceExtension;
|
|||
|
deviceExtension->DeviceObject = newObject;
|
|||
|
|
|||
|
//
|
|||
|
// Indicate new device needs IRPs with MDLs.
|
|||
|
//
|
|||
|
|
|||
|
newObject->Flags |= DO_DIRECT_IO;
|
|||
|
|
|||
|
//
|
|||
|
// Construct unicode name for attach object.
|
|||
|
//
|
|||
|
|
|||
|
RtlInitAnsiString(&ansiDeviceName,
|
|||
|
DeviceName);
|
|||
|
status = RtlAnsiStringToUnicodeString(&unicodeDeviceName,
|
|||
|
&ansiDeviceName,
|
|||
|
TRUE);
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
IoDeleteDevice(newObject);
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Attach to the partition. This call links the newly created
|
|||
|
// device to the target device, returning the target device object.
|
|||
|
//
|
|||
|
|
|||
|
status = IoAttachDevice(newObject,
|
|||
|
&unicodeDeviceName,
|
|||
|
&deviceExtension->TargetObject);
|
|||
|
RtlFreeUnicodeString(&unicodeDeviceName);
|
|||
|
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
|
|||
|
//
|
|||
|
// Device for attach was not present in the system.
|
|||
|
// Free the device object created for the attach.
|
|||
|
//
|
|||
|
|
|||
|
DebugPrint((1,
|
|||
|
"FtpAttach: IoAttachDevice failed %s (%x)\n",
|
|||
|
DeviceName,
|
|||
|
status));
|
|||
|
|
|||
|
IoDeleteDevice(newObject);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Set alignment from target device object.
|
|||
|
//
|
|||
|
|
|||
|
newObject->AlignmentRequirement =
|
|||
|
deviceExtension->TargetObject->AlignmentRequirement;
|
|||
|
|
|||
|
*DeviceExtension = deviceExtension;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return(status);
|
|||
|
|
|||
|
} // end FtpAttach()
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
FtpVolumeLength(
|
|||
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|||
|
IN PLARGE_INTEGER ResultLength
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Given the beginning of a device extension, add up the total of all
|
|||
|
the components to determine the volume size. This includes special
|
|||
|
calculations for all FT volumes.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceExtension - Base of the FT volume.
|
|||
|
ResultLength - Pointer to the result value location.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
LARGE_INTEGER total;
|
|||
|
PDEVICE_EXTENSION currentExtension;
|
|||
|
|
|||
|
switch (DeviceExtension->Type) {
|
|||
|
|
|||
|
case Stripe:
|
|||
|
case StripeWithParity:
|
|||
|
|
|||
|
total = DeviceExtension->FtUnion.Identity.PartitionLength;
|
|||
|
|
|||
|
currentExtension = DeviceExtension->NextMember;
|
|||
|
|
|||
|
while (currentExtension != NULL) {
|
|||
|
|
|||
|
//
|
|||
|
// Find smallest member.
|
|||
|
//
|
|||
|
|
|||
|
if (total.QuadPart >
|
|||
|
currentExtension->FtUnion.Identity.PartitionLength.QuadPart) {
|
|||
|
|
|||
|
total = currentExtension->FtUnion.Identity.PartitionLength;
|
|||
|
}
|
|||
|
currentExtension = currentExtension->NextMember;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Stripes size must be multiple of stripe size.
|
|||
|
//
|
|||
|
|
|||
|
total.LowPart &= ~(STRIPE_SIZE -1);
|
|||
|
|
|||
|
//
|
|||
|
// Multiply size of smallest member by number of data members.
|
|||
|
//
|
|||
|
|
|||
|
total.QuadPart *= (DeviceExtension->Type == Stripe ?
|
|||
|
DeviceExtension->FtCount.NumberOfMembers :
|
|||
|
DeviceExtension->FtCount.NumberOfMembers - 1);
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case VolumeSet:
|
|||
|
|
|||
|
//
|
|||
|
// Add up total size of all members.
|
|||
|
//
|
|||
|
|
|||
|
total.QuadPart = 0;
|
|||
|
|
|||
|
currentExtension = DeviceExtension;
|
|||
|
|
|||
|
while (currentExtension != NULL) {
|
|||
|
|
|||
|
total.QuadPart += currentExtension->FtUnion.Identity.PartitionLength.QuadPart;
|
|||
|
currentExtension = currentExtension->NextMember;
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
} // end switch
|
|||
|
|
|||
|
*ResultLength = total;
|
|||
|
|
|||
|
} // end FtpVolumeLength()
|
|||
|
|
|||
|
|
|||
|
PIRP
|
|||
|
FtpDuplicatePartialIrp(
|
|||
|
IN PDEVICE_OBJECT FtObject,
|
|||
|
IN PIRP InIrp,
|
|||
|
IN PVOID VirtualAddress,
|
|||
|
IN LARGE_INTEGER ByteOffset,
|
|||
|
IN ULONG Length
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine creates a new Irp based on the InIrp provided. It creates
|
|||
|
enough stack space in the new Irp to provide the FT driver with its own
|
|||
|
stack location. It also allocates and adjusts the MDL in the new Irp
|
|||
|
to frame a subset of memory within the original MDL provided in the
|
|||
|
input Irp (InIrp).
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
FtObject - pointer to the FT created device object.
|
|||
|
InIrp - pointer to the Irp to duplicate.
|
|||
|
VirtualAddress - Location for the base of the memory descriptor list.
|
|||
|
ByteOffset - offset for the I/O on the device located by FtObject.
|
|||
|
Length - length of both the I/O and the memory descriptor list.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
New IRP pointer.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PIRP newIrp;
|
|||
|
PIO_STACK_LOCATION newIrpStack;
|
|||
|
PDEVICE_EXTENSION deviceExtension = FtObject->DeviceExtension;
|
|||
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(InIrp);
|
|||
|
|
|||
|
//
|
|||
|
// Allocate and set up a new Irp.
|
|||
|
//
|
|||
|
|
|||
|
newIrp = IoAllocateIrp(FtObject->StackSize, FALSE);
|
|||
|
|
|||
|
if (newIrp) {
|
|||
|
|
|||
|
newIrp->Tail.Overlay.Thread = InIrp->Tail.Overlay.Thread;
|
|||
|
|
|||
|
//
|
|||
|
// Reserve a stack location for FT use.
|
|||
|
//
|
|||
|
|
|||
|
IoSetNextIrpStackLocation(newIrp);
|
|||
|
|
|||
|
newIrp->MdlAddress = IoAllocateMdl(VirtualAddress,
|
|||
|
Length,
|
|||
|
FALSE,
|
|||
|
FALSE,
|
|||
|
(PIRP)NULL);
|
|||
|
IoBuildPartialMdl(InIrp->MdlAddress,
|
|||
|
newIrp->MdlAddress,
|
|||
|
VirtualAddress,
|
|||
|
Length);
|
|||
|
|
|||
|
//
|
|||
|
// Save a back pointer to the original Irp in the FT stack.
|
|||
|
//
|
|||
|
|
|||
|
newIrpStack = IoGetCurrentIrpStackLocation(newIrp);
|
|||
|
newIrpStack->FtLowIrpMasterIrp = InIrp;
|
|||
|
newIrpStack->FtLowIrpAllocatedMdl = (PVOID) 1;
|
|||
|
|
|||
|
//
|
|||
|
// Construct a stack for the next driver.
|
|||
|
//
|
|||
|
|
|||
|
newIrpStack = IoGetNextIrpStackLocation(newIrp);
|
|||
|
newIrpStack->Parameters.Write.ByteOffset = ByteOffset;
|
|||
|
newIrpStack->Parameters.Write.Length = Length;
|
|||
|
newIrpStack->MajorFunction = irpStack->MajorFunction;
|
|||
|
newIrpStack->DeviceObject = deviceExtension->TargetObject;
|
|||
|
newIrpStack->Flags = irpStack->Flags;
|
|||
|
}
|
|||
|
return newIrp;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
PDEVICE_OBJECT
|
|||
|
FtpGetTargetObject(
|
|||
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|||
|
IN ULONG MemberRole
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Get device object for target disk partition.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceExtension
|
|||
|
MemberRole - ordinal of member in volume.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
target device object
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION nextDeviceExtension = DeviceExtension;
|
|||
|
|
|||
|
while (nextDeviceExtension != NULL) {
|
|||
|
if (nextDeviceExtension->MemberRole == (USHORT) MemberRole) {
|
|||
|
return(nextDeviceExtension->TargetObject);
|
|||
|
}
|
|||
|
|
|||
|
nextDeviceExtension = nextDeviceExtension->NextMember;
|
|||
|
}
|
|||
|
|
|||
|
return(NULL);
|
|||
|
|
|||
|
} // end FtpGetTargetObject()
|
|||
|
|
|||
|
|
|||
|
PRCB
|
|||
|
FtpAllocateRcb(
|
|||
|
PDEVICE_EXTENSION DeviceExtension
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine attempts to allocate a request control block from the Rcb
|
|||
|
lookaside list.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceExtension - zero member device extension.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The address of the RCB or NULL is returned as the function value.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION ftRootExtension =
|
|||
|
DeviceExtension->ObjectUnion.FtRootObject->DeviceExtension;
|
|||
|
PRCB rcb;
|
|||
|
|
|||
|
//
|
|||
|
// Allocate request control packet from lookaside list.
|
|||
|
//
|
|||
|
|
|||
|
rcb = ExAllocateFromNPagedLookasideList(&ftRootExtension->RcbLookasideListHead);
|
|||
|
if (rcb != NULL) {
|
|||
|
|
|||
|
//
|
|||
|
// Set up device extension pointer.
|
|||
|
//
|
|||
|
|
|||
|
rcb->ZeroExtension = DeviceExtension;
|
|||
|
|
|||
|
//
|
|||
|
// Set active bit in flags to indicate that RCB is in use.
|
|||
|
//
|
|||
|
|
|||
|
rcb->Flags = RCB_FLAGS_ACTIVE;
|
|||
|
|
|||
|
//
|
|||
|
// Set type and size fields.
|
|||
|
//
|
|||
|
|
|||
|
rcb->Type = RCB_TYPE;
|
|||
|
rcb->Size = sizeof(RCB);
|
|||
|
|
|||
|
//
|
|||
|
// Set links to zero.
|
|||
|
//
|
|||
|
|
|||
|
rcb->Left = NULL;
|
|||
|
rcb->Right = NULL;
|
|||
|
rcb->Middle = NULL;
|
|||
|
rcb->Link = NULL;
|
|||
|
}
|
|||
|
|
|||
|
return rcb;
|
|||
|
|
|||
|
} // end FtpAllocateRcb()
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
FtpFreeRcb(
|
|||
|
PRCB Rcb
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine frees an RCB to the RCB lookaside list.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Rcb
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = Rcb->ZeroExtension;
|
|||
|
PDEVICE_EXTENSION ftRootExtension =
|
|||
|
deviceExtension->ObjectUnion.FtRootObject->DeviceExtension;
|
|||
|
|
|||
|
//
|
|||
|
// Clear active bit in RCB flags for sanity checking.
|
|||
|
//
|
|||
|
|
|||
|
Rcb->Flags &= ~RCB_FLAGS_ACTIVE;
|
|||
|
|
|||
|
//
|
|||
|
// Free request control block to lookaside list.
|
|||
|
//
|
|||
|
|
|||
|
ExFreeToNPagedLookasideList(&ftRootExtension->RcbLookasideListHead,
|
|||
|
Rcb);
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
} // end FtpFreeRcb()
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
FtpLogError(
|
|||
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|||
|
IN NTSTATUS SpecificIoStatus,
|
|||
|
IN NTSTATUS FinalStatus,
|
|||
|
IN ULONG UniqueErrorValue,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine performs error logging for the FT driver.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceExtension - Extension representing failing device.
|
|||
|
SpecificIoStatus - IO error status value.
|
|||
|
FinalStatus - Status returned for failure.
|
|||
|
UniqueErrorValue - Values defined to uniquely identify error location.
|
|||
|
Irp - If there is an irp this is the pointer to it.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PIO_ERROR_LOG_PACKET errorLogPacket;
|
|||
|
PIO_STACK_LOCATION irpStack;
|
|||
|
|
|||
|
DebugPrint((2, "FtpLogError: DE %x:%x, unique %x, status %x, Irp %x\n",
|
|||
|
DeviceExtension,
|
|||
|
DeviceExtension->DeviceObject,
|
|||
|
UniqueErrorValue,
|
|||
|
SpecificIoStatus,
|
|||
|
Irp));
|
|||
|
errorLogPacket = IoAllocateErrorLogEntry(DeviceExtension->DeviceObject,
|
|||
|
(UCHAR)((sizeof(IO_ERROR_LOG_PACKET)) +
|
|||
|
((Irp == NULL) ? 0 : 3 * sizeof(ULONG))));
|
|||
|
if (errorLogPacket != NULL) {
|
|||
|
|
|||
|
errorLogPacket->ErrorCode = SpecificIoStatus;
|
|||
|
errorLogPacket->SequenceNumber = FtErrorLogSequence++;
|
|||
|
errorLogPacket->FinalStatus = FinalStatus;
|
|||
|
errorLogPacket->UniqueErrorValue = UniqueErrorValue;
|
|||
|
errorLogPacket->DumpDataSize = 0;
|
|||
|
errorLogPacket->NumberOfStrings = 0;
|
|||
|
errorLogPacket->RetryCount = 0;
|
|||
|
errorLogPacket->StringOffset = 0;
|
|||
|
|
|||
|
if (Irp != NULL) {
|
|||
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
|
|||
|
errorLogPacket->MajorFunctionCode = irpStack->MajorFunction;
|
|||
|
errorLogPacket->FinalStatus = Irp->IoStatus.Status;
|
|||
|
errorLogPacket->DeviceOffset = irpStack->Parameters.Read.ByteOffset;
|
|||
|
errorLogPacket->DumpDataSize = 3;
|
|||
|
errorLogPacket->DumpData[0] =
|
|||
|
irpStack->Parameters.Read.ByteOffset.LowPart;
|
|||
|
errorLogPacket->DumpData[1] =
|
|||
|
irpStack->Parameters.Read.ByteOffset.HighPart;
|
|||
|
errorLogPacket->DumpData[2] = irpStack->Parameters.Read.Length;
|
|||
|
}
|
|||
|
|
|||
|
IoWriteErrorLogEntry(errorLogPacket);
|
|||
|
} else {
|
|||
|
DebugPrint((1, "FtpLogError: unable to allocate error log packet\n"));
|
|||
|
}
|
|||
|
} // end FtpLogError()
|
|||
|
|
|||
|
VOID
|
|||
|
FtpFreeMdl(
|
|||
|
IN PMDL Mdl
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine frees a Memory Descriptor List (MDL).
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Mdl - Pointer to the Memory Descriptor List to be freed.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
//
|
|||
|
// Check if MDL pages must be freed.
|
|||
|
//
|
|||
|
|
|||
|
if ((Mdl->MdlFlags & MDL_PAGES_LOCKED) &&
|
|||
|
!(Mdl->MdlFlags & MDL_PARTIAL_HAS_BEEN_MAPPED)) {
|
|||
|
MmUnlockPages(Mdl);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Let IO subsystem free MDL to zone or pool.
|
|||
|
//
|
|||
|
|
|||
|
#if MARK_MDL_ALLOCATIONS
|
|||
|
if (FtWatchMdlFree) {
|
|||
|
ASSERT(Mdl->MdlFlags & 0x8000);
|
|||
|
}
|
|||
|
|
|||
|
// watch(0);
|
|||
|
Mdl->MdlFlags &= (~0x8000);
|
|||
|
#endif
|
|||
|
IoFreeMdl(Mdl);
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
} // end FtpFreeMdl()
|
|||
|
|
|||
|
|
|||
|
PDEVICE_EXTENSION
|
|||
|
FtpGetMemberDeviceExtension(
|
|||
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|||
|
IN USHORT MemberNumber
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Walk device extension change to find member device extension.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceExtension - the device extension for the set (zeroth member).
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Device extension for member indicated.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = DeviceExtension;
|
|||
|
|
|||
|
while (deviceExtension) {
|
|||
|
if (deviceExtension->MemberRole == MemberNumber) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
deviceExtension = deviceExtension->NextMember;
|
|||
|
}
|
|||
|
|
|||
|
return(deviceExtension);
|
|||
|
|
|||
|
} // end FtpGetMemberDeviceExtension()
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
FtpSpecialReadCompletion(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp,
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine moves the status of the complete irp to the one to be
|
|||
|
completed, frees the input irp and performs an io complete on the
|
|||
|
context irp.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - FT device object.
|
|||
|
Irp - the completed IRP.
|
|||
|
Context - the Irp to complete.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - This is set to STATUS_MORE_PROCESSING_REQUIRED to stop
|
|||
|
completion routine processing.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PIRP originalIrp = (PIRP) Context;
|
|||
|
|
|||
|
originalIrp->IoStatus = Irp->IoStatus;
|
|||
|
|
|||
|
MmUnlockPages(Irp->MdlAddress);
|
|||
|
IoFreeMdl(Irp->MdlAddress);
|
|||
|
Irp->MdlAddress = NULL;
|
|||
|
IoFreeIrp(Irp);
|
|||
|
FtpCompleteRequest(originalIrp, IO_DISK_INCREMENT);
|
|||
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
FtpSpecialRead(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PVOID Buffer,
|
|||
|
IN PLARGE_INTEGER Offset,
|
|||
|
IN ULONG Size,
|
|||
|
IN PIRP IrpToComplete
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine attempts to read or write sectors synchronously.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - target device object.
|
|||
|
Buffer - pointer to the data buffer.
|
|||
|
Size - size of the I/O in bytes.
|
|||
|
Offset - location of the I/O.
|
|||
|
IrpToComplete - The irp to complete when done.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PIRP irp;
|
|||
|
PIO_STACK_LOCATION irpStack;
|
|||
|
|
|||
|
//
|
|||
|
// Initialize bytes transferred to 0.
|
|||
|
//
|
|||
|
|
|||
|
irp = IoBuildAsynchronousFsdRequest(IRP_MJ_READ,
|
|||
|
DeviceObject,
|
|||
|
Buffer,
|
|||
|
Size,
|
|||
|
Offset,
|
|||
|
NULL);
|
|||
|
|
|||
|
if (irp == NULL) {
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
irpStack = IoGetNextIrpStackLocation(irp);
|
|||
|
irpStack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
|
|||
|
|
|||
|
IoSetCompletionRoutine(irp,
|
|||
|
(PIO_COMPLETION_ROUTINE)FtpSpecialReadCompletion,
|
|||
|
(PVOID) IrpToComplete,
|
|||
|
TRUE,
|
|||
|
TRUE,
|
|||
|
TRUE);
|
|||
|
IoCallDriver(DeviceObject, irp);
|
|||
|
return STATUS_PENDING;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
FtpMarkMirrorPartitionType(
|
|||
|
IN PDEVICE_EXTENSION DeviceExtension
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine will change the partition type for the remaining member
|
|||
|
of a mirror to indicate that it is the valid member.
|
|||
|
NOTE: Must be called in a thread context since it allocates memory.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceExtension - represents remaining member.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PPARTITION_INFORMATION outputBuffer;
|
|||
|
NTSTATUS status;
|
|||
|
PIRP irp;
|
|||
|
IO_STATUS_BLOCK ioStatusBlock;
|
|||
|
KEVENT event;
|
|||
|
ULONG size;
|
|||
|
|
|||
|
//
|
|||
|
// Allocate buffer by spawning a thread.
|
|||
|
//
|
|||
|
|
|||
|
size = sizeof(PARTITION_INFORMATION);
|
|||
|
outputBuffer = (PPARTITION_INFORMATION) FtThreadAllocateBuffer(&size,
|
|||
|
TRUE);
|
|||
|
//
|
|||
|
// Set the event object to the unsignaled state.
|
|||
|
// It will be used to signal request completion.
|
|||
|
//
|
|||
|
|
|||
|
KeInitializeEvent(&event,
|
|||
|
NotificationEvent,
|
|||
|
FALSE);
|
|||
|
//
|
|||
|
// Get the current partition type.
|
|||
|
//
|
|||
|
|
|||
|
irp = NULL;
|
|||
|
while (irp == NULL) {
|
|||
|
irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO,
|
|||
|
DeviceExtension->TargetObject,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
outputBuffer,
|
|||
|
sizeof(PARTITION_INFORMATION),
|
|||
|
FALSE,
|
|||
|
&event,
|
|||
|
&ioStatusBlock);
|
|||
|
if (irp == NULL) {
|
|||
|
LARGE_INTEGER delayTime;
|
|||
|
delayTime.QuadPart = -(IRP_DELAY);
|
|||
|
KeDelayExecutionThread(KernelMode,
|
|||
|
FALSE,
|
|||
|
&delayTime);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Could get error - call driver check status
|
|||
|
//
|
|||
|
|
|||
|
status = IoCallDriver(DeviceExtension->TargetObject, irp);
|
|||
|
if (status == STATUS_PENDING) {
|
|||
|
KeWaitForSingleObject(&event,
|
|||
|
Suspended,
|
|||
|
KernelMode,
|
|||
|
FALSE,
|
|||
|
NULL);
|
|||
|
} else {
|
|||
|
ioStatusBlock.Status = status;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// irp was freed by the I/O subsystem.
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(ioStatusBlock.Status)) {
|
|||
|
|
|||
|
SET_PARTITION_INFORMATION setPartition;
|
|||
|
|
|||
|
//
|
|||
|
// Set high two bits of partition type to indicate remaining
|
|||
|
// mirror member.
|
|||
|
//
|
|||
|
|
|||
|
setPartition.PartitionType = outputBuffer->PartitionType | VALID_NTFT;
|
|||
|
|
|||
|
KeInitializeEvent(&event,
|
|||
|
NotificationEvent,
|
|||
|
FALSE);
|
|||
|
//
|
|||
|
// Set the new partition type.
|
|||
|
//
|
|||
|
|
|||
|
irp = NULL;
|
|||
|
while (irp == NULL) {
|
|||
|
irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_SET_PARTITION_INFO,
|
|||
|
DeviceExtension->TargetObject,
|
|||
|
&setPartition,
|
|||
|
sizeof(SET_PARTITION_INFORMATION),
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
FALSE,
|
|||
|
&event,
|
|||
|
&ioStatusBlock);
|
|||
|
if (irp == NULL) {
|
|||
|
LARGE_INTEGER delayTime;
|
|||
|
delayTime.QuadPart = -(IRP_DELAY);
|
|||
|
KeDelayExecutionThread(KernelMode,
|
|||
|
FALSE,
|
|||
|
&delayTime);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
status = IoCallDriver(DeviceExtension->TargetObject, irp);
|
|||
|
if (status == STATUS_PENDING) {
|
|||
|
(VOID) KeWaitForSingleObject(&event,
|
|||
|
Suspended,
|
|||
|
KernelMode,
|
|||
|
FALSE,
|
|||
|
NULL);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Update FTDISK structures.
|
|||
|
//
|
|||
|
|
|||
|
DeviceExtension->FtUnion.Identity.PartitionType = outputBuffer->PartitionType;
|
|||
|
}
|
|||
|
ExFreePool(outputBuffer);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
FtpOrphanMemberInRegistry(
|
|||
|
IN PDEVICE_EXTENSION DeviceExtension
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine orphans a member of a mirror or SWP set. It
|
|||
|
first checks for another orphan. If one exists, the set is
|
|||
|
disabled. Otherwise the failing member is orphaned. Note
|
|||
|
that sets are only orphaned as a result of failing write
|
|||
|
operations.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceExtension - The device extension to orphan.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
KIRQL irql;
|
|||
|
PDEVICE_EXTENSION zeroExtension;
|
|||
|
PDEVICE_EXTENSION currentExtension;
|
|||
|
|
|||
|
KeAcquireSpinLock(&DeviceExtension->WorkItemSpinLock, &irql);
|
|||
|
DeviceExtension->WorkItemBusy = FALSE;
|
|||
|
KeReleaseSpinLock(&DeviceExtension->WorkItemSpinLock, irql);
|
|||
|
|
|||
|
//
|
|||
|
// Get device extension for zero member.
|
|||
|
//
|
|||
|
|
|||
|
zeroExtension = DeviceExtension->ZeroMember;
|
|||
|
|
|||
|
currentExtension = zeroExtension;
|
|||
|
|
|||
|
//
|
|||
|
// Check if set already has an orphan.
|
|||
|
//
|
|||
|
|
|||
|
while (currentExtension) {
|
|||
|
|
|||
|
if (currentExtension != DeviceExtension) {
|
|||
|
if (IsMemberAnOrphan(currentExtension)) {
|
|||
|
|
|||
|
//
|
|||
|
// This set already has an orphaned member.
|
|||
|
// A second orphan means the set can be accessed.
|
|||
|
// Mark the set as disabled and log the event.
|
|||
|
//
|
|||
|
|
|||
|
DebugPrint((1,
|
|||
|
"FtpOrphanMember: Second orphan disabled set\n"));
|
|||
|
|
|||
|
MarkSetAsDisabled(zeroExtension);
|
|||
|
FtpLogError(DeviceExtension,
|
|||
|
FT_SET_DISABLED,
|
|||
|
STATUS_SUCCESS,
|
|||
|
(ULONG) IO_ERR_DRIVER_ERROR,
|
|||
|
NULL);
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
currentExtension = currentExtension->NextMember;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Mark zero extension and offending extension with FT state.
|
|||
|
// This field is also used to indicate that the registry has been updated.
|
|||
|
//
|
|||
|
|
|||
|
zeroExtension->VolumeState = FtHasOrphan;
|
|||
|
DeviceExtension->VolumeState = FtHasOrphan;
|
|||
|
|
|||
|
//
|
|||
|
// Set member state in registry to 'orphaned'.
|
|||
|
//
|
|||
|
|
|||
|
FtpChangeMemberStateInRegistry(DeviceExtension, Orphaned);
|
|||
|
|
|||
|
FtpLogError(DeviceExtension,
|
|||
|
FT_ORPHANING,
|
|||
|
STATUS_SUCCESS,
|
|||
|
0,
|
|||
|
NULL);
|
|||
|
|
|||
|
if (DeviceExtension->Type == Mirror) {
|
|||
|
|
|||
|
//
|
|||
|
// Special case mirrors for the remaining member's
|
|||
|
// partition type.
|
|||
|
//
|
|||
|
|
|||
|
if (DeviceExtension->NextMember) {
|
|||
|
|
|||
|
FtpMarkMirrorPartitionType(DeviceExtension->NextMember);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
FtpMarkMirrorPartitionType(DeviceExtension->ZeroMember);
|
|||
|
}
|
|||
|
}
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
FtpOrphanMember(
|
|||
|
IN PDEVICE_EXTENSION DeviceExtension
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called anytime a mirror or stripe with parity
|
|||
|
determines a member of the FT volume is not longer working and
|
|||
|
wants to orphan the member. This routine will queue a call
|
|||
|
to orphan the member to the system worker thread.
|
|||
|
|
|||
|
The call must be queued since it will do I/O to the registry. This
|
|||
|
I/O could end up recursing into the FT member that is being orphaned
|
|||
|
and lead to deadlock. By passing it to a system worker thread it
|
|||
|
will not deadlock the normal sector recovery logic of the mirror
|
|||
|
or stripe with parity.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceExtension - Device extension of member to be orphaned.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Nothing.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
KIRQL irql;
|
|||
|
|
|||
|
//
|
|||
|
// Set device extension FT state for this member to 'orphaned'.
|
|||
|
//
|
|||
|
|
|||
|
MarkMemberAsOrphan(DeviceExtension);
|
|||
|
|
|||
|
KeAcquireSpinLock(&DeviceExtension->WorkItemSpinLock, &irql);
|
|||
|
if (DeviceExtension->WorkItemBusy == TRUE) {
|
|||
|
|
|||
|
//
|
|||
|
// Queue for orphaning has already happened.
|
|||
|
//
|
|||
|
KeReleaseSpinLock(&DeviceExtension->WorkItemSpinLock, irql);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
DebugPrint((1, "FtpQueueOrphanMemberInRegistry: Queueing call.\n"));
|
|||
|
DeviceExtension->WorkItemBusy = TRUE;
|
|||
|
KeReleaseSpinLock(&DeviceExtension->WorkItemSpinLock, irql);
|
|||
|
|
|||
|
ExInitializeWorkItem(&DeviceExtension->WorkItem,
|
|||
|
(PWORKER_THREAD_ROUTINE)FtpOrphanMemberInRegistry,
|
|||
|
(PVOID)DeviceExtension);
|
|||
|
ExQueueWorkItem(&DeviceExtension->WorkItem, CriticalWorkQueue);
|
|||
|
|
|||
|
//
|
|||
|
// Let user know a disk is gone.
|
|||
|
//
|
|||
|
|
|||
|
IoRaiseInformationalHardError(STATUS_FT_ORPHANING,
|
|||
|
NULL,
|
|||
|
NULL);
|
|||
|
|
|||
|
} // end FtpOrphanMember()
|