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);
|
||
}
|
||
}
|