NT4/private/ntos/dd/ntft/volset.c
2020-09-30 17:12:29 +02:00

653 lines
18 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
volset.c
Abstract:
This module contains the code specific to volume sets for the fault
tolerance driver.
Author:
Bob Rinne (bobri) 2-Feb-1992
Mike Glass (mglass)
Environment:
kernel mode only
Notes:
Revision History:
--*/
#include "ntddk.h"
#include "ftdisk.h"
#ifdef POOL_TAGGING
#ifdef ExAllocatePool
#undef ExAllocatePool
#endif
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,' VtF')
#endif
#define FTDUP_IRP 1
//
// Short hand definitions for state information saved in the stack
// reserved for FT use for Irp's passed to lower drivers. The Irp
// and Mdl flags are set in Ftp..() routines located in ftutil.c
//
#define VolSetAllocatedSystemBuffer Parameters.Others.Argument2
BOOLEAN
VsetpFindSetLocation(
IN OUT PDEVICE_EXTENSION *DeviceExtension,
IN OUT PLARGE_INTEGER IoOffset,
IN OUT PULONG Length
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PDEVICE_EXTENSION extension = *DeviceExtension;
LARGE_INTEGER logicalEnd = extension->FtUnion.Identity.PartitionLength;
LARGE_INTEGER ioStart = *IoOffset;
LARGE_INTEGER offset = *IoOffset;
LARGE_INTEGER ioEnd;
ioEnd.QuadPart = ioStart.QuadPart + *Length;
//
// The list of partitions that comprise the volume set is searched
// to determine where the I/O belongs. This can be broken into the
// following parts:
//
// 1. The I/O ends in the current partition. Therefore is
// contained in this partition.
//
// 2. The I/O does not start in the current partition. Continue
// to the next partition.
//
// 3. The I/O starts in this partition, but did not end in this
// partition. This I/O must be broken into two parts, the first
// for the current partition and the next for the following
// partition.
//
while (extension != NULL) {
DebugPrint((5,
"VspFindSetLocation: ioStart %x:%x ioEnd %x:%x - End %x:%x\n",
ioStart.HighPart,
ioStart.LowPart,
ioEnd.HighPart,
ioEnd.LowPart,
logicalEnd.HighPart,
logicalEnd.LowPart));
//
// If the end of the I/O operation is before the end of this partition,
// then the I/O occurs here.
//
if (logicalEnd.QuadPart >= ioEnd.QuadPart) {
*DeviceExtension = extension;
*IoOffset = offset;
//
// Length is the same as that passed in.
//
return TRUE;
}
//
// If the start of the I/O is greater than the logical end of this
// partition then the I/O is in the next partition.
//
if (ioStart.QuadPart >= logicalEnd.QuadPart) {
//
// I/O does not start in this partition.
// Update the current offset for the I/O to reflect its relative
// position to the next partition.
//
offset.QuadPart -= extension->FtUnion.Identity.PartitionLength.QuadPart;
//
// Move to the next member of the volume set. If it is null
// the while loop will exit and an error will be returned.
// If it is not null the concept of where the logical end for
// the volume set is updated to include the next member partition.
//
extension = extension->NextMember;
if (extension != NULL) {
//
// update the logical end of the complete volume set with
// the size of this partition.
//
logicalEnd.QuadPart += extension->FtUnion.Identity.PartitionLength.QuadPart;
}
continue;
}
//
// This I/O splits two members of the volume set.
//
*DeviceExtension = extension;
*IoOffset = offset;
*Length = (ULONG) (logicalEnd.QuadPart - ioStart.QuadPart);
return TRUE;
}
return FALSE;
}
NTSTATUS
VolumeSetReadWrite(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is given control from the FtDiskReadWrite() routine
whenever an I/O request is made for a volume set. Activity includes
determining the partition for the I/O and creating new Irps if
needed.
Arguments:
DeviceExtension - The device extension for the mirror.
Irp - The i/o request.
Return Value:
NTSTATUS
--*/
{
PIRP newIrp;
PIO_STACK_LOCATION newIrpStack;
PVOID dataBuffer;
PDEVICE_EXTENSION zeroExtension = DeviceObject->DeviceExtension;
PDEVICE_EXTENSION deviceExtension = zeroExtension;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
PIO_STACK_LOCATION ftIrpStack = IoGetNextIrpStackLocation(Irp);
ULONG ioLength = irpStack->Parameters.Write.Length;
LARGE_INTEGER offset = irpStack->Parameters.Write.ByteOffset;
LARGE_INTEGER zero;
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
//
// This routine always returns STATUS_PENDING. The master Irp must
// be marked as pending or the I/O system will throw it away.
//
IoMarkIrpPending(Irp);
if (VsetpFindSetLocation(&deviceExtension, &offset, &ioLength) == TRUE) {
//
// The I/O is legal within the volume set.
//
if (ioLength == irpStack->Parameters.Write.Length) {
//
// I/O is completely contained within this member of the
// volume set.
//
newIrp = FtpDuplicateIrp(deviceExtension->TargetObject,
Irp);
//
// Adjust the beginning offset for the I/O.
// This is in the NEXT stack, or the stack for the lower driver.
//
newIrpStack = IoGetNextIrpStackLocation(newIrp);
newIrpStack->Parameters.Write.ByteOffset = offset;
//
// Set completion routine callback for the Irp.
//
IoSetCompletionRoutine(newIrp,
(PIO_COMPLETION_ROUTINE)VolumeSetIoCompletion,
(PVOID) zeroExtension,
TRUE,
TRUE,
TRUE);
DebugPrint((3,
"VolumeSetReadWrite: %s volume IRP=%x, newIrp=%x, O=%x:%x,L=%x\n",
(irpStack->MajorFunction == IRP_MJ_READ) ? "Reading" : "Writing",
Irp,
newIrp,
offset.HighPart,
offset.LowPart,
ioLength));
ftIrpStack->FtOrgIrpCount = (PVOID) 1;
IoCallDriver(deviceExtension->TargetObject, newIrp);
return STATUS_PENDING;
}
//
// I/O starts in this partition, but ends in the next partition.
// Calculate how much is in the current partition and create an
// Irp for the device. Must adjust the length of the I/O operation
// to end in this partition in the stack area for the NEXT driver.
//
dataBuffer = MmGetMdlVirtualAddress(Irp->MdlAddress);
newIrp = FtpDuplicatePartialIrp(deviceExtension->DeviceObject,
Irp,
dataBuffer,
offset,
ioLength);
//
// Mark the original Irp that two I/Os are to occur and start
// the first I/O.
//
DebugPrint((3,
"VolumeSetReadWrite: %s split, IRP=%x, newIrp=%x, O=%x:%x,L=%x\n",
(irpStack->MajorFunction == IRP_MJ_READ) ? "Read" : "Write",
Irp,
newIrp,
offset.HighPart,
offset.LowPart,
ioLength));
if (deviceExtension->NextMember == NULL) {
//
// This I/O extends beyond the end of the volume set.
//
ftIrpStack->FtOrgIrpCount = (PVOID) 1;
} else {
ftIrpStack->FtOrgIrpCount = (PVOID) 2;
}
//
// Set completion routine callback for the Irp.
//
IoSetCompletionRoutine(newIrp,
(PIO_COMPLETION_ROUTINE)VolumeSetIoCompletion,
(PVOID) zeroExtension,
TRUE,
TRUE,
TRUE);
(VOID) IoCallDriver(deviceExtension->TargetObject, newIrp);
//
// The second I/O starts at offset zero for the partition and
// is the remaining size of the I/O.
//
deviceExtension = deviceExtension->NextMember;
if (deviceExtension == NULL) {
//
// The completion routine will handle the partial read status.
//
return STATUS_MORE_PROCESSING_REQUIRED;
}
//
// Calculate starting memory offset for second I/O.
//
dataBuffer = (PUCHAR)dataBuffer + ioLength;
//
// Calculate the second I/O length.
//
ioLength = irpStack->Parameters.Write.Length - ioLength;
//
// Allocate and set up a new Irp.
//
zero.QuadPart = 0;
newIrp = FtpDuplicatePartialIrp(deviceExtension->DeviceObject,
Irp,
dataBuffer,
zero,
ioLength);
DebugPrint((3,
"VolumeSetReadWrite: %s 2nd IRP=0x%x,newIrp=0x%x, O=%x:%x,L=%x\n",
(irpStack->MajorFunction == IRP_MJ_READ) ? "Read" : "Write",
Irp,
newIrp,
0,
0,
ioLength));
IoSetCompletionRoutine(newIrp,
(PIO_COMPLETION_ROUTINE)VolumeSetIoCompletion,
(PVOID) zeroExtension,
TRUE,
TRUE,
TRUE);
(VOID) IoCallDriver(deviceExtension->TargetObject, newIrp);
return STATUS_PENDING;
}
DebugPrint((2,"VolumeSetReadWrite: beyond volume set.\n"));
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
FtpCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INVALID_PARAMETER;
}
NTSTATUS
VolumeSetIoCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
This routine is called when an IRP completes for a volume set.
The IRP could be for any member of the volume set. In the original
IRP there is a count of outstanding IRPs.
Arguments:
DeviceObject - FT device object.
Irp - the completed IRP.
Context - the FT deviceExtension for the IRP.
Return Value:
NTSTATUS == STATUS_MORE_PROCESSING_REQUIRED
--*/
{
LONG irpCount;
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) Context;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
PIRP masterIrp = (PIRP) irpStack->FtLowIrpMasterIrp;
PIO_STACK_LOCATION masterIrpStack = IoGetNextIrpStackLocation(masterIrp);
ASSERT(masterIrp != NULL);
DebugPrint((4,
"VolumeSetIoCompletion: I/O complete status %x for function %x\n",
Irp->IoStatus.Status,
irpStack->MajorFunction));
if (irpStack->VolSetAllocatedSystemBuffer == (PVOID) 1) {
//
// The system buffer was allocated by the volume set code.
//
ExFreePool(Irp->AssociatedIrp.SystemBuffer);
}
if (irpStack->FtLowIrpAllocatedMdl == (PVOID) 1) {
//
// The MDL in this Irp was allocated by the FT driver.
//
IoFreeMdl(Irp->MdlAddress);
}
//
// NOTE: If the first I/O fails the second I/O will continue, meaning
// a portion of the I/O request will complete that will not be
// notified to the caller. This is different from the normal one
// device case.
//
if (NT_SUCCESS(masterIrp->IoStatus.Status)) {
if (NT_SUCCESS(masterIrp->IoStatus.Status = Irp->IoStatus.Status)) {
//
// Update the information field.
//
masterIrp->IoStatus.Information += Irp->IoStatus.Information;
} else {
//
// Store the information.
//
masterIrp->IoStatus.Information = Irp->IoStatus.Information;
}
}
IoFreeIrp(Irp);
irpCount = InterlockedDecrement((PLONG) &masterIrpStack->FtOrgIrpCount);
if (irpCount == 0) {
//
// I/O processing is complete. Return the master IRP.
//
DebugPrint((2,
"VolumeSetIoCompletion: Completed (0x%x) 0x%x status %x info 0x%x\n",
Irp,
masterIrp,
masterIrp->IoStatus.Status,
masterIrp->IoStatus.Information));
FtpCompleteRequest(masterIrp, IO_DISK_INCREMENT);
} else {
//
// Multiple I/O. One item has completed. Decrement the request
// count, free this Irp and return indicating that more work is needed
// to complete the master request.
//
DebugPrint((2,
"VolumeSetIoCompletion: First I/O 0x%x I/O complete (%x).\n",
Irp,
masterIrp->IoStatus.Status));
}
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
VolumeSetVerify(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is called when the device control to verify an area
of disk within a volume set is performed.
Arguments:
DeviceObject - The device object for the mirror.
Irp - The i/o request.
Return Value:
NTSTATUS
--*/
{
PIRP newIrp;
PIO_STACK_LOCATION newIrpStack;
PDEVICE_EXTENSION zeroExtension = DeviceObject->DeviceExtension;
PDEVICE_EXTENSION deviceExtension = zeroExtension;
PIO_STACK_LOCATION ftIrpStack = IoGetNextIrpStackLocation(Irp);
PVERIFY_INFORMATION verifyInfo = Irp->AssociatedIrp.SystemBuffer;
LARGE_INTEGER offset = verifyInfo->StartingOffset;
ULONG length = verifyInfo->Length;
BOOLEAN twoIrps = FALSE;
if (VsetpFindSetLocation(&deviceExtension, &offset, &length) == FALSE) {
DebugPrint((2,"VolumeSetVerify: beyond volume set.\n"));
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
FtpCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INVALID_PARAMETER;
}
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
newIrp = FtpDuplicateIrp(deviceExtension->TargetObject,
Irp);
ASSERT(newIrp != NULL);
IoMarkIrpPending(Irp);
//
// Set completion routine callback for both Irps.
//
IoSetCompletionRoutine(newIrp,
(PIO_COMPLETION_ROUTINE)VolumeSetIoCompletion,
(PVOID) zeroExtension,
TRUE,
TRUE,
TRUE);
newIrpStack = IoGetCurrentIrpStackLocation(newIrp);
//
// Update the offset for the verify since it may have changed.
//
verifyInfo->StartingOffset = offset;
if (length == verifyInfo->Length) {
//
// Only the one Irp.
//
ftIrpStack->FtOrgIrpCount = (PVOID) 1;
newIrp->AssociatedIrp.SystemBuffer = Irp->AssociatedIrp.SystemBuffer;
newIrpStack->VolSetAllocatedSystemBuffer = (PVOID) 0;
} else {
PVERIFY_INFORMATION newVerify = ExAllocatePool(NonPagedPool,
sizeof(VERIFY_INFORMATION));
//
// There will be two Irp's must make a copy of the system
// buffer for one of the device drivers.
//
twoIrps = TRUE;
ftIrpStack->FtOrgIrpCount = (PVOID) 2;
newIrp->AssociatedIrp.SystemBuffer = (PVOID) newVerify;
*newVerify= *verifyInfo;
newVerify->Length = length;
newIrpStack->VolSetAllocatedSystemBuffer = (PVOID) 1;
//
// Now update the current parameter structure for the second Irp.
//
verifyInfo->StartingOffset.HighPart = 0;
verifyInfo->StartingOffset.LowPart = 0;
verifyInfo->Length = verifyInfo->Length - length;
DebugPrint((2,
"VolumeSetVerify: First extension %x with %x length %x\n",
deviceExtension,
newIrp,
length));
}
DebugPrint((4, "VolumeSetVerify: Starting extension %x with %x length %x\n",
deviceExtension,
newIrp,
length));
(VOID) IoCallDriver(deviceExtension->TargetObject, newIrp);
if (twoIrps == TRUE) {
//
// Need to perform the remaining verify.
//
deviceExtension = deviceExtension->NextMember;
if (deviceExtension != NULL) {
newIrp = FtpDuplicateIrp(deviceExtension->TargetObject,
Irp);
IoSetCompletionRoutine(newIrp,
(PIO_COMPLETION_ROUTINE)VolumeSetIoCompletion,
(PVOID) zeroExtension,
TRUE,
TRUE,
TRUE);
newIrpStack = IoGetCurrentIrpStackLocation(newIrp);
newIrpStack->VolSetAllocatedSystemBuffer = (PVOID) 0;
newIrp->AssociatedIrp.SystemBuffer= Irp->AssociatedIrp.SystemBuffer;
DebugPrint((2,
"VolumeSetVerify: Second extension %x with %x length %x\n",
deviceExtension,
newIrp,
verifyInfo->Length));
(VOID) IoCallDriver(deviceExtension->TargetObject, newIrp);
}
}
return STATUS_PENDING;
}