2282 lines
65 KiB
C
2282 lines
65 KiB
C
/*++
|
||
|
||
Copyright (c) 1991 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)
|
||
|
||
Environment:
|
||
|
||
kernel mode only
|
||
|
||
Notes:
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "ntddk.h"
|
||
#include "ftdisk.h"
|
||
|
||
|
||
|
||
NTSTATUS
|
||
FtFlushOrShutCompletion(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
);
|
||
|
||
NTSTATUS
|
||
FtpDiskSetDriveLayout(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
FtNewDiskCompletion(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(INIT, FtDiskInitialize)
|
||
#endif
|
||
|
||
|
||
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;
|
||
UCHAR 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;
|
||
}
|
||
|
||
//
|
||
// 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)) {
|
||
ExFreePool(freePoolAddress);
|
||
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));
|
||
|
||
ExFreePool(freePoolAddress);
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Indicate 2 stacks. The second stack will be used to store the
|
||
// context block for queuing worker threads in device controls to
|
||
// the ft root.
|
||
//
|
||
|
||
deviceObject->StackSize = 2;
|
||
|
||
ftRootExtension = deviceObject->DeviceExtension;
|
||
ftRootExtension->DeviceObject = deviceObject;
|
||
ftRootExtension->ObjectUnion.FtDriverObject = DriverObject;
|
||
ftRootExtension->Type = FtRoot;
|
||
IoRegisterShutdownNotification(deviceObject);
|
||
|
||
//
|
||
// Initialize ftRootExtension flags.
|
||
//
|
||
|
||
ftRootExtension->Flags = 0;
|
||
|
||
DebugPrint((1,
|
||
"FtDiskInitialize: FtRootExtension flags address %x\n",
|
||
&ftRootExtension->Flags));
|
||
|
||
//
|
||
// 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,
|
||
FtDiskFindDisks,
|
||
deviceObject);
|
||
ExFreePool(freePoolAddress);
|
||
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
|
||
|
||
--*/
|
||
|
||
{
|
||
#if DBG
|
||
FtpInitializeIrpLog();
|
||
#endif
|
||
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;
|
||
FtpCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
return STATUS_SUCCESS;
|
||
} // end FtDiskCreate()
|
||
|
||
|
||
NTSTATUS
|
||
FtDiskIoCompletion(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is currently not used. It is here for future work when
|
||
FtDisk supports performance data collection for disks.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - FT device object.
|
||
Irp - the completed IRP.
|
||
Context - the FT deviceExtension for the IRP.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) Context;
|
||
|
||
UNREFERENCED_PARAMETER(DeviceObject);
|
||
|
||
DebugPrint((5, "FtDiskIoCompletion: entered\n"));
|
||
|
||
//
|
||
// Decrement queue length.
|
||
//
|
||
|
||
DECREMENT_QUEUELENGTH(deviceExtension);
|
||
|
||
FtpCompleteRequest(Irp, IO_DISK_INCREMENT);
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
FtMarkPendingCompletionRoutine(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is temporary (hopefully). The Io subsystem changed to require a mark
|
||
irp pending call for every Irp stack.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - FT device object.
|
||
Irp - the completed IRP.
|
||
Context - the FT deviceExtension for the IRP.
|
||
|
||
ReturnValue:
|
||
|
||
Status from the Irp.
|
||
|
||
--*/
|
||
|
||
{
|
||
if (Irp->PendingReturned) {
|
||
IoMarkIrpPending(Irp);
|
||
}
|
||
return Irp->IoStatus.Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
FtDiskReadWrite(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the driver entry point for read and write requests
|
||
to FtDisk disks. This routine determines if mirroring or striping
|
||
is a part of the IRP by checking the device extension type and
|
||
forwards the request to the appropriate handler for mirrors or stripes.
|
||
If the IRP is for a partition that is not a part of a mirror or stripe,
|
||
this routine updates the IRP by copying the IrpStack for the next
|
||
driver and calling the lower layer driver.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Context for the activity.
|
||
Irp - The device control argument block.
|
||
|
||
Return Value:
|
||
|
||
The status of the Irp.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
||
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
||
PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
|
||
|
||
if (deviceExtension->Type != NotAnFtMember) {
|
||
#if DBG
|
||
extern ULONG FtRecordIrps;
|
||
|
||
if (FtRecordIrps) {
|
||
FtpRecordIrp(Irp);
|
||
}
|
||
#endif
|
||
//
|
||
// Is I/O to the volume allowed?
|
||
//
|
||
|
||
if (deviceExtension->VolumeState == FtDisabled) {
|
||
|
||
//
|
||
// There is something wrong with the FT volume - typically
|
||
// too many missing members.
|
||
//
|
||
|
||
Irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
|
||
FtpCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
return STATUS_NO_SUCH_DEVICE;
|
||
}
|
||
|
||
switch (deviceExtension->Type) {
|
||
|
||
case Mirror:
|
||
|
||
return MirrorReadWrite(DeviceObject, Irp);
|
||
break;
|
||
|
||
case Stripe:
|
||
|
||
return StripeDispatch(DeviceObject, Irp);
|
||
break;
|
||
|
||
case StripeWithParity:
|
||
|
||
return StripeDispatch(DeviceObject, Irp);
|
||
break;
|
||
|
||
case VolumeSet:
|
||
|
||
return VolumeSetReadWrite(DeviceObject, Irp);
|
||
break;
|
||
|
||
case WholeDisk:
|
||
|
||
//
|
||
// Check if this is a write request.
|
||
//
|
||
|
||
if (currentIrpStack->MajorFunction == IRP_MJ_WRITE) {
|
||
|
||
PDEVICE_EXTENSION memberExtension =
|
||
deviceExtension->ProtectChain;
|
||
LARGE_INTEGER ioStart;
|
||
LARGE_INTEGER ioEnd;
|
||
|
||
ioStart = currentIrpStack->Parameters.Write.ByteOffset;
|
||
ioEnd.QuadPart = currentIrpStack->Parameters.Write.Length +
|
||
currentIrpStack->Parameters.Write.ByteOffset.QuadPart;
|
||
|
||
//
|
||
// Walk through the protect list.
|
||
//
|
||
|
||
while (memberExtension) {
|
||
|
||
//
|
||
// Check to see if IO falls within the boundaries
|
||
// of this partition.
|
||
//
|
||
|
||
LARGE_INTEGER partitionStart =
|
||
memberExtension->FtUnion.Identity.PartitionOffset;
|
||
LARGE_INTEGER partitionEnd =
|
||
memberExtension->FtUnion.Identity.PartitionEnd;
|
||
|
||
//
|
||
// If the current I/O offset is greater than the start
|
||
// of this member partition and less than the end of the
|
||
// partition, then this I/O starts within the member.
|
||
//
|
||
|
||
if (partitionStart.QuadPart < ioStart.QuadPart &&
|
||
partitionEnd.QuadPart > ioStart.QuadPart) {
|
||
|
||
//
|
||
// I/O starts in this partition.
|
||
//
|
||
|
||
if (ioEnd.QuadPart > partitionEnd.QuadPart) {
|
||
|
||
//
|
||
// this I/O starts within the member, but extends
|
||
// beyond this members partition.
|
||
// It is not allowed.
|
||
//
|
||
|
||
DebugPrint((1,
|
||
"FtReadWrite:" // no comma
|
||
" Wholedisk Irp %x crosses end DE %x\n",
|
||
Irp,
|
||
memberExtension));
|
||
ASSERT(0);
|
||
|
||
return STATUS_IO_PRIVILEGE_FAILED;
|
||
}
|
||
|
||
//
|
||
// The complete I/O is within this member. Allow the
|
||
// member handling routines to perform the I/O.
|
||
//
|
||
// NOTE: Either the IRP Must be adjusted or the
|
||
// WholeDisk extension passed with the expectation
|
||
// the mirror/stripe code will handle the I/O. For
|
||
// now the WholeDisk extension is passed.
|
||
//
|
||
|
||
switch (memberExtension->Type) {
|
||
|
||
case Mirror:
|
||
|
||
return MirrorReadWrite(DeviceObject, Irp);
|
||
|
||
case Stripe:
|
||
case StripeWithParity:
|
||
|
||
return StripeDispatch(DeviceObject, Irp);
|
||
|
||
case VolumeSet:
|
||
|
||
return VolumeSetReadWrite(DeviceObject, Irp);
|
||
}
|
||
}
|
||
|
||
//
|
||
// If the current I/O end is greater than the start
|
||
// of this member partition and less than the end of the
|
||
// member, then this I/O ends within the member.
|
||
//
|
||
|
||
if (ioEnd.QuadPart > partitionStart.QuadPart &&
|
||
ioEnd.QuadPart < partitionEnd.QuadPart) {
|
||
|
||
//
|
||
// This I/O operation does not start within the member,
|
||
// but ends within the member. It is not allowed.
|
||
//
|
||
|
||
DebugPrint((1,
|
||
"FtDiskReadWrite:" // no comma
|
||
" Wholedisk Irp %x crosses start DE %x\n",
|
||
Irp,
|
||
memberExtension));
|
||
|
||
ASSERT(0);
|
||
|
||
return STATUS_IO_PRIVILEGE_FAILED;
|
||
}
|
||
|
||
//
|
||
// If the current I/O start is less than the start of this
|
||
// member and the end is greater than the end of this member
|
||
// then this I/O spans the member.
|
||
//
|
||
|
||
if (ioStart.QuadPart < partitionStart.QuadPart &&
|
||
ioEnd.QuadPart > partitionEnd.QuadPart) {
|
||
|
||
//
|
||
// This I/O spans the member and is not allowed.
|
||
//
|
||
|
||
DebugPrint((1,
|
||
"FtDiskReadWrite: Wholedisk Irp %x spans DE %x\n",
|
||
Irp,
|
||
memberExtension));
|
||
|
||
ASSERT(0);
|
||
|
||
return STATUS_IO_PRIVILEGE_FAILED;
|
||
}
|
||
|
||
//
|
||
// Get next member in list.
|
||
//
|
||
|
||
memberExtension = memberExtension->ProtectChain;
|
||
|
||
} // end while (memberExtension)
|
||
|
||
} // end if (currentIrpStack->MajorFunction == IRP_MJ_WRITE)
|
||
|
||
break;
|
||
|
||
default:
|
||
|
||
//
|
||
// This is an indication of corruption.
|
||
//
|
||
|
||
DebugPrint((1,
|
||
"FtDiskReadWrite: Unknown Type %x\n",
|
||
deviceExtension->Type));
|
||
ASSERT(0);
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Update queue length.
|
||
//
|
||
|
||
INCREMENT_QUEUELENGTH(deviceExtension);
|
||
|
||
//
|
||
// Set current stack back one.
|
||
//
|
||
|
||
Irp->CurrentLocation++,
|
||
Irp->Tail.Overlay.CurrentStackLocation++;
|
||
|
||
return IoCallDriver(deviceExtension->TargetObject, Irp);
|
||
|
||
} // end FtDiskReadWrite()
|
||
|
||
VOID
|
||
FtpSectorSize(
|
||
IN PDEVICE_EXTENSION DeviceExtension,
|
||
IN PULONG ResultSectorSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Given the beginning of a device extension, take the greatest
|
||
sector size from all of the components as the sector size.
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - Base of the FT volume.
|
||
ResultSectorSize - The resulting sector size.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_EXTENSION currentExtension;
|
||
|
||
*ResultSectorSize = 0;
|
||
|
||
currentExtension = DeviceExtension;
|
||
while (currentExtension != NULL) {
|
||
|
||
if (currentExtension->FtUnion.Identity.DiskGeometry.BytesPerSector >
|
||
*ResultSectorSize) {
|
||
|
||
*ResultSectorSize =
|
||
currentExtension->FtUnion.Identity.DiskGeometry.BytesPerSector;
|
||
}
|
||
|
||
currentExtension = currentExtension->NextMember;
|
||
}
|
||
|
||
// Let's be a bit careful here, just in case.
|
||
|
||
switch (*ResultSectorSize) {
|
||
case 512:
|
||
case 1024:
|
||
case 2048:
|
||
case 4096:
|
||
break;
|
||
|
||
default:
|
||
*ResultSectorSize = 512;
|
||
break;
|
||
}
|
||
}
|
||
|
||
NTSTATUS
|
||
FtDiskDeviceControl(
|
||
PDEVICE_OBJECT DeviceObject,
|
||
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 deviceExtension = DeviceObject->DeviceExtension;
|
||
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
||
PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
PPARTITION_INFORMATION outputBuffer;
|
||
HANDLE threadHandle;
|
||
|
||
if (deviceExtension->Type != FtRoot) {
|
||
|
||
switch (currentIrpStack->Parameters.DeviceIoControl.IoControlCode) {
|
||
|
||
case IOCTL_DISK_VERIFY:
|
||
|
||
DebugPrint((3,
|
||
"FtDiskDeviceControl: IOCTL_DISK_VERIFY" // no comma
|
||
" - deviceExtension 0x%x\n",
|
||
deviceExtension));
|
||
|
||
//
|
||
// Is I/O to the volume allowed?
|
||
//
|
||
|
||
if (deviceExtension->VolumeState == FtDisabled) {
|
||
|
||
//
|
||
// There is something wrong with the FT volume - typically
|
||
// too many missing members.
|
||
//
|
||
|
||
Irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
|
||
FtpCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
return STATUS_NO_SUCH_DEVICE;
|
||
}
|
||
|
||
//
|
||
// Routine IRP to handler for this NTFT type.
|
||
//
|
||
|
||
switch (deviceExtension->Type) {
|
||
|
||
case Mirror:
|
||
return MirrorVerify(DeviceObject, Irp);
|
||
|
||
case VolumeSet:
|
||
return VolumeSetVerify(DeviceObject, Irp);
|
||
|
||
case StripeWithParity:
|
||
case Stripe:
|
||
return StripeDispatch(DeviceObject, Irp);
|
||
|
||
} // end switch()
|
||
|
||
break;
|
||
|
||
case IOCTL_DISK_REASSIGN_BLOCKS:
|
||
|
||
DebugPrint((3,
|
||
"FtDiskDeviceControl: IOCTL_DISK_REASSIGN_BLOCKS" // no comma
|
||
" - deviceExtension 0x%x\n",
|
||
deviceExtension));
|
||
|
||
//
|
||
// Tough one... for mirrors or stripes, where is this intended
|
||
// to be executed?
|
||
//
|
||
|
||
break;
|
||
|
||
case IOCTL_DISK_SET_DRIVE_LAYOUT: {
|
||
|
||
PIRP newIrp;
|
||
IO_STATUS_BLOCK ioStatusBlock;
|
||
KEVENT event;
|
||
CCHAR boost;
|
||
PDRIVE_LAYOUT_INFORMATION driveLayout =
|
||
(PDRIVE_LAYOUT_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
DebugPrint((3,
|
||
"FtDiskDeviceControl: IOCTL_DISK_SET_DRIVE_LAYOUT" // no comma
|
||
" - deviceExtension 0x%x\n",
|
||
deviceExtension));
|
||
|
||
//
|
||
// 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,
|
||
deviceExtension->TargetObject,
|
||
driveLayout,
|
||
currentIrpStack->Parameters.DeviceIoControl.InputBufferLength,
|
||
driveLayout,
|
||
currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength,
|
||
FALSE,
|
||
&event,
|
||
&ioStatusBlock);
|
||
|
||
status = IoCallDriver(deviceExtension->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 (!deviceExtension->FtUnion.Identity.Signature) {
|
||
deviceExtension->FtUnion.Identity.Signature = driveLayout->Signature;
|
||
}
|
||
|
||
//
|
||
// Process the new partition table. The work for the
|
||
// set drive layout was done synchronously because this
|
||
// routine performs synchronous activities.
|
||
//
|
||
|
||
FtpDiskSetDriveLayout(DeviceObject, Irp);
|
||
boost = IO_DISK_INCREMENT;
|
||
} else {
|
||
boost = IO_NO_INCREMENT;
|
||
}
|
||
FtpCompleteRequest(Irp, boost);
|
||
return status;
|
||
}
|
||
|
||
case IOCTL_DISK_SET_PARTITION_INFO:
|
||
|
||
//
|
||
// Check to see if this is a logical volume. If so, then
|
||
// the lower level driver will not tell the caller the correct
|
||
// size for the volume. It must be handled here.
|
||
//
|
||
|
||
DebugPrint((2,"FtDiskDeviceControl: Set partition info\n"));
|
||
|
||
switch (deviceExtension->Type) {
|
||
case Stripe:
|
||
case VolumeSet:
|
||
case StripeWithParity:
|
||
case Mirror:
|
||
|
||
{
|
||
PSET_PARTITION_INFORMATION outputBuffer =
|
||
(PSET_PARTITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
//
|
||
// Set high bit of partition type to indicate
|
||
// NTFT logical volume.
|
||
//
|
||
|
||
DebugPrint((2,
|
||
"FtDiskDeviceControl: Set high bit of SystemID %x\n",
|
||
outputBuffer->PartitionType));
|
||
|
||
outputBuffer->PartitionType |= 0x80;
|
||
|
||
//
|
||
// Update FTDISK structures.
|
||
//
|
||
|
||
deviceExtension->FtUnion.Identity.PartitionType =
|
||
outputBuffer->PartitionType;
|
||
|
||
break;
|
||
}
|
||
|
||
default:
|
||
|
||
break;
|
||
|
||
} // end switch()
|
||
|
||
//
|
||
// Send request on to the physical driver.
|
||
//
|
||
|
||
break;
|
||
|
||
case IOCTL_DISK_GET_PARTITION_INFO:
|
||
|
||
//
|
||
// Check to see if this is a logical volume. If so, then
|
||
// the lower level driver will not tell the caller the correct
|
||
// size for the volume. It must be handled here.
|
||
//
|
||
|
||
DebugPrint((3,"FtDiskDeviceControl: Get partition info\n"));
|
||
|
||
switch (deviceExtension->Type) {
|
||
case Stripe:
|
||
case VolumeSet:
|
||
case StripeWithParity:
|
||
|
||
if (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
||
sizeof(PARTITION_INFORMATION)) {
|
||
|
||
DebugPrint((3, "FtDiskDeviceControl: Buffer length %lx\n",
|
||
currentIrpStack->
|
||
Parameters.DeviceIoControl.OutputBufferLength));
|
||
|
||
status = STATUS_BUFFER_TOO_SMALL;
|
||
|
||
} else {
|
||
|
||
outputBuffer =
|
||
(PPARTITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
|
||
outputBuffer->PartitionType =
|
||
deviceExtension->FtUnion.Identity.PartitionType;
|
||
outputBuffer->StartingOffset =
|
||
deviceExtension->FtUnion.Identity.PartitionOffset;
|
||
outputBuffer->HiddenSectors =
|
||
deviceExtension->FtUnion.Identity.HiddenSectors;
|
||
outputBuffer->PartitionNumber =
|
||
deviceExtension->FtUnion.Identity.PartitionNumber;
|
||
|
||
//
|
||
// Calculate length of the volume.
|
||
//
|
||
|
||
FtpVolumeLength(deviceExtension,
|
||
&outputBuffer->PartitionLength);
|
||
|
||
DebugPrint((3,
|
||
"FtDiskDeviceControl: VolLength=%x:%x Hidden=%x Type=%x\n",
|
||
outputBuffer->PartitionLength.HighPart,
|
||
outputBuffer->PartitionLength.LowPart,
|
||
outputBuffer->HiddenSectors,
|
||
outputBuffer->PartitionType));
|
||
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
|
||
Irp->IoStatus.Status = status;
|
||
Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION);
|
||
FtpCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
return status;
|
||
|
||
default:
|
||
#if 0
|
||
//
|
||
// If this partition was a member of a mirror, special
|
||
// case the size value to reflect the original mirror
|
||
// member size.
|
||
//
|
||
|
||
if (deviceExtension->FtUnion.Identity.OriginalLength.QuadPart > 0) {
|
||
outputBuffer =
|
||
(PPARTITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
|
||
outputBuffer->PartitionType =
|
||
deviceExtension->FtUnion.Identity.PartitionType;
|
||
outputBuffer->StartingOffset =
|
||
deviceExtension->FtUnion.Identity.PartitionOffset;
|
||
outputBuffer->HiddenSectors =
|
||
deviceExtension->FtUnion.Identity.OriginalHidden;
|
||
outputBuffer->PartitionLength =
|
||
deviceExtension->FtUnion.Identity.OriginalLength;
|
||
DebugPrint((2,
|
||
"FtDiskDeviceControl: FakeLength=%x:%x Hidden=%x Type=%x\n",
|
||
outputBuffer->PartitionLength.HighPart,
|
||
outputBuffer->PartitionLength.LowPart,
|
||
outputBuffer->HiddenSectors,
|
||
outputBuffer->HiddenSectors,
|
||
outputBuffer->PartitionType));
|
||
|
||
//
|
||
// Return this faked information.
|
||
//
|
||
|
||
Irp->IoStatus.Status = status;
|
||
Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION);
|
||
FtpCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
return status;
|
||
}
|
||
#endif
|
||
break;
|
||
}
|
||
break;
|
||
|
||
case IOCTL_DISK_GET_DRIVE_GEOMETRY:
|
||
|
||
DebugPrint((3,"FtDiskDeviceControl: Get drive geometry\n"));
|
||
|
||
switch (deviceExtension->Type) {
|
||
|
||
case Stripe:
|
||
case VolumeSet:
|
||
case StripeWithParity:
|
||
|
||
if (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
||
sizeof(DISK_GEOMETRY)) {
|
||
|
||
Irp->IoStatus.Information = 0;
|
||
status = STATUS_BUFFER_TOO_SMALL;
|
||
|
||
} else {
|
||
|
||
PDISK_GEOMETRY diskGeometry = Irp->AssociatedIrp.SystemBuffer;
|
||
LARGE_INTEGER volumeLength;
|
||
ULONG sectorSize;
|
||
|
||
//
|
||
// Calculate length of the volume.
|
||
//
|
||
|
||
FtpVolumeLength(deviceExtension,
|
||
&volumeLength);
|
||
|
||
FtpSectorSize(deviceExtension, §orSize);
|
||
|
||
//
|
||
// Fill in geometry structure.
|
||
// Calculate cylinders as volume length in bytes
|
||
// divided by sector size, track size and number of heads.
|
||
//
|
||
|
||
diskGeometry->MediaType = FixedMedia;
|
||
diskGeometry->Cylinders.QuadPart = volumeLength.QuadPart >> 20;
|
||
diskGeometry->TracksPerCylinder = 32;
|
||
diskGeometry->BytesPerSector = sectorSize;
|
||
diskGeometry->SectorsPerTrack = 32768/diskGeometry->BytesPerSector;
|
||
|
||
//
|
||
// Return bytes transferred and status.
|
||
//
|
||
|
||
Irp->IoStatus.Information = sizeof(DISK_GEOMETRY);
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
|
||
Irp->IoStatus.Status = status;
|
||
FtpCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
return status;
|
||
|
||
} // end switch (deviceExtension->Type)
|
||
|
||
break;
|
||
|
||
case IOCTL_DISK_FIND_NEW_DEVICES:
|
||
|
||
//
|
||
// Copy current stack to next stack.
|
||
//
|
||
|
||
*nextIrpStack = *currentIrpStack;
|
||
|
||
//
|
||
// Ask to be called back during request completion.
|
||
//
|
||
|
||
IoSetCompletionRoutine(Irp,
|
||
FtNewDiskCompletion,
|
||
Irp,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
|
||
//
|
||
// Call target driver.
|
||
//
|
||
|
||
return IoCallDriver(deviceExtension->TargetObject, Irp);
|
||
|
||
case FT_SECONDARY_READ:
|
||
case FT_PRIMARY_READ:
|
||
|
||
if (deviceExtension->MemberRole != 0) {
|
||
|
||
//
|
||
// This request is not on the zero member. Therefore
|
||
// the zero is missing and there is only one copy of
|
||
// the data.
|
||
//
|
||
|
||
status = STATUS_INVALID_DEVICE_REQUEST;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Stuff device extension address in next stack.
|
||
// Note: This is not the FtRootExtension.
|
||
//
|
||
|
||
nextIrpStack->FtRootExtensionPtr = (ULONG)deviceExtension;
|
||
|
||
//
|
||
// Zero thread handle.
|
||
//
|
||
|
||
threadHandle = 0;
|
||
|
||
if (deviceExtension->Type == Mirror) {
|
||
|
||
status = PsCreateSystemThread(&threadHandle,
|
||
(ACCESS_MASK) 0L,
|
||
(POBJECT_ATTRIBUTES) NULL,
|
||
(HANDLE) 0L,
|
||
(PCLIENT_ID) NULL,
|
||
(PKSTART_ROUTINE) MirrorSpecialRead,
|
||
(PVOID) Irp);
|
||
|
||
} else if (deviceExtension->Type == StripeWithParity) {
|
||
|
||
status = PsCreateSystemThread(&threadHandle,
|
||
(ACCESS_MASK) 0L,
|
||
(POBJECT_ATTRIBUTES) NULL,
|
||
(HANDLE) 0L,
|
||
(PCLIENT_ID) NULL,
|
||
(PKSTART_ROUTINE) StripeSpecialRead,
|
||
(PVOID) Irp);
|
||
|
||
} else {
|
||
|
||
//
|
||
// Request only valid SWP and mirror sets.
|
||
//
|
||
|
||
status = STATUS_INVALID_DEVICE_REQUEST;
|
||
}
|
||
}
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
Irp->IoStatus.Status = status;
|
||
FtpCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
return status;
|
||
|
||
} else {
|
||
|
||
if (threadHandle) {
|
||
ZwClose(threadHandle);
|
||
}
|
||
|
||
return STATUS_PENDING;
|
||
}
|
||
|
||
|
||
case FT_SYNC_REDUNDANT_COPY:
|
||
|
||
switch (deviceExtension->Type) {
|
||
|
||
case Mirror:
|
||
case StripeWithParity:
|
||
|
||
if ((deviceExtension->MemberRole != 0) ||
|
||
(deviceExtension->VolumeState != FtStateOk)) {
|
||
|
||
//
|
||
// Don't attempt synchronization on anything but the
|
||
// zeroth member of an FT set and on sets that are
|
||
// healthy.
|
||
//
|
||
|
||
status = STATUS_INVALID_PARAMETER;
|
||
break;
|
||
}
|
||
|
||
status = FtThreadStartNewThread(deviceExtension,
|
||
FT_SYNC_REDUNDANT_COPY,
|
||
Irp);
|
||
|
||
break;
|
||
|
||
default:
|
||
|
||
status = STATUS_INVALID_DEVICE_REQUEST;
|
||
}
|
||
|
||
//
|
||
// The filesystems use this device control at DPC level
|
||
// so the IRP must be returned without blocking.
|
||
//
|
||
|
||
FtpCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
return status;
|
||
|
||
case FT_BALANCED_READ_MODE:
|
||
|
||
switch (deviceExtension->Type) {
|
||
|
||
case Mirror:
|
||
case StripeWithParity:
|
||
|
||
//
|
||
// Set balanced read mode bit in flags.
|
||
//
|
||
|
||
deviceExtension->ReadPolicy = ReadBoth;
|
||
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
FtpCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
return status;
|
||
break;
|
||
}
|
||
DebugPrint((2, "FtDiskDeviceControl: Enable balanced reads\n"));
|
||
break;
|
||
|
||
case FT_SEQUENTIAL_WRITE_MODE:
|
||
case FT_PARALLEL_WRITE_MODE:
|
||
switch (deviceExtension->Type) {
|
||
|
||
case Mirror:
|
||
case StripeWithParity:
|
||
|
||
if (currentIrpStack->Parameters.DeviceIoControl.IoControlCode
|
||
== FT_SEQUENTIAL_WRITE_MODE) {
|
||
deviceExtension->WritePolicy = Sequential;
|
||
} else {
|
||
deviceExtension->WritePolicy = Parallel;
|
||
}
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
FtpCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
return status;
|
||
break;
|
||
}
|
||
|
||
case FT_QUERY_SET_STATE: {
|
||
PFT_SET_INFORMATION setInformation = Irp->AssociatedIrp.SystemBuffer;
|
||
PDEVICE_EXTENSION memberExtension = deviceExtension->ZeroMember;
|
||
ULONG count = 0;
|
||
|
||
if (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
||
sizeof(FT_SET_INFORMATION)) {
|
||
|
||
Irp->IoStatus.Information = 0;
|
||
status = STATUS_BUFFER_TOO_SMALL;
|
||
} else {
|
||
if (deviceExtension->Type == NotAnFtMember) {
|
||
setInformation->Type = NotAnFtMember;
|
||
setInformation->SetState = FtStateOk;
|
||
setInformation->NumberOfMembers = 1;
|
||
} else {
|
||
setInformation->Type = memberExtension->Type;
|
||
setInformation->SetState = memberExtension->VolumeState;
|
||
while (memberExtension) {
|
||
count++;
|
||
memberExtension = memberExtension->NextMember;
|
||
}
|
||
setInformation->NumberOfMembers = count;
|
||
}
|
||
Irp->IoStatus.Information = sizeof(FT_SET_INFORMATION);
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
Irp->IoStatus.Status = status;
|
||
FtpCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
return status;
|
||
break;
|
||
}
|
||
|
||
} // end switch (currentIrpStack->Parameters.DeviceIoControl.IoControlCode)
|
||
|
||
//
|
||
// Pass this device control through.
|
||
//
|
||
|
||
DebugPrint((4, "FtDiskDeviceControl: pass through %x\n",
|
||
currentIrpStack->Parameters.DeviceIoControl.IoControlCode));
|
||
|
||
//
|
||
// Set current stack back one.
|
||
//
|
||
|
||
Irp->CurrentLocation++,
|
||
Irp->Tail.Overlay.CurrentStackLocation++;
|
||
|
||
//
|
||
// Call target driver.
|
||
//
|
||
|
||
return IoCallDriver(deviceExtension->TargetObject, Irp);
|
||
|
||
}
|
||
|
||
if (currentIrpStack->Parameters.DeviceIoControl.IoControlCode == FT_VERIFY) {
|
||
|
||
DebugPrint((1,
|
||
"FtDiskDeviceControl: IOCTL FT_VERIFY called\n"));
|
||
|
||
if (deviceExtension->Type == Mirror) {
|
||
|
||
//
|
||
// Compare the two components of a mirror.
|
||
// Return on completion or when a difference is found.
|
||
//
|
||
|
||
FtThreadVerifyMirror(deviceExtension);
|
||
status = STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
status = FtThreadVerifyStripe(deviceExtension,
|
||
Irp);
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Spawn thread to kick off synchronization task.
|
||
//
|
||
|
||
status =
|
||
FtThreadStartNewThread(deviceExtension,
|
||
currentIrpStack->Parameters.DeviceIoControl.IoControlCode,
|
||
Irp);
|
||
}
|
||
|
||
if (status == STATUS_PENDING) {
|
||
return status;
|
||
}
|
||
|
||
Irp->IoStatus.Status = status;
|
||
|
||
if (!NT_SUCCESS(status) && IoIsErrorUserInduced(status)) {
|
||
IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
|
||
}
|
||
|
||
FtpCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
return status;
|
||
|
||
} // end FtDiskDeviceControl()
|
||
|
||
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.
|
||
NOTE: This routine should switch on FTTYPE and send shutdown or flush
|
||
messages to each member partition.
|
||
|
||
Arguments:
|
||
|
||
DriverObject - Pointer to device object to being shutdown by system.
|
||
Irp - IRP involved.
|
||
|
||
Return Value:
|
||
|
||
NT Status
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
||
PIRP irpToSend;
|
||
PVOID freePoolAddress;
|
||
PDISK_CONFIG_HEADER registry;
|
||
NTSTATUS status;
|
||
PKEVENT eventPtr;
|
||
|
||
//
|
||
// Determine if this is the FtRootExtension.
|
||
//
|
||
|
||
if (deviceExtension->ObjectUnion.FtDriverObject == DeviceObject->DriverObject) {
|
||
|
||
//
|
||
// 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;
|
||
FtpCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
if (deviceExtension->Type == NotAnFtMember) {
|
||
|
||
//
|
||
// Set current stack back one.
|
||
//
|
||
|
||
Irp->CurrentLocation++,
|
||
Irp->Tail.Overlay.CurrentStackLocation++;
|
||
|
||
return IoCallDriver(deviceExtension->TargetObject, Irp);
|
||
}
|
||
|
||
//
|
||
// Because we may have write cache enabled drives below, we need to
|
||
// send each request (to the separate drives) synchronously. We need
|
||
// to do this since the lower levels expect this to happen synchronously.
|
||
//
|
||
// First get an event. If we can do that then loop over all the device
|
||
// extensions in the chain, allocating an appropriately sized irp (since
|
||
// not all drives may be using the same sized stack). Send the request
|
||
// down, and wait for it to complete (we don't really care about the request
|
||
// completion status since there is no recovery this late in the game).
|
||
//
|
||
// Allocate the event.
|
||
//
|
||
|
||
eventPtr = ExAllocatePool(NonPagedPool,sizeof(KEVENT));
|
||
|
||
if (eventPtr) {
|
||
|
||
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
||
PDEVICE_EXTENSION currentExtension = deviceExtension->ZeroMember;
|
||
|
||
|
||
//
|
||
// Control while loop by device extensions
|
||
//
|
||
|
||
while (currentExtension) {
|
||
|
||
PIRP currentSyncIrp;
|
||
IO_STATUS_BLOCK ioStatus;
|
||
|
||
if (IsMemberAnOrphan(currentExtension)) {
|
||
|
||
currentExtension = currentExtension->NextMember;
|
||
continue;
|
||
|
||
}
|
||
|
||
KeInitializeEvent(eventPtr,NotificationEvent,FALSE);
|
||
currentSyncIrp = IoBuildSynchronousFsdRequest( irpSp->MajorFunction,
|
||
currentExtension->TargetObject,
|
||
NULL,
|
||
0,
|
||
NULL,
|
||
eventPtr,
|
||
&ioStatus );
|
||
|
||
if (currentSyncIrp) {
|
||
|
||
if (IoCallDriver(currentExtension->TargetObject,currentSyncIrp)
|
||
== STATUS_PENDING) {
|
||
(VOID) KeWaitForSingleObject( eventPtr,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
(PLARGE_INTEGER) NULL);
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// All done with this disk stack. On to the next.
|
||
//
|
||
|
||
currentExtension = currentExtension->NextMember;
|
||
|
||
}
|
||
|
||
ExFreePool(eventPtr);
|
||
|
||
}
|
||
|
||
//
|
||
// Complete this request.
|
||
//
|
||
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
FtpCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
} // end FtDiskShutdownFlush()
|
||
|
||
|
||
VOID
|
||
FtpPrepareDisk(
|
||
PDRIVER_OBJECT DriverObject,
|
||
PDEVICE_OBJECT FtRootDevice,
|
||
PDEVICE_EXTENSION WholeDevice,
|
||
ULONG DiskNumber
|
||
)
|
||
|
||
/*++
|
||
|
||
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
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_EXTENSION ftRootExtension = FtRootDevice->DeviceExtension;
|
||
PDEVICE_OBJECT deviceObject;
|
||
PDEVICE_EXTENSION deviceExtension;
|
||
UCHAR ntDeviceName[64];
|
||
UCHAR ntAttachName[64];
|
||
STRING ntString;
|
||
UNICODE_STRING ntUnicodeString;
|
||
PFILE_OBJECT fileObject;
|
||
NTSTATUS status;
|
||
ULONG partitionNumber;
|
||
ULONG partitionEntry;
|
||
PDRIVE_LAYOUT_INFORMATION driveLayout;
|
||
PPARTITION_INFORMATION partitionInformation;
|
||
|
||
//
|
||
// Get partition information.
|
||
//
|
||
|
||
sprintf(ntDeviceName,
|
||
"\\Device\\Harddisk%d\\Partition0",
|
||
DiskNumber);
|
||
status = FtpGetPartitionInformation(ntDeviceName,
|
||
&driveLayout,
|
||
&WholeDevice->FtUnion.Identity.DiskGeometry);
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// Device control failed. Give up on this disk and try next.
|
||
//
|
||
|
||
DebugPrint((1,
|
||
"FtDiskInitialize: Couldn't get partition info for %s\n",
|
||
ntDeviceName));
|
||
return;
|
||
}
|
||
|
||
WholeDevice->FtUnion.Identity.Signature = driveLayout->Signature;
|
||
DebugPrint((3,
|
||
"FtDiskInitialize: Signature for disk %d is %x.\n",
|
||
DiskNumber,
|
||
driveLayout->Signature));
|
||
|
||
//
|
||
// 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++) {
|
||
|
||
//
|
||
// Setup the partition name string and perform the attach.
|
||
//
|
||
|
||
sprintf(ntDeviceName,
|
||
"\\Device\\Harddisk%d\\Partition%d",
|
||
DiskNumber,
|
||
partitionNumber);
|
||
|
||
RtlInitAnsiString(&ntString,
|
||
ntDeviceName);
|
||
|
||
status = RtlAnsiStringToUnicodeString(&ntUnicodeString,
|
||
&ntString,
|
||
TRUE);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Get target device object.
|
||
//
|
||
|
||
status = IoGetDeviceObjectPointer(&ntUnicodeString,
|
||
FILE_READ_ATTRIBUTES,
|
||
&fileObject,
|
||
&deviceObject);
|
||
|
||
RtlFreeUnicodeString(&ntUnicodeString);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
DebugPrint((1,
|
||
"FtpPrepareDisk: Can't get target object %s\n",
|
||
ntDeviceName));
|
||
|
||
continue;
|
||
}
|
||
|
||
ObDereferenceObject(fileObject);
|
||
|
||
//
|
||
// Check if this device is already mounted.
|
||
//
|
||
|
||
if (!deviceObject->Vpb ||
|
||
(deviceObject->Vpb->Flags & VPB_MOUNTED)) {
|
||
|
||
//
|
||
// Can't attach to a device that is already mounted.
|
||
//
|
||
|
||
DebugPrint((1,
|
||
"FtpPrepareDisk: %s already mounted\n",
|
||
ntDeviceName));
|
||
|
||
continue;
|
||
}
|
||
|
||
sprintf(ntAttachName,
|
||
"\\Device\\Harddisk%d\\Ft%d",
|
||
DiskNumber,
|
||
partitionNumber);
|
||
status = FtpAttach(DriverObject,
|
||
ntAttachName,
|
||
ntDeviceName,
|
||
&deviceExtension);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// Check if FTDISK has already attached to this partition.
|
||
//
|
||
|
||
if (status == STATUS_OBJECT_NAME_EXISTS) {
|
||
|
||
DebugPrint((1,
|
||
"FtpPrepareDisk: %s already attached\n",
|
||
ntDeviceName));
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Mark the partition and locate the partition information.
|
||
//
|
||
|
||
partitionInformation = &driveLayout->PartitionEntry[partitionEntry];
|
||
deviceExtension->Type = NotAnFtMember;
|
||
deviceExtension->FtCount.NumberOfMembers = 1;
|
||
deviceExtension->VolumeState = FtStateOk;
|
||
deviceExtension->NextMember = deviceExtension->ZeroMember = NULL;
|
||
|
||
//
|
||
// Save pointer to FtRoot device object.
|
||
//
|
||
|
||
deviceExtension->ObjectUnion.FtRootObject = FtRootDevice;
|
||
|
||
DebugPrint((1,
|
||
"FtpPrepareDisk: Created %s for %s\n",
|
||
ntAttachName,
|
||
ntDeviceName));
|
||
|
||
//
|
||
// Initialize FT identity fields.
|
||
//
|
||
|
||
deviceExtension->FtUnion.Identity.BusId = 0;
|
||
deviceExtension->FtUnion.Identity.PartitionType =
|
||
partitionInformation->PartitionType;
|
||
deviceExtension->FtUnion.Identity.Signature = driveLayout->Signature;
|
||
deviceExtension->FtUnion.Identity.DiskNumber = DiskNumber;
|
||
deviceExtension->FtUnion.Identity.DiskGeometry =
|
||
WholeDevice->FtUnion.Identity.DiskGeometry;
|
||
deviceExtension->FtUnion.Identity.PartitionNumber = partitionNumber;
|
||
deviceExtension->FtUnion.Identity.PartitionOffset =
|
||
partitionInformation->StartingOffset;
|
||
deviceExtension->FtUnion.Identity.HiddenSectors =
|
||
partitionInformation->HiddenSectors;
|
||
deviceExtension->FtUnion.Identity.PartitionLength =
|
||
partitionInformation->PartitionLength;
|
||
deviceExtension->FtUnion.Identity.PartitionEnd.QuadPart =
|
||
deviceExtension->FtUnion.Identity.PartitionOffset.QuadPart +
|
||
partitionInformation->PartitionLength.QuadPart;
|
||
|
||
//
|
||
// Point back to whole disk.
|
||
//
|
||
|
||
deviceExtension->WholeDisk = WholeDevice;
|
||
|
||
//
|
||
// No disk chains from a partition and no RegenerateRegion is valid.
|
||
//
|
||
|
||
deviceExtension->DiskChain = NULL;
|
||
|
||
//
|
||
// Link partition onto protect list for this whole disk.
|
||
//
|
||
|
||
deviceExtension->ProtectChain = WholeDevice->ProtectChain;
|
||
WholeDevice->ProtectChain = deviceExtension;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Failed attach, assume there are not valid partitions
|
||
// left on this device.
|
||
//
|
||
|
||
DebugPrint((1,
|
||
"FtpPrepareDisk: Couldn't attach to %s(%x)\n",
|
||
ntDeviceName,
|
||
status));
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Free drive layout structure for this drive.
|
||
//
|
||
|
||
ExFreePool(driveLayout);
|
||
return;
|
||
|
||
} //end FtpPrepareDisk()
|
||
|
||
|
||
NTSTATUS
|
||
FtpDiskSetDriveLayout(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called after an IOCTL to set drive layout completes.
|
||
It attempts to attach to each partition in the system. If it fails
|
||
then it is assumed that ftdisk has already attached.
|
||
|
||
After all partitions are attached a pass is made on all disk
|
||
partitions to obtain the partition information and determine if
|
||
existing partitions were modified by the IOCTL.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Pointer to device object for the disk just changed.
|
||
Irp - IRP involved.
|
||
|
||
Return Value:
|
||
|
||
NT Status
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_EXTENSION physicalExtension = DeviceObject->DeviceExtension;
|
||
PDEVICE_OBJECT targetObject;
|
||
PDEVICE_EXTENSION deviceExtension;
|
||
UCHAR ntDeviceName[64];
|
||
UCHAR ntAttachName[64];
|
||
STRING ntString;
|
||
UNICODE_STRING ntUnicodeString;
|
||
PFILE_OBJECT fileObject;
|
||
PARTITION_INFORMATION partitionInformation;
|
||
NTSTATUS status;
|
||
ULONG partitionNumber = 0;
|
||
PIRP irp;
|
||
IO_STATUS_BLOCK ioStatusBlock;
|
||
KEVENT event;
|
||
LARGE_INTEGER partitionOffset;
|
||
LARGE_INTEGER partitionSize;
|
||
LARGE_INTEGER newOffset;
|
||
LARGE_INTEGER newSize;
|
||
|
||
do {
|
||
|
||
//
|
||
// Get first/next partition. Assume we are already attached
|
||
// to the first disk. NOTE: If new disks arrive this isn't true.
|
||
//
|
||
|
||
partitionNumber++;
|
||
|
||
//
|
||
// Create unicode NT device name.
|
||
//
|
||
|
||
sprintf(ntDeviceName,
|
||
"\\Device\\Harddisk%d\\Partition%d",
|
||
physicalExtension->FtUnion.Identity.DiskNumber,
|
||
partitionNumber);
|
||
|
||
RtlInitAnsiString(&ntString,
|
||
ntDeviceName);
|
||
|
||
status = RtlAnsiStringToUnicodeString(&ntUnicodeString,
|
||
&ntString,
|
||
TRUE);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Get target device object.
|
||
//
|
||
|
||
status = IoGetDeviceObjectPointer(&ntUnicodeString,
|
||
FILE_READ_ATTRIBUTES,
|
||
&fileObject,
|
||
&targetObject);
|
||
|
||
RtlFreeUnicodeString(&ntUnicodeString);
|
||
|
||
//
|
||
// If this fails then it is because there is no such device
|
||
// which signals completion.
|
||
//
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Dereference file object as these are the rules.
|
||
//
|
||
|
||
ObDereferenceObject(fileObject);
|
||
|
||
//
|
||
// Check if this device is already mounted.
|
||
//
|
||
|
||
if ((!targetObject->Vpb) || (targetObject->Vpb->Flags & VPB_MOUNTED)) {
|
||
|
||
//
|
||
// Assume this device has already been attached.
|
||
//
|
||
|
||
DebugPrint((1,
|
||
"FtpDiskSetDriveLayout: %s is mounted\n",
|
||
ntDeviceName));
|
||
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Create FT attach name for this partition.
|
||
//
|
||
|
||
sprintf(ntAttachName,
|
||
"\\Device\\Harddisk%d\\Ft%d",
|
||
physicalExtension->FtUnion.Identity.DiskNumber,
|
||
partitionNumber);
|
||
|
||
status =
|
||
FtpAttach(((PDEVICE_EXTENSION)physicalExtension->ObjectUnion.FtRootObject->DeviceExtension)->ObjectUnion.FtDriverObject,
|
||
ntAttachName,
|
||
ntDeviceName,
|
||
&deviceExtension);
|
||
|
||
if ((!NT_SUCCESS(status)) || (status == STATUS_OBJECT_NAME_EXISTS)) {
|
||
|
||
//
|
||
// Assume this device is already attached.
|
||
//
|
||
|
||
DebugPrint((1,
|
||
"FtpDiskSetDriveLayout: %s already attached\n",
|
||
ntDeviceName));
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Set up new device extension. The assumption is that
|
||
// this new partition is not an FT set member until the
|
||
// reconfiguration process takes place.
|
||
//
|
||
|
||
deviceExtension->Type = NotAnFtMember;
|
||
deviceExtension->MemberRole = 0;
|
||
deviceExtension->NextMember = NULL;
|
||
deviceExtension->FtGroup = (USHORT) -1;
|
||
deviceExtension->FtCount.NumberOfMembers = 1;
|
||
deviceExtension->VolumeState = FtStateOk;
|
||
deviceExtension->FtUnion.Identity.BusId = 0;
|
||
deviceExtension->FtUnion.Identity.PartitionOffset.LowPart =
|
||
deviceExtension->FtUnion.Identity.PartitionOffset.HighPart = 0;
|
||
deviceExtension->FtUnion.Identity.PartitionLength.LowPart =
|
||
deviceExtension->FtUnion.Identity.PartitionLength.HighPart = 0;
|
||
deviceExtension->FtUnion.Identity.Signature =
|
||
physicalExtension->FtUnion.Identity.Signature;
|
||
|
||
//
|
||
// Save pointer to FtRoot device object.
|
||
//
|
||
|
||
deviceExtension->ObjectUnion.FtRootObject =
|
||
physicalExtension->ObjectUnion.FtRootObject;
|
||
|
||
DebugPrint((1,
|
||
"FtpDiskSetDriveLayout: Created %s for %s\n",
|
||
ntAttachName,
|
||
ntDeviceName));
|
||
|
||
//
|
||
// Point back to whole disk.
|
||
//
|
||
|
||
deviceExtension->WholeDisk = physicalExtension;
|
||
|
||
//
|
||
// No disk chains from a partition and no RegenerateRegion is valid.
|
||
//
|
||
|
||
deviceExtension->DiskChain = NULL;
|
||
|
||
//
|
||
// Link partition onto protect list for this whole disk.
|
||
//
|
||
|
||
deviceExtension->ProtectChain = physicalExtension->ProtectChain;
|
||
physicalExtension->ProtectChain = deviceExtension;
|
||
|
||
} while (TRUE);
|
||
|
||
deviceExtension = physicalExtension->ProtectChain;
|
||
|
||
while (deviceExtension) {
|
||
|
||
//
|
||
// Process all partitions on this disk to see if any were
|
||
// modified by the IOCTL.
|
||
//
|
||
|
||
targetObject = deviceExtension->TargetObject;
|
||
KeInitializeEvent(&event,
|
||
NotificationEvent,
|
||
FALSE);
|
||
|
||
//
|
||
// Build IRP to get partition information.
|
||
//
|
||
|
||
irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO,
|
||
targetObject,
|
||
NULL,
|
||
0,
|
||
&partitionInformation,
|
||
sizeof(PARTITION_INFORMATION),
|
||
FALSE,
|
||
&event,
|
||
&ioStatusBlock);
|
||
|
||
//
|
||
// Issue synchronous call to get partition information.
|
||
//
|
||
|
||
status = IoCallDriver(targetObject, irp);
|
||
|
||
if (status == STATUS_PENDING) {
|
||
KeWaitForSingleObject(&event,
|
||
Suspended,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL);
|
||
status = ioStatusBlock.Status;
|
||
}
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// Determine if this partition was modified by comparing
|
||
// the current offset and size with the values returned.
|
||
//
|
||
|
||
partitionOffset = deviceExtension->FtUnion.Identity.PartitionOffset;
|
||
partitionSize = deviceExtension->FtUnion.Identity.PartitionLength;
|
||
newOffset = partitionInformation.StartingOffset;
|
||
newSize = partitionInformation.PartitionLength;
|
||
|
||
if (partitionOffset.QuadPart != newOffset.QuadPart ||
|
||
partitionSize.QuadPart != newSize.QuadPart) {
|
||
|
||
//
|
||
// This partition was modified by the IOCTL.
|
||
//
|
||
|
||
deviceExtension->FtUnion.Identity.PartitionType =
|
||
partitionInformation.PartitionType;
|
||
deviceExtension->FtUnion.Identity.DiskNumber =
|
||
physicalExtension->FtUnion.Identity.DiskNumber;
|
||
deviceExtension->FtUnion.Identity.DiskGeometry =
|
||
physicalExtension->FtUnion.Identity.DiskGeometry;
|
||
deviceExtension->FtUnion.Identity.PartitionNumber = partitionNumber;
|
||
deviceExtension->FtUnion.Identity.PartitionOffset = newOffset;
|
||
deviceExtension->FtUnion.Identity.HiddenSectors =
|
||
partitionInformation.HiddenSectors;
|
||
deviceExtension->FtUnion.Identity.PartitionLength = newSize;
|
||
deviceExtension->FtUnion.Identity.PartitionEnd.QuadPart =
|
||
newOffset.QuadPart + newSize.QuadPart;
|
||
deviceExtension->Flags |= FTF_CONFIGURATION_CHANGED;
|
||
}
|
||
}
|
||
deviceExtension = deviceExtension->ProtectChain;
|
||
}
|
||
|
||
return Irp->IoStatus.Status;
|
||
|
||
} // end FtDiskSetDriveLayout()
|
||
|
||
|
||
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;
|
||
UCHAR ntDeviceName[64];
|
||
UCHAR ntAttachName[64];
|
||
PDEVICE_EXTENSION wholeDevice;
|
||
PDEVICE_EXTENSION currentExtension = NULL;
|
||
NTSTATUS status;
|
||
ULONG diskNumber;
|
||
|
||
DebugPrint((6, "FtDiskFindDisks: Entered %x\n", DriverObject));
|
||
|
||
ftRootExtension = 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->FtCount.NumberOfDisks;
|
||
diskNumber < configurationInformation->DiskCount;
|
||
diskNumber++) {
|
||
|
||
sprintf(ntDeviceName,
|
||
"\\Device\\Harddisk%d\\Partition0",
|
||
diskNumber);
|
||
sprintf(ntAttachName,
|
||
"\\Device\\Harddisk%d\\Physical0",
|
||
diskNumber);
|
||
status = FtpAttach(DriverObject,
|
||
ntAttachName,
|
||
ntDeviceName,
|
||
&wholeDevice);
|
||
|
||
if ((!NT_SUCCESS(status)) || (status == STATUS_OBJECT_NAME_EXISTS)) {
|
||
|
||
//
|
||
// If can't attach to this disk or
|
||
// already attached to this disk then go on to the next one.
|
||
//
|
||
|
||
continue;
|
||
|
||
} else {
|
||
|
||
DebugPrint((6,
|
||
"FtDiskInitialize: Created %s for %s\n",
|
||
ntAttachName,
|
||
ntDeviceName));
|
||
|
||
//
|
||
// Initialize whole disk device extension.
|
||
//
|
||
|
||
wholeDevice->Type = NotAnFtMember;
|
||
wholeDevice->FtCount.NumberOfMembers = 0;
|
||
wholeDevice->ProtectChain = NULL;
|
||
wholeDevice->ObjectUnion.FtRootObject = FtRootDevice;
|
||
wholeDevice->FtUnion.Identity.BusId = 0;
|
||
wholeDevice->FtUnion.Identity.DiskNumber = diskNumber;
|
||
wholeDevice->FtUnion.Identity.PartitionNumber = 0;
|
||
|
||
if (diskNumber == 0) {
|
||
|
||
//
|
||
// First one, link it to the root object as the start
|
||
// of the FT extension chain.
|
||
//
|
||
|
||
ftRootExtension->DiskChain = wholeDevice;
|
||
|
||
} else {
|
||
|
||
if (currentExtension == NULL) {
|
||
|
||
//
|
||
// This is the second call during initialization.
|
||
// Walk the existing chain and find the end for the
|
||
// link of this disk.
|
||
//
|
||
|
||
currentExtension = ftRootExtension->DiskChain;
|
||
while (currentExtension->DiskChain != NULL) {
|
||
currentExtension = currentExtension->DiskChain;
|
||
}
|
||
}
|
||
|
||
currentExtension->DiskChain = wholeDevice;
|
||
}
|
||
|
||
currentExtension = wholeDevice;
|
||
|
||
//
|
||
// Increment count of disks processed.
|
||
//
|
||
|
||
ftRootExtension->FtCount.NumberOfDisks++;
|
||
}
|
||
|
||
//
|
||
// Call routine to attach to all of the partitions on this disk.
|
||
//
|
||
|
||
FtpPrepareDisk(DriverObject,
|
||
FtRootDevice,
|
||
wholeDevice,
|
||
diskNumber);
|
||
|
||
}
|
||
|
||
//
|
||
// If this is the final time this routine is to be called then
|
||
// set up the FtDisk structures.
|
||
//
|
||
|
||
if (Count == 1) {
|
||
|
||
FtpConfigure(ftRootExtension,
|
||
FALSE);
|
||
}
|
||
|
||
return;
|
||
|
||
} // end FtDiskFindDisks()
|
||
|
||
|
||
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 deviceExtension =
|
||
(PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
|
||
|
||
//
|
||
// Find new disk devices and attach to disk and all of its partitions.
|
||
//
|
||
|
||
FtDiskFindDisks(DeviceObject->DriverObject,
|
||
deviceExtension->ObjectUnion.FtRootObject,
|
||
0);
|
||
|
||
return Irp->IoStatus.Status;
|
||
}
|
||
|