/*++ 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) ®istry); 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, ¤tMember); 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(¤tMember->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) ®istry); 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()