3884 lines
104 KiB
C++
3884 lines
104 KiB
C++
/*++
|
||
|
||
Copyright (c) 1991-5 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
ftdisk.c
|
||
|
||
Abstract:
|
||
|
||
This driver provides fault tolerance through disk mirroring and striping.
|
||
This module contains routines that support calls from the NT I/O system.
|
||
|
||
Author:
|
||
|
||
Bob Rinne (bobri) 2-Feb-1992
|
||
Mike Glass (mglass)
|
||
Norbert Kusters 2-Feb-1995
|
||
|
||
Environment:
|
||
|
||
kernel mode only
|
||
|
||
Notes:
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "ftdisk.h"
|
||
#include <stdarg.h>
|
||
|
||
//
|
||
// Global Sequence number for error log.
|
||
//
|
||
|
||
ULONG FtErrorLogSequence = 0;
|
||
|
||
//
|
||
// Function declarations called by the I/O system.
|
||
//
|
||
|
||
NTSTATUS
|
||
FtDiskCreate(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
FtDiskReadWrite(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
FtDiskDeviceControl(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
FtDiskShutdownFlush(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
extern "C" {
|
||
|
||
NTSTATUS
|
||
FtDiskInitialize(
|
||
IN PDRIVER_OBJECT DriverObject
|
||
);
|
||
|
||
VOID
|
||
FtpConfigure(
|
||
IN PDEVICE_EXTENSION FtRootExtension
|
||
);
|
||
|
||
NTSTATUS
|
||
DriverEntry(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PUNICODE_STRING RegistryPath
|
||
);
|
||
|
||
}
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(INIT, DriverEntry)
|
||
#pragma alloc_text(INIT, FtDiskInitialize)
|
||
#pragma alloc_text(INIT, FtpConfigure)
|
||
#endif
|
||
|
||
|
||
#define FtpFindPartitionRegistry(REGSTART, MEMBER) \
|
||
(PDISK_PARTITION) ((PUCHAR)REGSTART + MEMBER->OffsetToPartitionInfo)
|
||
|
||
|
||
BOOLEAN
|
||
FtpIsWorseStatus(
|
||
IN NTSTATUS Status1,
|
||
IN NTSTATUS Status2
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine compares two NTSTATUS codes and decides if Status1 is
|
||
worse than Status2.
|
||
|
||
Arguments:
|
||
|
||
Status1 - Supplies the first status.
|
||
|
||
Status2 - Supplies the second status.
|
||
|
||
Return Value:
|
||
|
||
FALSE - Status1 is not worse than Status2.
|
||
|
||
TRUE - Status1 is worse than Status2.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
if (NT_ERROR(Status2) && FsRtlIsTotalDeviceFailure(Status2)) {
|
||
return FALSE;
|
||
}
|
||
|
||
if (NT_ERROR(Status1) && FsRtlIsTotalDeviceFailure(Status1)) {
|
||
return TRUE;
|
||
}
|
||
|
||
if (NT_ERROR(Status2)) {
|
||
return FALSE;
|
||
}
|
||
|
||
if (NT_ERROR(Status1)) {
|
||
return TRUE;
|
||
}
|
||
|
||
if (NT_WARNING(Status2)) {
|
||
return FALSE;
|
||
}
|
||
|
||
if (NT_WARNING(Status1)) {
|
||
return TRUE;
|
||
}
|
||
|
||
if (NT_INFORMATION(Status2)) {
|
||
return FALSE;
|
||
}
|
||
|
||
if (NT_INFORMATION(Status1)) {
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
#if DBG
|
||
|
||
ULONG FtDebug;
|
||
|
||
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()
|
||
|
||
#endif
|
||
|
||
NTSTATUS
|
||
FtpGetPartitionInformation(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN OUT PDRIVE_LAYOUT_INFORMATION *DriveLayout,
|
||
OUT PDISK_GEOMETRY DiskGeometry
|
||
)
|
||
|
||
/*++
|
||
|
||
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.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
IO_STATUS_BLOCK ioStatusBlock;
|
||
PIRP irp;
|
||
KEVENT event;
|
||
|
||
DebugPrint((4, "FtpGetPartitionInformation: Entered \n"));
|
||
|
||
//
|
||
// Allocate buffer for drive geometry.
|
||
//
|
||
|
||
//
|
||
// 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) {
|
||
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 status;
|
||
|
||
} // FtpGetPartitionInformation
|
||
|
||
VOID
|
||
FtpAttach(
|
||
IN ULONG DiskNumber,
|
||
IN ULONG PartitionNumber,
|
||
IN PPARTITION_INFORMATION PartitionInformation,
|
||
IN PDEVICE_EXTENSION WholeDisk
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine creates a new device object for this driver and
|
||
attaches it to the current device object for the given disk and
|
||
partition.
|
||
|
||
Arguments:
|
||
|
||
DiskNumber - Supplies the disk number.
|
||
|
||
PartitionNumber - Supplies the partition number.
|
||
|
||
PartitionInformation - Supplies the partition information.
|
||
|
||
WholeDisk - Supplies the device extension for the whole disk.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_OBJECT deviceObject, targetObject;
|
||
PDEVICE_EXTENSION extension;
|
||
WCHAR deviceNameBuf[64];
|
||
WCHAR targetNameBuf[64];
|
||
UNICODE_STRING deviceName, targetName;
|
||
PFILE_OBJECT fileObject;
|
||
NTSTATUS status;
|
||
PPARTITION partitionVolume;
|
||
KIRQL irql, irql2;
|
||
|
||
//
|
||
// Setup the partition name string and perform the attach.
|
||
//
|
||
|
||
swprintf(targetNameBuf,
|
||
L"\\Device\\Harddisk%d\\Partition%d",
|
||
DiskNumber,
|
||
PartitionNumber);
|
||
|
||
RtlInitUnicodeString(&targetName, targetNameBuf);
|
||
|
||
//
|
||
// Get target device object.
|
||
//
|
||
|
||
status = IoGetDeviceObjectPointer(&targetName,
|
||
FILE_READ_ATTRIBUTES,
|
||
&fileObject,
|
||
&targetObject);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
DebugPrint((1,
|
||
"FtpPrepareDisk: Can't get target object\n"));
|
||
|
||
return;
|
||
}
|
||
|
||
ObDereferenceObject(fileObject);
|
||
|
||
//
|
||
// Check if this device is already mounted.
|
||
//
|
||
|
||
if (!targetObject->Vpb ||
|
||
(targetObject->Vpb->Flags & VPB_MOUNTED)) {
|
||
|
||
//
|
||
// Can't attach to a device that is already mounted.
|
||
//
|
||
|
||
DebugPrint((1,
|
||
"FtpPrepareDisk: already mounted\n"));
|
||
|
||
return;
|
||
}
|
||
|
||
partitionVolume = new PARTITION;
|
||
if (!partitionVolume) {
|
||
return;
|
||
}
|
||
|
||
status = partitionVolume->Initialize(targetObject,
|
||
WholeDisk->u.WholeDisk.DiskGeometry.BytesPerSector,
|
||
WholeDisk->u.WholeDisk.Signature,
|
||
PartitionInformation->StartingOffset.QuadPart,
|
||
PartitionInformation->PartitionLength.QuadPart,
|
||
TRUE, DiskNumber, PartitionNumber);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
delete partitionVolume;
|
||
return;
|
||
}
|
||
|
||
swprintf(deviceNameBuf,
|
||
L"\\Device\\Harddisk%d\\Ft%d",
|
||
DiskNumber,
|
||
PartitionNumber);
|
||
|
||
RtlInitUnicodeString(&deviceName, deviceNameBuf);
|
||
|
||
status = IoGetDeviceObjectPointer(&deviceName,
|
||
FILE_READ_ATTRIBUTES,
|
||
&fileObject,
|
||
&deviceObject);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
// Already done this one.
|
||
|
||
ObDereferenceObject(fileObject);
|
||
delete partitionVolume;
|
||
return;
|
||
}
|
||
|
||
status = IoCreateDevice(WholeDisk->Root->u.Root.DriverObject,
|
||
sizeof(DEVICE_EXTENSION),
|
||
&deviceName,
|
||
FILE_DEVICE_DISK,
|
||
0,
|
||
FALSE,
|
||
&deviceObject);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
delete partitionVolume;
|
||
return;
|
||
}
|
||
|
||
deviceObject->Flags |= DO_DIRECT_IO;
|
||
deviceObject->AlignmentRequirement = targetObject->AlignmentRequirement;
|
||
|
||
extension = (PDEVICE_EXTENSION) deviceObject->DeviceExtension;
|
||
RtlZeroMemory(extension, sizeof(DEVICE_EXTENSION));
|
||
extension->DeviceObject = deviceObject;
|
||
extension->DiskNumber = DiskNumber;
|
||
extension->PartitionNumber = PartitionNumber;
|
||
extension->Root = WholeDisk->Root;
|
||
extension->TargetObject = targetObject;
|
||
extension->u.Partition.FtVolume = partitionVolume;
|
||
extension->u.Partition.WholeDisk = WholeDisk;
|
||
extension->u.Partition.PartitionOffset =
|
||
PartitionInformation->StartingOffset;
|
||
extension->u.Partition.PartitionLength =
|
||
PartitionInformation->PartitionLength;
|
||
extension->u.Partition.EmergencyTransferPacket = new DISPATCH_TP;
|
||
|
||
if (!extension->u.Partition.EmergencyTransferPacket) {
|
||
delete partitionVolume;
|
||
IoDeleteDevice(deviceObject);
|
||
return;
|
||
}
|
||
|
||
InitializeListHead(&extension->u.Partition.EmergencyTransferPacketQueue);
|
||
extension->u.Partition.EmergencyTransferPacketInUse = FALSE;
|
||
KeInitializeSpinLock(&extension->SpinLock);
|
||
|
||
status = IoAttachDeviceByPointer(deviceObject, targetObject);
|
||
if (!NT_SUCCESS(status)) {
|
||
delete partitionVolume;
|
||
IoDeleteDevice(deviceObject);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Link partition onto protect list for this whole disk.
|
||
//
|
||
|
||
KeAcquireSpinLock(&extension->SpinLock, &irql);
|
||
KeAcquireSpinLock(&WholeDisk->SpinLock, &irql2);
|
||
extension->u.Partition.PartitionChain =
|
||
WholeDisk->u.WholeDisk.PartitionChain;
|
||
WholeDisk->u.WholeDisk.PartitionChain = extension;
|
||
KeReleaseSpinLock(&WholeDisk->SpinLock, irql2);
|
||
KeReleaseSpinLock(&extension->SpinLock, irql);
|
||
}
|
||
|
||
VOID
|
||
FtpPrepareDisk(
|
||
PDRIVER_OBJECT DriverObject,
|
||
PDEVICE_OBJECT FtRootDevice,
|
||
PDEVICE_EXTENSION WholeDevice,
|
||
ULONG DiskNumber,
|
||
PDRIVE_LAYOUT_INFORMATION DriveLayout
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called from FtDiskFindDisks to attach to each partition
|
||
on a disk.
|
||
|
||
|
||
Arguments:
|
||
|
||
DriverObject
|
||
FtRootDevice - Ft Root Device Object.
|
||
WholeDevice - Device extension for this disk.
|
||
DiskNumber - Identifies which disk.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG partitionNumber;
|
||
ULONG partitionEntry;
|
||
|
||
//
|
||
// Attach to all partitions located on this disk.
|
||
// partitionEntry is a zero based index into the partition information.
|
||
// partitionNumber is a one base index for use in creating partition
|
||
// names.
|
||
//
|
||
|
||
DebugPrint((1,
|
||
"FtpPrepareDisk: Number of partitions %x\n",
|
||
DriveLayout->PartitionCount));
|
||
|
||
for (partitionEntry = 0, partitionNumber = 1;
|
||
partitionEntry < DriveLayout->PartitionCount;
|
||
partitionEntry++, partitionNumber++) {
|
||
|
||
FtpAttach(DiskNumber, partitionNumber,
|
||
&DriveLayout->PartitionEntry[partitionEntry],
|
||
WholeDevice);
|
||
}
|
||
|
||
} //end FtpPrepareDisk()
|
||
|
||
NTSTATUS
|
||
FtpOpenKey(
|
||
IN PHANDLE HandlePtr,
|
||
IN PUNICODE_STRING KeyName
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Routine to open a key in the configuration registry.
|
||
|
||
Arguments:
|
||
|
||
HandlePtr - Pointer to a location for the resulting handle.
|
||
KeyName - Ascii string for the name of the key.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
OBJECT_ATTRIBUTES objectAttributes;
|
||
|
||
RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES));
|
||
InitializeObjectAttributes(&objectAttributes,
|
||
KeyName,
|
||
OBJ_CASE_INSENSITIVE,
|
||
NULL,
|
||
NULL);
|
||
|
||
status = ZwOpenKey(HandlePtr,
|
||
KEY_READ | KEY_WRITE,
|
||
&objectAttributes);
|
||
return status;
|
||
} // FtpOpenKey
|
||
|
||
NTSTATUS
|
||
FtpReturnRegistryInformation(
|
||
IN PCHAR ValueName,
|
||
IN OUT PVOID *FreePoolAddress,
|
||
IN OUT PVOID *Information
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine queries the configuration registry
|
||
for the configuration information of the FT subsystem.
|
||
NOTE: It must be called with a thread context since it calls into
|
||
the thread logic to insure a buffer is allocated for the data.
|
||
|
||
Arguments:
|
||
|
||
ValueName - an Ascii string for the value name to be returned.
|
||
FreePoolAddress - a pointer to a pointer for the address to free when
|
||
done using information.
|
||
Information - a pointer to a pointer for the information.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
HANDLE handle;
|
||
ULONG requestLength;
|
||
ULONG resultLength;
|
||
STRING string;
|
||
UNICODE_STRING unicodeName;
|
||
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
||
|
||
RtlInitString(&string, DISK_REGISTRY_KEY);
|
||
|
||
status = RtlAnsiStringToUnicodeString(&unicodeName,
|
||
&string,
|
||
TRUE);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
status = FtpOpenKey(&handle,
|
||
&unicodeName);
|
||
RtlFreeUnicodeString(&unicodeName);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
RtlInitString(&string,
|
||
ValueName);
|
||
status = RtlAnsiStringToUnicodeString(&unicodeName,
|
||
&string,
|
||
TRUE);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
requestLength = 4096;
|
||
|
||
for (;;) {
|
||
|
||
keyValueInformation = (PKEY_VALUE_FULL_INFORMATION)
|
||
ExAllocatePool(NonPagedPool,
|
||
requestLength);
|
||
|
||
status = ZwQueryValueKey(handle,
|
||
&unicodeName,
|
||
KeyValueFullInformation,
|
||
keyValueInformation,
|
||
requestLength,
|
||
&resultLength);
|
||
|
||
if (status == STATUS_BUFFER_OVERFLOW) {
|
||
|
||
//
|
||
// Try to get a buffer big enough.
|
||
//
|
||
|
||
ExFreePool(keyValueInformation);
|
||
requestLength += 256;
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
|
||
RtlFreeUnicodeString(&unicodeName);
|
||
ZwClose(handle);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
if (keyValueInformation->DataLength != 0) {
|
||
|
||
//
|
||
// Return the pointers to the caller.
|
||
//
|
||
|
||
*Information =
|
||
(PUCHAR)keyValueInformation + keyValueInformation->DataOffset;
|
||
*FreePoolAddress = keyValueInformation;
|
||
} else {
|
||
|
||
//
|
||
// Treat as a no value case.
|
||
//
|
||
|
||
DebugPrint((3, "FtpReturnRegistryInformation: No Size\n"));
|
||
ExFreePool(keyValueInformation);
|
||
status = STATUS_OBJECT_NAME_NOT_FOUND;
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// Free the memory on failure.
|
||
//
|
||
|
||
DebugPrint((3, "FtpReturnRegistryInformation: No Value => %x\n",
|
||
status));
|
||
ExFreePool(keyValueInformation);
|
||
}
|
||
|
||
return status;
|
||
|
||
} // FtpReturnRegistryInformation
|
||
|
||
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 = (PIO_ERROR_LOG_PACKET)
|
||
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()
|
||
|
||
PDEVICE_EXTENSION
|
||
FtpFindDeviceExtension(
|
||
IN PDEVICE_EXTENSION Extension,
|
||
IN ULONG Signature,
|
||
IN LONGLONG StartingOffset,
|
||
IN LONGLONG Length
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine searches the extension tree for the requested device
|
||
extension.
|
||
|
||
Arguments:
|
||
|
||
Extension - Supplies an extension already in the tree.
|
||
|
||
Signature - Supplies the signature of the requested device extension.
|
||
|
||
StartingOffset - Supplies the offset of the requested device extension.
|
||
|
||
Length - Supplies the length of the requested device extension.
|
||
|
||
ReturnValue:
|
||
|
||
The requested device extension.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_EXTENSION root = Extension->Root;
|
||
PDEVICE_EXTENSION disk, partition;
|
||
KIRQL irql;
|
||
PKSPIN_LOCK s;
|
||
|
||
KeAcquireSpinLock(&root->SpinLock, &irql);
|
||
disk = root->u.Root.DiskChain;
|
||
KeReleaseSpinLock(&root->SpinLock, irql);
|
||
|
||
while (disk) {
|
||
|
||
if (disk->u.WholeDisk.Signature == Signature) {
|
||
|
||
KeAcquireSpinLock(&disk->SpinLock, &irql);
|
||
partition = disk->u.WholeDisk.PartitionChain;
|
||
KeReleaseSpinLock(&disk->SpinLock, irql);
|
||
|
||
while (partition) {
|
||
|
||
if (partition->u.Partition.PartitionOffset.QuadPart == StartingOffset &&
|
||
partition->u.Partition.PartitionLength.QuadPart == Length) {
|
||
|
||
return partition;
|
||
}
|
||
|
||
s = &partition->SpinLock;
|
||
KeAcquireSpinLock(s, &irql);
|
||
partition = partition->u.Partition.PartitionChain;
|
||
KeReleaseSpinLock(s, irql);
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
s = &disk->SpinLock;
|
||
KeAcquireSpinLock(s, &irql);
|
||
disk = disk->u.WholeDisk.DiskChain;
|
||
KeReleaseSpinLock(s, irql);
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
NTSTATUS
|
||
FtpWriteRegistryInformation(
|
||
IN PCHAR ValueName,
|
||
IN PVOID Information,
|
||
IN ULONG InformationLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine writes the configuration registry
|
||
for the configuration information of the FT subsystem.
|
||
|
||
Arguments:
|
||
|
||
ValueName - an Ascii string for the value name to be written.
|
||
Information - a pointer to a buffer area containing the information.
|
||
InformationLength - the length of the buffer area.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
HANDLE handle;
|
||
STRING string;
|
||
UNICODE_STRING unicodeName;
|
||
|
||
RtlInitString(&string, DISK_REGISTRY_KEY);
|
||
|
||
status = RtlAnsiStringToUnicodeString(&unicodeName,
|
||
&string,
|
||
TRUE);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
status = FtpOpenKey(&handle,
|
||
&unicodeName);
|
||
RtlFreeUnicodeString(&unicodeName);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
RtlInitString(&string,
|
||
ValueName);
|
||
status = RtlAnsiStringToUnicodeString(&unicodeName,
|
||
&string,
|
||
TRUE);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
status = ZwSetValueKey(handle,
|
||
&unicodeName,
|
||
0,
|
||
REG_BINARY,
|
||
Information,
|
||
InformationLength);
|
||
RtlFreeUnicodeString(&unicodeName);
|
||
|
||
//
|
||
// Force this out to disk.
|
||
//
|
||
|
||
ZwFlushKey(handle);
|
||
ZwClose(handle);
|
||
}
|
||
|
||
return status;
|
||
|
||
} // FtpWriteRegistryInformation
|
||
|
||
VOID
|
||
FtRegenerateCompletionRoutine(
|
||
IN PVOID Extension,
|
||
IN NTSTATUS Status
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Completion routine of type FT_COMPLETION_ROUTINE for regenerate
|
||
and initialize requests. An error will be logged if there was a
|
||
problem since the IRP returns immediately.
|
||
|
||
Arguments:
|
||
|
||
Extension - Supplies the device extension.
|
||
|
||
Status - Supplies the status of the operation.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_EXTENSION extension = (PDEVICE_EXTENSION) Extension;
|
||
KIRQL irql;
|
||
|
||
KeAcquireSpinLock(&extension->SpinLock, &irql);
|
||
ASSERT(extension->u.Partition.RefCount > 0);
|
||
extension->u.Partition.RefCount--;
|
||
KeReleaseSpinLock(&extension->SpinLock, irql);
|
||
}
|
||
|
||
VOID
|
||
FtpConfigure(
|
||
IN PDEVICE_EXTENSION FtRootExtension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine queries the configuration registry
|
||
for the configuration information of the FT subsystem,
|
||
then proceeds to locate all FT members defined in the
|
||
registry and link FT device extensions to create the
|
||
FT components.
|
||
|
||
Arguments:
|
||
|
||
FtRootExtension - pointer to the device extension for the root of
|
||
the FT device list.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
ULONG index;
|
||
ULONG member;
|
||
PVOID freePoolAddress;
|
||
PDEVICE_EXTENSION currentMember;
|
||
PDEVICE_EXTENSION zeroMember;
|
||
PDISK_CONFIG_HEADER registry;
|
||
PDISK_PARTITION diskPartition;
|
||
PFT_REGISTRY ftRegistry;
|
||
PFT_DESCRIPTION ftDescription;
|
||
PFT_MEMBER_DESCRIPTION ftMember;
|
||
BOOLEAN writeRegistryBack = FALSE;
|
||
BOOLEAN dirtyShutdown = FALSE;
|
||
PCOMPOSITE_FT_VOLUME compVol;
|
||
PFT_VOLUME* volumeArray;
|
||
PPARTITION partition;
|
||
BOOLEAN initializing;
|
||
|
||
//
|
||
// Find the FT section in the configuration.
|
||
//
|
||
|
||
status = FtpReturnRegistryInformation(DISK_REGISTRY_VALUE,
|
||
&freePoolAddress,
|
||
(PVOID*) ®istry);
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// No registry data.
|
||
//
|
||
|
||
return;
|
||
}
|
||
|
||
if (registry->FtInformationSize == 0) {
|
||
|
||
//
|
||
// No FT components in the registry.
|
||
//
|
||
|
||
ExFreePool(freePoolAddress);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Determine if system was shutdown properly.
|
||
//
|
||
|
||
if (registry->DirtyShutdown) {
|
||
|
||
//
|
||
// Log that a dirty shutdown was detected.
|
||
//
|
||
|
||
dirtyShutdown = TRUE;
|
||
FtpLogError(FtRootExtension,
|
||
FT_DIRTY_SHUTDOWN,
|
||
0,
|
||
0,
|
||
NULL);
|
||
|
||
} else {
|
||
|
||
//
|
||
// Write back registry now setting dirty flag.
|
||
//
|
||
|
||
registry->DirtyShutdown = TRUE;
|
||
|
||
FtpWriteRegistryInformation(DISK_REGISTRY_VALUE,
|
||
registry,
|
||
registry->FtInformationOffset +
|
||
registry->FtInformationSize);
|
||
}
|
||
|
||
//
|
||
// Construct the necessary links for the NTFT volumes in the system.
|
||
//
|
||
|
||
ftRegistry = (PFT_REGISTRY)
|
||
((PUCHAR)registry + registry->FtInformationOffset);
|
||
ftDescription = &ftRegistry->FtDescription[0];
|
||
|
||
for (index = 0; index < (ULONG) ftRegistry->NumberOfComponents; index++) {
|
||
|
||
switch (ftDescription->Type) {
|
||
|
||
case Mirror:
|
||
compVol = new MIRROR(FtRootExtension);
|
||
break;
|
||
|
||
case Stripe:
|
||
compVol = new STRIPE(STRIPE_SIZE);
|
||
break;
|
||
|
||
case StripeWithParity:
|
||
compVol = new STRIPE_WP(FtRootExtension, STRIPE_SIZE);
|
||
break;
|
||
|
||
case VolumeSet:
|
||
compVol = new VOLUME_SET;
|
||
break;
|
||
|
||
default:
|
||
compVol = NULL;
|
||
break;
|
||
|
||
}
|
||
|
||
volumeArray = (PFT_VOLUME*)
|
||
ExAllocatePool(NonPagedPool,
|
||
ftDescription->NumberOfMembers*sizeof(PFT_VOLUME));
|
||
|
||
if (!volumeArray) {
|
||
if (compVol) {
|
||
delete compVol;
|
||
compVol = NULL;
|
||
}
|
||
}
|
||
|
||
if (!compVol) {
|
||
if (volumeArray) {
|
||
ExFreePool(volumeArray);
|
||
}
|
||
ftDescription = (PFT_DESCRIPTION)
|
||
&ftDescription->FtMemberDescription[
|
||
ftDescription->NumberOfMembers];
|
||
continue;
|
||
}
|
||
|
||
initializing = FALSE;
|
||
zeroMember = NULL;
|
||
for (member = 0;
|
||
member < (ULONG) ftDescription->NumberOfMembers;
|
||
member++) {
|
||
|
||
ftMember = &ftDescription->FtMemberDescription[member];
|
||
diskPartition = FtpFindPartitionRegistry(registry, ftMember);
|
||
|
||
//
|
||
// Find a corresponding device extension for this registry
|
||
// entry.
|
||
//
|
||
|
||
currentMember = FtpFindDeviceExtension(FtRootExtension,
|
||
ftMember->Signature,
|
||
diskPartition->StartingOffset.QuadPart,
|
||
diskPartition->Length.QuadPart);
|
||
|
||
if (currentMember && currentMember->PartitionNumber > 0) {
|
||
volumeArray[member] = currentMember->u.Partition.FtVolume;
|
||
currentMember->u.Partition.FtVolume = NULL;
|
||
if (!zeroMember) {
|
||
zeroMember = currentMember;
|
||
}
|
||
} else {
|
||
volumeArray[member] = NULL;
|
||
}
|
||
|
||
if (!volumeArray[member]) {
|
||
|
||
partition = new PARTITION;
|
||
if (!partition) {
|
||
return;
|
||
}
|
||
|
||
status = partition->Initialize(NULL, 512, ftMember->Signature,
|
||
diskPartition->StartingOffset.QuadPart,
|
||
diskPartition->Length.QuadPart,
|
||
FALSE, 0, 0);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
delete partition;
|
||
return;
|
||
}
|
||
|
||
volumeArray[member] = partition;
|
||
|
||
if (compVol->QueryVolumeType() == StripeWithParity ||
|
||
compVol->QueryVolumeType() == Mirror) {
|
||
|
||
diskPartition->FtState = Orphaned;
|
||
writeRegistryBack = TRUE;
|
||
partition->SetMemberState(Orphaned);
|
||
}
|
||
}
|
||
|
||
if (diskPartition->FtState == Initializing) {
|
||
initializing = TRUE;
|
||
} else {
|
||
volumeArray[member]->SetMemberState(diskPartition->FtState);
|
||
}
|
||
|
||
volumeArray[member]->SetMemberInformation(compVol, currentMember);
|
||
}
|
||
|
||
if (zeroMember) {
|
||
|
||
status = compVol->Initialize(volumeArray, ftDescription->NumberOfMembers);
|
||
if (!NT_SUCCESS(status)) {
|
||
delete compVol;
|
||
continue;
|
||
}
|
||
|
||
zeroMember->DeviceObject->AlignmentRequirement =
|
||
compVol->QueryAlignmentRequirement();
|
||
|
||
zeroMember->u.Partition.FtVolume = compVol;
|
||
|
||
zeroMember->u.Partition.RefCount++;
|
||
|
||
if (initializing) {
|
||
compVol->SetCheckDataDirty();
|
||
} else if (dirtyShutdown) {
|
||
if (compVol->QueryVolumeState() == FtStateOk) {
|
||
compVol->SetCheckDataDirty();
|
||
}
|
||
}
|
||
|
||
partition = (PPARTITION) volumeArray[0];
|
||
ASSERT(partition->IsPartition());
|
||
|
||
if (partition->IsOnline()) {
|
||
|
||
// Make sure that the first member has the drive letter
|
||
// assignment.
|
||
|
||
ftMember = &ftDescription->FtMemberDescription[1];
|
||
diskPartition = FtpFindPartitionRegistry(registry, ftMember);
|
||
if (diskPartition->AssignDriveLetter) {
|
||
|
||
diskPartition->AssignDriveLetter = FALSE;
|
||
|
||
ftMember = &ftDescription->FtMemberDescription[0];
|
||
diskPartition = FtpFindPartitionRegistry(registry, ftMember);
|
||
diskPartition->AssignDriveLetter = TRUE;
|
||
writeRegistryBack = TRUE;
|
||
}
|
||
|
||
} else if (compVol->QueryVolumeState() == FtHasOrphan) {
|
||
|
||
// Put the volume letter assignment on the second member.
|
||
|
||
ftMember = &ftDescription->FtMemberDescription[0];
|
||
diskPartition = FtpFindPartitionRegistry(registry, ftMember);
|
||
if (diskPartition->AssignDriveLetter) {
|
||
|
||
diskPartition->AssignDriveLetter = FALSE;
|
||
|
||
ftMember = &ftDescription->FtMemberDescription[1];
|
||
diskPartition = FtpFindPartitionRegistry(registry, ftMember);
|
||
diskPartition->AssignDriveLetter = TRUE;
|
||
writeRegistryBack = TRUE;
|
||
}
|
||
}
|
||
|
||
if (compVol->QueryVolumeState() == FtHasOrphan &&
|
||
volumeArray[0]->QueryMemberState() == Orphaned &&
|
||
compVol->QueryVolumeType() == Mirror) {
|
||
|
||
volumeArray[1]->SetFtBitInPartitionType(TRUE, TRUE);
|
||
}
|
||
|
||
if (compVol->QueryVolumeType() == Mirror &&
|
||
volumeArray[0]->QueryMemberState() != Orphaned &&
|
||
volumeArray[1]->IsPartition() &&
|
||
volumeArray[0]->IsPartition() &&
|
||
(((PPARTITION) volumeArray[1])->QueryPartitionType()&VALID_NTFT)
|
||
== VALID_NTFT) {
|
||
|
||
partition = (PPARTITION) volumeArray[0];
|
||
|
||
KeBugCheckEx(FTDISK_INTERNAL_ERROR,
|
||
(ULONG) zeroMember,
|
||
(ULONG) Mirror,
|
||
(ULONG) diskPartition->FtGroup,
|
||
partition->QueryDiskSignature());
|
||
}
|
||
|
||
compVol->StartSyncOperations(FtRegenerateCompletionRoutine,
|
||
zeroMember);
|
||
|
||
} else {
|
||
delete compVol;
|
||
}
|
||
|
||
ftDescription = (PFT_DESCRIPTION)
|
||
&ftDescription->FtMemberDescription[
|
||
ftDescription->NumberOfMembers];
|
||
}
|
||
|
||
if (writeRegistryBack == TRUE) {
|
||
|
||
//
|
||
// The registry is written back if during initialization of the
|
||
// FT components it turns out that a member of a mirror set
|
||
// or stripe with parity is missing. The missing member is orphaned
|
||
// immediately in the registry.
|
||
//
|
||
|
||
FtpWriteRegistryInformation(DISK_REGISTRY_VALUE,
|
||
registry,
|
||
registry->FtInformationOffset +
|
||
registry->FtInformationSize);
|
||
}
|
||
|
||
ExFreePool(freePoolAddress);
|
||
}
|
||
|
||
VOID
|
||
FtDiskFindDisks(
|
||
PDRIVER_OBJECT DriverObject,
|
||
PDEVICE_OBJECT FtRootDevice,
|
||
ULONG Count
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called from FtDiskInitialize to find disk devices
|
||
serviced by the boot device drivers and then called again by the
|
||
IO system to find disk devices serviced by nonboot device drivers.
|
||
|
||
Arguments:
|
||
|
||
DriverObject
|
||
FtRoot - Ft Root Device Object.
|
||
Count - Used to determine if this is the first or second time called.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PCONFIGURATION_INFORMATION configurationInformation;
|
||
PDEVICE_EXTENSION ftRootExtension;
|
||
PDEVICE_EXTENSION extension;
|
||
PDEVICE_OBJECT deviceObject, targetObject;
|
||
NTSTATUS status;
|
||
ULONG diskNumber;
|
||
KIRQL irql, irql2;
|
||
UNICODE_STRING deviceName, targetName;
|
||
WCHAR deviceNameBuf[64], targetNameBuf[64];
|
||
PFILE_OBJECT fileObject;
|
||
PDRIVE_LAYOUT_INFORMATION driveLayout;
|
||
DISK_GEOMETRY diskGeometry;
|
||
|
||
DebugPrint((6, "FtDiskFindDisks: Entered %x\n", DriverObject));
|
||
|
||
ftRootExtension = (PDEVICE_EXTENSION) FtRootDevice->DeviceExtension;
|
||
|
||
//
|
||
// Get the configuration information for this driver.
|
||
//
|
||
|
||
configurationInformation = IoGetConfigurationInformation();
|
||
|
||
//
|
||
// Try to attach to all disks since this routine was last called.
|
||
//
|
||
|
||
for (diskNumber = ftRootExtension->u.Root.NumberOfDisks;
|
||
diskNumber < configurationInformation->DiskCount;
|
||
diskNumber++) {
|
||
|
||
swprintf(targetNameBuf,
|
||
L"\\Device\\Harddisk%d\\Partition0",
|
||
diskNumber);
|
||
swprintf(deviceNameBuf,
|
||
L"\\Device\\Harddisk%d\\Physical0",
|
||
diskNumber);
|
||
|
||
RtlInitUnicodeString(&deviceName, deviceNameBuf);
|
||
RtlInitUnicodeString(&targetName, targetNameBuf);
|
||
|
||
status = IoGetDeviceObjectPointer(&deviceName, FILE_READ_ATTRIBUTES,
|
||
&fileObject, &deviceObject);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// FTDISK has already attached to this disk or partition.
|
||
//
|
||
|
||
ObDereferenceObject(fileObject);
|
||
continue;
|
||
}
|
||
|
||
status = IoGetDeviceObjectPointer(&targetName, FILE_READ_ATTRIBUTES,
|
||
&fileObject, &targetObject);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
// The one to attach to is missing.
|
||
continue;
|
||
}
|
||
ObDereferenceObject(fileObject);
|
||
|
||
status = FtpGetPartitionInformation(targetObject, &driveLayout,
|
||
&diskGeometry);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
continue;
|
||
}
|
||
|
||
status = IoCreateDevice(DriverObject,
|
||
sizeof(DEVICE_EXTENSION),
|
||
&deviceName,
|
||
FILE_DEVICE_DISK,
|
||
0,
|
||
FALSE,
|
||
&deviceObject);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
DebugPrint((1,
|
||
"IoCreateDevice failed (%x)\n",
|
||
status));
|
||
|
||
ExFreePool(driveLayout);
|
||
continue;
|
||
}
|
||
|
||
deviceObject->Flags |= DO_DIRECT_IO;
|
||
deviceObject->AlignmentRequirement = targetObject->AlignmentRequirement;
|
||
|
||
extension = (PDEVICE_EXTENSION) deviceObject->DeviceExtension;
|
||
RtlZeroMemory(extension, sizeof(DEVICE_EXTENSION));
|
||
extension->DeviceObject = deviceObject;
|
||
extension->DiskNumber = diskNumber;
|
||
extension->Root = ftRootExtension;
|
||
extension->TargetObject = targetObject;
|
||
extension->u.WholeDisk.DiskGeometry = diskGeometry;
|
||
extension->u.WholeDisk.Signature = driveLayout->Signature;
|
||
KeInitializeSpinLock(&extension->SpinLock);
|
||
|
||
status = IoAttachDeviceByPointer(deviceObject, targetObject);
|
||
if (!NT_SUCCESS(status)) {
|
||
IoDeleteDevice(deviceObject);
|
||
ExFreePool(driveLayout);
|
||
continue;
|
||
}
|
||
|
||
|
||
KeAcquireSpinLock(&extension->SpinLock, &irql);
|
||
KeAcquireSpinLock(&ftRootExtension->SpinLock, &irql2);
|
||
extension->u.WholeDisk.DiskChain =
|
||
ftRootExtension->u.Root.DiskChain;
|
||
ftRootExtension->u.Root.DiskChain = extension;
|
||
KeReleaseSpinLock(&ftRootExtension->SpinLock, irql2);
|
||
KeReleaseSpinLock(&extension->SpinLock, irql);
|
||
|
||
//
|
||
// Increment count of disks processed.
|
||
//
|
||
|
||
ftRootExtension->u.Root.NumberOfDisks++;
|
||
|
||
//
|
||
// Call routine to attach to all of the partitions on this disk.
|
||
//
|
||
|
||
FtpPrepareDisk(DriverObject,
|
||
FtRootDevice,
|
||
extension,
|
||
diskNumber,
|
||
driveLayout);
|
||
|
||
ExFreePool(driveLayout);
|
||
}
|
||
|
||
//
|
||
// If this is the final time this routine is to be called then
|
||
// set up the FtDisk structures.
|
||
//
|
||
|
||
if (Count == 1) {
|
||
FtpConfigure(ftRootExtension);
|
||
}
|
||
|
||
return;
|
||
|
||
} // end FtDiskFindDisks()
|
||
|
||
NTSTATUS
|
||
FtDiskInitialize(
|
||
IN PDRIVER_OBJECT DriverObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Initialize FtDisk driver.
|
||
This return is the system initialization entry point when
|
||
the driver is linked into the kernel.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Context for the activity.
|
||
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_OBJECT deviceObject;
|
||
PDEVICE_EXTENSION ftRootExtension;
|
||
CHAR ntDeviceName[64];
|
||
STRING ntNameString;
|
||
OBJECT_ATTRIBUTES objectAttributes;
|
||
UNICODE_STRING ntUnicodeString;
|
||
NTSTATUS status;
|
||
PDISK_CONFIG_HEADER registry;
|
||
PVOID freePoolAddress;
|
||
|
||
DebugPrint((1, "Fault Tolerant Driver\n"));
|
||
|
||
//
|
||
// Find the FT section in the configuration.
|
||
//
|
||
|
||
status = FtpReturnRegistryInformation(DISK_REGISTRY_VALUE,
|
||
&freePoolAddress,
|
||
(PVOID*) ®istry);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// No registry data.
|
||
//
|
||
|
||
return STATUS_NO_SUCH_DEVICE;
|
||
}
|
||
|
||
if (registry->FtInformationSize == 0) {
|
||
|
||
//
|
||
// No FT components in the registry.
|
||
//
|
||
|
||
ExFreePool(freePoolAddress);
|
||
return STATUS_NO_SUCH_DEVICE;
|
||
}
|
||
|
||
ExFreePool(freePoolAddress);
|
||
|
||
//
|
||
// Set up the device driver entry points.
|
||
//
|
||
|
||
DriverObject->MajorFunction[IRP_MJ_CREATE] = FtDiskCreate;
|
||
DriverObject->MajorFunction[IRP_MJ_READ] = FtDiskReadWrite;
|
||
DriverObject->MajorFunction[IRP_MJ_WRITE] = FtDiskReadWrite;
|
||
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = FtDiskDeviceControl;
|
||
DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = FtDiskShutdownFlush;
|
||
DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = FtDiskShutdownFlush;
|
||
|
||
//
|
||
// Create the FT root device.
|
||
//
|
||
|
||
sprintf(ntDeviceName,
|
||
"%s",
|
||
"\\Device\\FtControl");
|
||
|
||
RtlInitString(&ntNameString,
|
||
ntDeviceName);
|
||
|
||
status = RtlAnsiStringToUnicodeString(&ntUnicodeString,
|
||
&ntNameString,
|
||
TRUE);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
InitializeObjectAttributes(&objectAttributes,
|
||
&ntUnicodeString,
|
||
OBJ_CASE_INSENSITIVE,
|
||
NULL,
|
||
NULL);
|
||
|
||
status = IoCreateDevice(DriverObject,
|
||
sizeof(DEVICE_EXTENSION),
|
||
&ntUnicodeString,
|
||
FILE_DEVICE_UNKNOWN,
|
||
0,
|
||
FALSE,
|
||
&deviceObject);
|
||
|
||
RtlFreeUnicodeString(&ntUnicodeString);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
DebugPrint((1,
|
||
"FtDiskInitialize: Failed creation of FT root %x\n",
|
||
status));
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
ftRootExtension = (PDEVICE_EXTENSION) deviceObject->DeviceExtension;
|
||
RtlZeroMemory(ftRootExtension, sizeof(DEVICE_EXTENSION));
|
||
ftRootExtension->DeviceObject = deviceObject;
|
||
ftRootExtension->DiskNumber = (ULONG) -1;
|
||
ftRootExtension->Root = ftRootExtension;
|
||
ftRootExtension->u.Root.DriverObject = DriverObject;
|
||
KeInitializeSpinLock(&ftRootExtension->SpinLock);
|
||
IoRegisterShutdownNotification(deviceObject);
|
||
|
||
//
|
||
// Go out and attempt some configuration at this time. This is needed
|
||
// in the case where the boot or system partition is a part of an FT
|
||
// volume.
|
||
//
|
||
|
||
FtDiskFindDisks(DriverObject,
|
||
deviceObject,
|
||
0);
|
||
|
||
//
|
||
// Register with IO system to be called a second time after all
|
||
// other device drivers have initialized. This allows the FT
|
||
// subsystem to set up FT volumes from devices that were not loaded
|
||
// when FT first initialized.
|
||
//
|
||
|
||
IoRegisterDriverReinitialization(DriverObject,
|
||
(PDRIVER_REINITIALIZE) FtDiskFindDisks,
|
||
deviceObject);
|
||
return STATUS_SUCCESS;
|
||
|
||
} // end FtDiskInitialize()
|
||
|
||
NTSTATUS
|
||
DriverEntry(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PUNICODE_STRING RegistryPath
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Called when FtDisk.sys loads. This routine calls the initialization
|
||
routine.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Context for the activity.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
return FtDiskInitialize(DriverObject);
|
||
} // DriverEntry
|
||
|
||
NTSTATUS
|
||
FtDiskCreate(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine serves create commands. It does no more than
|
||
establish the drivers existance by returning status success.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Context for the activity.
|
||
Irp - The device control argument block.
|
||
|
||
Return Value:
|
||
|
||
NT Status
|
||
|
||
--*/
|
||
|
||
{
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
return STATUS_SUCCESS;
|
||
} // end FtDiskCreate()
|
||
|
||
VOID
|
||
DispatchTransferCompletionRoutine(
|
||
IN PTRANSFER_PACKET TransferPacket
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Completion routine for FtDiskReadWrite dispatch routine.
|
||
|
||
Arguments:
|
||
|
||
TransferPacket - Supplies the transfer packet.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDISPATCH_TP transferPacket = (PDISPATCH_TP) TransferPacket;
|
||
PIRP irp = transferPacket->Irp;
|
||
PDEVICE_EXTENSION extension = transferPacket->Extension;
|
||
KIRQL irql;
|
||
PLIST_ENTRY l;
|
||
PDISPATCH_TP p;
|
||
PIO_STACK_LOCATION irpSp;
|
||
PIRP nextIrp;
|
||
|
||
irp->IoStatus = transferPacket->IoStatus;
|
||
if (transferPacket == extension->u.Partition.EmergencyTransferPacket) {
|
||
|
||
for (;;) {
|
||
|
||
KeAcquireSpinLock(&extension->SpinLock, &irql);
|
||
if (IsListEmpty(&extension->u.Partition.EmergencyTransferPacketQueue)) {
|
||
extension->u.Partition.EmergencyTransferPacketInUse = FALSE;
|
||
KeReleaseSpinLock(&extension->SpinLock, irql);
|
||
break;
|
||
}
|
||
|
||
l = RemoveHeadList(&extension->u.Partition.EmergencyTransferPacketQueue);
|
||
KeReleaseSpinLock(&extension->SpinLock, irql);
|
||
|
||
nextIrp = CONTAINING_RECORD(l, IRP, Tail.Overlay.ListEntry);
|
||
|
||
p = new DISPATCH_TP;
|
||
if (!p) {
|
||
p = transferPacket;
|
||
}
|
||
|
||
irpSp = IoGetCurrentIrpStackLocation(nextIrp);
|
||
|
||
p->Mdl = nextIrp->MdlAddress;
|
||
p->Offset = irpSp->Parameters.Read.ByteOffset.QuadPart;
|
||
p->Length = irpSp->Parameters.Read.Length;
|
||
p->CompletionRoutine = DispatchTransferCompletionRoutine;
|
||
p->TargetVolume = extension->u.Partition.FtVolume;
|
||
p->Thread = nextIrp->Tail.Overlay.Thread;
|
||
p->IrpFlags = irpSp->Flags;
|
||
if (irpSp->MajorFunction == IRP_MJ_READ) {
|
||
p->ReadPacket = TRUE;
|
||
} else {
|
||
p->ReadPacket = FALSE;
|
||
}
|
||
p->Irp = nextIrp;
|
||
p->Extension = extension;
|
||
|
||
if (p == transferPacket) {
|
||
TRANSFER(p);
|
||
break;
|
||
} else {
|
||
TRANSFER(p);
|
||
}
|
||
}
|
||
|
||
} else {
|
||
delete transferPacket;
|
||
}
|
||
IoCompleteRequest(irp, IO_DISK_INCREMENT);
|
||
|
||
KeAcquireSpinLock(&extension->SpinLock, &irql);
|
||
ASSERT(extension->u.Partition.RefCount > 0);
|
||
extension->u.Partition.RefCount--;
|
||
KeReleaseSpinLock(&extension->SpinLock, irql);
|
||
}
|
||
|
||
NTSTATUS
|
||
FtDiskReadWrite(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
{
|
||
PDEVICE_EXTENSION extension;
|
||
KIRQL irql;
|
||
PFT_VOLUME vol;
|
||
PDISPATCH_TP packet;
|
||
PIO_STACK_LOCATION irpSp;
|
||
|
||
extension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
||
|
||
if (extension->PartitionNumber > 0) {
|
||
|
||
// This request is for a partition. Use the C++ machinery for this.
|
||
|
||
KeAcquireSpinLock(&extension->SpinLock, &irql);
|
||
if (vol = extension->u.Partition.FtVolume) {
|
||
extension->u.Partition.RefCount++;
|
||
if (extension->u.Partition.EmergencyTransferPacketInUse) {
|
||
IoMarkIrpPending(Irp);
|
||
InsertTailList(&extension->u.Partition.
|
||
EmergencyTransferPacketQueue,
|
||
&Irp->Tail.Overlay.ListEntry);
|
||
KeReleaseSpinLock(&extension->SpinLock, irql);
|
||
return STATUS_PENDING;
|
||
}
|
||
}
|
||
KeReleaseSpinLock(&extension->SpinLock, irql);
|
||
|
||
if (!vol) {
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
return STATUS_NO_SUCH_DEVICE;
|
||
}
|
||
|
||
packet = new DISPATCH_TP;
|
||
if (!packet) {
|
||
|
||
KeAcquireSpinLock(&extension->SpinLock, &irql);
|
||
if (extension->u.Partition.EmergencyTransferPacketInUse) {
|
||
IoMarkIrpPending(Irp);
|
||
InsertTailList(&extension->u.Partition.
|
||
EmergencyTransferPacketQueue,
|
||
&Irp->Tail.Overlay.ListEntry);
|
||
KeReleaseSpinLock(&extension->SpinLock, irql);
|
||
return STATUS_PENDING;
|
||
}
|
||
packet = extension->u.Partition.EmergencyTransferPacket;
|
||
extension->u.Partition.EmergencyTransferPacketInUse = TRUE;
|
||
KeReleaseSpinLock(&extension->SpinLock, irql);
|
||
}
|
||
|
||
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
||
|
||
packet->Mdl = Irp->MdlAddress;
|
||
packet->Offset = irpSp->Parameters.Read.ByteOffset.QuadPart;
|
||
packet->Length = irpSp->Parameters.Read.Length;
|
||
packet->CompletionRoutine = DispatchTransferCompletionRoutine;
|
||
packet->TargetVolume = vol;
|
||
packet->Thread = Irp->Tail.Overlay.Thread;
|
||
packet->IrpFlags = irpSp->Flags;
|
||
if (irpSp->MajorFunction == IRP_MJ_READ) {
|
||
packet->ReadPacket = TRUE;
|
||
} else {
|
||
packet->ReadPacket = FALSE;
|
||
}
|
||
packet->Irp = Irp;
|
||
packet->Extension = extension;
|
||
|
||
IoMarkIrpPending(Irp);
|
||
|
||
TRANSFER(packet);
|
||
|
||
return STATUS_PENDING;
|
||
}
|
||
|
||
if (extension->DiskNumber == -1) {
|
||
|
||
// This request is for the \FtControl.
|
||
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// This request is for the physical disk, just pass it down.
|
||
//
|
||
|
||
Irp->CurrentLocation++,
|
||
Irp->Tail.Overlay.CurrentStackLocation++;
|
||
|
||
return IoCallDriver(extension->TargetObject, Irp);
|
||
}
|
||
|
||
NTSTATUS
|
||
FtGetPartitionInfoCompletionRoutine(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Extension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the completion routine for a get partition info request.
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the IRP.
|
||
|
||
Extension - Supplies the device extension.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_EXTENSION extension = (PDEVICE_EXTENSION) Extension;
|
||
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
||
PPARTITION_INFORMATION partitionInfo;
|
||
KIRQL irql;
|
||
|
||
partitionInfo = (PPARTITION_INFORMATION) Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
KeAcquireSpinLock(&extension->SpinLock, &irql);
|
||
partitionInfo->PartitionLength.QuadPart =
|
||
extension->u.Partition.FtVolume->QueryVolumeSize();
|
||
ASSERT(extension->u.Partition.RefCount > 0);
|
||
extension->u.Partition.RefCount--;
|
||
KeReleaseSpinLock(&extension->SpinLock, irql);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
PDEVICE_EXTENSION
|
||
FtpGetExtensionForDiskPartition(
|
||
IN PDEVICE_EXTENSION Extension,
|
||
IN ULONG DiskNumber,
|
||
IN ULONG PartitionNumber
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine searches the extension tree for the request device
|
||
extension.
|
||
|
||
Arguments:
|
||
|
||
Extension - Supplies an extension already in the tree.
|
||
|
||
DiskNumber - Supplies the disk number of the requested extension.
|
||
|
||
PartitionNumber - Supplies the partition number of the requested extension.
|
||
|
||
ReturnValue:
|
||
|
||
The requested device extension.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_EXTENSION root = Extension->Root;
|
||
PDEVICE_EXTENSION disk, partition, p;
|
||
KIRQL irql;
|
||
|
||
KeAcquireSpinLock(&root->SpinLock, &irql);
|
||
disk = root->u.Root.DiskChain;
|
||
KeReleaseSpinLock(&root->SpinLock, irql);
|
||
|
||
while (disk) {
|
||
|
||
if (disk->DiskNumber == DiskNumber) {
|
||
|
||
KeAcquireSpinLock(&disk->SpinLock, &irql);
|
||
partition = disk->u.WholeDisk.PartitionChain;
|
||
KeReleaseSpinLock(&disk->SpinLock, irql);
|
||
|
||
while (partition) {
|
||
|
||
if (partition->PartitionNumber == PartitionNumber) {
|
||
return partition;
|
||
}
|
||
|
||
KeAcquireSpinLock(&partition->SpinLock, &irql);
|
||
p = partition->u.Partition.PartitionChain;
|
||
KeReleaseSpinLock(&partition->SpinLock, irql);
|
||
partition = p;
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
KeAcquireSpinLock(&disk->SpinLock, &irql);
|
||
p = disk->u.WholeDisk.DiskChain;
|
||
KeReleaseSpinLock(&disk->SpinLock, irql);
|
||
disk = p;
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
PDEVICE_EXTENSION
|
||
FtpGetExtensionForVolumeDescriptor(
|
||
IN PDEVICE_EXTENSION Extension,
|
||
IN ULONG VolumeDescriptor
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine searches the extension tree for the request device
|
||
extension.
|
||
|
||
Arguments:
|
||
|
||
Extension - Supplies an extension already in the tree.
|
||
|
||
VolumeDescriptor - Supplies the volume descriptor of the requested extension.
|
||
|
||
PartitionNumber - Supplies the partition number of the requested extension.
|
||
|
||
ReturnValue:
|
||
|
||
The requested device extension.
|
||
|
||
--*/
|
||
|
||
{
|
||
PUSHORT pu;
|
||
|
||
pu = (PUSHORT) &VolumeDescriptor;
|
||
return FtpGetExtensionForDiskPartition(Extension, pu[1], pu[0]);
|
||
}
|
||
|
||
VOID
|
||
FtpDisolveVolume(
|
||
IN PDEVICE_EXTENSION Extension,
|
||
IN PFT_VOLUME Volume
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine breaks apart this given volume into it's individual
|
||
partitions.
|
||
|
||
Arguments:
|
||
|
||
Extension - Supplies the device extension.
|
||
|
||
Volume - Supplies the volume to disolve.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PPARTITION p;
|
||
PDEVICE_EXTENSION e;
|
||
KIRQL irql;
|
||
ULONG i, l;
|
||
|
||
if (Volume->IsPartition()) {
|
||
p = (PPARTITION) Volume;
|
||
|
||
if (p->IsOnline()) {
|
||
|
||
p->SetMemberInformation(NULL, NULL);
|
||
p->SetMemberState(Healthy);
|
||
|
||
e = FtpGetExtensionForDiskPartition(Extension,
|
||
p->QueryDiskNumber(),
|
||
p->QueryPartitionNumber());
|
||
|
||
ASSERT(e);
|
||
|
||
KeAcquireSpinLock(&e->SpinLock, &irql);
|
||
if (e->u.Partition.FtVolume == NULL) {
|
||
e->u.Partition.FtVolume = p;
|
||
} else {
|
||
delete p;
|
||
}
|
||
KeReleaseSpinLock(&e->SpinLock, irql);
|
||
}
|
||
return;
|
||
}
|
||
|
||
l = Volume->QueryNumberOfMembers();
|
||
for (i = 0; i < l; i++) {
|
||
FtpDisolveVolume(Extension, Volume->GetMember(i));
|
||
}
|
||
|
||
delete Volume;
|
||
}
|
||
|
||
ULONG
|
||
FtpComputeNumberOfVolumeUnitDescriptions(
|
||
IN PFT_VOLUME Volume
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine computes the number of volume unit descriptions needed for
|
||
a full volume description for this volume.
|
||
|
||
Arguments:
|
||
|
||
Volume - Supplies the volume.
|
||
|
||
Return Value:
|
||
|
||
The number of volume unit descriptions needed for this volume.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG i, l, r;
|
||
|
||
l = Volume->QueryNumberOfMembers();
|
||
r = 1;
|
||
for (i = 0; i < l; i++) {
|
||
r += FtpComputeNumberOfVolumeUnitDescriptions(Volume->GetMember(i));
|
||
}
|
||
|
||
return r;
|
||
}
|
||
|
||
#if 0
|
||
ULONG
|
||
FtpComputeVolumeDescriptionLength(
|
||
IN PFT_VOLUME Volume
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine computes the number of bytes needed for a full volume
|
||
description for this volume.
|
||
|
||
Arguments:
|
||
|
||
Volume - Supplies the volume.
|
||
|
||
Return Value:
|
||
|
||
The number of bytes needed for a volume description for this volume.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG r;
|
||
|
||
r = FtpComputeNumberOfVolumeUnitDescriptions(Volume);
|
||
return r*sizeof(FT_VOLUME_UNIT_DESCRIPTION) +
|
||
FIELD_OFFSET(FT_VOLUME_DESCRIPTION, VolumeUnit);
|
||
}
|
||
|
||
ULONG
|
||
FtpQueryVolumeUnitDescriptions(
|
||
IN PFT_VOLUME Volume,
|
||
IN ULONG ThisVolumeUnitNumber,
|
||
IN ULONG ParentVolumeUnitNumber,
|
||
IN ULONG MemberRoleInParent,
|
||
OUT PFT_VOLUME_UNIT_DESCRIPTION VolumeUnits
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine computes the list of volume unit descriptions for this volume volume.
|
||
|
||
Arguments:
|
||
|
||
Volume - Supplies the volume.
|
||
|
||
ThisVolumeUnitNumber - Supplies the volume unit number for this volume unit.
|
||
|
||
ParentVolumeUnitNumber - Supplies the parents volume unit number.
|
||
|
||
MemberRoleInParent - Supplies the role of this unit in the parent.
|
||
|
||
VolumeUnits - Returns the volume units.
|
||
|
||
Return Value:
|
||
|
||
The number of volume unit descriptions consumed.
|
||
|
||
--*/
|
||
|
||
{
|
||
PPARTITION p;
|
||
ULONG i, l, r;
|
||
|
||
VolumeUnits[0].VolumeUnitNumber = ThisVolumeUnitNumber;
|
||
VolumeUnits[0].VolumeSize = Volume->QueryVolumeSize();
|
||
VolumeUnits[0].IsPartition = Volume->IsPartition();
|
||
|
||
if (Volume->IsPartition()) {
|
||
p = (PPARTITION) Volume;
|
||
VolumeUnits[0].u.Partition.Signature = p->QueryDiskSignature();
|
||
VolumeUnits[0].u.Partition.Offset = p->QueryPartitionOffset();
|
||
VolumeUnits[0].u.Partition.Length = p->QueryPartitionLength();
|
||
VolumeUnits[0].u.Partition.Online = p->IsOnline();
|
||
if (p->IsOnline()) {
|
||
VolumeUnits[0].u.Partition.DiskNumber = p->QueryDiskNumber();
|
||
VolumeUnits[0].u.Partition.PartitionNumber = p->QueryPartitionNumber();
|
||
} else {
|
||
VolumeUnits[0].u.Partition.DiskNumber = 0;
|
||
VolumeUnits[0].u.Partition.PartitionNumber = 0;
|
||
}
|
||
} else {
|
||
VolumeUnits[0].u.Composite.VolumeType = Volume->QueryVolumeType();
|
||
VolumeUnits[0].u.Composite.Initializing = Volume->IsCreatingCheckData();
|
||
}
|
||
|
||
VolumeUnits[0].ParentVolumeNumber = ParentVolumeUnitNumber;
|
||
VolumeUnits[0].MemberRoleInParent = MemberRoleInParent;
|
||
VolumeUnits[0].MemberState = Volume->QueryMemberState();
|
||
|
||
r = 1;
|
||
l = Volume->QueryNumberOfMembers();
|
||
for (i = 0; i < l; i++) {
|
||
r += FtpQueryVolumeUnitDescriptions(
|
||
Volume->GetMember(i), ThisVolumeUnitNumber + r,
|
||
ThisVolumeUnitNumber, i, &VolumeUnits[r]);
|
||
}
|
||
|
||
return r;
|
||
}
|
||
|
||
VOID
|
||
FtpQueryVolumeDescription(
|
||
IN PFT_VOLUME Volume,
|
||
OUT PFT_VOLUME_DESCRIPTION VolumeDescription
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine computes the full volume descriptions for this volume.
|
||
|
||
Arguments:
|
||
|
||
Volume - Supplies the volume.
|
||
|
||
VolumeDescription - Returns the volume description.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
VolumeDescription->NumberOfVolumeUnits =
|
||
FtpQueryVolumeUnitDescriptions(Volume, 1, 0, 0,
|
||
VolumeDescription->VolumeUnit);
|
||
}
|
||
#endif
|
||
|
||
VOID
|
||
FtpReplaceOfflineWithOnline(
|
||
IN PDEVICE_EXTENSION Root
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine looks for offline partitions within FT_VOLUMEs and
|
||
tries to replace them with newly found online partitions.
|
||
|
||
Arguments:
|
||
|
||
Root - Supplies the root device extension.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_EXTENSION disk, partition, p;
|
||
KIRQL irql;
|
||
ULONG i, n;
|
||
PFT_VOLUME vol;
|
||
PCOMPOSITE_FT_VOLUME compVol;
|
||
PPARTITION partVol;
|
||
PDEVICE_EXTENSION e;
|
||
|
||
KeAcquireSpinLock(&Root->SpinLock, &irql);
|
||
disk = Root->u.Root.DiskChain;
|
||
KeReleaseSpinLock(&Root->SpinLock, irql);
|
||
|
||
while (disk) {
|
||
|
||
KeAcquireSpinLock(&disk->SpinLock, &irql);
|
||
partition = disk->u.WholeDisk.PartitionChain;
|
||
KeReleaseSpinLock(&disk->SpinLock, irql);
|
||
|
||
while (partition) {
|
||
|
||
KeAcquireSpinLock(&partition->SpinLock, &irql);
|
||
p = partition->u.Partition.PartitionChain;
|
||
vol = partition->u.Partition.FtVolume;
|
||
KeReleaseSpinLock(&partition->SpinLock, irql);
|
||
|
||
if (vol && !vol->IsPartition()) {
|
||
compVol = (PCOMPOSITE_FT_VOLUME) vol;
|
||
n = compVol->QueryNumberOfMembers();
|
||
for (i = 0; i < n; i++) {
|
||
partVol = (PPARTITION) compVol->GetMember(i);
|
||
ASSERT(partVol->IsPartition());
|
||
if (!partVol->IsOnline()) {
|
||
e = FtpFindDeviceExtension(Root,
|
||
partVol->QueryDiskSignature(),
|
||
partVol->QueryPartitionOffset(),
|
||
partVol->QueryPartitionLength());
|
||
if (e) {
|
||
KeAcquireSpinLock(&e->SpinLock, &irql);
|
||
vol = e->u.Partition.FtVolume;
|
||
if (vol && vol->IsPartition()) {
|
||
e->u.Partition.FtVolume = NULL;
|
||
} else {
|
||
vol = NULL;
|
||
}
|
||
KeReleaseSpinLock(&e->SpinLock, irql);
|
||
|
||
if (vol) {
|
||
vol->SetMemberState(partVol->QueryMemberState());
|
||
vol->SetMemberInformation(compVol, e);
|
||
compVol->SetMember(i, vol);
|
||
delete partVol;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
partition = p;
|
||
}
|
||
|
||
KeAcquireSpinLock(&disk->SpinLock, &irql);
|
||
p = disk->u.WholeDisk.DiskChain;
|
||
KeReleaseSpinLock(&disk->SpinLock, irql);
|
||
disk = p;
|
||
}
|
||
}
|
||
|
||
NTSTATUS
|
||
FtNewDiskCompletion(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the completion routine for IOCTL_DISK_FIND_NEW_DEVICES. It
|
||
calls FtDiskFindDisks to process new disk devices.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Pointer to device object to being shutdown by system.
|
||
Irp - IRP involved.
|
||
Context - Not used.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_EXTENSION extension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
||
|
||
//
|
||
// Find new disk devices and attach to disk and all of its partitions.
|
||
//
|
||
|
||
FtDiskFindDisks(DeviceObject->DriverObject,
|
||
extension->Root->DeviceObject, 0);
|
||
|
||
// Go through the new disks and finds any partitions that are
|
||
// "offline" in current FT sets and then sub them in for the
|
||
// off line versions.
|
||
|
||
FtpReplaceOfflineWithOnline(extension->Root);
|
||
|
||
return Irp->IoStatus.Status;
|
||
}
|
||
|
||
PDEVICE_EXTENSION
|
||
FtpFindContainingExtension(
|
||
IN PDEVICE_EXTENSION Extension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine finds the device extension whose FtVolume contains the
|
||
partition represented by the given extension.
|
||
|
||
Arguments:
|
||
|
||
Extension - Supplies the child extension.
|
||
|
||
Return Value:
|
||
|
||
The extension whose FtVolume contains the partition represented by the
|
||
given child extension.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_EXTENSION root = Extension->Root;
|
||
PDEVICE_EXTENSION disk, partition, p;
|
||
KIRQL irql;
|
||
PFT_VOLUME vol;
|
||
|
||
KeAcquireSpinLock(&root->SpinLock, &irql);
|
||
disk = root->u.Root.DiskChain;
|
||
KeReleaseSpinLock(&root->SpinLock, irql);
|
||
|
||
while (disk) {
|
||
|
||
KeAcquireSpinLock(&disk->SpinLock, &irql);
|
||
partition = disk->u.WholeDisk.PartitionChain;
|
||
KeReleaseSpinLock(&disk->SpinLock, irql);
|
||
|
||
while (partition) {
|
||
|
||
vol = partition->u.Partition.FtVolume;
|
||
if (vol && vol->FindPartition(partition->DiskNumber,
|
||
partition->PartitionNumber)) {
|
||
|
||
return partition;
|
||
}
|
||
|
||
KeAcquireSpinLock(&partition->SpinLock, &irql);
|
||
p = partition->u.Partition.PartitionChain;
|
||
KeReleaseSpinLock(&partition->SpinLock, irql);
|
||
partition = p;
|
||
}
|
||
|
||
KeAcquireSpinLock(&disk->SpinLock, &irql);
|
||
p = disk->u.WholeDisk.DiskChain;
|
||
KeReleaseSpinLock(&disk->SpinLock, irql);
|
||
disk = p;
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
VOID
|
||
FtpDisolveContainingVolume(
|
||
IN PDEVICE_EXTENSION Extension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine disolves the volume which contains the partition represented
|
||
by the given device extension.
|
||
|
||
Arguments:
|
||
|
||
Extension - Supplies the device extension for the partition whose parent
|
||
volume needs to be disolved.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_EXTENSION ParentExtension;
|
||
KIRQL irql;
|
||
PFT_VOLUME vol;
|
||
|
||
ParentExtension = FtpFindContainingExtension(Extension);
|
||
if (ParentExtension) {
|
||
KeAcquireSpinLock(&ParentExtension->SpinLock, &irql);
|
||
vol = ParentExtension->u.Partition.FtVolume;
|
||
ParentExtension->u.Partition.FtVolume = NULL;
|
||
KeReleaseSpinLock(&ParentExtension->SpinLock, irql);
|
||
|
||
if (vol) {
|
||
FtpDisolveVolume(Extension, vol);
|
||
}
|
||
}
|
||
}
|
||
|
||
VOID
|
||
FtpDiskSetDriveLayout(
|
||
IN PDEVICE_EXTENSION WholeDisk,
|
||
IN PDRIVE_LAYOUT_INFORMATION DriveLayout
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine takes the new drive layout information and makes the
|
||
necessary adjustments to the device extensions and volume objects.
|
||
|
||
Arguments:
|
||
|
||
WholeDisk - Supplies the device extension for the whole disk.
|
||
|
||
DriveLayout - Supplies the new drive layout.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL irql;
|
||
PDEVICE_EXTENSION extension, p;
|
||
ULONG i;
|
||
PPARTITION_INFORMATION partInfo;
|
||
PFT_VOLUME vol;
|
||
PPARTITION partition;
|
||
NTSTATUS status;
|
||
|
||
KeAcquireSpinLock(&WholeDisk->SpinLock, &irql);
|
||
extension = WholeDisk->u.WholeDisk.PartitionChain;
|
||
KeReleaseSpinLock(&WholeDisk->SpinLock, irql);
|
||
|
||
for (i = 0; extension; i++) {
|
||
|
||
if (extension->PartitionNumber > DriveLayout->PartitionCount) {
|
||
|
||
// Take this extension out since it doesn't exist anymore.
|
||
|
||
ASSERT(extension->u.Partition.RefCount == 0);
|
||
FtpDisolveContainingVolume(extension);
|
||
KeAcquireSpinLock(&extension->SpinLock, &irql);
|
||
vol = extension->u.Partition.FtVolume;
|
||
extension->u.Partition.FtVolume = NULL;
|
||
KeReleaseSpinLock(&extension->SpinLock, irql);
|
||
|
||
if (vol) {
|
||
delete vol;
|
||
}
|
||
|
||
extension->u.Partition.PartitionLength.QuadPart = 0;
|
||
|
||
} else {
|
||
|
||
partInfo = &DriveLayout->PartitionEntry[extension->PartitionNumber - 1];
|
||
|
||
if (partInfo->StartingOffset.QuadPart !=
|
||
extension->u.Partition.PartitionOffset.QuadPart ||
|
||
partInfo->PartitionLength.QuadPart !=
|
||
extension->u.Partition.PartitionLength.QuadPart) {
|
||
|
||
ASSERT(extension->u.Partition.RefCount == 0);
|
||
FtpDisolveContainingVolume(extension);
|
||
KeAcquireSpinLock(&extension->SpinLock, &irql);
|
||
vol = extension->u.Partition.FtVolume;
|
||
extension->u.Partition.FtVolume = NULL;
|
||
KeReleaseSpinLock(&extension->SpinLock, irql);
|
||
|
||
if (vol) {
|
||
delete vol;
|
||
}
|
||
|
||
extension->u.Partition.PartitionOffset =
|
||
partInfo->StartingOffset;
|
||
extension->u.Partition.PartitionLength =
|
||
partInfo->PartitionLength;
|
||
|
||
partition = new PARTITION;
|
||
if (partition) {
|
||
status = partition->Initialize(extension->TargetObject,
|
||
WholeDisk->u.WholeDisk.DiskGeometry.BytesPerSector,
|
||
WholeDisk->u.WholeDisk.Signature,
|
||
extension->u.Partition.PartitionOffset.QuadPart,
|
||
extension->u.Partition.PartitionLength.QuadPart,
|
||
TRUE, extension->DiskNumber,
|
||
extension->PartitionNumber);
|
||
} else {
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
KeAcquireSpinLock(&extension->SpinLock, &irql);
|
||
extension->u.Partition.FtVolume = partition;
|
||
KeReleaseSpinLock(&extension->SpinLock, irql);
|
||
} else {
|
||
delete partition;
|
||
}
|
||
}
|
||
}
|
||
|
||
KeAcquireSpinLock(&extension->SpinLock, &irql);
|
||
p = extension->u.Partition.PartitionChain;
|
||
KeReleaseSpinLock(&extension->SpinLock, irql);
|
||
extension = p;
|
||
}
|
||
|
||
for (; i < DriveLayout->PartitionCount; i++) {
|
||
|
||
// This loop finds new partitions that are not yet attached to.
|
||
// Attach to these new partitions and create PARTITION objects
|
||
// for them.
|
||
|
||
FtpAttach(WholeDisk->DiskNumber, i + 1,
|
||
&DriveLayout->PartitionEntry[i],
|
||
WholeDisk);
|
||
}
|
||
}
|
||
|
||
VOID
|
||
FtpMatchUpWithRegistry(
|
||
IN PDEVICE_EXTENSION Extension,
|
||
IN PDISK_CONFIG_HEADER Registry
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine tries to find the given volume in the registry and then
|
||
either delete the volume if it is not in the registry or make the
|
||
member state changes or member substitutions as reflected in the
|
||
registry.
|
||
|
||
Arguments:
|
||
|
||
Extension - Supplies the extension of the volume to match up.
|
||
|
||
Registry - Supplies the registry information on FT sets in the system.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PFT_VOLUME vol;
|
||
PPARTITION partition;
|
||
PFT_REGISTRY ftRegistry;
|
||
PFT_DESCRIPTION ftDescription;
|
||
PFT_MEMBER_DESCRIPTION ftMember;
|
||
ULONG i, j, regenIndex;
|
||
PDISK_PARTITION diskPartition;
|
||
PDEVICE_EXTENSION e;
|
||
KIRQL irql;
|
||
FT_PARTITION_STATE state;
|
||
|
||
vol = Extension->u.Partition.FtVolume;
|
||
ASSERT(vol->QueryNumberOfMembers() > 1);
|
||
|
||
if (!Registry) {
|
||
goto TubeIt;
|
||
}
|
||
|
||
ftRegistry = (PFT_REGISTRY)
|
||
((PUCHAR)Registry + Registry->FtInformationOffset);
|
||
ftDescription = &ftRegistry->FtDescription[0];
|
||
|
||
for (i = 0; i < ftRegistry->NumberOfComponents; i++) {
|
||
|
||
if (ftDescription->NumberOfMembers != vol->QueryNumberOfMembers() ||
|
||
ftDescription->Type != vol->QueryVolumeType()) {
|
||
|
||
ftDescription = (PFT_DESCRIPTION)
|
||
&ftDescription->FtMemberDescription[
|
||
ftDescription->NumberOfMembers];
|
||
continue;
|
||
}
|
||
|
||
partition = (PPARTITION) vol->GetMember(0);
|
||
ASSERT(partition->IsPartition());
|
||
ftMember = &ftDescription->FtMemberDescription[0];
|
||
diskPartition = FtpFindPartitionRegistry(Registry, ftMember);
|
||
|
||
if (partition->QueryDiskSignature() == ftMember->Signature &&
|
||
partition->QueryPartitionOffset() == diskPartition->StartingOffset.QuadPart &&
|
||
partition->QueryPartitionLength() == diskPartition->Length.QuadPart) {
|
||
|
||
break;
|
||
}
|
||
|
||
partition = (PPARTITION) vol->GetMember(1);
|
||
ASSERT(partition->IsPartition());
|
||
ftMember = &ftDescription->FtMemberDescription[1];
|
||
diskPartition = FtpFindPartitionRegistry(Registry, ftMember);
|
||
|
||
if (partition->QueryDiskSignature() == ftMember->Signature &&
|
||
partition->QueryPartitionOffset() == diskPartition->StartingOffset.QuadPart &&
|
||
partition->QueryPartitionLength() == diskPartition->Length.QuadPart) {
|
||
|
||
break;
|
||
}
|
||
|
||
ftDescription = (PFT_DESCRIPTION)
|
||
&ftDescription->FtMemberDescription[
|
||
ftDescription->NumberOfMembers];
|
||
}
|
||
|
||
if (i < ftRegistry->NumberOfComponents) {
|
||
|
||
regenIndex = ftDescription->NumberOfMembers;
|
||
for (j = 0; j < ftDescription->NumberOfMembers; j++) {
|
||
|
||
partition = (PPARTITION) vol->GetMember(j);
|
||
ASSERT(partition->IsPartition());
|
||
ftMember = &ftDescription->FtMemberDescription[j];
|
||
diskPartition = FtpFindPartitionRegistry(Registry, ftMember);
|
||
|
||
if (partition->QueryDiskSignature() == ftMember->Signature &&
|
||
partition->QueryPartitionOffset() == diskPartition->StartingOffset.QuadPart &&
|
||
partition->QueryPartitionLength() == diskPartition->Length.QuadPart) {
|
||
|
||
if (diskPartition->FtState == Initializing) {
|
||
regenIndex = ftDescription->NumberOfMembers;
|
||
i = ftRegistry->NumberOfComponents;
|
||
break;
|
||
}
|
||
|
||
state = diskPartition->FtState;
|
||
if (partition->QueryMemberState() != state) {
|
||
|
||
if (state == Healthy) {
|
||
regenIndex = ftDescription->NumberOfMembers;
|
||
i = ftRegistry->NumberOfComponents;
|
||
break;
|
||
}
|
||
|
||
if (state == Regenerating) {
|
||
if (regenIndex == ftDescription->NumberOfMembers) {
|
||
regenIndex = j;
|
||
} else {
|
||
regenIndex = ftDescription->NumberOfMembers;
|
||
i = ftRegistry->NumberOfComponents;
|
||
break;
|
||
}
|
||
} else if (state == Orphaned) {
|
||
vol->OrphanPartition(partition);
|
||
}
|
||
}
|
||
|
||
} else if (regenIndex == ftDescription->NumberOfMembers &&
|
||
diskPartition->FtState == Regenerating) {
|
||
|
||
regenIndex = j;
|
||
|
||
} else {
|
||
regenIndex = ftDescription->NumberOfMembers;
|
||
i = ftRegistry->NumberOfComponents;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (regenIndex < ftDescription->NumberOfMembers) {
|
||
|
||
ftMember = &ftDescription->FtMemberDescription[regenIndex];
|
||
diskPartition = FtpFindPartitionRegistry(Registry, ftMember);
|
||
|
||
e = FtpFindDeviceExtension(Extension,
|
||
ftMember->Signature,
|
||
diskPartition->StartingOffset.QuadPart,
|
||
diskPartition->Length.QuadPart);
|
||
|
||
if (e) {
|
||
|
||
ASSERT(e->u.Partition.RefCount == 0);
|
||
partition = (PPARTITION) e->u.Partition.FtVolume;
|
||
if (partition && partition->IsPartition()) {
|
||
e->u.Partition.FtVolume = NULL;
|
||
partition->SetMemberInformation(vol, e);
|
||
} else {
|
||
partition = (PPARTITION) vol->GetMember(regenIndex);
|
||
}
|
||
ASSERT(partition->IsPartition());
|
||
|
||
KeAcquireSpinLock(&Extension->SpinLock, &irql);
|
||
Extension->u.Partition.RefCount++;
|
||
KeReleaseSpinLock(&Extension->SpinLock, irql);
|
||
|
||
if (!vol->Regenerate(partition,
|
||
FtRegenerateCompletionRoutine,
|
||
Extension)) {
|
||
|
||
ASSERT(0);
|
||
|
||
e->u.Partition.FtVolume = partition;
|
||
|
||
KeAcquireSpinLock(&Extension->SpinLock, &irql);
|
||
Extension->u.Partition.RefCount--;
|
||
KeReleaseSpinLock(&Extension->SpinLock, irql);
|
||
}
|
||
|
||
} else {
|
||
ASSERT(0);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (i == ftRegistry->NumberOfComponents) {
|
||
TubeIt:
|
||
|
||
// Tube this one.
|
||
|
||
KeAcquireSpinLock(&Extension->SpinLock, &irql);
|
||
Extension->u.Partition.FtVolume = NULL;
|
||
ASSERT(Extension->u.Partition.RefCount == 0);
|
||
KeReleaseSpinLock(&Extension->SpinLock, irql);
|
||
|
||
FtpDisolveVolume(Extension, vol);
|
||
}
|
||
}
|
||
|
||
VOID
|
||
FtpCreateIfNew(
|
||
IN PDISK_CONFIG_HEADER Registry,
|
||
IN PFT_DESCRIPTION FtDescription,
|
||
IN PDEVICE_EXTENSION Root
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine looks to see if the given FT set has already been
|
||
created and if not then it creates it.
|
||
|
||
Arguments:
|
||
|
||
Registry - Supplies the FT registry.
|
||
|
||
FtDescription - Supplies the description for the FT set.
|
||
|
||
Root - Supplies the root device extension.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_EXTENSION disk, partition, p;
|
||
KIRQL irql;
|
||
PFT_VOLUME vol;
|
||
PFT_MEMBER_DESCRIPTION ftMember;
|
||
PDISK_PARTITION diskPartition;
|
||
PPARTITION partVol;
|
||
PCOMPOSITE_FT_VOLUME compVol;
|
||
PFT_VOLUME* volumeArray;
|
||
BOOLEAN initializing;
|
||
PDEVICE_EXTENSION zeroMember, currentMember;
|
||
ULONG member;
|
||
NTSTATUS status;
|
||
|
||
KeAcquireSpinLock(&Root->SpinLock, &irql);
|
||
disk = Root->u.Root.DiskChain;
|
||
KeReleaseSpinLock(&Root->SpinLock, irql);
|
||
|
||
while (disk) {
|
||
|
||
KeAcquireSpinLock(&disk->SpinLock, &irql);
|
||
partition = disk->u.WholeDisk.PartitionChain;
|
||
KeReleaseSpinLock(&disk->SpinLock, irql);
|
||
|
||
while (partition) {
|
||
|
||
vol = partition->u.Partition.FtVolume;
|
||
if (vol && !vol->IsPartition()) {
|
||
|
||
partVol = (PPARTITION) vol->GetMember(0);
|
||
ASSERT(partVol->IsPartition());
|
||
|
||
ftMember = &FtDescription->FtMemberDescription[0];
|
||
diskPartition = FtpFindPartitionRegistry(Registry, ftMember);
|
||
|
||
if (partVol->QueryDiskSignature() == ftMember->Signature &&
|
||
partVol->QueryPartitionOffset() == diskPartition->StartingOffset.QuadPart &&
|
||
partVol->QueryPartitionLength() == diskPartition->Length.QuadPart) {
|
||
|
||
// This is not new so we are done.
|
||
return;
|
||
}
|
||
}
|
||
|
||
KeAcquireSpinLock(&partition->SpinLock, &irql);
|
||
p = partition->u.Partition.PartitionChain;
|
||
KeReleaseSpinLock(&partition->SpinLock, irql);
|
||
partition = p;
|
||
}
|
||
|
||
KeAcquireSpinLock(&disk->SpinLock, &irql);
|
||
p = disk->u.WholeDisk.DiskChain;
|
||
KeReleaseSpinLock(&disk->SpinLock, irql);
|
||
disk = p;
|
||
}
|
||
|
||
|
||
// The volume is new so we must create it.
|
||
|
||
switch (FtDescription->Type) {
|
||
|
||
case Mirror:
|
||
compVol = new MIRROR(Root);
|
||
break;
|
||
|
||
case Stripe:
|
||
compVol = new STRIPE(STRIPE_SIZE);
|
||
break;
|
||
|
||
case StripeWithParity:
|
||
compVol = new STRIPE_WP(Root, STRIPE_SIZE);
|
||
break;
|
||
|
||
case VolumeSet:
|
||
compVol = new VOLUME_SET;
|
||
break;
|
||
|
||
default:
|
||
compVol = NULL;
|
||
break;
|
||
|
||
}
|
||
|
||
volumeArray = (PFT_VOLUME*)
|
||
ExAllocatePool(NonPagedPool,
|
||
FtDescription->NumberOfMembers*sizeof(PFT_VOLUME));
|
||
|
||
if (!volumeArray) {
|
||
if (compVol) {
|
||
delete compVol;
|
||
compVol = NULL;
|
||
}
|
||
}
|
||
|
||
if (!compVol) {
|
||
if (volumeArray) {
|
||
ExFreePool(volumeArray);
|
||
}
|
||
return;
|
||
}
|
||
|
||
initializing = FALSE;
|
||
zeroMember = NULL;
|
||
for (member = 0; member < FtDescription->NumberOfMembers; member++) {
|
||
|
||
ftMember = &FtDescription->FtMemberDescription[member];
|
||
diskPartition = FtpFindPartitionRegistry(Registry, ftMember);
|
||
|
||
//
|
||
// Find a corresponding device extension for this registry
|
||
// entry.
|
||
//
|
||
|
||
currentMember = FtpFindDeviceExtension(Root,
|
||
ftMember->Signature,
|
||
diskPartition->StartingOffset.QuadPart,
|
||
diskPartition->Length.QuadPart);
|
||
|
||
if (currentMember && currentMember->PartitionNumber > 0) {
|
||
volumeArray[member] = currentMember->u.Partition.FtVolume;
|
||
currentMember->u.Partition.FtVolume = NULL;
|
||
if (!zeroMember) {
|
||
zeroMember = currentMember;
|
||
}
|
||
} else {
|
||
volumeArray[member] = NULL;
|
||
}
|
||
|
||
if (!volumeArray[member]) {
|
||
|
||
partVol = new PARTITION;
|
||
if (!partVol) {
|
||
return;
|
||
}
|
||
|
||
status = partVol->Initialize(NULL, 512, ftMember->Signature,
|
||
diskPartition->StartingOffset.QuadPart,
|
||
diskPartition->Length.QuadPart,
|
||
FALSE, 0, 0);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
delete partVol;
|
||
return;
|
||
}
|
||
|
||
volumeArray[member] = partVol;
|
||
|
||
if (compVol->QueryVolumeType() == StripeWithParity ||
|
||
compVol->QueryVolumeType() == Mirror) {
|
||
|
||
diskPartition->FtState = Orphaned;
|
||
partVol->SetMemberState(Orphaned);
|
||
}
|
||
}
|
||
|
||
if (diskPartition->FtState == Initializing) {
|
||
initializing = TRUE;
|
||
} else {
|
||
volumeArray[member]->SetMemberState(diskPartition->FtState);
|
||
}
|
||
|
||
volumeArray[member]->SetMemberInformation(compVol, currentMember);
|
||
}
|
||
|
||
if (zeroMember) {
|
||
|
||
status = compVol->Initialize(volumeArray, FtDescription->NumberOfMembers);
|
||
if (!NT_SUCCESS(status)) {
|
||
delete compVol;
|
||
return;
|
||
}
|
||
|
||
zeroMember->DeviceObject->AlignmentRequirement =
|
||
compVol->QueryAlignmentRequirement();
|
||
|
||
zeroMember->u.Partition.FtVolume = compVol;
|
||
|
||
zeroMember->u.Partition.RefCount++;
|
||
|
||
if (initializing) {
|
||
compVol->SetCheckDataDirty();
|
||
}
|
||
|
||
compVol->StartSyncOperations(FtRegenerateCompletionRoutine,
|
||
zeroMember);
|
||
|
||
} else {
|
||
delete compVol;
|
||
}
|
||
}
|
||
|
||
VOID
|
||
FtpDynamicConfigure(
|
||
IN PDEVICE_EXTENSION Root
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine reconfigures the existing FT sets according to
|
||
changes found in the registry.
|
||
|
||
Arguments:
|
||
|
||
Root - Supplies the root device extension.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
PVOID freePoolAddress;
|
||
PDISK_CONFIG_HEADER registry;
|
||
KIRQL irql;
|
||
PDEVICE_EXTENSION disk, partition, p;
|
||
PFT_REGISTRY ftRegistry;
|
||
PFT_DESCRIPTION ftDescription;
|
||
ULONG i;
|
||
PFT_VOLUME vol;
|
||
|
||
|
||
// Get the FT set information from the registry.
|
||
|
||
status = FtpReturnRegistryInformation(DISK_REGISTRY_VALUE,
|
||
&freePoolAddress,
|
||
(PVOID*) ®istry);
|
||
if (!NT_SUCCESS(status)) {
|
||
freePoolAddress = NULL;
|
||
registry = NULL;
|
||
} else if (registry->FtInformationSize == 0) {
|
||
ExFreePool(freePoolAddress);
|
||
freePoolAddress = NULL;
|
||
registry = NULL;
|
||
}
|
||
|
||
|
||
// First, go through all of the existing sets and make state changes
|
||
// or delete them as the registry prescribes.
|
||
|
||
KeAcquireSpinLock(&Root->SpinLock, &irql);
|
||
disk = Root->u.Root.DiskChain;
|
||
KeReleaseSpinLock(&Root->SpinLock, irql);
|
||
|
||
while (disk) {
|
||
|
||
KeAcquireSpinLock(&disk->SpinLock, &irql);
|
||
partition = disk->u.WholeDisk.PartitionChain;
|
||
KeReleaseSpinLock(&disk->SpinLock, irql);
|
||
|
||
while (partition) {
|
||
|
||
vol = partition->u.Partition.FtVolume;
|
||
if (vol && !vol->IsPartition()) {
|
||
FtpMatchUpWithRegistry(partition, registry);
|
||
}
|
||
|
||
KeAcquireSpinLock(&partition->SpinLock, &irql);
|
||
p = partition->u.Partition.PartitionChain;
|
||
KeReleaseSpinLock(&partition->SpinLock, irql);
|
||
partition = p;
|
||
}
|
||
|
||
KeAcquireSpinLock(&disk->SpinLock, &irql);
|
||
p = disk->u.WholeDisk.DiskChain;
|
||
KeReleaseSpinLock(&disk->SpinLock, irql);
|
||
disk = p;
|
||
}
|
||
|
||
|
||
// Now go through all of the sets in the registry and make sure that
|
||
// they exist and create them as necessary.
|
||
|
||
if (!registry) {
|
||
return;
|
||
}
|
||
|
||
ftRegistry = (PFT_REGISTRY)
|
||
((PUCHAR) registry + registry->FtInformationOffset);
|
||
ftDescription = &ftRegistry->FtDescription[0];
|
||
|
||
for (i = 0; i < ftRegistry->NumberOfComponents; i++) {
|
||
FtpCreateIfNew(registry, ftDescription, Root);
|
||
ftDescription = (PFT_DESCRIPTION)
|
||
&ftDescription->FtMemberDescription[
|
||
ftDescription->NumberOfMembers];
|
||
}
|
||
|
||
FtpWriteRegistryInformation(DISK_REGISTRY_VALUE,
|
||
registry,
|
||
registry->FtInformationOffset +
|
||
registry->FtInformationSize);
|
||
|
||
ExFreePool(freePoolAddress);
|
||
}
|
||
|
||
NTSTATUS
|
||
FtDiskDeviceControl(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The entry point in the driver for specific FT device control functions.
|
||
This routine controls FT actions, such as setting up a mirror or stripe
|
||
with "mirror copy" or verifying a mirror or stripe with "mirror verify".
|
||
Other control codes allow for cooperating subsystems or file systems to
|
||
work on the primary and mirror information without interference from the
|
||
FT driver.
|
||
|
||
This entry will also gain control of device controls intended for the
|
||
target device. In the case of mirrors or stripes, some device controls
|
||
are intercepted and the action must be performed on both components of
|
||
the mirror or stripe. For example, a VERIFY must be performed on both
|
||
components and on a failure an attempt to map the failing location from
|
||
use is made.. if this does not succeed, then a failure of the location
|
||
is returned to the caller even if the second side of the location (mirror
|
||
or parity stripe) succeeds.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Context for the activity.
|
||
Irp - The device control argument block.
|
||
|
||
Return Value:
|
||
|
||
Status is returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_EXTENSION extension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
||
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
||
ULONG ioctl = irpSp->Parameters.DeviceIoControl.IoControlCode;
|
||
NTSTATUS status;
|
||
ULONG arraySize, i, j;
|
||
PFT_VOLUME* volumeArray;
|
||
PCOMPOSITE_FT_VOLUME compVol;
|
||
KIRQL irql, irql2;
|
||
PFT_VOLUME vol, volarg;
|
||
PDISPATCH_TP packet;
|
||
PVERIFY_INFORMATION verifyInfo;
|
||
BOOLEAN passThrough;
|
||
PSET_PARTITION_INFORMATION setPartitionInfo;
|
||
PDISK_GEOMETRY diskGeometry;
|
||
PFT_SPECIAL_READ specialRead;
|
||
PFT_SYNC_INFORMATION syncInfo;
|
||
PFT_SET_INFORMATION setInfo;
|
||
PDEVICE_EXTENSION e, ne, extarg;
|
||
PIO_STACK_LOCATION nextSp;
|
||
PUSHORT pu;
|
||
|
||
#if 0
|
||
PFT_VOLUME_DESCRIPTION_LENGTH volDescriptionLength;
|
||
#endif
|
||
|
||
|
||
Irp->IoStatus.Information = 0;
|
||
|
||
if (extension->DiskNumber == -1) {
|
||
|
||
// This is the root extension.
|
||
|
||
compVol = NULL;
|
||
|
||
switch (irpSp->Parameters.DeviceIoControl.IoControlCode) {
|
||
|
||
case FT_CONFIGURE:
|
||
|
||
// No longer supported.
|
||
|
||
FtpDynamicConfigure(extension);
|
||
status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
|
||
default:
|
||
status = STATUS_INVALID_PARAMETER;
|
||
break;
|
||
|
||
}
|
||
|
||
Irp->IoStatus.Status = status;
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
return status;
|
||
}
|
||
|
||
if (ioctl == IOCTL_DISK_FIND_NEW_DEVICES) {
|
||
|
||
nextSp = IoGetNextIrpStackLocation(Irp);
|
||
*nextSp = *irpSp;
|
||
|
||
IoSetCompletionRoutine(Irp, FtNewDiskCompletion,
|
||
Irp, TRUE, TRUE, TRUE);
|
||
|
||
return IoCallDriver(extension->TargetObject, Irp);
|
||
}
|
||
|
||
if (extension->PartitionNumber == 0) {
|
||
|
||
if (ioctl == IOCTL_DISK_SET_DRIVE_LAYOUT) {
|
||
|
||
PIRP newIrp;
|
||
IO_STATUS_BLOCK ioStatusBlock;
|
||
KEVENT event;
|
||
CCHAR boost;
|
||
DISK_GEOMETRY geometry;
|
||
PDRIVE_LAYOUT_INFORMATION layout;
|
||
PDRIVE_LAYOUT_INFORMATION driveLayout =
|
||
(PDRIVE_LAYOUT_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
//
|
||
// Perform the set drive layout synchronously. Set both
|
||
// the input and output buffers as the buffer passed.
|
||
//
|
||
|
||
KeInitializeEvent(&event,
|
||
NotificationEvent,
|
||
FALSE);
|
||
newIrp = IoBuildDeviceIoControlRequest(IOCTL_DISK_SET_DRIVE_LAYOUT,
|
||
extension->TargetObject,
|
||
driveLayout,
|
||
irpSp->Parameters.DeviceIoControl.InputBufferLength,
|
||
driveLayout,
|
||
irpSp->Parameters.DeviceIoControl.OutputBufferLength,
|
||
FALSE,
|
||
&event,
|
||
&ioStatusBlock);
|
||
|
||
status = IoCallDriver(extension->TargetObject, newIrp);
|
||
|
||
if (status == STATUS_PENDING) {
|
||
KeWaitForSingleObject(&event,
|
||
Suspended,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL);
|
||
status = ioStatusBlock.Status;
|
||
}
|
||
|
||
Irp->IoStatus = ioStatusBlock;
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// The HAL has decided that it will return a zero signature
|
||
// when there are no partitions on a disk. Due to this, it
|
||
// is possible for the Partition0 device extension that was
|
||
// attached to a disk to have a zero value for the signature.
|
||
// Since the dynamic partitioning code depends on disk signatures,
|
||
// check for this condition and if it is true that there is
|
||
// currently no signature for the disk, use the one provided
|
||
// by the caller (typically Disk Administrator).
|
||
//
|
||
|
||
if (!extension->u.WholeDisk.Signature) {
|
||
extension->u.WholeDisk.Signature = driveLayout->Signature;
|
||
}
|
||
|
||
//
|
||
// Process the new partition table. The work for the
|
||
// set drive layout was done synchronously because this
|
||
// routine performs synchronous activities.
|
||
//
|
||
|
||
status = FtpGetPartitionInformation(DeviceObject, &layout,
|
||
&geometry);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
FtpDiskSetDriveLayout(extension, layout);
|
||
boost = IO_DISK_INCREMENT;
|
||
ExFreePool(layout);
|
||
} else {
|
||
boost = IO_NO_INCREMENT;
|
||
}
|
||
|
||
} else {
|
||
boost = IO_NO_INCREMENT;
|
||
}
|
||
|
||
IoCompleteRequest(Irp, boost);
|
||
return status;
|
||
}
|
||
|
||
// All other IOCTLs just pass through.
|
||
|
||
Irp->CurrentLocation++,
|
||
Irp->Tail.Overlay.CurrentStackLocation++;
|
||
|
||
return IoCallDriver(extension->TargetObject, Irp);
|
||
}
|
||
|
||
|
||
// Now we know that this request is being passed to a partition.
|
||
|
||
KeAcquireSpinLock(&extension->SpinLock, &irql);
|
||
if (vol = extension->u.Partition.FtVolume) {
|
||
e = extension;
|
||
e->u.Partition.RefCount++;
|
||
} else {
|
||
e = NULL;
|
||
}
|
||
KeReleaseSpinLock(&extension->SpinLock, irql);
|
||
|
||
Irp->IoStatus.Information = 0;
|
||
status = STATUS_PENDING;
|
||
switch (irpSp->Parameters.DeviceIoControl.IoControlCode) {
|
||
|
||
case IOCTL_DISK_VERIFY:
|
||
|
||
if (!vol) {
|
||
status = STATUS_INVALID_DEVICE_REQUEST;
|
||
break;
|
||
}
|
||
|
||
if (irpSp->Parameters.DeviceIoControl.InputBufferLength <
|
||
sizeof(VERIFY_INFORMATION)) {
|
||
|
||
status = STATUS_INVALID_PARAMETER;
|
||
break;
|
||
}
|
||
|
||
packet = new DISPATCH_TP;
|
||
if (!packet) {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
break;
|
||
}
|
||
|
||
verifyInfo = (PVERIFY_INFORMATION) Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
packet->Mdl = NULL;
|
||
packet->Offset = verifyInfo->StartingOffset.QuadPart;
|
||
packet->Length = verifyInfo->Length;
|
||
packet->CompletionRoutine = DispatchTransferCompletionRoutine;
|
||
packet->TargetVolume = vol;
|
||
packet->Thread = Irp->Tail.Overlay.Thread;
|
||
packet->IrpFlags = irpSp->Flags;
|
||
packet->ReadPacket = TRUE;
|
||
packet->Irp = Irp;
|
||
packet->Extension = extension;
|
||
|
||
IoMarkIrpPending(Irp);
|
||
|
||
TRANSFER(packet);
|
||
|
||
return STATUS_PENDING;
|
||
|
||
case IOCTL_DISK_SET_PARTITION_INFO:
|
||
|
||
if (!vol || !vol->IsPartition()) {
|
||
setPartitionInfo = (PSET_PARTITION_INFORMATION)
|
||
Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
setPartitionInfo->PartitionType |= 0x80;
|
||
}
|
||
break;
|
||
|
||
case IOCTL_DISK_GET_PARTITION_INFO:
|
||
|
||
if (!vol || vol->IsPartition()) {
|
||
break;
|
||
}
|
||
|
||
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
||
sizeof(PARTITION_INFORMATION)) {
|
||
|
||
status = STATUS_BUFFER_TOO_SMALL;
|
||
break;
|
||
}
|
||
|
||
nextSp = IoGetNextIrpStackLocation(Irp);
|
||
*nextSp = *irpSp;
|
||
|
||
IoMarkIrpPending(Irp);
|
||
IoSetCompletionRoutine(Irp, FtGetPartitionInfoCompletionRoutine,
|
||
extension, TRUE, TRUE, TRUE);
|
||
|
||
IoCallDriver(extension->TargetObject, Irp);
|
||
|
||
return STATUS_PENDING;
|
||
|
||
case IOCTL_DISK_GET_DRIVE_GEOMETRY:
|
||
|
||
if (!vol || vol->IsPartition()) {
|
||
break;
|
||
}
|
||
|
||
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
||
sizeof(DISK_GEOMETRY)) {
|
||
|
||
status = STATUS_BUFFER_TOO_SMALL;
|
||
break;
|
||
}
|
||
|
||
diskGeometry = (PDISK_GEOMETRY) Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
diskGeometry->MediaType = FixedMedia;
|
||
diskGeometry->Cylinders.QuadPart = vol->QueryVolumeSize() >> 20;
|
||
diskGeometry->TracksPerCylinder = 32;
|
||
diskGeometry->BytesPerSector = vol->QuerySectorSize();
|
||
diskGeometry->SectorsPerTrack = 32768/diskGeometry->BytesPerSector;
|
||
|
||
//
|
||
// Return bytes transferred and status.
|
||
//
|
||
|
||
Irp->IoStatus.Information = sizeof(DISK_GEOMETRY);
|
||
status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
case FT_INITIALIZE_SET:
|
||
case FT_SYNC_REDUNDANT_COPY:
|
||
|
||
// For right now ignore the range and just do an initialize.
|
||
|
||
if (!vol) {
|
||
status = STATUS_INVALID_DEVICE_REQUEST;
|
||
break;
|
||
}
|
||
|
||
vol->StartSyncOperations(FtRegenerateCompletionRoutine,
|
||
extension);
|
||
|
||
status = STATUS_SUCCESS;
|
||
e = NULL;
|
||
break;
|
||
|
||
case FT_REGENERATE:
|
||
case FT_VERIFY:
|
||
|
||
status = STATUS_NOT_IMPLEMENTED;
|
||
break;
|
||
|
||
case FT_SECONDARY_READ:
|
||
case FT_PRIMARY_READ:
|
||
|
||
if (!vol) {
|
||
status = STATUS_INVALID_DEVICE_REQUEST;
|
||
break;
|
||
}
|
||
|
||
if (irpSp->Parameters.DeviceIoControl.InputBufferLength <
|
||
sizeof(FT_SPECIAL_READ)) {
|
||
|
||
status = STATUS_BUFFER_TOO_SMALL;
|
||
break;
|
||
}
|
||
|
||
packet = new DISPATCH_TP;
|
||
if (!packet) {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
break;
|
||
}
|
||
|
||
specialRead = (PFT_SPECIAL_READ) Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
packet->Mdl = Irp->MdlAddress;
|
||
packet->Offset = specialRead->ByteOffset.QuadPart;
|
||
packet->Length = specialRead->Length;
|
||
packet->CompletionRoutine = DispatchTransferCompletionRoutine;
|
||
packet->TargetVolume = vol;
|
||
packet->Thread = Irp->Tail.Overlay.Thread;
|
||
packet->IrpFlags = irpSp->Flags;
|
||
packet->ReadPacket = TRUE;
|
||
if (ioctl == FT_SECONDARY_READ) {
|
||
packet->SpecialRead = TP_SPECIAL_READ_SECONDARY;
|
||
} else {
|
||
packet->SpecialRead = TP_SPECIAL_READ_PRIMARY;
|
||
}
|
||
packet->Irp = Irp;
|
||
packet->Extension = extension;
|
||
|
||
IoMarkIrpPending(Irp);
|
||
|
||
TRANSFER(packet);
|
||
|
||
return STATUS_PENDING;
|
||
|
||
case FT_BALANCED_READ_MODE:
|
||
case FT_SEQUENTIAL_WRITE_MODE:
|
||
case FT_PARALLEL_WRITE_MODE:
|
||
|
||
// No-op these operations for now.
|
||
|
||
status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
case FT_QUERY_SET_STATE:
|
||
|
||
if (!vol) {
|
||
status = STATUS_INVALID_DEVICE_REQUEST;
|
||
break;
|
||
}
|
||
|
||
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
||
sizeof(FT_SET_INFORMATION)) {
|
||
|
||
status = STATUS_BUFFER_TOO_SMALL;
|
||
break;
|
||
}
|
||
|
||
setInfo = (PFT_SET_INFORMATION) Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
setInfo->NumberOfMembers = vol->QueryNumberOfMembers();
|
||
setInfo->Type = vol->QueryVolumeType();
|
||
setInfo->SetState = vol->QueryVolumeState();
|
||
|
||
Irp->IoStatus.Information = sizeof(FT_SET_INFORMATION);
|
||
|
||
status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
#if 0
|
||
case FT_QUERY_VOLUME_DESCRIPTION_LENGTH:
|
||
|
||
if (!vol) {
|
||
status = STATUS_INVALID_DEVICE_REQUEST;
|
||
break;
|
||
}
|
||
|
||
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
||
sizeof(FT_VOLUME_DESCRIPTION_LENGTH)) {
|
||
|
||
status = STATUS_BUFFER_TOO_SMALL;
|
||
break;
|
||
}
|
||
|
||
volDescriptionLength = (PFT_VOLUME_DESCRIPTION_LENGTH)
|
||
Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
volDescriptionLength->Length = FtpComputeVolumeDescriptionLength(vol);
|
||
Irp->IoStatus.Information = sizeof(FT_VOLUME_DESCRIPTION_LENGTH);
|
||
status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
case FT_QUERY_VOLUME_DESCRIPTION:
|
||
|
||
if (!vol) {
|
||
status = STATUS_INVALID_DEVICE_REQUEST;
|
||
break;
|
||
}
|
||
|
||
Irp->IoStatus.Information =
|
||
FtpComputeVolumeDescriptionLength(vol);
|
||
|
||
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
||
Irp->IoStatus.Information) {
|
||
|
||
Irp->IoStatus.Information = 0;
|
||
status = STATUS_BUFFER_TOO_SMALL;
|
||
break;
|
||
}
|
||
|
||
FtpQueryVolumeDescription(vol,
|
||
(PFT_VOLUME_DESCRIPTION)
|
||
Irp->AssociatedIrp.SystemBuffer);
|
||
status = STATUS_SUCCESS;
|
||
break;
|
||
#endif
|
||
|
||
default:
|
||
break;
|
||
|
||
}
|
||
|
||
if (e) {
|
||
KeAcquireSpinLock(&e->SpinLock, &irql);
|
||
ASSERT(e->u.Partition.RefCount > 0);
|
||
e->u.Partition.RefCount--;
|
||
KeReleaseSpinLock(&e->SpinLock, irql);
|
||
}
|
||
|
||
if (status != STATUS_PENDING) {
|
||
Irp->IoStatus.Status = status;
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
return status;
|
||
}
|
||
|
||
Irp->CurrentLocation++;
|
||
Irp->Tail.Overlay.CurrentStackLocation++;
|
||
|
||
return IoCallDriver(extension->TargetObject, Irp);
|
||
}
|
||
|
||
VOID
|
||
FtDiskShutdownFlushCompletionRoutine(
|
||
IN PVOID Irp,
|
||
IN NTSTATUS Status
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the completion routine for FtDiskShutdownFlush.
|
||
|
||
Arguments:
|
||
|
||
Irp - IRP involved.
|
||
|
||
Status - Status of operation.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PIRP irp = (PIRP) Irp;
|
||
PIO_STACK_LOCATION irpSp;
|
||
PDEVICE_EXTENSION extension;
|
||
KIRQL irql;
|
||
|
||
irpSp = IoGetCurrentIrpStackLocation(irp);
|
||
extension = (PDEVICE_EXTENSION) irpSp->DeviceObject->DeviceExtension;
|
||
KeAcquireSpinLock(&extension->SpinLock, &irql);
|
||
ASSERT(extension->u.Partition.RefCount > 0);
|
||
extension->u.Partition.RefCount--;
|
||
KeReleaseSpinLock(&extension->SpinLock, irql);
|
||
|
||
irp->IoStatus.Status = Status;
|
||
irp->IoStatus.Information = 0;
|
||
IoCompleteRequest(irp, IO_DISK_INCREMENT);
|
||
}
|
||
|
||
NTSTATUS
|
||
FtDiskShutdownFlush(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called for a shutdown and flush IRPs. These are sent by the
|
||
system before it actually shuts down or when the file system does a flush.
|
||
|
||
Arguments:
|
||
|
||
DriverObject - Pointer to device object to being shutdown by system.
|
||
Irp - IRP involved.
|
||
|
||
Return Value:
|
||
|
||
NT Status
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_EXTENSION extension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
||
PVOID freePoolAddress;
|
||
PDISK_CONFIG_HEADER registry;
|
||
NTSTATUS status;
|
||
PFT_VOLUME vol;
|
||
KIRQL irql;
|
||
PIO_STACK_LOCATION irpSp;
|
||
|
||
//
|
||
// Determine if this is the FtRootExtension.
|
||
//
|
||
|
||
if (extension->DiskNumber == -1) {
|
||
|
||
//
|
||
// This is the call registered by FT.
|
||
//
|
||
|
||
status = FtpReturnRegistryInformation(DISK_REGISTRY_VALUE,
|
||
&freePoolAddress,
|
||
(PVOID*) ®istry);
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// No registry data.
|
||
//
|
||
|
||
// DebugPrint((2, "FtDiskShutDownFlush: Can't get registry information\n"));
|
||
|
||
} else {
|
||
|
||
//
|
||
// Indicate a clean shutdown occured in the registry.
|
||
//
|
||
|
||
registry->DirtyShutdown = FALSE;
|
||
FtpWriteRegistryInformation(DISK_REGISTRY_VALUE,
|
||
registry,
|
||
registry->FtInformationOffset +
|
||
registry->FtInformationSize);
|
||
}
|
||
|
||
//
|
||
// Complete this request.
|
||
//
|
||
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
if (extension->PartitionNumber == 0) {
|
||
|
||
//
|
||
// This is for the physical disk so just pass it down.
|
||
//
|
||
|
||
Irp->CurrentLocation++,
|
||
Irp->Tail.Overlay.CurrentStackLocation++;
|
||
|
||
return IoCallDriver(extension->TargetObject, Irp);
|
||
}
|
||
|
||
KeAcquireSpinLock(&extension->SpinLock, &irql);
|
||
if (vol = extension->u.Partition.FtVolume) {
|
||
extension->u.Partition.RefCount++;
|
||
}
|
||
KeReleaseSpinLock(&extension->SpinLock, irql);
|
||
|
||
if (!vol) {
|
||
|
||
//
|
||
// Complete this request.
|
||
//
|
||
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Pass this request through the C++ machinery.
|
||
//
|
||
|
||
IoMarkIrpPending(Irp);
|
||
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
||
irpSp->DeviceObject = DeviceObject;
|
||
|
||
vol->FlushBuffers(FtDiskShutdownFlushCompletionRoutine, Irp);
|
||
|
||
return STATUS_PENDING;
|
||
|
||
} // end FtDiskShutdownFlush()
|
||
|
||
VOID
|
||
FtpComputeParity(
|
||
IN PVOID TargetBuffer,
|
||
IN PVOID SourceBuffer,
|
||
IN ULONG BufferLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine computes the parity of the source and target buffers
|
||
and places the result of the computation into the target buffer.
|
||
I.E. TargetBuffer ^= SourceBuffer.
|
||
|
||
Arguments:
|
||
|
||
TargetBuffer - Supplies the target buffer.
|
||
|
||
SourceBuffer - Supplies the source buffer.
|
||
|
||
BufferLength - Supplies the buffer length.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PULONGLONG p, q;
|
||
ULONG i, n;
|
||
|
||
ASSERT(sizeof(ULONGLONG) == 8);
|
||
|
||
p = (PULONGLONG) TargetBuffer;
|
||
q = (PULONGLONG) SourceBuffer;
|
||
n = BufferLength/128;
|
||
ASSERT(BufferLength%128 == 0);
|
||
for (i = 0; i < n; i++) {
|
||
*p++ ^= *q++;
|
||
*p++ ^= *q++;
|
||
*p++ ^= *q++;
|
||
*p++ ^= *q++;
|
||
*p++ ^= *q++;
|
||
*p++ ^= *q++;
|
||
*p++ ^= *q++;
|
||
*p++ ^= *q++;
|
||
*p++ ^= *q++;
|
||
*p++ ^= *q++;
|
||
*p++ ^= *q++;
|
||
*p++ ^= *q++;
|
||
*p++ ^= *q++;
|
||
*p++ ^= *q++;
|
||
*p++ ^= *q++;
|
||
*p++ ^= *q++;
|
||
}
|
||
}
|