NT4/private/ntos/dd/ntft/config.c

1604 lines
47 KiB
C
Raw Normal View History

2001-01-01 00:00:00 +01:00
/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
config.c
Abstract:
This module provides the configuration information to the FT
device driver.
Currently it is implemented using the ZwXxx routines to read
information from the registry.
The format of the registry information is described in the two
include files ntdskreg.h and ntddft.h. The registry information
is stored in a single value within the key \registry\machine\system\disk.
The value name is "information". The format of this single value is
a collection of "compressed" structures. Compressed structures are
multi element structures where the following structure starts at the
end of the preceeding structure. The picture below attempts to display
this:
+---------------------------------------+
| |
| DISK_CONFIG_HEADER |
| contains the offset to the |
| DISK_REGISTRY header and the |
| FT_REGISTRY header. |
+---------------------------------------+
| |
| DISK_REGISTRY |
| contains a count of disks |
+---------------------------------------+
| |
| DISK_DESCRIPTION |
| contains a count of partitions |
+---------------------------------------+
| |
| PARTITION_DESCRIPTION |
| entry for each partition |
+---------------------------------------+
| |
= More DISK_DESCRIPTION plus =
= PARTITION_DESCRIPTIONS for =
= the number of disks in the =
= system. Note, the second disk =
= description starts in the "n"th =
= partition location of the memory =
= area. This is the meaning of =
= "compressed" format. =
| |
+---------------------------------------+
| |
| FT_REGISTRY |
| contains a count of FT components |
| this is located by an offset in |
| the DISK_CONFIG_HEADER |
+---------------------------------------+
| |
| FT_DESCRIPTION |
| contains a count of FT members |
+---------------------------------------+
| |
| FT_MEMBER |
| entry for each member |
+---------------------------------------+
| |
= More FT_DESCRIPTION plus =
= FT_MEMBER entries for the number =
= of FT compenents in the system =
| |
+---------------------------------------+
This packing of structures is done for two reasons:
1. to conserve space in the registry. If there are only two partitions
on a disk then there are only two PARTITION_DESCRIPTIONs in the
registry for that disk.
2. to not impose a maximum on the number of items that can be described
in the registry. For example if the number of members in a stripe
set were to change from 32 to 64 there would be no effect on the
registry format, only on the UI that presents it to the user.
Author:
Bob Rinne (bobri) 2-Feb-1992
Mike Glass (mglass)
Environment:
kernel mode only
Notes:
The code to access the registry needs to allocate memory and
potentially delay execution until memory is available. Therefore
it should only be called from the context of a thread (initialization
or FT created).
Revision History:
--*/
#include "ntddk.h"
#include "ftdisk.h"
#ifdef POOL_TAGGING
#ifdef ExAllocatePool
#undef ExAllocatePool
#endif
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,' CtF')
#endif
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, FtpConfigure)
#endif
NTSTATUS
FtpCreateMissingDevice(
IN PDEVICE_EXTENSION DeviceExtension,
IN FT_TYPE Type,
IN USHORT FtGroup,
IN USHORT MemberRole,
IN OUT PDEVICE_EXTENSION *DeviceExtensionPtr
);
VOID
FtpMarkMirrorPartitionType(
IN PDEVICE_EXTENSION DeviceExtension
);
//
// Size of default work area allocated when getting information from
// the registry.
//
#define WORK_AREA 4096
NTSTATUS
FtpOpenKey(
IN PHANDLE HandlePtr,
IN PUNICODE_STRING KeyName
)
/*++
Routine Description:
Routine to open a key in the configuration registry.
Arguments:
HandlePtr - Pointer to a location for the resulting handle.
KeyName - Ascii string for the name of the key.
Return Value:
NTSTATUS
--*/
{
NTSTATUS status;
OBJECT_ATTRIBUTES objectAttributes;
memset(&objectAttributes, 0, sizeof(OBJECT_ATTRIBUTES));
InitializeObjectAttributes(&objectAttributes,
KeyName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
status = ZwOpenKey(HandlePtr,
KEY_READ | KEY_WRITE,
&objectAttributes);
return status;
} // FtpOpenKey
NTSTATUS
FtpReturnRegistryInformation(
IN PUCHAR ValueName,
IN OUT PVOID *FreePoolAddress,
IN OUT PVOID *Information
)
/*++
Routine Description:
This routine queries the configuration registry
for the configuration information of the FT subsystem.
NOTE: It must be called with a thread context since it calls into
the thread logic to insure a buffer is allocated for the data.
Arguments:
ValueName - an Ascii string for the value name to be returned.
FreePoolAddress - a pointer to a pointer for the address to free when
done using information.
Information - a pointer to a pointer for the information.
Return Value:
NTSTATUS
--*/
{
NTSTATUS status;
HANDLE handle;
ULONG requestLength;
ULONG resultLength;
STRING string;
UNICODE_STRING unicodeName;
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
RtlInitString(&string, DISK_REGISTRY_KEY);
status = RtlAnsiStringToUnicodeString(&unicodeName,
&string,
TRUE);
if (!NT_SUCCESS(status)) {
return status;
}
status = FtpOpenKey(&handle,
&unicodeName);
RtlFreeUnicodeString(&unicodeName);
if (!NT_SUCCESS(status)) {
return status;
}
RtlInitString(&string,
ValueName);
status = RtlAnsiStringToUnicodeString(&unicodeName,
&string,
TRUE);
if (!NT_SUCCESS(status)) {
return status;
}
requestLength = WORK_AREA;
while (1) {
keyValueInformation = (PKEY_VALUE_FULL_INFORMATION)
ExAllocatePool(NonPagedPool,
requestLength);
status = ZwQueryValueKey(handle,
&unicodeName,
KeyValueFullInformation,
keyValueInformation,
requestLength,
&resultLength);
if (status == STATUS_BUFFER_OVERFLOW) {
//
// Try to get a buffer big enough.
//
ExFreePool(keyValueInformation);
requestLength += 256;
} else {
break;
}
}
RtlFreeUnicodeString(&unicodeName);
ZwClose(handle);
if (NT_SUCCESS(status)) {
if (keyValueInformation->DataLength != 0) {
//
// Return the pointers to the caller.
//
*Information =
(PUCHAR)keyValueInformation + keyValueInformation->DataOffset;
*FreePoolAddress = keyValueInformation;
} else {
//
// Treat as a no value case.
//
DebugPrint((3, "FtpReturnRegistryInformation: No Size\n"));
ExFreePool(keyValueInformation);
status = STATUS_OBJECT_NAME_NOT_FOUND;
}
} else {
//
// Free the memory on failure.
//
DebugPrint((3, "FtpReturnRegistryInformation: No Value => %x\n",
status));
ExFreePool(keyValueInformation);
}
return status;
} // FtpReturnRegistryInformation
NTSTATUS
FtpWriteRegistryInformation(
IN PUCHAR ValueName,
IN PVOID Information,
IN ULONG InformationLength
)
/*++
Routine Description:
This routine writes the configuration registry
for the configuration information of the FT subsystem.
Arguments:
ValueName - an Ascii string for the value name to be written.
Information - a pointer to a buffer area containing the information.
InformationLength - the length of the buffer area.
Return Value:
NTSTATUS
--*/
{
NTSTATUS status;
HANDLE handle;
STRING string;
UNICODE_STRING unicodeName;
RtlInitString(&string, DISK_REGISTRY_KEY);
status = RtlAnsiStringToUnicodeString(&unicodeName,
&string,
TRUE);
if (!NT_SUCCESS(status)) {
return status;
}
status = FtpOpenKey(&handle,
&unicodeName);
RtlFreeUnicodeString(&unicodeName);
if (NT_SUCCESS(status)) {
RtlInitString(&string,
ValueName);
status = RtlAnsiStringToUnicodeString(&unicodeName,
&string,
TRUE);
if (!NT_SUCCESS(status)) {
return status;
}
status = ZwSetValueKey(handle,
&unicodeName,
0,
REG_BINARY,
Information,
InformationLength);
RtlFreeUnicodeString(&unicodeName);
//
// Force this out to disk.
//
ZwFlushKey(handle);
ZwClose(handle);
}
return status;
} // FtpWriteRegistryInformation
#define TOO_MANY_BAD_MEMBERS 2
VOID
FtpConfigure(
IN PDEVICE_EXTENSION FtRootExtension,
IN BOOLEAN MaintenanceMode
)
/*++
Routine Description:
This routine queries the configuration registry
for the configuration information of the FT subsystem,
then proceeds to locate all FT members defined in the
registry and link FT device extensions to create the
FT components.
WARNING: How does the potential registry update that this routine
performs get synchronized with a registry update due to orphaning
an existing FT set? This can happen in the dynamic partitioning
case.
Arguments:
FtRootExtension - pointer to the device extension for the root of
the FT device list.
MaintenanceMode - Indicates that this is a maintenance invocation.
Return Value:
None.
--*/
{
NTSTATUS status;
ULONG index;
ULONG member;
ULONG badMembers;
PVOID freePoolAddress;
PDEVICE_EXTENSION currentMember;
PDEVICE_EXTENSION previousMember;
PDEVICE_EXTENSION zeroMember;
PDISK_CONFIG_HEADER registry;
PDISK_PARTITION diskPartition;
PDISK_PARTITION orphanPartition;
PFT_REGISTRY ftRegistry;
PFT_DESCRIPTION ftDescription;
PFT_MEMBER_DESCRIPTION ftMember;
PFT_REGENERATE_REGION regenerateRegion;
ULONG alignmentRequirement;
BOOLEAN writeRegistryBack = FALSE;
BOOLEAN haveMemberToRegenerate = FALSE;
BOOLEAN dirtyShutdown = FALSE;
//
// Find the FT section in the configuration.
//
status = FtpReturnRegistryInformation(DISK_REGISTRY_VALUE,
&freePoolAddress,
(PVOID) &registry);
if (!NT_SUCCESS(status)) {
//
// No registry data.
//
return;
}
if (registry->FtInformationSize == 0) {
//
// No FT components in the registry.
//
ExFreePool(freePoolAddress);
return;
}
if (!MaintenanceMode) {
//
// Determine if system was shutdown properly.
//
if (registry->DirtyShutdown) {
//
// Log that a dirty shutdown was detected.
//
dirtyShutdown = TRUE;
FtpLogError(FtRootExtension,
FT_DIRTY_SHUTDOWN,
0,
0,
NULL);
} else {
//
// Write back registry now setting dirty flag.
//
registry->DirtyShutdown = TRUE;
FtpWriteRegistryInformation(DISK_REGISTRY_VALUE,
registry,
registry->FtInformationOffset +
registry->FtInformationSize);
}
}
//
// Construct the necessary links for the NTFT volumes in the system.
//
ftRegistry = (PFT_REGISTRY)
((PUCHAR)registry + registry->FtInformationOffset);
ftDescription = &ftRegistry->FtDescription[0];
for (index = 0; index < (ULONG) ftRegistry->NumberOfComponents; index++) {
previousMember = NULL;
badMembers = 0;
orphanPartition = NULL;
zeroMember = NULL;
for (member = 0;
member < (ULONG) ftDescription->NumberOfMembers;
member++) {
ftMember = &ftDescription->FtMemberDescription[member];
diskPartition = FtpFindPartitionRegistry(registry,
ftMember);
//
// Find a corresponding device extension for this registry
// entry.
//
currentMember = FtpFindDeviceExtension(FtRootExtension,
ftMember->Signature,
diskPartition->StartingOffset,
diskPartition->Length);
if (currentMember == NULL) {
//
// Failure to find member of FT volume.
// Create a fake member device extension and mark it as
// orphaned.
//
DebugPrint((1,
"FtpConfigure: Missing NTFT member %x\n",
member));
//
// Create a place holder for this device extension in the
// FT chain.
//
status = FtpCreateMissingDevice(FtRootExtension,
ftDescription->Type,
diskPartition->FtGroup,
diskPartition->FtMember,
&currentMember);
if (!NT_SUCCESS(status)) {
//
// Set zero member to NULL so this set can't be accessed.
//
zeroMember = NULL;
break;
}
//
// Indicate internally that the member is missing.
//
currentMember->MemberState = Orphaned;
//
// Notify the user via the event log.
//
FtpLogError(currentMember,
FT_MISSING_MEMBER,
0,
0,
NULL);
} else {
if (currentMember->Type == NotAnFtMember) {
//
// Only let new sets be created inside this routine
//
currentMember->MemberState = diskPartition->FtState;
currentMember->Flags &= ~FTF_CONFIGURATION_CHANGED;
} else {
if (currentMember->Flags & FTF_CONFIGURATION_CHANGED) {
//
// This set is expected to change so it will be
// "reconstructed" at this time. The assumption
// is that the set is locked so no I/O may be
// occurring on the set will it is being built.
//
currentMember->MemberState = diskPartition->FtState;
currentMember->Flags &= ~FTF_CONFIGURATION_CHANGED;
} else {
//
// If this set is not marked for change then there
// is the possibility of I/O being active on the
// set so do not modify the set.
//
member = ftDescription->NumberOfMembers;
goto NextGroup;
}
}
//
// If this partition is going to be something other than
// the zeroth member of the set, clear the verify flag.
//
if (diskPartition->FtMember) {
//
// For dynamic partitioning, it is possible for the target
// device object to be in the "do verify" state. Since
// this object is now going to be used in a new FT set,
// and is not going to be the "real device" object,
// remove this flag from the target object.
//
FtThreadSetVerifyState(currentMember, FALSE);
}
//
// Since this extension is being used for a new FT set
// insure that the next member pointer is NULL.
//
currentMember->NextMember = NULL;
}
//
// If these fields are already set up and haven't changed
// then leave them alone.
//
if (!MaintenanceMode || diskPartition->Modified) {
//
// Fill in as much of the member device extension as possible.
//
currentMember->Type = ftDescription->Type;
currentMember->FtGroup = diskPartition->FtGroup;
currentMember->MemberRole = diskPartition->FtMember;
currentMember->FtCount.NumberOfMembers = ftDescription->NumberOfMembers;
currentMember->FtUnion.Identity.Signature = ftMember->Signature;
currentMember->FtUnion.Identity.PartitionOffset =
diskPartition->StartingOffset;
currentMember->FtUnion.Identity.PartitionLength =
diskPartition->Length;
currentMember->FtUnion.Identity.OriginalLength =
diskPartition->FtLength;
currentMember->ObjectUnion.FtRootObject =
FtRootExtension->DeviceObject;
currentMember->WritePolicy = Parallel;
currentMember->ReadPolicy = ReadPrimary;
currentMember->IgnoreReadPolicy = FALSE;
currentMember->Flags = FtRootExtension->Flags;
}
//
// Perform member specific work. If this is member zero,
// set up the variables for it. Otherwise check for member
// one of an Stripe with parity or Mirror and member zero
// is orphaned.
//
if (currentMember->MemberRole == 0) {
//
// Establish zero member and set state to initializing.
// This is a very primitive mechanism to synchronize this
// routine with active IO.
//
// What about dynamic configuration? (ie Maintanance mode)
//
zeroMember = currentMember;
zeroMember->VolumeState = FtInitializing;
//
// Set up the regeneration region if necessary.
//
regenerateRegion = &zeroMember->RegenerateRegion;
if (!(zeroMember->Flags & FTF_REGENERATION_REGION_INITIALIZED)) {
KeInitializeSpinLock(&currentMember->IrpCountSpinLock);
InitializeRegenerateRegion(zeroMember, regenerateRegion);
zeroMember->Flags |= FTF_REGENERATION_REGION_INITIALIZED;
}
//
// Check if Stripe or StripeWithParity to determine if
// lookaside listhead should be initialized.
//
switch (zeroMember->Type) {
case Stripe:
//
// Initialize RCB lookaside listhead if necessary.
//
if (!(FtRootExtension->Flags & FTF_RCB_LOOKASIDE_ALLOCATED)) {
//
// Set up lookaside listhead for RCBs.
//
FtpInitializeRcbLookasideListHead(FtRootExtension);
//
// Zero active stripe recovery thread count.
//
FtRootExtension->StripeThreadCount = 0;
//
// Indicate lookaside listhead is ready.
//
FtRootExtension->Flags |= FTF_RCB_LOOKASIDE_ALLOCATED;
}
break;
case StripeWithParity:
//
// Initialize RCB lookaside listhead if necessary.
//
if (!(FtRootExtension->Flags & FTF_RCB_LOOKASIDE_ALLOCATED)) {
//
// Set up lookaside listhead for RCBs.
//
FtpInitializeRcbLookasideListHead(FtRootExtension);
//
// Zero active stripe recovery thread count.
//
FtRootExtension->StripeThreadCount = 0;
//
// Indicate lookaside listhead is ready.
//
FtRootExtension->Flags |= FTF_RCB_LOOKASIDE_ALLOCATED;
}
//
// Initialize restart thread if necessary.
//
if (!(FtRootExtension->Flags & FTF_RESTART_THREAD_STARTED)) {
FtCreateThread(FtRootExtension,
&FtRootExtension->RestartThread,
(PKSTART_ROUTINE)FtRestartThread);
FtRootExtension->Flags |= FTF_RESTART_THREAD_STARTED;
}
//
// Allocate emergency buffers to handle the situation
// where there is no memory for some operation so the
// cache manager flushes pages to create pool, but the
// FTDISK driver doesn't have the extra buffers to
// process the request.
//
if (!(FtRootExtension->Flags & FTF_EMERGENCY_BUFFER_ALLOCATED)) {
FtRootExtension->ParityBuffers =
ExAllocatePool(NonPagedPoolCacheAligned,
STRIPE_SIZE * 2);
//
// Set bit showing emergency buffer is allocated.
//
if (FtRootExtension->ParityBuffers) {
FtRootExtension->Flags |= FTF_EMERGENCY_BUFFER_ALLOCATED;
}
}
//
// Fall through to common code.
//
case Mirror:
//
// Start recovery thread if necessary.
//
if (!(FtRootExtension->Flags & FTF_RECOVERY_THREAD_STARTED)) {
FtCreateThread(FtRootExtension,
&FtRootExtension->FtUnion.Thread,
(PKSTART_ROUTINE)FtRecoveryThread);
FtRootExtension->Flags |= FTF_RECOVERY_THREAD_STARTED;
}
break;
}
} else {
if (currentMember->Type == Mirror && !MaintenanceMode) {
//
// If the secondary mirror is smaller than the primary,
// this is a configuration error.
//
if (currentMember->FtUnion.Identity.PartitionLength.QuadPart <
previousMember->FtUnion.Identity.PartitionLength.QuadPart) {
//
// Orphan this member and set it up so it will
// be written back to the registry.
//
currentMember->MemberState = Orphaned;
orphanPartition = diskPartition;
diskPartition->FtState = Orphaned;
}
if (((currentMember->FtUnion.Identity.PartitionType & VALID_NTFT) == VALID_NTFT) &&
(zeroMember->MemberState != Orphaned)) {
//
// The system was booted from a revived primary partition
// so the hives say the mirror is ok, when in fact they
// are not. The "real" image of the hives are on the
// shadow. Force the user to boot from the shadow.
//
KeBugCheckEx(FTDISK_INTERNAL_ERROR,
(ULONG)zeroMember,
(ULONG)zeroMember->Type,
(ULONG)zeroMember->FtGroup,
zeroMember->FtUnion.Identity.Signature);
}
}
}
//
// Set zero member address in device extension.
//
currentMember->ZeroMember = zeroMember;
//
// If member orphaned or regenerating, increment bad member counter.
//
if (currentMember->MemberState == Orphaned) {
zeroMember->VolumeState = FtHasOrphan;
orphanPartition = diskPartition;
badMembers++;
} else if (currentMember->MemberState == Regenerating) {
zeroMember->VolumeState = FtRegenerating;
badMembers++;
}
//
// Point all members to the zero member regeneration region.
//
currentMember->RegenerateRegionForGroup = regenerateRegion;
//
// Link the current member.
//
if (previousMember) {
previousMember->NextMember = currentMember;
}
previousMember = currentMember;
} // end for (member...)
//
// Perform sanity check.
//
if (zeroMember == NULL) {
//
// Log this error. Bad configuration information.
//
DebugPrint((1,
"FtpConfigure: Bad configuration\n"));
FtpLogError(FtRootExtension,
FT_BAD_CONFIGURATION,
0,
0,
NULL);
break;
}
//
// Take appropriate action based on number of bad members.
//
switch (badMembers) {
case 0:
if (zeroMember->MemberState == Initializing) {
//
// Create and start a system thread to initialize this set.
//
zeroMember->VolumeState = FtInitializing;
FtThreadStartNewThread(zeroMember,
FT_INITIALIZE_SET,
NULL);
} else {
//
// Set volume state to healthy.
//
zeroMember->VolumeState = FtStateOk;
//
// Check for dirty shutdown. Dirty shutdowns can
// cause primary and secondary data to be out of
// sync. The dirty flag has already been set in
// the registry for this boot. A clean shutdown
// clears the dirty flag.
//
if (dirtyShutdown) {
//
// Check if fault-tolerant FT type.
//
if ((zeroMember->Type == StripeWithParity) ||
(zeroMember->Type == Mirror)) {
//
// Synchronize primary and secondary data.
//
FtThreadStartNewThread(zeroMember,
FT_SYNC_REDUNDANT_COPY,
NULL);
}
}
}
break;
case 1:
//
// If this is a stripe or volume set then is must be disabled. If
// a volume that needs initialization has a bad member it should
// be disabled as well.
//
if (zeroMember->Type == Stripe ||
zeroMember->Type == VolumeSet ||
zeroMember->MemberState == Initializing) {
//
// Change volume state to disabled and log error.
//
zeroMember->VolumeState = FtDisabled;
FtpLogError(zeroMember,
FT_CANT_USE_SET,
0,
0,
NULL);
} else if (zeroMember->VolumeState == FtRegenerating) {
//
// Create and start the system thread to do the regenerate.
//
FtThreadStartNewThread(zeroMember,
FT_REGENERATE,
NULL);
} else if (zeroMember->VolumeState == FtHasOrphan) {
//
// If the zero member is orphaned then the drive letter
// assignment may have to be switched to the next member.
//
if (zeroMember->MemberState == Orphaned) {
//
// Need to assign the drive letter to the first
// member of the set after the zeroth member.
//
ftMember = &ftDescription->FtMemberDescription[1];
diskPartition = FtpFindPartitionRegistry(registry,
ftMember);
diskPartition->AssignDriveLetter = TRUE;
orphanPartition->AssignDriveLetter = FALSE;
if (zeroMember->Type == Mirror) {
//
// Make sure the partition type is marked so if
// the user attempts to boot from the primary
// it will fail.
//
if (zeroMember->NextMember) {
FtpMarkMirrorPartitionType(zeroMember->NextMember);
}
}
}
//
// Write back registry with new orphan informaiton.
//
orphanPartition->FtState = Orphaned;
ftDescription->FtVolumeState = FtHasOrphan;
writeRegistryBack = TRUE;
}
break;
default:
//
// Too many bad members to do anything useful.
// Change volume state to disabled and log error.
//
zeroMember->VolumeState = FtDisabled;
FtpLogError(zeroMember,
FT_CANT_USE_SET,
0,
0,
NULL);
break;
}
//
// Propogate target alignment requirements.
//
currentMember = zeroMember;
alignmentRequirement = 0;
//
// Prepare alignment mask.
//
while (currentMember) {
alignmentRequirement |=
currentMember->DeviceObject->AlignmentRequirement;
currentMember = currentMember->NextMember;
}
//
// Jam zero member with alignment requirement. If zero member
// is orphaned then set alignment requirements in device object
// which is exposed.
//
if (zeroMember->MemberState == Orphaned &&
zeroMember->NextMember) {
zeroMember->NextMember->DeviceObject->AlignmentRequirement =
alignmentRequirement;
} else {
zeroMember->DeviceObject->AlignmentRequirement =
alignmentRequirement;
}
//
// Get next group.
//
NextGroup:
ftDescription = (PFT_DESCRIPTION)
&ftDescription->FtMemberDescription[member];
} // end for each FT component
if (writeRegistryBack == TRUE) {
//
// The registry is written back if during initialization of the
// FT components it turns out that a member of a mirror set
// or stripe with parity is missing. The missing member is orphaned
// immediately in the registry.
//
FtpWriteRegistryInformation(DISK_REGISTRY_VALUE,
registry,
registry->FtInformationOffset +
registry->FtInformationSize);
}
ExFreePool(freePoolAddress);
return;
} // FtpConfigure
NTSTATUS
FtpCreateMissingDevice(
IN PDEVICE_EXTENSION FtRootExtension,
IN FT_TYPE Type,
IN USHORT FtGroup,
IN USHORT MemberRole,
IN OUT PDEVICE_EXTENSION *DeviceExtension
)
/*++
Routine Description:
Used during FT initialization, this routine will create a device
to represent a missing member.
Arguments:
DriverObject - the driver object passed in by the system.
Type - the FT volume type.
FtGroup - the group number for the volume.
MemberRole - the member role within this group.
DeviceExtensionPtr - Place to return the pointer to the newly created
device extension.
Return Value:
NTSTATUS
--*/
{
UCHAR nameBuffer[64];
ANSI_STRING deviceNameString;
UNICODE_STRING unicodeDeviceName;
OBJECT_ATTRIBUTES objectAttributes;
PDEVICE_EXTENSION deviceExtension;
PDEVICE_OBJECT newObject;
NTSTATUS status;
PUCHAR typeName;
PFILE_OBJECT fileObject;
//
// Get the string for the type of the missing member.
//
switch (Type) {
case Mirror:
typeName = "Mirror";
break;
case StripeWithParity:
typeName = "ParityStripe";
break;
case VolumeSet:
typeName = "VolumeSet";
break;
case Stripe:
typeName = "Stripe";
break;
default:
DebugPrint((4, "FtpCreateMissingDevice: Improper type %x\n", Type));
ASSERT(Type == Mirror);
return STATUS_UNSUCCESSFUL;
break;
}
//
// Construct the missing member name.
//
sprintf(nameBuffer,
"\\Device\\Missing%s%dMember%d",
typeName,
FtGroup,
MemberRole);
//
// Create device object for this partition.
//
RtlInitString(&deviceNameString,
nameBuffer);
status = RtlAnsiStringToUnicodeString(&unicodeDeviceName,
&deviceNameString,
TRUE);
if (!NT_SUCCESS(status)) {
return status;
}
InitializeObjectAttributes(&objectAttributes,
&unicodeDeviceName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
//
// Check if this object exists.
//
status = IoGetDeviceObjectPointer(&unicodeDeviceName,
FILE_READ_ATTRIBUTES,
&fileObject,
&newObject);
if (!NT_SUCCESS(status)) {
//
// If it doesn't exist, then create it.
//
DebugPrint((4, "FtpCreateMissingDevice: Create device %s\n", nameBuffer));
status = IoCreateDevice(FtRootExtension->ObjectUnion.FtDriverObject,
sizeof(DEVICE_EXTENSION),
&unicodeDeviceName,
FILE_DEVICE_DISK,
0,
FALSE,
&newObject);
if (!NT_SUCCESS(status)) {
//
// Give up.
//
RtlFreeUnicodeString(&unicodeDeviceName);
return status;
}
//
// Initialize the new device extension and link it on the list
// of missing devices anchored at the root extension.
//
*DeviceExtension = (PDEVICE_EXTENSION) newObject->DeviceExtension;
(*DeviceExtension)->MissingMemberChain = NULL;
(*DeviceExtension)->DeviceObject = newObject;
//
// Link this device extension into the missing member chain.
//
if ((deviceExtension = FtRootExtension->MissingMemberChain) == NULL) {
FtRootExtension->MissingMemberChain = *DeviceExtension;
} else {
while (deviceExtension->MissingMemberChain != NULL) {
deviceExtension = deviceExtension->MissingMemberChain;
}
deviceExtension->MissingMemberChain = *DeviceExtension;
}
} else {
//
// Assume device extension is already linked in the missing devices
// list anchored at the root extension.
//
DebugPrint((4, "FtpCreateMissingDevice: Device %s already exists\n", nameBuffer));
*DeviceExtension = (PDEVICE_EXTENSION) newObject->DeviceExtension;
ObDereferenceObject(fileObject);
}
RtlFreeUnicodeString(&unicodeDeviceName);
return status;
}
VOID
FtpChangeMemberStateInRegistry(
IN PDEVICE_EXTENSION DeviceExtension,
IN FT_PARTITION_STATE NewState
)
/*++
Routine Description:
This routine is called either during initialization or via the
system worker thread. This routine will read the FT
registry information and mark the partition state as indicated, then
write the result.
Arguments:
DeviceExtension - the internal description of the partition to change.
NewState - the new state of the partition.
Return Value:
NTSTATUS
--*/
{
ULONG signature = DeviceExtension->FtUnion.Identity.Signature;
FT_TYPE type = DeviceExtension->Type;
USHORT group = DeviceExtension->FtGroup;
USHORT member = DeviceExtension->MemberRole;
PDISK_CONFIG_HEADER registry;
PDISK_REGISTRY diskRegistry;
PDISK_DESCRIPTION diskDescription;
PVOID freePoolAddress;
USHORT i;
NTSTATUS status;
DebugPrint((1,
"FtpChangeMemberStateInRegistry: Called for %x %d %d %d\n",
signature,
type,
group,
member));
status = FtpReturnRegistryInformation(DISK_REGISTRY_VALUE,
&freePoolAddress,
(PVOID) &registry);
if (!NT_SUCCESS(status)) {
//
// No registry data.
//
DebugPrint((1,
"FtpChangeMemberStateInRegistry: No Value => 0x%x\n",
status));
ASSERT(0);
return;
}
if (registry->FtInformationSize == 0) {
//
// No FT components in the registry.
//
ExFreePool(freePoolAddress);
DebugPrint((1, "FtpChangeMemberStateInRegistry: No FT components.\n"));
ASSERT(0);
return;
}
//
// Find the registry information for the disk partition.
//
diskRegistry = (PDISK_REGISTRY)
((PUCHAR)registry + registry->DiskInformationOffset);
diskDescription = &diskRegistry->Disks[0];
for (i = 0; i < diskRegistry->NumberOfDisks; i++) {
DebugPrint((2,
"FtpChangeMemberStateInRegistry: Checking disk %x\n",
diskDescription->Signature));
if (diskDescription->Signature == signature) {
USHORT j;
PDISK_PARTITION diskPartition;
for (j = 0; j < diskDescription->NumberOfPartitions; j++) {
diskPartition = &diskDescription->Partitions[j];
if ((diskPartition->FtType == type) &&
(diskPartition->FtGroup == group) &&
(diskPartition->FtMember == member)) {
//
// Found the member.
//
diskPartition->FtState = NewState;
DebugPrint((2,
"FtpChangeMemberStateInRegistry: Writing new info %x\n",
signature));
FtpWriteRegistryInformation(DISK_REGISTRY_VALUE,
registry,
registry->FtInformationOffset +
registry->FtInformationSize);
ExFreePool(freePoolAddress);
return;
}
}
}
diskDescription = (PDISK_DESCRIPTION)
&diskDescription->Partitions[diskDescription->NumberOfPartitions];
}
DebugPrint((1,
"FtpChangeMemberStateInRegistry: Did not update registry\n"));
ExFreePool(freePoolAddress);
return;
}
VOID
FtpInitializeRcbLookasideListHead(
IN PDEVICE_EXTENSION FtRootExtension
)
/*++
Routine Description:
This routine sets up a lookaside listhead for request control blocks.
Arguments:
FtRootExtension - FtRoot device extension.
Return Value:
None.
--*/
{
//
// Initialize Rcb lookaide listhead.
//
ExInitializeNPagedLookasideList(&FtRootExtension->RcbLookasideListHead,
NULL,
NULL,
0,
sizeof(RCB),
'crTF',
4096 / sizeof(RCB));
return;
} // end FtpInitializeRcbLookasideListHead()
NTSTATUS
FtpAttachDevices(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
This is the completion routine for handling IOCTL_SET_DRIVE_LAYOUT.
Arguments:
DeviceObject - not used.
Irp - completing IRP.
Context - RCB
Return Value:
NTSTATUS
--*/
{
PRCB rcb = Context;
IN PDRIVE_LAYOUT_INFORMATION partitionList;
PDEVICE_EXTENSION deviceExtension = rcb->ZeroExtension;
PDEVICE_EXTENSION ftRootExtension =
deviceExtension->ObjectUnion.FtRootObject->DeviceExtension;
PDRIVER_OBJECT driverObject = ftRootExtension->ObjectUnion.FtDriverObject;
ULONG recognizedPartition = 0;
ULONG partitionNumber;
PPARTITION_INFORMATION partitionEntry;
UCHAR ntNameBuffer[64];
UCHAR ftNameBuffer[64];
NTSTATUS status;
//
// Get partition list from IRP.
//
partitionList = Irp->AssociatedIrp.SystemBuffer;
for (partitionNumber = 0; partitionNumber <
partitionList->PartitionCount; partitionNumber++) {
//
// Get pointer to partition entry.
//
partitionEntry =
&partitionList->PartitionEntry[partitionNumber];
//
// Check if partition entry describes a partition.
//
if ((partitionEntry->PartitionType != PARTITION_ENTRY_UNUSED) &&
!IsContainerPartition(partitionEntry->PartitionType)) {
//
// Bump count of recognized partitions.
//
recognizedPartition++;
//
// Check if this partition entry has changed.
//
if (partitionEntry->RewritePartition) {
//
// Create NT device name for this partition.
//
sprintf(ntNameBuffer,
"\\Device\\Harddisk%d\\Partition%d",
deviceExtension->FtUnion.Identity.DiskNumber,
recognizedPartition);
sprintf(ftNameBuffer,
"\\Device\\Harddisk%d\\Ft%d",
deviceExtension->FtUnion.Identity.DiskNumber,
recognizedPartition);
status = FtpAttach(driverObject,
ftNameBuffer,
ntNameBuffer,
&deviceExtension);
if (!NT_SUCCESS(status)) {
DebugPrint((1,
"FtpAttachDevices: Couldn't attach to %s status %x\n",
ntNameBuffer,
status));
}
} // end if (partitionEntry->RewritePartition)
} // end if (partitionEntry->PartitionType ...)
} // end for (partitionNumber ...)
return STATUS_SUCCESS;
} // end FtpAttachDevices()