1189 lines
22 KiB
C++
1189 lines
22 KiB
C++
|
/*++
|
|||
|
|
|||
|
Copyright (C) 1991-5 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
composit.cxx
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module contains the code specific to all composite volume objects.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Norbert Kusters 2-Feb-1995
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
kernel mode only
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
This code assumes that the volume array is static. If these values
|
|||
|
changes (as in Stripes or Mirrors) then it is up to the subclass to
|
|||
|
provide the proper synchronization.
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
extern "C" {
|
|||
|
#include <ntddk.h>
|
|||
|
}
|
|||
|
|
|||
|
#include <ftdisk.h>
|
|||
|
|
|||
|
VOID
|
|||
|
SimpleFtCompletionRoutine(
|
|||
|
IN PVOID CompletionContext,
|
|||
|
IN NTSTATUS Status
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma code_seg("PAGE")
|
|||
|
#endif
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
COMPOSITE_FT_VOLUME::Initialize(
|
|||
|
IN OUT PROOT_EXTENSION RootExtension,
|
|||
|
IN FT_LOGICAL_DISK_ID LogicalDiskId,
|
|||
|
IN OUT PFT_VOLUME* VolumeArray,
|
|||
|
IN USHORT ArraySize,
|
|||
|
IN PVOID ConfigInfo,
|
|||
|
IN PVOID StateInfo
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Initialize routine for FT_VOLUME of type COMPOSITE_FT_VOLUME.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
RootExtension - Supplies the root device extension.
|
|||
|
|
|||
|
LogicalDiskId - Supplies the logical disk id for this volume.
|
|||
|
|
|||
|
VolumeArray - Supplies the array of volumes for this volume set.
|
|||
|
|
|||
|
ArraySize - Supplies the number of volumes in the volume array.
|
|||
|
|
|||
|
ConfigInfo - Supplies the configuration information.
|
|||
|
|
|||
|
StateInfo - Supplies the state information.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
ULONG i, secsize;
|
|||
|
|
|||
|
FT_VOLUME::Initialize(RootExtension, LogicalDiskId);
|
|||
|
|
|||
|
_volumeArray = VolumeArray;
|
|||
|
_arraySize = ArraySize;
|
|||
|
|
|||
|
_sectorSize = 0;
|
|||
|
for (i = 0; i < _arraySize; i++) {
|
|||
|
if (_volumeArray[i]) {
|
|||
|
secsize = _volumeArray[i]->QuerySectorSize();
|
|||
|
if (_sectorSize < secsize) {
|
|||
|
_sectorSize = secsize;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
COMPOSITE_FT_VOLUME::OrphanMember(
|
|||
|
IN USHORT MemberNumber,
|
|||
|
IN FT_COMPLETION_ROUTINE CompletionRoutine,
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine tries to orphan the given member of this logical disk.
|
|||
|
A completion routine will be called if and only if this attempt is successful.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
MemberNumber - Supplies the member number to orphan.
|
|||
|
|
|||
|
CompletionRoutine - Supplies the completion routine.
|
|||
|
|
|||
|
Context - Supplies the completion routine context.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
return STATUS_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
COMPOSITE_FT_VOLUME::RegenerateMember(
|
|||
|
IN USHORT MemberNumber,
|
|||
|
IN OUT PFT_VOLUME NewMember,
|
|||
|
IN FT_COMPLETION_ROUTINE CompletionRoutine,
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine regenerates the given member of this volume with
|
|||
|
the given volume.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
MemberNumber - Supplies the member number to regenerate.
|
|||
|
|
|||
|
NewMember - Supplies the new member to regenerate to.
|
|||
|
|
|||
|
CompletionRoutine - Supplies the completion routine.
|
|||
|
|
|||
|
Context - Supplies the completion routine context.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
return STATUS_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
COMPOSITE_FT_VOLUME::StopSyncOperations(
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine stops all sync operations.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
USHORT i;
|
|||
|
PFT_VOLUME vol;
|
|||
|
|
|||
|
for (i = 0; i < _arraySize; i++) {
|
|||
|
if (vol = GetMember(i)) {
|
|||
|
vol->StopSyncOperations();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
COMPOSITE_FT_VOLUME::BroadcastIrp(
|
|||
|
IN PIRP Irp,
|
|||
|
IN FT_COMPLETION_ROUTINE CompletionRoutine,
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine broadcasts a copy of the given IRP to every partition that
|
|||
|
is a member of the logical disk.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Irp - Supplies the I/O request packet.
|
|||
|
|
|||
|
CompletionRoutine - Supplies the routine to be called when the operation
|
|||
|
completes.
|
|||
|
|
|||
|
Context - Supplies the completion routine context.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PFT_COMPLETION_ROUTINE_CONTEXT completionContext;
|
|||
|
USHORT i;
|
|||
|
PFT_VOLUME vol;
|
|||
|
|
|||
|
completionContext = (PFT_COMPLETION_ROUTINE_CONTEXT)
|
|||
|
ExAllocatePool(NonPagedPool,
|
|||
|
sizeof(FT_COMPLETION_ROUTINE_CONTEXT));
|
|||
|
if (!completionContext) {
|
|||
|
CompletionRoutine(Context, STATUS_INSUFFICIENT_RESOURCES);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
KeInitializeSpinLock(&completionContext->SpinLock);
|
|||
|
completionContext->Status = STATUS_SUCCESS;
|
|||
|
completionContext->RefCount = _arraySize;
|
|||
|
completionContext->CompletionRoutine = CompletionRoutine;
|
|||
|
completionContext->Context = Context;
|
|||
|
|
|||
|
for (i = 0; i < _arraySize; i++) {
|
|||
|
if (vol = GetMember(i)) {
|
|||
|
vol->BroadcastIrp(Irp, SimpleFtCompletionRoutine,
|
|||
|
completionContext);
|
|||
|
} else {
|
|||
|
SimpleFtCompletionRoutine(completionContext, STATUS_SUCCESS);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
PFT_VOLUME
|
|||
|
COMPOSITE_FT_VOLUME::GetParentLogicalDisk(
|
|||
|
IN PFT_VOLUME Volume
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine returns the parent of the given logical disk within
|
|||
|
this volume.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Volume - Supplies the sub-volume of which we are looking for the parent.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The parent volume or NULL;
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
USHORT n, i;
|
|||
|
PFT_VOLUME vol;
|
|||
|
|
|||
|
n = QueryNumMembers();
|
|||
|
for (i = 0; i < n; i++) {
|
|||
|
if (!(vol = GetMember(i))) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (vol == Volume) {
|
|||
|
return this;
|
|||
|
}
|
|||
|
|
|||
|
vol = vol->GetParentLogicalDisk(Volume);
|
|||
|
if (vol) {
|
|||
|
return vol;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
COMPOSITE_FT_VOLUME::SetPartitionType(
|
|||
|
IN UCHAR PartitionType
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine sets the partition type on all the members of the
|
|||
|
FT set.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
PartitionType - Supplies the partition type.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS status, finalStatus;
|
|||
|
USHORT n, i;
|
|||
|
PFT_VOLUME vol;
|
|||
|
|
|||
|
finalStatus = STATUS_SUCCESS;
|
|||
|
n = QueryNumMembers();
|
|||
|
for (i = 0; i < n; i++) {
|
|||
|
if (!(vol = GetMember(i))) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
status = vol->SetPartitionType(PartitionType);
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
finalStatus = status;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return finalStatus;
|
|||
|
}
|
|||
|
|
|||
|
UCHAR
|
|||
|
COMPOSITE_FT_VOLUME::QueryPartitionType(
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine queries the partition type.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The partition type.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
USHORT n, i;
|
|||
|
PFT_VOLUME vol;
|
|||
|
UCHAR type;
|
|||
|
|
|||
|
n = QueryNumMembers();
|
|||
|
for (i = 0; i < n; i++) {
|
|||
|
if (!(vol = GetMember(i))) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
type = vol->QueryPartitionType();
|
|||
|
if (type) {
|
|||
|
return type;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
UCHAR
|
|||
|
COMPOSITE_FT_VOLUME::QueryStackSize(
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine queries IRP stack size.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The IRP stack size.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
USHORT n, i;
|
|||
|
PFT_VOLUME vol;
|
|||
|
UCHAR stackSize, t;
|
|||
|
|
|||
|
stackSize = 0;
|
|||
|
n = QueryNumMembers();
|
|||
|
for (i = 0; i < n; i++) {
|
|||
|
if (!(vol = GetMember(i))) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
t = vol->QueryStackSize();
|
|||
|
if (t > stackSize) {
|
|||
|
stackSize = t;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return stackSize;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
COMPOSITE_FT_VOLUME::CreateLegacyNameLinks(
|
|||
|
IN PUNICODE_STRING DeviceName
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine creates the \Device\HarddiskN\PartitionM links for
|
|||
|
this object to the given device name.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceName - Supplies the device name.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
USHORT n, i;
|
|||
|
PFT_VOLUME vol;
|
|||
|
|
|||
|
n = QueryNumMembers();
|
|||
|
for (i = 0; i < n; i++) {
|
|||
|
if (!(vol = GetMember(i))) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
vol->CreateLegacyNameLinks(DeviceName);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma code_seg("PAGELK")
|
|||
|
#endif
|
|||
|
|
|||
|
USHORT
|
|||
|
COMPOSITE_FT_VOLUME::QueryNumberOfMembers(
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine returns the number of members in this volume.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The number of members in this volume.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
return _arraySize;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
COMPOSITE_FT_VOLUME::SetDirtyBit(
|
|||
|
IN BOOLEAN IsDirty,
|
|||
|
IN FT_COMPLETION_ROUTINE CompletionRoutine,
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine sets the dirty bit on the volume. This bit is used at
|
|||
|
startup to determine whether or not there was a clean shutdown.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
IsDirty - Supplies the value of the dirty bit.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
USHORT n, i;
|
|||
|
PFT_VOLUME vol;
|
|||
|
PFT_COMPLETION_ROUTINE_CONTEXT completionContext;
|
|||
|
|
|||
|
if (!CompletionRoutine) {
|
|||
|
n = QueryNumMembers();
|
|||
|
for (i = 0; i < n; i++) {
|
|||
|
if (vol = GetMember(i)) {
|
|||
|
vol->SetDirtyBit(IsDirty, NULL, NULL);
|
|||
|
}
|
|||
|
}
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
completionContext = (PFT_COMPLETION_ROUTINE_CONTEXT)
|
|||
|
ExAllocatePool(NonPagedPool,
|
|||
|
sizeof(FT_COMPLETION_ROUTINE_CONTEXT));
|
|||
|
if (!completionContext) {
|
|||
|
CompletionRoutine(Context, STATUS_INSUFFICIENT_RESOURCES);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
KeInitializeSpinLock(&completionContext->SpinLock);
|
|||
|
completionContext->Status = STATUS_SUCCESS;
|
|||
|
completionContext->RefCount = _arraySize;
|
|||
|
completionContext->CompletionRoutine = CompletionRoutine;
|
|||
|
completionContext->Context = Context;
|
|||
|
|
|||
|
for (i = 0; i < _arraySize; i++) {
|
|||
|
if (vol = GetMember(i)) {
|
|||
|
vol->SetDirtyBit(IsDirty, SimpleFtCompletionRoutine,
|
|||
|
completionContext);
|
|||
|
} else {
|
|||
|
SimpleFtCompletionRoutine(completionContext, STATUS_SUCCESS);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
PFT_VOLUME
|
|||
|
COMPOSITE_FT_VOLUME::GetMember(
|
|||
|
IN USHORT MemberNumber
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine returns the 'MemberNumber'th member of this volume.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
MemberNumber - Supplies the zero based member number desired.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
A pointer to the 'MemberNumber'th member or NULL if no such member.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
KIRQL irql;
|
|||
|
PFT_VOLUME r;
|
|||
|
|
|||
|
ASSERT(MemberNumber < _arraySize);
|
|||
|
|
|||
|
KeAcquireSpinLock(&_spinLock, &irql);
|
|||
|
r = _volumeArray[MemberNumber];
|
|||
|
KeReleaseSpinLock(&_spinLock, irql);
|
|||
|
return r;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
SimpleFtCompletionRoutine(
|
|||
|
IN PVOID CompletionContext,
|
|||
|
IN NTSTATUS Status
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is a simple completion routine that expects the CompletionContext
|
|||
|
to be a FT_COMPLETION_ROUTINE_CONTEXT. It decrements the ref count and
|
|||
|
consolidates all of the status codes. When the ref count goes to zero it
|
|||
|
call the original completion routine with the result.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
CompletionContext - Supplies the completion context.
|
|||
|
|
|||
|
Status - Supplies the status of the request.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PFT_COMPLETION_ROUTINE_CONTEXT completionContext;
|
|||
|
KIRQL oldIrql;
|
|||
|
LONG count;
|
|||
|
|
|||
|
completionContext = (PFT_COMPLETION_ROUTINE_CONTEXT) CompletionContext;
|
|||
|
|
|||
|
KeAcquireSpinLock(&completionContext->SpinLock, &oldIrql);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status) &&
|
|||
|
FtpIsWorseStatus(Status, completionContext->Status)) {
|
|||
|
|
|||
|
completionContext->Status = Status;
|
|||
|
}
|
|||
|
|
|||
|
count = --completionContext->RefCount;
|
|||
|
|
|||
|
KeReleaseSpinLock(&completionContext->SpinLock, oldIrql);
|
|||
|
|
|||
|
if (!count) {
|
|||
|
completionContext->CompletionRoutine(completionContext->Context,
|
|||
|
completionContext->Status);
|
|||
|
ExFreePool(completionContext);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
COMPOSITE_FT_VOLUME::StartSyncOperations(
|
|||
|
IN BOOLEAN RegenerateOrphans,
|
|||
|
IN FT_COMPLETION_ROUTINE CompletionRoutine,
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine restarts any regenerate and initialize requests that were
|
|||
|
suspended because of a reboot. The volume examines the member state of
|
|||
|
all of its constituents and restarts any regenerations pending.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
RegenerateOrphans - Supplies whether or not to try and regenerate
|
|||
|
orphaned members.
|
|||
|
|
|||
|
CompletionRoutine - Supplies the completion routine.
|
|||
|
|
|||
|
Context - Supplies the context for the completion routine.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PFT_COMPLETION_ROUTINE_CONTEXT completionContext;
|
|||
|
USHORT i;
|
|||
|
PFT_VOLUME vol;
|
|||
|
|
|||
|
completionContext = (PFT_COMPLETION_ROUTINE_CONTEXT)
|
|||
|
ExAllocatePool(NonPagedPool,
|
|||
|
sizeof(FT_COMPLETION_ROUTINE_CONTEXT));
|
|||
|
if (!completionContext) {
|
|||
|
CompletionRoutine(Context, STATUS_INSUFFICIENT_RESOURCES);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
KeInitializeSpinLock(&completionContext->SpinLock);
|
|||
|
completionContext->Status = STATUS_SUCCESS;
|
|||
|
completionContext->RefCount = _arraySize;
|
|||
|
completionContext->CompletionRoutine = CompletionRoutine;
|
|||
|
completionContext->Context = Context;
|
|||
|
|
|||
|
for (i = 0; i < _arraySize; i++) {
|
|||
|
if (vol = GetMember(i)) {
|
|||
|
vol->StartSyncOperations(RegenerateOrphans,
|
|||
|
SimpleFtCompletionRoutine,
|
|||
|
completionContext);
|
|||
|
} else {
|
|||
|
SimpleFtCompletionRoutine(completionContext, STATUS_SUCCESS);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
ULONG
|
|||
|
COMPOSITE_FT_VOLUME::QuerySectorSize(
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Returns the sector size for the volume.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The volume sector size in bytes.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
return _sectorSize;
|
|||
|
}
|
|||
|
|
|||
|
PFT_VOLUME
|
|||
|
COMPOSITE_FT_VOLUME::GetContainedLogicalDisk(
|
|||
|
IN FT_LOGICAL_DISK_ID LogicalDiskId
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine returns TRUE if the given logical disk id
|
|||
|
represents this logical disk or if this logical disk contains
|
|||
|
the given logical disk id either directly or indirectly.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
LogicalDiskId - Supplies the logical disk id that we are searching for.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
FALSE - The given logical disk id is not contained in this logical disk.
|
|||
|
|
|||
|
TRUE - The given logical disk id is contained in this logical disk.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
USHORT n, i;
|
|||
|
PFT_VOLUME vol;
|
|||
|
|
|||
|
if (LogicalDiskId == QueryLogicalDiskId()) {
|
|||
|
return this;
|
|||
|
}
|
|||
|
|
|||
|
n = QueryNumMembers();
|
|||
|
for (i = 0; i < n; i++) {
|
|||
|
if ((vol = GetMember(i)) &&
|
|||
|
(vol = vol->GetContainedLogicalDisk(LogicalDiskId))) {
|
|||
|
|
|||
|
return vol;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
PFT_VOLUME
|
|||
|
COMPOSITE_FT_VOLUME::GetContainedLogicalDisk(
|
|||
|
IN PDEVICE_OBJECT TargetObject
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine returns TRUE if the given logical disk id
|
|||
|
represents this logical disk or if this logical disk contains
|
|||
|
the given logical disk id either directly or indirectly.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
TargetObject - Supplies the target object.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
FALSE - The given logical disk id is not contained in this logical disk.
|
|||
|
|
|||
|
TRUE - The given logical disk id is contained in this logical disk.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
USHORT n, i;
|
|||
|
PFT_VOLUME vol;
|
|||
|
|
|||
|
n = QueryNumMembers();
|
|||
|
for (i = 0; i < n; i++) {
|
|||
|
if ((vol = GetMember(i)) &&
|
|||
|
(vol = vol->GetContainedLogicalDisk(TargetObject))) {
|
|||
|
|
|||
|
return vol;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
PFT_VOLUME
|
|||
|
COMPOSITE_FT_VOLUME::GetContainedLogicalDisk(
|
|||
|
IN ULONG Signature,
|
|||
|
IN LONGLONG Offset
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine returns TRUE if the given logical disk id
|
|||
|
represents this logical disk or if this logical disk contains
|
|||
|
the given logical disk id either directly or indirectly.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
TargetObject - Supplies the target object.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
FALSE - The given logical disk id is not contained in this logical disk.
|
|||
|
|
|||
|
TRUE - The given logical disk id is contained in this logical disk.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
USHORT n, i;
|
|||
|
PFT_VOLUME vol;
|
|||
|
|
|||
|
n = QueryNumMembers();
|
|||
|
for (i = 0; i < n; i++) {
|
|||
|
vol = GetMember(i);
|
|||
|
if (!vol) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
vol = vol->GetContainedLogicalDisk(Signature, Offset);
|
|||
|
if (vol) {
|
|||
|
return vol;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
COMPOSITE_FT_VOLUME::SetMember(
|
|||
|
IN USHORT MemberNumber,
|
|||
|
IN PFT_VOLUME Member
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine sets the given member in this volume.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
MemberNumber - Supplies the member number.
|
|||
|
|
|||
|
Member - Supplies the member.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
KIRQL irql;
|
|||
|
|
|||
|
KeAcquireSpinLock(&_spinLock, &irql);
|
|||
|
SetMemberUnprotected(MemberNumber, Member);
|
|||
|
KeReleaseSpinLock(&_spinLock, irql);
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
COMPOSITE_FT_VOLUME::IsComplete(
|
|||
|
IN BOOLEAN IoPending
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine computes whether or not this volume has either all
|
|||
|
(if IoPending is FALSE) of its members or enough (if IoPending is TRUE) of
|
|||
|
its members.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
IoPending - Supplies whether or not there is IO pending.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
USHORT n, i;
|
|||
|
PFT_VOLUME vol;
|
|||
|
ULONG secsize;
|
|||
|
|
|||
|
n = QueryNumMembers();
|
|||
|
for (i = 0; i < n; i++) {
|
|||
|
vol = GetMember(i);
|
|||
|
if (!vol || !vol->IsComplete(IoPending)) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
COMPOSITE_FT_VOLUME::CompleteNotification(
|
|||
|
IN BOOLEAN IoPending
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called to notify the volume that it is complete and
|
|||
|
to therefore prepare for incoming requests.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
IoPending - Supplies whether or not there is IO pending.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
USHORT n, i;
|
|||
|
PFT_VOLUME vol;
|
|||
|
ULONG secsize;
|
|||
|
|
|||
|
n = QueryNumMembers();
|
|||
|
for (i = 0; i < n; i++) {
|
|||
|
vol = GetMember(i);
|
|||
|
if (!vol) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
if (vol->IsComplete(IoPending)) {
|
|||
|
vol->CompleteNotification(IoPending);
|
|||
|
secsize = vol->QuerySectorSize();
|
|||
|
if (secsize > _sectorSize) {
|
|||
|
_sectorSize = secsize;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
ULONG
|
|||
|
COMPOSITE_FT_VOLUME::QueryNumberOfPartitions(
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine returns the number of partitions covered by this volume
|
|||
|
set.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The number of partitions covered by this volume set.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
ULONG r;
|
|||
|
USHORT n, i;
|
|||
|
PFT_VOLUME vol;
|
|||
|
|
|||
|
r = 0;
|
|||
|
n = QueryNumMembers();
|
|||
|
for (i = 0; i < n; i++) {
|
|||
|
if (!(vol = GetMember(i))) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
r += vol->QueryNumberOfPartitions();
|
|||
|
}
|
|||
|
|
|||
|
return r;
|
|||
|
}
|
|||
|
|
|||
|
PDEVICE_OBJECT
|
|||
|
COMPOSITE_FT_VOLUME::GetLeftmostPartitionObject(
|
|||
|
)
|
|||
|
|
|||
|
{
|
|||
|
USHORT n, i;
|
|||
|
PFT_VOLUME vol;
|
|||
|
|
|||
|
n = QueryNumMembers();
|
|||
|
for (i = 0; i < n; i++) {
|
|||
|
vol = GetMember(i);
|
|||
|
if (vol) {
|
|||
|
return vol->GetLeftmostPartitionObject();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
COMPOSITE_FT_VOLUME::QueryDiskExtents(
|
|||
|
OUT PDISK_EXTENT* DiskExtents,
|
|||
|
OUT PULONG NumberOfDiskExtents
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine returns an array of disk extents that describe the
|
|||
|
location of this volume.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DiskExtents - Returns the disk extents.
|
|||
|
|
|||
|
NumberOfDiskExtents - Returns the number of disk extents.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
ULONG totalExtents, numExtents, newTotal;
|
|||
|
USHORT n, i;
|
|||
|
PFT_VOLUME vol;
|
|||
|
NTSTATUS status;
|
|||
|
PDISK_EXTENT extents, allExtents;
|
|||
|
|
|||
|
Restart:
|
|||
|
|
|||
|
totalExtents = 0;
|
|||
|
n = QueryNumMembers();
|
|||
|
for (i = 0; i < n; i++) {
|
|||
|
if (!(vol = GetMember(i))) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
status = vol->QueryDiskExtents(&extents, &numExtents);
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
return status;
|
|||
|
}
|
|||
|
ExFreePool(extents);
|
|||
|
|
|||
|
totalExtents += numExtents;
|
|||
|
}
|
|||
|
|
|||
|
allExtents = (PDISK_EXTENT) ExAllocatePool(PagedPool, totalExtents*
|
|||
|
sizeof(DISK_EXTENT));
|
|||
|
if (!allExtents) {
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
newTotal = 0;
|
|||
|
for (i = 0; i < n; i++) {
|
|||
|
if (!(vol = GetMember(i))) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
status = vol->QueryDiskExtents(&extents, &numExtents);
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
ExFreePool(allExtents);
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
if (newTotal + numExtents > totalExtents) {
|
|||
|
ExFreePool(extents);
|
|||
|
ExFreePool(allExtents);
|
|||
|
goto Restart;
|
|||
|
}
|
|||
|
|
|||
|
RtlCopyMemory(&allExtents[newTotal], extents,
|
|||
|
numExtents*sizeof(DISK_EXTENT));
|
|||
|
ExFreePool(extents);
|
|||
|
|
|||
|
newTotal += numExtents;
|
|||
|
}
|
|||
|
|
|||
|
*DiskExtents = allExtents;
|
|||
|
*NumberOfDiskExtents = newTotal;
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
COMPOSITE_FT_VOLUME::QueryVolumeState(
|
|||
|
IN PFT_VOLUME Volume,
|
|||
|
OUT PFT_MEMBER_STATE State
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine returns the state of the given volume considered as a
|
|||
|
member of this volume.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Volume - Supplies the volume to query the state for.
|
|||
|
|
|||
|
State - Returns the state.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
FALSE - The given Volume is not a member of this volume.
|
|||
|
|
|||
|
TRUE - The state was successfully computed.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
USHORT n, i;
|
|||
|
PFT_VOLUME vol;
|
|||
|
|
|||
|
n = QueryNumMembers();
|
|||
|
for (i = 0; i < n; i++) {
|
|||
|
vol = GetMember(i);
|
|||
|
if (!vol) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (vol->QueryVolumeState(Volume, State)) {
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
COMPOSITE_FT_VOLUME::~COMPOSITE_FT_VOLUME(
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Routine called to cleanup resources being used by the object.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
if (_volumeArray) {
|
|||
|
ExFreePool(_volumeArray);
|
|||
|
}
|
|||
|
}
|