NT4/private/utils/fdisk/fdft.c
2020-09-30 17:12:29 +02:00

1501 lines
39 KiB
C

/*++
Copyright (c) 1991-1993 Microsoft Corporation
Module Name:
fdft.c
Abstract:
This module contains FT support routines for Disk Administrator
Author:
Edward (Ted) Miller (TedM) 11/15/91
Environment:
User process.
Notes:
Revision History:
11-Nov-93 (bobri) minor changes - mostly cosmetic.
--*/
#include "fdisk.h"
#include <string.h>
// This variable heads a linked list of ft object sets.
PFT_OBJECT_SET FtObjects = NULL;
// Array of pointers to registry disk descriptors that we
// remember, ie, save for later use when a disk is not physically
// present on the machine.
PDISK_DESCRIPTION *RememberedDisks;
ULONG RememberedDiskCount;
ULONG
FdpDetermineDiskDescriptionSize(
PDISKSTATE DiskState
);
ULONG
FdpConstructDiskDescription(
IN PDISKSTATE DiskState,
OUT PDISK_DESCRIPTION DiskDescription
);
VOID
FdpRememberDisk(
IN PDISK_DESCRIPTION DiskDescription
);
VOID
FdpInitializeMirrors(
VOID
);
#define MAX_FT_SET_TYPES 4
ULONG OrdinalToAllocate[MAX_FT_SET_TYPES] = {0, 0, 0, 0};
VOID
MaintainOrdinalTables(
IN FT_TYPE FtType,
IN ULONG Ordinal
)
/*++
Routine Description:
Maintain the minimum and maximum Ordinal value recorded.
Arguments:
FtType - the type of the FT set.
Ordinal - the in use FtGroup (or ordinal) number
Return Value:
None
--*/
{
if (Ordinal > OrdinalToAllocate[FtType]) {
OrdinalToAllocate[FtType] = Ordinal;
}
}
DWORD
FdftNextOrdinal(
IN FT_TYPE FtType
)
/*++
Routine Description:
Allocate a number that will uniquely identify the FT set
from other sets of the same type. This number must be unique
from any given or used by FT sets of the same type due to
requirements of FT dynamic partitioning.
Arguments:
FtType - The type of the FT set.
Return Value:
The FtGroup number -- called an "ordinal" in the internal
structures.
--*/
{
DWORD ord;
PFT_OBJECT_SET pftset;
BOOL looping;
// The Ordinal value is going to be used as an FtGroup number
// FtGroups are USHORTs so don't wrap on the Ordinal. Try
// to keep the next ordinal in the largest opening range, that
// is if the minimum found is > half way through a USHORT, start
// the ordinals over at zero.
if (OrdinalToAllocate[FtType] > 0x7FFE) {
OrdinalToAllocate[FtType] = 0;
}
ord = OrdinalToAllocate[FtType];
ord++;
do {
looping = FALSE;
pftset = FtObjects;
while (pftset) {
if ((pftset->Type == FtType) && (pftset->Ordinal == ord)) {
ord++;
looping = TRUE;
break;
}
pftset = pftset->Next;
}
} while (looping);
OrdinalToAllocate[FtType] = (ord + 1);
return ord;
}
VOID
FdftCreateFtObjectSet(
IN FT_TYPE FtType,
IN PREGION_DESCRIPTOR *Regions,
IN DWORD RegionCount,
IN FT_SET_STATUS Status
)
/*++
Routine Description:
Create the FT set structures for the give collection of
region pointers.
Arguments:
FtType
Regions
RegionCount
Status
Return Value:
None
--*/
{
DWORD Ord;
PFT_OBJECT_SET FtSet;
PFT_OBJECT FtObject;
FtSet = Malloc(sizeof(FT_OBJECT_SET));
// Figure out an ordinal for the new object set.
FtSet->Ordinal = FdftNextOrdinal(FtType);
FtSet->Type = FtType;
FtSet->Members = NULL;
FtSet->Member0 = NULL;
FtSet->Status = Status;
// Link the new object set into the list.
FtSet->Next = FtObjects;
FtObjects = FtSet;
// For each region in the set, associate the ft info with it.
for (Ord=0; Ord<RegionCount; Ord++) {
FtObject = Malloc(sizeof(FT_OBJECT));
// If this is a creation of a stripe set with parity, then
// we must mark the 0th item 'Initializing' instead of 'Healthy'.
if ((Ord == 0)
&& (FtType == StripeWithParity)
&& (Status == FtSetNewNeedsInitialization)) {
FtObject->State = Initializing;
} else {
FtObject->State = Healthy;
}
if (!Ord) {
FtSet->Member0 = FtObject;
}
FtObject->Set = FtSet;
FtObject->MemberIndex = Ord;
FtObject->Next = FtSet->Members;
FtSet->Members = FtObject;
SET_FT_OBJECT(Regions[Ord],FtObject);
}
}
BOOL
FdftUpdateFtObjectSet(
IN PFT_OBJECT_SET FtSet,
IN FT_SET_STATUS SetState
)
/*++
Routine Description:
Given an FT set, go back to the registry information and
update the state of the members with the state in the registry.
NOTE: The following condition may exist. It is possible for
the FtDisk driver to return that the set is in an initializing
or regenerating state and not have this fact reflected in the
registry. This can happen when the system has crashed and
on restart the FtDisk driver started the regeneration of the
check data (parity).
Arguments:
FtSet - the set to update.
Return Value:
TRUE if the set state provided has a strong likelyhood of being correct
FALSE if the NOTE condition above is occuring.
--*/
{
BOOLEAN allHealthy = TRUE;
PFT_OBJECT ftObject;
PDISK_REGISTRY diskRegistry;
PDISK_PARTITION partition;
PDISK_DESCRIPTION diskDescription;
DWORD ec;
ULONG diskIndex,
partitionIndex;
ec = MyDiskRegistryGet(&diskRegistry);
if (ec != NO_ERROR) {
// No registry information.
return TRUE;
}
diskDescription = diskRegistry->Disks;
for (diskIndex=0; diskIndex<diskRegistry->NumberOfDisks; diskIndex++) {
for (partitionIndex=0; partitionIndex<diskDescription->NumberOfPartitions; partitionIndex++) {
partition = &diskDescription->Partitions[partitionIndex];
if ((partition->FtType == FtSet->Type) && (partition->FtGroup == (USHORT) FtSet->Ordinal)) {
// Have a match for a partition within this set.
// Find the region descriptor for this partition and
// update its state accordingly.
for (ftObject = FtSet->Members; ftObject; ftObject = ftObject->Next) {
if (ftObject->MemberIndex == (ULONG) partition->FtMember) {
ftObject->State = partition->FtState;
break;
}
if (partition->FtState != Healthy) {
allHealthy = FALSE;
}
}
}
}
diskDescription = (PDISK_DESCRIPTION)&diskDescription->Partitions[diskDescription->NumberOfPartitions];
}
Free(diskRegistry);
if ((allHealthy) && (SetState != FtSetHealthy)) {
// This is a condition where the system must be
// updating the check data for redundant sets.
return FALSE;
}
return TRUE;
}
VOID
FdftDeleteFtObjectSet(
IN PFT_OBJECT_SET FtSet,
IN BOOL OffLineDisksOnly
)
/*++
Routine Description:
Delete an ft set, or rather its internal representation as a linked
list of ft member structures.
Arguments:
FtSet - supplies pointer to ft set structure for set to delete.
OffLineDisksOnly - if TRUE, then do not delete the set but instead
scan remembered disks for members of the set and remove such members.
Return Value:
None.
--*/
{
PFT_OBJECT ftObject = FtSet->Members;
PFT_OBJECT next;
PFT_OBJECT_SET ftSetTemp;
PDISK_DESCRIPTION diskDescription;
PDISK_PARTITION diskPartition;
ULONG partitionCount,
size,
i,
j;
// Locate any members of the ft set on remembered disks and
// remove the entries for such partitions.
for (i=0; i<RememberedDiskCount; i++) {
diskDescription = RememberedDisks[i];
partitionCount = diskDescription->NumberOfPartitions;
for (j=0; j<partitionCount; j++) {
diskPartition = &diskDescription->Partitions[j];
if ((diskPartition->FtType == FtSet->Type)
&& (diskPartition->FtGroup == (USHORT)FtSet->Ordinal)) {
// Found a member of the ft set being deleted on a
// remembered disk. Remove the partition from the
// remembered disk.
RtlMoveMemory( diskPartition,
diskPartition+1,
(partitionCount - j - 1) * sizeof(DISK_PARTITION)
);
partitionCount--;
j--;
}
}
if (partitionCount != diskDescription->NumberOfPartitions) {
diskDescription->NumberOfPartitions = (USHORT)partitionCount;
size = sizeof(DISK_DESCRIPTION);
if (partitionCount > 1) {
size += (partitionCount - 1) * sizeof(DISK_PARTITION);
}
RememberedDisks[i] = Realloc(RememberedDisks[i], size);
}
}
if (OffLineDisksOnly) {
return;
}
// First, free all members of the set
while (ftObject) {
next = ftObject->Next;
Free(ftObject);
ftObject = next;
}
// now, remove the set from the linked list of sets.
if (FtObjects == FtSet) {
FtObjects = FtSet->Next;
} else {
ftSetTemp = FtObjects;
while (1) {
FDASSERT(ftSetTemp);
if (ftSetTemp == NULL) {
break;
}
if (ftSetTemp->Next == FtSet) {
ftSetTemp->Next = FtSet->Next;
break;
}
ftSetTemp = ftSetTemp->Next;
}
}
Free(FtSet);
}
VOID
FdftExtendFtObjectSet(
IN OUT PFT_OBJECT_SET FtSet,
IN OUT PREGION_DESCRIPTOR* Regions,
IN DWORD RegionCount
)
/*++
Routine Description:
This function adds regions to an existing FT-set.
Arguments:
FtSet -- Supplies the set to extend.
Regions -- Supplies the regions to add to the set. Note
that these regions are updated with the FT
information.
RegionCount -- Supplies the number of regions to add.
Return Value:
None.
--*/
{
PFT_OBJECT FtObject;
DWORD i, StartingIndex;
// Determine the starting member index for the new regions.
// It is the greatest of the existing member indices plus one.
StartingIndex = 0;
for( FtObject = FtSet->Members; FtObject; FtObject = FtObject->Next ) {
if( FtObject->MemberIndex > StartingIndex ) {
StartingIndex = FtObject->MemberIndex;
}
}
StartingIndex++;
// Associate the ft-set's information with each of the
// new regions.
for( i = 0; i < RegionCount; i++ ) {
FtObject = Malloc( sizeof(FT_OBJECT) );
FtObject->Set = FtSet;
FtObject->MemberIndex = StartingIndex + i;
FtObject->Next = FtSet->Members;
FtObject->State = Healthy;
FtSet->Members = FtObject;
SET_FT_OBJECT(Regions[i],FtObject);
}
FtSet->Status = FtSetExtended;
}
PULONG DiskHadRegistryEntry;
ULONG
ActualPartitionCount(
IN PDISKSTATE DiskState
)
/*++
Routine Description:
Given a disk, this routine counts the number of partitions on it.
The number of partitions is the number of regions that appear in
the NT name space (ie, the maximum value of <x> in
\device\harddiskn\partition<x>).
Arguments:
DiskState - descriptor for the disk in question.
Return Value:
Partition count (may be 0).
--*/
{
ULONG i,PartitionCount=0;
PREGION_DESCRIPTOR region;
for(i=0; i<DiskState->RegionCount; i++) {
region = &DiskState->RegionArray[i];
if((region->SysID != SYSID_UNUSED) &&
!IsExtended(region->SysID) &&
IsRecognizedPartition(region->SysID)) {
PartitionCount++;
}
}
return(PartitionCount);
}
PDISKSTATE
LookUpDiskBySignature(
IN ULONG Signature
)
/*++
Routine Description:
This routine will look through the disk descriptors created by the
fdisk back end looking for a disk with a particular signature.
Arguments:
Signature - signature of disk to locate
Return Value:
Pointer to disk descriptor or NULL if no disk with the given signature
was found.
--*/
{
ULONG disk;
PDISKSTATE ds;
for(disk=0; disk<DiskCount; disk++) {
ds = Disks[disk];
if(ds->Signature == Signature) {
return(ds);
}
}
return(NULL);
}
PREGION_DESCRIPTOR
LookUpPartition(
IN PDISKSTATE DiskState,
IN LARGE_INTEGER Offset,
IN LARGE_INTEGER Length
)
/*++
Routine Description:
This routine will look through a region descriptor array for a
partition with a particular length and starting offset.
Arguments:
DiskState - disk on which to locate the partition
Offset - offset of partition on the disk to find
Length - size of the partition to find
Return Value:
Pointer to region descriptor or NULL if no such partition on that disk
--*/
{
ULONG regionIndex,
maxRegion = DiskState->RegionCount;
PREGION_DESCRIPTOR regionDescriptor;
LARGE_INTEGER offset,
length;
for (regionIndex=0; regionIndex<maxRegion; regionIndex++) {
regionDescriptor = &DiskState->RegionArray[regionIndex];
if ((regionDescriptor->SysID != SYSID_UNUSED) && !IsExtended(regionDescriptor->SysID)) {
offset = FdGetExactOffset(regionDescriptor);
length = FdGetExactSize(regionDescriptor, FALSE);
if ((offset.LowPart == Offset.LowPart )
&& (offset.HighPart == Offset.HighPart)
&& (length.LowPart == Length.LowPart)
&& (length.HighPart == Length.HighPart)) {
return regionDescriptor;
}
}
}
return NULL;
}
VOID
AddObjectToSet(
IN PFT_OBJECT FtObjectToAdd,
IN FT_TYPE FtType,
IN USHORT FtGroup
)
/*++
Routine Description:
Find the FtSet for that this object belongs to and insert
it into the chain of members. If the set cannot be found
in the existing collection of sets, create a new one.
Arguments:
FtObjectToAdd - the object point to be added.
FtType - the type of the FT set.
FtGroup - group for this object.
Return Value:
None
--*/
{
PFT_OBJECT_SET ftSet = FtObjects;
while (ftSet) {
if ((ftSet->Type == FtType) && (ftSet->Ordinal == FtGroup)) {
break;
}
ftSet = ftSet->Next;
}
if (!ftSet) {
// There is no such existing ft set. Create one.
ftSet = Malloc(sizeof(FT_OBJECT_SET));
ftSet->Status = FtSetHealthy;
ftSet->Type = FtType;
ftSet->Ordinal = FtGroup;
ftSet->Members = NULL;
ftSet->Next = FtObjects;
ftSet->Member0 = NULL;
ftSet->NumberOfMembers = 0;
FtObjects = ftSet;
}
FDASSERT(ftSet);
FtObjectToAdd->Next = ftSet->Members;
ftSet->Members = FtObjectToAdd;
ftSet->NumberOfMembers++;
FtObjectToAdd->Set = ftSet;
if (FtObjectToAdd->MemberIndex == 0) {
ftSet->Member0 = FtObjectToAdd;
}
if (FtType == StripeWithParity || FtType == Mirror) {
// Update the set's state based on the state of the new member:
switch (FtObjectToAdd->State) {
case Healthy:
// Doesn't change state of set.
break;
case Regenerating:
ftSet->Status = (ftSet->Status == FtSetHealthy ||
ftSet->Status == FtSetRegenerating)
? FtSetRegenerating
: FtSetBroken;
break;
case Initializing:
ftSet->Status = (ftSet->Status == FtSetHealthy ||
ftSet->Status == FtSetInitializing)
? FtSetInitializing
: FtSetBroken;
break;
default:
// If only one member is bad, the set is recoverable;
// otherwise, it's broken.
ftSet->Status = (ftSet->Status == FtSetHealthy)
? FtSetRecoverable
: FtSetDisabled;
break;
}
}
}
ULONG
InitializeFt(
IN BOOL DiskSignaturesCreated
)
/*++
Routine Description:
Search the disk registry information to construct the FT
relationships in the system.
Arguments:
DiskSignaturesCreated - boolean to indicate that new disks
were located in the system.
Return Value:
An error code if the disk registry could not be obtained.
--*/
{
ULONG disk,
partitionIndex,
partitionCount;
PDISK_REGISTRY diskRegistry;
PDISK_PARTITION partition;
PDISK_DESCRIPTION diskDescription;
PDISKSTATE diskState;
PREGION_DESCRIPTOR regionDescriptor;
DWORD ec;
BOOL configDiskChanged = FALSE,
configMissingDisk = FALSE,
configExtraDisk = FALSE;
PFT_OBJECT ftObject;
BOOL anyDisksOffLine;
TCHAR name[100];
RememberedDisks = Malloc(0);
RememberedDiskCount = 0;
ec = MyDiskRegistryGet(&diskRegistry);
if (ec != NO_ERROR) {
FDLOG((0,"InitializeFt: Error %u from MyDiskRegistryGet\n",ec));
return ec;
}
DiskHadRegistryEntry = Malloc(DiskCount * sizeof(ULONG));
memset(DiskHadRegistryEntry,0,DiskCount * sizeof(ULONG));
diskDescription = diskRegistry->Disks;
for (disk = 0; disk < diskRegistry->NumberOfDisks; disk++) {
// For the disk described in the registry, look up the
// corresponding actual disk found by the fdisk init code.
diskState = LookUpDiskBySignature(diskDescription->Signature);
if (diskState) {
FDLOG((2,
"InitializeFt: disk w/ signature %08lx is disk #%u\n",
diskDescription->Signature,
diskState->Disk));
DiskHadRegistryEntry[diskState->Disk]++;
partitionCount = ActualPartitionCount(diskState);
if (partitionCount != diskDescription->NumberOfPartitions) {
FDLOG((1,"InitializeFt: partition counts for disk %08lx don't match:\n", diskState->Signature));
FDLOG((1," Count from actual disk: %u\n",partitionCount));
FDLOG((1," Count from registry : %u\n",diskDescription->NumberOfPartitions));
configDiskChanged = TRUE;
}
} else {
// there's an entry in the registry that does not have a
// real disk to match. Remember this disk; if it has any
// FT partitions, we also want to display a message telling
// the user that something's missing.
FDLOG((1,"InitializeFt: Entry for disk w/ signature %08lx has no matching real disk\n", diskDescription->Signature));
for (partitionIndex = 0; partitionIndex < diskDescription->NumberOfPartitions; partitionIndex++) {
partition = &diskDescription->Partitions[partitionIndex];
if (partition->FtType != NotAnFtMember) {
// This disk has an FT partition, so Windisk will
// want to tell the user that some disks are missing.
configMissingDisk = TRUE;
break;
}
}
FdpRememberDisk(diskDescription);
}
for (partitionIndex = 0; partitionIndex < diskDescription->NumberOfPartitions; partitionIndex++) {
partition = &diskDescription->Partitions[partitionIndex];
regionDescriptor = NULL;
if (diskState) {
regionDescriptor = LookUpPartition(diskState,
partition->StartingOffset,
partition->Length);
}
// At this point one of three conditions exists.
//
// 1. There is no disk related to this registry information
// diskState == NULL && regionDescriptor == NULL
// 2. There is a disk, but no partition related to this information
// diskState != NULL && regionDescriptor == NULL
// 3. There is a disk and a partition related to this information
// diskState != NULL && regionDescriptor != NULL
//
// In any of these conditions, if the registry entry is part
// of an FT set and FT object must be created.
//
// that corresponds to a partition's entry in the
// disk registry database.
if (partition->FtType != NotAnFtMember) {
ftObject = Malloc(sizeof(FT_OBJECT));
ftObject->Next = NULL;
ftObject->Set = NULL;
ftObject->MemberIndex = partition->FtMember;
ftObject->State = partition->FtState;
// if a partition was actually found there will be a
// regionDescriptor that needs to be updated.
if (regionDescriptor && regionDescriptor->PersistentData) {
FT_SET_STATUS setState;
ULONG numberOfMembers;
SET_FT_OBJECT(regionDescriptor, ftObject);
// Before the drive letter is moved into the region
// data, be certain that the FT volume exists at this
// drive letter.
LowFtVolumeStatusByLetter(partition->DriveLetter,
&setState,
&numberOfMembers);
// If the numberOfMembers gets set to 1 then
// this letter is not the letter for the FT set,
// but rather a default letter assigned because the
// FT sets letter could not be assigned.
if (numberOfMembers > 1) {
PERSISTENT_DATA(regionDescriptor)->DriveLetter = partition->DriveLetter;
}
} else {
// There is no region for this partition
// so update the set state.
ftObject->State = Orphaned;
}
// Now place the ft object in the correct set,
// creating the set if necessary.
AddObjectToSet(ftObject, partition->FtType, partition->FtGroup);
MaintainOrdinalTables(partition->FtType, (ULONG) partition->FtGroup);
}
}
diskDescription = (PDISK_DESCRIPTION)&diskDescription->Partitions[diskDescription->NumberOfPartitions];
}
Free(diskRegistry);
// Check to see if every disk found by the fdisk back end has a
// corresponding registry entry.
for (disk = 0; disk < DiskCount; disk++) {
if (Disks[disk]->OffLine) {
continue;
}
if ((!DiskHadRegistryEntry[disk]) && (!IsRemovable(disk))) {
// a real disk does not have a matching registry entry.
FDLOG((1,"InitializeFt: Disk %u does not have a registry entry (disk sig = %08lx)\n",disk,Disks[disk]->Signature));
configExtraDisk = TRUE;
}
}
// Determine whether any disks are off line
anyDisksOffLine = FALSE;
for (disk = 0; disk < DiskCount; disk++) {
if (Disks[disk]->OffLine) {
anyDisksOffLine = TRUE;
break;
}
}
if (configMissingDisk || anyDisksOffLine) {
WarningDialog(MSG_CONFIG_MISSING_DISK);
}
if (configDiskChanged) {
RegistryChanged = TRUE;
WarningDialog(MSG_CONFIG_DISK_CHANGED);
}
if (configExtraDisk || DiskSignaturesCreated) {
BOOL BadConfigSet = FALSE;
WarningDialog(MSG_CONFIG_EXTRA_DISK);
// Update ft signature on each disk for which a new signature
// was created. and update registry for each disk with
// DiskHadRegistryEntry[Disk] == 0.
for (disk = 0; disk < DiskCount; disk++) {
BOOL b1 = TRUE,
b2 = TRUE;
if (Disks[disk]->OffLine) {
continue;
}
wsprintf(name, DiskN, disk);
if (Disks[disk]->SigWasCreated) {
if (ConfirmationDialog(MSG_NO_SIGNATURE, MB_ICONEXCLAMATION | MB_YESNO, name) == IDYES) {
b1 = (MasterBootCode(disk, Disks[disk]->Signature, TRUE, TRUE) == NO_ERROR);
} else {
Disks[disk]->OffLine = TRUE;
continue;
}
}
if (!DiskHadRegistryEntry[disk]) {
ULONG size;
size = FdpDetermineDiskDescriptionSize(Disks[disk]);
diskDescription = Malloc(size);
FdpConstructDiskDescription(Disks[disk], diskDescription);
FDLOG((2,"InitializeFt: Adding new disk %08lx to registry.\n", diskDescription->Signature));
LOG_ONE_DISK_REGISTRY_DISK_ENTRY("InitializeFt", diskDescription);
b2 = (EC(DiskRegistryAddNewDisk(diskDescription)) == NO_ERROR);
Free(diskDescription);
}
if (!(b1 && b2)) {
BadConfigSet = TRUE;
}
}
if (BadConfigSet) {
ErrorDialog(MSG_BAD_CONFIG_SET);
}
}
return NO_ERROR;
}
BOOLEAN
NewConfigurationRequiresFt(
VOID
)
/*++
Routine Description:
Search the diskstate and region arrays to determine if a single
FtDisk element (i.e. stripe, stripe set with parity, mirror or
volume set) is contained in the configuration.
Arguments:
None
Return Value:
TRUE if the new configuration requires the FtDisk driver.
FALSE otherwise.
--*/
{
ULONG disk,
region;
PDISKSTATE diskState;
PREGION_DESCRIPTOR regionDescriptor;
// Look at all disks in the system.
for (disk = 0; disk < DiskCount; disk++) {
diskState = Disks[disk];
if (diskState->OffLine || IsDiskRemovable[disk]) {
continue;
}
// Check each region on the disk.
for (region = 0; region < diskState->RegionCount; region++) {
regionDescriptor = &diskState->RegionArray[region];
if ((regionDescriptor->SysID != SYSID_UNUSED) && !IsExtended(regionDescriptor->SysID) && IsRecognizedPartition(regionDescriptor->SysID)) {
// If a single region has an FT Object, then FT
// is required and the search may be stopped.
if (GET_FT_OBJECT(regionDescriptor)) {
return TRUE;
}
}
}
}
// no FtObject was found.
return FALSE;
}
ULONG
SaveFt(
VOID
)
/*++
Routine Description:
This routine walks all of the internal structures and creates
the interface structure for the DiskRegistry interface.
Arguments:
None
Return Value:
success/failure code. NO_ERROR is success.
--*/
{
ULONG i;
ULONG disk,
partition;
ULONG size;
PDISK_REGISTRY diskRegistry;
PDISK_DESCRIPTION diskDescription;
PBYTE start,
end;
DWORD ec;
ULONG offLineDiskCount;
ULONG removableDiskCount;
// First count partitions and disks so we can allocate a structure
// of the correct size.
size = sizeof(DISK_REGISTRY) - sizeof(DISK_DESCRIPTION);
offLineDiskCount = 0;
removableDiskCount = 0;
for (i=0; i<DiskCount; i++) {
if (Disks[i]->OffLine) {
offLineDiskCount++;
} else if (IsDiskRemovable[i]) {
removableDiskCount++;
} else {
size += FdpDetermineDiskDescriptionSize(Disks[i]);
}
}
// Account for remembered disks.
size += RememberedDiskCount * sizeof(DISK_DESCRIPTION);
for (i=0; i<RememberedDiskCount; i++) {
if (RememberedDisks[i]->NumberOfPartitions > 1) {
size += (RememberedDisks[i]->NumberOfPartitions - 1) * sizeof(DISK_PARTITION);
}
}
diskRegistry = Malloc(size);
diskRegistry->NumberOfDisks = (USHORT)( DiskCount
+ RememberedDiskCount
- offLineDiskCount
- removableDiskCount);
diskRegistry->ReservedShort = 0;
diskDescription = diskRegistry->Disks;
for (disk=0; disk<DiskCount; disk++) {
if (Disks[disk]->OffLine || IsDiskRemovable[disk]) {
continue;
}
partition = FdpConstructDiskDescription(Disks[disk], diskDescription);
diskDescription = (PDISK_DESCRIPTION)&diskDescription->Partitions[partition];
}
// Toss in remembered disks.
for (i=0; i<RememberedDiskCount; i++) {
// Compute the beginning and end of this remembered disk's
// Disk Description:
partition = RememberedDisks[i]->NumberOfPartitions;
start = (PBYTE)RememberedDisks[i];
end = (PBYTE)&(RememberedDisks[i]->Partitions[partition]);
RtlMoveMemory(diskDescription, RememberedDisks[i], end - start);
diskDescription = (PDISK_DESCRIPTION)&diskDescription->Partitions[partition];
}
LOG_DISK_REGISTRY("SaveFt", diskRegistry);
ec = EC(DiskRegistrySet(diskRegistry));
Free(diskRegistry);
if (ec == NO_ERROR) {
FdpInitializeMirrors();
}
return(ec);
}
ULONG
FdpDetermineDiskDescriptionSize(
PDISKSTATE DiskState
)
/*++
Routine Description:
This routine takes a pointer to a disk and determines how much
memory is needed to contain the description of the disk by
counting the number of partitions on the disk and multiplying
the appropriate counts by the appropriate size of the structures.
Arguments:
DiskState - the disk in question.
Return Value:
The memory size needed to contain all of the information on the disk.
--*/
{
ULONG partitionCount;
ULONG size;
if (DiskState->OffLine) {
return(0);
}
size = sizeof(DISK_DESCRIPTION);
partitionCount = ActualPartitionCount(DiskState);
size += (partitionCount ? partitionCount-1 : 0) * sizeof(DISK_PARTITION);
return(size);
}
ULONG
FdpConstructDiskDescription(
IN PDISKSTATE DiskState,
OUT PDISK_DESCRIPTION DiskDescription
)
/*++
Routine Description:
Given a disk state pointer as input, construct the FtRegistry
structure to describe the partitions on the disk.
Arguments:
DiskState - the disk for which to construct the information
DiskDescription - the memory location where the registry
structure is to be created.
Return Value:
The number of partitions described in the DiskDescription.
--*/
{
PDISKSTATE ds = DiskState;
ULONG partition,
region;
PREGION_DESCRIPTOR regionDescriptor;
PDISK_PARTITION diskPartition;
CHAR driveLetter;
BOOLEAN assignDriveLetter;
PFT_OBJECT ftObject;
PFT_OBJECT_SET ftSet;
partition = 0;
for (region=0; region<ds->RegionCount; region++) {
regionDescriptor = &ds->RegionArray[region];
if ((regionDescriptor->SysID != SYSID_UNUSED) && !IsExtended(regionDescriptor->SysID) && IsRecognizedPartition(regionDescriptor->SysID)) {
diskPartition = &DiskDescription->Partitions[partition++];
diskPartition->StartingOffset = FdGetExactOffset(regionDescriptor);
diskPartition->Length = FdGetExactSize(regionDescriptor, FALSE);
diskPartition->LogicalNumber = (USHORT)regionDescriptor->PartitionNumber;
switch (driveLetter = PERSISTENT_DATA(regionDescriptor)->DriveLetter) {
case NO_DRIVE_LETTER_YET:
assignDriveLetter = TRUE;
driveLetter = 0;
break;
case NO_DRIVE_LETTER_EVER:
assignDriveLetter = FALSE;
driveLetter = 0;
break;
default:
assignDriveLetter = TRUE;
break;
}
diskPartition->DriveLetter = driveLetter;
diskPartition->FtLength.LowPart = 0;
diskPartition->FtLength.HighPart = 0;
diskPartition->ReservedTwoLongs[0] = 0;
diskPartition->ReservedTwoLongs[1] = 0;
diskPartition->Modified = TRUE;
if (ftObject = GET_FT_OBJECT(regionDescriptor)) {
PREGION_DESCRIPTOR tmpDescriptor;
ftSet = ftObject->Set;
tmpDescriptor = LocateRegionForFtObject(ftSet->Member0);
// Only update status if member zero is present.
// otherwise the status is know to be Orphaned or
// needs regeneration.
#if 0
// need to do something here, but currently this does not work.
if (tmpDescriptor) {
ULONG numberOfMembers;
FT_SET_STATUS setState;
STATUS_CODE status;
// If the partition number is zero, then this set
// has not been committed to the disk yet. Only
// update status for existing sets.
if ((tmpDescriptor->PartitionNumber) &&
(ftSet->Status != FtSetNew) &&
(ftSet->Status != FtSetNewNeedsInitialization)) {
status = LowFtVolumeStatus(tmpDescriptor->Disk,
tmpDescriptor->PartitionNumber,
&setState,
&numberOfMembers);
if (status == OK_STATUS) {
if (ftSet->Status != setState) {
// Problem here - the FT driver has
// updated the status of the set after
// windisk last got the status. Need
// to restart the process of building
// the FT information after updating
// the set to the new state.
FdftUpdateFtObjectSet(ftSet, setState);
// now recurse and start over
status =
FdpConstructDiskDescription(DiskState,
DiskDescription);
return status;
}
}
}
}
#endif
diskPartition->FtState = ftObject->State;
diskPartition->FtType = ftSet->Type;
diskPartition->FtGroup = (USHORT)ftSet->Ordinal;
diskPartition->FtMember = (USHORT)ftObject->MemberIndex;
if (assignDriveLetter && (ftObject == ftObject->Set->Member0)) {
diskPartition->AssignDriveLetter = TRUE;
} else {
diskPartition->AssignDriveLetter = FALSE;
}
} else {
diskPartition->FtState = Healthy;
diskPartition->FtType = NotAnFtMember;
diskPartition->FtGroup = (USHORT)(-1);
diskPartition->FtMember = 0;
diskPartition->AssignDriveLetter = assignDriveLetter;
}
}
}
DiskDescription->NumberOfPartitions = (USHORT)partition;
DiskDescription->Signature = ds->Signature;
DiskDescription->ReservedShort = 0;
return(partition);
}
VOID
FdpRememberDisk(
IN PDISK_DESCRIPTION DiskDescription
)
/*++
Routine Description:
Make a copy of a registry disk description structure for later use.
Arguments:
DiskDescription - supplies pointer to the registry descriptor for
the disk in question.
Return Value:
None.
--*/
{
PDISK_DESCRIPTION diskDescription;
ULONG Size;
// Only bother remembering disks with at least one partition.
if (DiskDescription->NumberOfPartitions == 0) {
return;
}
// Compute the size of the structure
Size = sizeof(DISK_DESCRIPTION);
if (DiskDescription->NumberOfPartitions > 1) {
Size += (DiskDescription->NumberOfPartitions - 1) * sizeof(DISK_PARTITION);
}
diskDescription = Malloc(Size);
RtlMoveMemory(diskDescription, DiskDescription, Size);
RememberedDisks = Realloc(RememberedDisks,
(RememberedDiskCount + 1) * sizeof(PDISK_DESCRIPTION));
RememberedDisks[RememberedDiskCount++] = diskDescription;
FDLOG((2,
"FdpRememberDisk: remembered disk %08lx, remembered count = %u\n",
diskDescription->Signature,
RememberedDiskCount));
}
VOID
FdpInitializeMirrors(
VOID
)
/*++
Routine Description:
For each existing partition that was mirrored by the user during this Disk Manager
session, call the FT driver to register initialization of the mirror (ie, cause
the primary to be copied to the secondary). Perform a similar initialization for
each stripe set with parity created by the user.
Arguments:
None.
Return Value:
None.
--*/
{
PFT_OBJECT_SET ftSet;
PFT_OBJECT ftMember;
// Look through the list of FT sets for mirrored pairs and parity stripes
for (ftSet = FtObjects; ftSet; ftSet = ftSet->Next) {
// If the set needs initialization, or was recovered,
// call the FT driver.
switch (ftSet->Status) {
case FtSetNewNeedsInitialization:
DiskRegistryInitializeSet((USHORT)ftSet->Type,
(USHORT)ftSet->Ordinal);
ftSet->Status = FtSetInitializing;
break;
case FtSetRecovered:
// Find the member that needs to be addressed.
for (ftMember=ftSet->Members; ftMember; ftMember=ftMember->Next) {
if (ftMember->State == Regenerating) {
break;
}
}
DiskRegistryRegenerateSet((USHORT)ftSet->Type,
(USHORT)ftSet->Ordinal,
(USHORT)ftMember->MemberIndex);
ftSet->Status = FtSetRegenerating;
break;
default:
break;
}
}
}