windows-nt/Source/XPSP1/NT/base/cluster/clusrtl/disk.cpp
2020-09-26 16:20:57 +08:00

4594 lines
117 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
disk.c
Abstract:
Routines that query and manipulate the
disk configuration of the current system.
Author:
John Vert (jvert) 10/10/1996
Revision History:
--*/
#include "disk.h"
#include <ntddvol.h>
#include <devguid.h>
#include <setupapi.h>
#include "clusrtl.h"
/*
NT5 porting notes - Charlie Wickham (2/10/98)
I tried to touch as little of this as possible since there is alot of code
here. Two major differences on NT5 are: 1) the System\Disk key is no longer
used as the central "database" of disk configuration information and 2) all
drive letters are sticky on NT5.
NT5 Clusters still needs a central point of information (such as DISK key)
since the joining node cannot determine anything about the disk configuration
when the disks are reserved by the sponsor.
Later... (3/29/99)
Much has changed since I wrote the first blurb above a year ago. This code has
been patched to keep up with the changes with slight improvements made due to
the ever changing NT5 landscape with regard to supported storage types.
*/
#if 1
#define DISKERR(_MsgId_, _Err_) (DiskErrorFatal((0),(_Err_),__FILE__, __LINE__))
#define DISKLOG(_x_) DiskErrorLogInfo _x_
#define DISKASSERT(_x_) if (!(_x_)) DISKERR(IDS_GENERAL_FAILURE,ERROR_INVALID_PARAMETER)
#else
#define DISKERR(x,y)
#define DISKLOG(_x_)
#define DISKASSERT(_x_)
#endif
//
// array that maps disk and partition numbers to drive letters. This
// facilitates figuring out which drive letters are associated with a drive
// and reduces the amount of calls to CreateFile dramatically. The array is
// indexed by drive letter.
//
DRIVE_LETTER_INFO DriveLetterMap[26];
//
// Some handy registry utility routines
//
BOOL
GetRegValue(
IN HKEY hKey,
IN LPCWSTR Name,
OUT LPBYTE *Value,
OUT LPDWORD Length
)
{
LPBYTE Data = NULL;
DWORD cbData=0;
LONG Status;
//
// Call once to find the required size.
//
Status = RegQueryValueExW(hKey,
Name,
NULL,
NULL,
NULL,
&cbData);
if (Status != ERROR_SUCCESS) {
SetLastError(Status);
return(FALSE);
}
//
// Allocate the buffer and call again to get the data.
//
retry:
Data = (LPBYTE)LocalAlloc(LMEM_FIXED, cbData);;
if (!Data) {
Status = GetLastError();
DISKERR(IDS_MEMORY_FAILURE, Status);
return FALSE;
}
Status = RegQueryValueExW(hKey,
Name,
NULL,
NULL,
Data,
&cbData);
if (Status == ERROR_MORE_DATA) {
LocalFree(Data);
goto retry;
}
if (Status != ERROR_SUCCESS) {
SetLastError(Status);
DISKERR(IDS_REGISTRY_FAILURE, Status);
return FALSE;
}
*Value = Data;
*Length = cbData;
return(TRUE);
}
BOOL
MapDosVolumeToPhysicalPartition(
CString DosVolume,
PDRIVE_LETTER_INFO DriveInfo
)
/*++
Routine Description:
For a given dos volume (with the object space cruft in front of
it), build a string that reflects the drive and partition numbers
to which it is mapped.
Arguments:
DosVolume - pointer to "\??\C:" style name
DeviceInfo - pointer to buffer to receive device info data
Return Value:
TRUE if completed successfully
--*/
{
BOOL success = TRUE;
HANDLE hVol;
DWORD dwSize;
DWORD Status;
UINT driveType;
DriveInfo->DriveType = GetDriveType( DosVolume );
DISKLOG(("%ws drive type = %u\n", DosVolume, DriveInfo->DriveType ));
if ( DriveInfo->DriveType == DRIVE_FIXED ) {
WCHAR ntDosVolume[7] = L"\\\\.\\A:";
ntDosVolume[4] = DosVolume[0];
//
// get handle to partition
//
hVol = CreateFile(ntDosVolume,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hVol == INVALID_HANDLE_VALUE) {
return FALSE;
}
//
// issue storage class ioctl to get drive and partition numbers
// for this device
//
success = DeviceIoControl(hVol,
IOCTL_STORAGE_GET_DEVICE_NUMBER,
NULL,
0,
&DriveInfo->DeviceNumber,
sizeof( DriveInfo->DeviceNumber ),
&dwSize,
NULL);
if ( !success ) {
DISK_EXTENT diskExtent;
success = DeviceIoControl(hVol,
IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
NULL,
0,
&diskExtent,
sizeof( diskExtent ),
&dwSize,
NULL);
if ( success ) {
DriveInfo->DeviceNumber.DeviceType = FILE_DEVICE_DISK;
DriveInfo->DeviceNumber.DeviceNumber = diskExtent.DiskNumber;
DriveInfo->DeviceNumber.PartitionNumber = 0;
}
}
CloseHandle( hVol );
}
return success;
}
CDiskConfig::~CDiskConfig()
/*++
Description:
Destructor for CDiskConfig. Run down the list of disks selected for
cluster control and remove them from the DiskConfig database
Arguments:
None
Return Value:
None
--*/
{
CPhysicalDisk *PhysicalDisk;
int diskIndex;
POSITION pos;
for(pos = m_PhysicalDisks.GetStartPosition(); pos;){
m_PhysicalDisks.GetNextAssoc(pos, diskIndex, PhysicalDisk);
RemoveDisk(PhysicalDisk);
}
}
BOOL
CDiskConfig::Initialize(
VOID
)
/*++
Routine Description:
Build up a disk config database by poking all available disks
on the system.
Arguments:
None
Return Value:
True if everything worked ok
--*/
{
WCHAR System[3];
DWORD Status;
POSITION DiskPos;
DWORD index;
CFtInfoFtSet *FtSet;
HDEVINFO setupDiskInfo;
GUID diskDriveGuid = DiskClassGuid;
CPhysicalDisk * PhysicalDisk;
//
// enum the disks through the SetupDi APIs and create physical disk
// objects for them
//
setupDiskInfo = SetupDiGetClassDevs(&diskDriveGuid,
NULL,
NULL,
DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
if (setupDiskInfo != NULL ) {
SP_DEVICE_INTERFACE_DATA interfaceData;
GUID classGuid = DiskClassGuid;
BOOL success;
PSP_DEVICE_INTERFACE_DETAIL_DATA detailData;
DWORD detailDataSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + MAX_PATH * sizeof(WCHAR);
DWORD requiredSize;
detailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LMEM_FIXED,
detailDataSize);
if ( detailData != NULL ) {
detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
for (index = 0; ; ++index ) {
success = SetupDiEnumDeviceInterfaces(
setupDiskInfo,
NULL,
&diskDriveGuid,
index,
&interfaceData);
if ( success ) {
success = SetupDiGetDeviceInterfaceDetail(
setupDiskInfo,
&interfaceData,
detailData,
detailDataSize,
&requiredSize,
NULL);
if ( success ) {
PhysicalDisk = new CPhysicalDisk;
if (PhysicalDisk == NULL) {
DISKERR(IDS_GENERAL_FAILURE, ERROR_NOT_ENOUGH_MEMORY);
break;
}
DISKLOG(("Initializing disk %ws\n", detailData->DevicePath));
Status = PhysicalDisk->Initialize(&m_FTInfo, detailData->DevicePath);
if (Status != ERROR_SUCCESS) {
DISKLOG(("Problem init'ing disk, status = %u\n", Status));
delete PhysicalDisk;
break;
}
//
// Ignore disks with no partitions.
//
if (PhysicalDisk->m_PartitionCount == 0) {
DISKLOG(("Partition count is zero on disk %ws\n",
detailData->DevicePath));
delete PhysicalDisk;
} else {
DISKLOG(("Drive number = %u\n", PhysicalDisk->m_DiskNumber));
m_PhysicalDisks[PhysicalDisk->m_DiskNumber] = PhysicalDisk;
}
} else {
Status = GetLastError();
DISKLOG(("Couldn't get detail data, status %u\n",
GetLastError()));
}
} else {
Status = GetLastError();
if ( Status != ERROR_NO_MORE_ITEMS ) {
DISKLOG(("Couldn't enum dev IF #%u - %u\n",
index, Status ));
}
break;
}
}
LocalFree( detailData );
} else {
DISKLOG(("Couldn't get memory for detail data\n"));
SetupDiDestroyDeviceInfoList( setupDiskInfo );
return FALSE;
}
SetupDiDestroyDeviceInfoList( setupDiskInfo );
} else {
DISKLOG(("Couldn't get ptr to device info - %u\n", GetLastError()));
return FALSE;
}
//
// Enumerate all the FT sets in the DISK registry. Add each FT set
// that does not share a disk with any other FT set to our list.
//
for (index=0; ; index++) {
CFtSet *NewSet;
FtSet = m_FTInfo.EnumFtSetInfo(index);
if (FtSet == NULL) {
break;
}
if (FtSet->IsAlone()) {
NewSet = new CFtSet;
if (NewSet == NULL) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
DISKERR(IDS_GENERAL_FAILURE, ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
DISKLOG(("Initializing FTSet %u\n", index));
if (NewSet->Initialize(this, FtSet)) {
m_FtSetList.AddTail(NewSet);
} else {
DISKLOG(("Error initializing FTSet %u\n", index));
delete NewSet;
}
}
}
//
// get the disk/parition numbers for all defined drive letters
//
DWORD DriveMap = GetLogicalDrives();
DWORD Letter = 0;
WCHAR DosVolume[4] = L"A:\\";
DISKLOG(("Getting Drive Letter mappings\n"));
while (DriveMap) {
if ( DriveMap & 1 ) {
DosVolume[0] = (WCHAR)(Letter + L'A');
DISKLOG(("Mapping %ws\n", DosVolume));
if (MapDosVolumeToPhysicalPartition(DosVolume,
&DriveLetterMap[ Letter ]))
{
if ( DriveLetterMap[ Letter ].DriveType != DRIVE_FIXED ) {
DISKLOG(("%ws is not a fixed disk\n", DosVolume));
DriveLetterMap[ Letter ].DeviceNumber.PartitionNumber = 0;
}
} else {
DISKLOG(("Can't map %ws: %u\n", DosVolume, GetLastError()));
}
}
DriveMap >>= 1;
Letter += 1;
}
//
// Go through all the physical partitions and create logical
// disk objects for each one.
//
int diskIndex;
DISKLOG(("Creating Logical disk objects\n"));
DiskPos = m_PhysicalDisks.GetStartPosition();
while (DiskPos != NULL) {
m_PhysicalDisks.GetNextAssoc(DiskPos, diskIndex, PhysicalDisk);
//
// If there are no FT partitions on this disk, create the logical
// volumes on this disk.
//
if (PhysicalDisk->FtPartitionCount() == 0) {
//
// Go through all the partitions on this disk.
//
POSITION PartitionPos = PhysicalDisk->m_PartitionList.GetHeadPosition();
CPhysicalPartition *Partition;
while (PartitionPos != NULL) {
Partition = PhysicalDisk->m_PartitionList.GetNext(PartitionPos);
//
// If the partition type is recognized, create a volume object
// for this partition.
//
if ( !IsFTPartition( Partition->m_Info.PartitionType ) &&
(IsRecognizedPartition(Partition->m_Info.PartitionType))) {
CLogicalDrive *Volume = new CLogicalDrive;
if (Volume == NULL) {
DISKERR(IDS_GENERAL_FAILURE, ERROR_NOT_ENOUGH_MEMORY);
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
DISKLOG(("Init'ing logical vol for disk %u, part %u\n",
Partition->m_PhysicalDisk->m_DiskNumber,
Partition->m_Info.PartitionNumber));
if (Volume->Initialize(Partition)) {
//
// Add this volume to our list.
//
m_LogicalDrives[Volume->m_DriveLetter] = Volume;
} else {
DISKLOG(("Failed init logical vol\n"));
delete(Volume);
}
}
}
}
}
//
// Now find the volume for the system drive
//
DISKLOG(("Getting system drive info\n"));
if (GetEnvironmentVariable(L"SystemDrive",
System,
sizeof(System)/sizeof(WCHAR)) == 0) {
DISKERR(IDS_ERR_DRIVE_CONFIG, ERROR_PATH_NOT_FOUND);
// Need to handle this failure
}
if (!m_LogicalDrives.Lookup(System[0], m_SystemVolume)) {
//
// There are some weird cases that cause us to not find the system
// volume. For example, the system volume is on an FT set that shares
// a member with another FT set. So we just leave m_SystemVolume==NULL
// and assume that no other disks in our list will be on the same bus.
//
m_SystemVolume = NULL;
}
DISKLOG(("Finished gathering disk config info\n"));
return(TRUE);
}
VOID
CDiskConfig::RemoveAllFtInfoData(
VOID
)
/*++
Routine Description:
clear out all FtInfo related data associated with each
physical disk and physical partition instance.
Arguments:
None
Return Value:
None.
--*/
{
POSITION diskPos;
POSITION partitionPos;
CPhysicalDisk *disk;
CPhysicalPartition *partition;
int Index;
//
// run through our list of physical disks, deleting any
// associated FtInfo data. We enum the PhysicalDisks since
// the back pointers to the FtInfoDisk and FtInfoPartition members
// need to be cleared and this is the only (easy) to do that
//
for( diskPos = m_PhysicalDisks.GetStartPosition(); diskPos; ) {
m_PhysicalDisks.GetNextAssoc(diskPos, Index, disk);
if ( disk->m_FtInfo != NULL ) {
DISKLOG(("Removing %08X from FtInfo DB\n", disk->m_Signature));
m_FTInfo.DeleteDiskInfo( disk->m_Signature );
disk->m_FtInfo = NULL;
partitionPos = disk->m_PartitionList.GetHeadPosition();
while (partitionPos) {
partition = disk->m_PartitionList.GetNext( partitionPos );
partition->m_FtPartitionInfo = NULL;
}
}
}
}
VOID
CDiskConfig::RemoveDisk(
IN CPhysicalDisk *Disk
)
/*++
Description:
walk the logical drive and physical partition lists, removing all
structures
Arguments:
Disk - pointer to physical disk that is being removed
Return Value:
None
--*/
{
CLogicalDrive *Volume;
//
// Remove all the logical drives on this disk.
//
while (!Disk->m_LogicalDriveList.IsEmpty()) {
Volume = Disk->m_LogicalDriveList.RemoveHead();
m_LogicalDrives.RemoveKey(Volume->m_DriveLetter);
delete(Volume);
}
//
// Remove all the physical partitions on this disk.
//
CPhysicalPartition *Partition;
while (!Disk->m_PartitionList.IsEmpty()) {
Partition = Disk->m_PartitionList.RemoveHead();
delete(Partition);
}
//
// Remove this disk
//
m_PhysicalDisks.RemoveKey(Disk->m_DiskNumber);
delete(Disk);
}
CPhysicalPartition *
CDiskConfig::FindPartition(
IN CFtInfoPartition *FtPartition
)
/*++
Routine Description:
Given the FtInfo description of a partition, attempts to find
the corresponding CPhysicalPartition
Arguments:
FtPartition - Supplies an FT partition description
Return Value:
A pointer to the CPhysicalPartition if successful
NULL otherwise
--*/
{
POSITION pos;
CPhysicalDisk *Disk;
CPhysicalPartition *Partition;
int DiskIndex;
BOOL Found = FALSE;
//
// First find the appropriate CPhysicalDisk
//
pos = m_PhysicalDisks.GetStartPosition();
while (pos) {
m_PhysicalDisks.GetNextAssoc(pos, DiskIndex, Disk);
if (Disk->m_FtInfo) {
if (Disk->m_FtInfo->m_Signature == FtPartition->m_ParentDisk->m_Signature) {
Found = TRUE;
break;
}
}
}
if (!Found) {
return(FALSE);
}
//
// Now find the appropriate CPhysicalPartition in this disk.
//
pos = Disk->m_PartitionList.GetHeadPosition();
while (pos) {
Partition = Disk->m_PartitionList.GetNext(pos);
if (Partition->m_FtPartitionInfo == FtPartition) {
//
// Found a match!
//
return(Partition);
}
}
return(FALSE);
}
DWORD
CDiskConfig::MakeSticky(
IN CPhysicalDisk *Disk
)
{
DWORD Status;
Status = Disk->MakeSticky(&m_FTInfo);
if (Status == ERROR_SUCCESS) {
Status = m_FTInfo.CommitRegistryData();
}
return(Status);
}
DWORD
CDiskConfig::MakeSticky(
IN CFtSet *FtSet
)
{
DWORD Status;
Status = FtSet->MakeSticky();
if (Status == ERROR_SUCCESS) {
Status = m_FTInfo.CommitRegistryData();
}
return(Status);
}
BOOL
CDiskConfig::OnSystemBus(
IN CPhysicalDisk *Disk
)
{
CPhysicalDisk *SystemDisk;
if (m_SystemVolume == NULL) {
return(FALSE);
}
SystemDisk = m_SystemVolume->m_Partition->m_PhysicalDisk;
if (Disk == SystemDisk) {
return(TRUE);
}
if (SystemDisk->ShareBus(Disk)) {
return(TRUE);
}
return(FALSE);
}
BOOL
CDiskConfig::OnSystemBus(
IN CFtSet *FtSet
)
{
POSITION pos = FtSet->m_Member.GetHeadPosition();
CPhysicalPartition *Partition;
while (pos) {
Partition = FtSet->m_Member.GetNext(pos);
if (OnSystemBus(Partition->m_PhysicalDisk)) {
return(TRUE);
}
}
return(FALSE);
}
//
// Functions for the logical disk object
//
BOOL
CLogicalDrive::Initialize(
IN CPhysicalPartition *Partition
)
/*++
Routine Description:
Initializes a new logical disk object.
Arguments:
Partition - Supplies the physical partition.
Return Value:
--*/
{
CString DosVolume;
WCHAR DriveLabel[32];
WCHAR FsName[16];
DWORD MaxLength;
DWORD Flags;
WCHAR Buff[128];
DISK_PARTITION UNALIGNED *FtInfo;
//
// See if this drive has a "sticky" drive letter in the registry.
//
m_Partition = Partition;
if (Partition->m_FtPartitionInfo != NULL) {
FtInfo = Partition->m_FtPartitionInfo->m_PartitionInfo;
} else {
FtInfo = NULL;
}
if ((FtInfo) &&
(FtInfo->AssignDriveLetter) &&
(FtInfo->DriveLetter != 0))
{
m_IsSticky = TRUE;
m_DriveLetter = (WCHAR)FtInfo->DriveLetter;
} else {
m_IsSticky = FALSE;
//
// There is no sticky drive letter for this device. Scan through the
// Partition/Drive Letter map looking for a matching drive letter.
//
DWORD letter;
for ( letter = 0; letter < 26; ++letter ) {
if (DriveLetterMap[ letter ].DriveType == DRIVE_FIXED
&&
Partition->m_PhysicalDisk->m_DiskNumber == DriveLetterMap[ letter ].DeviceNumber.DeviceNumber
&&
Partition->m_Info.PartitionNumber == DriveLetterMap[ letter ].DeviceNumber.PartitionNumber)
{
break;
}
}
if ( letter == 26 ) {
//
// There is no drive letter for this partition. Just ignore it.
//
return(FALSE);
}
m_DriveLetter = (WCHAR)(letter + L'A');
}
DosVolume = m_DriveLetter;
DosVolume += L":\\";
if (GetVolumeInformation(DosVolume,
DriveLabel,
sizeof(DriveLabel)/sizeof(WCHAR),
NULL,
&MaxLength,
&Flags,
FsName,
sizeof(FsName)/sizeof(WCHAR))) {
if (lstrcmpi(FsName, L"NTFS")==0) {
m_IsNTFS = TRUE;
} else {
m_IsNTFS = FALSE;
}
m_VolumeLabel = DriveLabel;
wsprintf(Buff,
L"%c: (%ws)",
m_DriveLetter,
(LPCTSTR)m_VolumeLabel);
} else {
m_IsNTFS = TRUE; // Lie and say it is NTFS
wsprintf(Buff,
L"%c: (RAW)",
m_DriveLetter);
}
m_Identifier = Buff;
m_Partition->m_PhysicalDisk->m_LogicalDriveList.AddTail(this);
m_ContainerSet = NULL;
return(TRUE);
}
BOOL
CLogicalDrive::IsSCSI(
VOID
)
/*++
Routine Description:
Returns whether or not a logical drive is SCSI. A logical
drive is SCSI if all of its partitions are on SCSI drives.
Arguments:
None.
Return Value:
TRUE if the drive is entirely SCSI
FALSE otherwise.
--*/
{
return(m_Partition->m_PhysicalDisk->m_IsSCSI);
}
DWORD
CLogicalDrive::MakeSticky(
VOID
)
/*++
Routine Description:
Attempts to assign a sticky drive letter to the specified volume.
Arguments:
None.
Return Value:
ERROR_SUCCESS if the drive was made sticky.
Win32 error code otherwise.
--*/
{
m_Partition->m_FtPartitionInfo->MakeSticky((UCHAR)m_DriveLetter);
m_IsSticky = TRUE;
return(ERROR_SUCCESS);
}
BOOL
CLogicalDrive::ShareBus(
IN CLogicalDrive *OtherDrive
)
/*++
Routine Description:
Returns whether or not this drive shares a bus with another
drive.
Arguments:
OtherDrive - Supplies the other drive
Return Value:
TRUE - if the drives have any of their partitions on the same bus.
FALSE - if the drives do not hae any of their partitiosn on the same bus.
--*/
{
PSCSI_ADDRESS MyAddress;
PSCSI_ADDRESS OtherAddress;
MyAddress = &m_Partition->m_PhysicalDisk->m_ScsiAddress;
OtherAddress = &OtherDrive->m_Partition->m_PhysicalDisk->m_ScsiAddress;
if ( (MyAddress->PortNumber == OtherAddress->PortNumber) &&
(MyAddress->PathId == OtherAddress->PathId) ) {
return(TRUE);
} else {
return(FALSE);
}
}
//
// Functions for the physical disk object
//
DWORD
CPhysicalDisk::Initialize(
CFtInfo *FtInfo,
IN LPWSTR DeviceName
)
/*++
Routine Description:
Initializes a physical disk object
Arguments:
FtInfo - pointer to object's FtInfo data
DeviceName - pointer to string of device to initialize
Return Value:
ERROR_SUCCESS if successful
--*/
{
HKEY DiskKey;
WCHAR Buff[100];
DWORD BuffSize;
DWORD dwType;
HANDLE hDisk;
DWORD Status;
DWORD dwSize;
PDRIVE_LAYOUT_INFORMATION DriveLayout;
WCHAR KeyName[256];
DISK_GEOMETRY Geometry;
STORAGE_DEVICE_NUMBER deviceNumber;
//
// Open the physical drive and start probing it to find disk number and
// other attributes
//
hDisk = GetPhysicalDriveHandle(GENERIC_READ, DeviceName);
if (hDisk == NULL) {
return(GetLastError());
}
if (!DeviceIoControl(hDisk,
IOCTL_STORAGE_GET_DEVICE_NUMBER,
NULL,
0,
&deviceNumber,
sizeof(deviceNumber),
&dwSize,
NULL))
{
Status = GetLastError();
DISKLOG(("get device number failed for drive %ws. status = %u\n",
DeviceName,
Status));
return Status;
} else {
m_DiskNumber = deviceNumber.DeviceNumber;
}
if (!DeviceIoControl(hDisk,
IOCTL_SCSI_GET_ADDRESS,
NULL,
0,
&m_ScsiAddress,
sizeof(SCSI_ADDRESS),
&dwSize,
NULL))
{
//
// If the IOCTL was invalid, the drive is not a SCSI drive.
//
DISKLOG(("IOCTL_SCSI_GET_ADDRESS failed for drive %u. status = %u\n",
m_DiskNumber,
GetLastError()));
m_IsSCSI = FALSE;
} else {
//
// [THINKTHINK] John Vert (jvert) 10/12/1996
// Need some way to make sure this is really SCSI and
// not ATAPI?
//
m_IsSCSI = TRUE;
//
// Get the description of the disk from the registry.
//
wsprintf(KeyName,
L"HARDWARE\\DeviceMap\\Scsi\\Scsi Port %d\\Scsi Bus %d\\Target Id %d\\Logical Unit Id %d",
m_ScsiAddress.PortNumber,
m_ScsiAddress.PathId,
m_ScsiAddress.TargetId,
m_ScsiAddress.Lun);
Status = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
KeyName,
0,
KEY_READ,
&DiskKey);
if (Status != ERROR_SUCCESS) {
DISKERR(IDS_ERR_DRIVE_CONFIG, Status);
// [REENGINEER] Need to handle this failure //
}
BuffSize = sizeof(Buff);
Status = RegQueryValueExW(DiskKey,
L"Identifier",
NULL,
&dwType,
(LPBYTE)Buff,
&BuffSize);
RegCloseKey(DiskKey);
if (Status != ERROR_SUCCESS) {
DISKERR(IDS_ERR_DRIVE_CONFIG, Status);
// [REENGINEER] Need to handle this failure //
}
m_Identifier = Buff;
}
//
// Get the drive layout.
//
m_PartitionCount = 0;
if (!ClRtlGetDriveLayoutTable( hDisk, &DriveLayout, NULL )) {
DISKLOG(("Couldn't get partition table for drive %u. status = %u\n",
m_DiskNumber,
GetLastError()));
m_Signature = 0;
m_FtInfo = NULL;
} else {
m_Signature = DriveLayout->Signature;
//
// Get the FT information
//
m_FtInfo = FtInfo->FindDiskInfo(m_Signature);
//
// build the partition objects.
//
DWORD i;
CPhysicalPartition *Partition;
for (i=0; i<DriveLayout->PartitionCount; i++) {
if (DriveLayout->PartitionEntry[i].RecognizedPartition) {
m_PartitionCount++;
Partition = new CPhysicalPartition(this, &DriveLayout->PartitionEntry[i]);
if (Partition != NULL) {
//
// If we have FT information for the disk, make sure we
// found it for each partition. If we didn't find it for
// the partition, the registry is stale and doesn't match
// the drive layout.
//
if ((m_FtInfo != NULL) &&
(Partition->m_FtPartitionInfo == NULL)) {
//
// Stale registry info. Make up some new stuff.
//
CFtInfoPartition *FtInfoPartition;
FtInfoPartition = new CFtInfoPartition(m_FtInfo, Partition);
if (FtInfoPartition == NULL) {
DISKERR(IDS_GENERAL_FAILURE, ERROR_NOT_ENOUGH_MEMORY);
LocalFree( DriveLayout );
return ERROR_NOT_ENOUGH_MEMORY;
}
Partition->m_FtPartitionInfo = FtInfoPartition;
}
m_PartitionList.AddTail(Partition);
} else {
DISKERR(IDS_GENERAL_FAILURE, ERROR_NOT_ENOUGH_MEMORY);
LocalFree( DriveLayout );
return ERROR_NOT_ENOUGH_MEMORY;
}
}
}
LocalFree( DriveLayout );
}
//
// Check whether it is removable or not.
//
if (!DeviceIoControl(hDisk,
IOCTL_DISK_GET_DRIVE_GEOMETRY,
NULL,
0,
&Geometry,
sizeof(Geometry),
&dwSize,
NULL)) {
Status = GetLastError();
if (Status == ERROR_NOT_READY) {
//
// Guess this must be removable!
//
m_IsRemovable = TRUE;
} else {
//
// [FUTURE] John Vert (jvert) 10/18/1996
// Remove this when we require the new SCSI driver.
// The disk is reserved on the other system, so we can't
// get the geometry.
//
m_IsRemovable = FALSE;
}
} else {
if (Geometry.MediaType == RemovableMedia) {
m_IsRemovable = TRUE;
} else {
m_IsRemovable = FALSE;
}
}
CloseHandle(hDisk);
return(ERROR_SUCCESS);
}
HANDLE
CPhysicalDisk::GetPhysicalDriveHandle(DWORD Access)
{
WCHAR Buff[100];
HANDLE hDisk;
wsprintf(Buff, L"\\\\.\\PhysicalDrive%d", m_DiskNumber);
hDisk = CreateFile(Buff,
Access,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hDisk == INVALID_HANDLE_VALUE) {
DISKLOG(("Failed to get handle for drive %u. status = %u\n",
m_DiskNumber,
GetLastError()));
return(NULL);
}
return(hDisk);
}
HANDLE
CPhysicalDisk::GetPhysicalDriveHandle(DWORD Access, LPWSTR DeviceName)
{
HANDLE hDisk;
hDisk = CreateFile(DeviceName,
Access,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hDisk == INVALID_HANDLE_VALUE) {
DISKLOG(("Failed to get handle for drive %u. status = %u\n",
m_DiskNumber,
GetLastError()));
return(NULL);
}
return(hDisk);
}
BOOL
CPhysicalDisk::ShareBus(
IN CPhysicalDisk *OtherDisk
)
/*++
Routine Description:
Returns whether or not this disk shares a bus with another
disk.
Arguments:
OtherDisk - Supplies the other disk
Return Value:
TRUE - if the disks share the same bus.
FALSE - if the disks do not share the same bus.
--*/
{
//
// Make sure they are either both SCSI or both not SCSI.
//
if (m_IsSCSI != OtherDisk->m_IsSCSI) {
return(FALSE);
}
if ( (m_ScsiAddress.PortNumber == OtherDisk->m_ScsiAddress.PortNumber) &&
(m_ScsiAddress.PathId == OtherDisk->m_ScsiAddress.PathId) ) {
return(TRUE);
} else {
return(FALSE);
}
}
BOOL
CPhysicalDisk::IsSticky(
VOID
)
/*++
Routine Description:
Returns whether or not this disk has a signature and all the partitions
on it have sticky drive letters.
Arguments:
None.
Return Value:
TRUE - if the disk is sticky
FALSE - if the disk is not sticky and needs to have some FT information
applied before it is suitable for clustering.
--*/
{
//
// If the signature is 0, return FALSE.
//
if ((m_FtInfo == NULL) ||
(m_FtInfo->m_Signature == 0)) {
return(FALSE);
}
//
// Check each volume to see if it has a sticky drive letter.
//
CLogicalDrive *Drive;
POSITION pos = m_LogicalDriveList.GetHeadPosition();
while (pos) {
Drive = m_LogicalDriveList.GetNext(pos);
if (!Drive->m_IsSticky) {
return(FALSE);
}
}
return(TRUE);
}
BOOL
CPhysicalDisk::IsNTFS(
VOID
)
/*++
Routine Description:
Returns whether or not all the partitions on this drive are NTFS.
Arguments:
None.
Return Value:
TRUE - if the disk is entirely NTFS
FALSE - if the disk is not entirely NTFS
--*/
{
//
// if no logical volumes were created for this drive, then it must not
// have any NTFS partitions
//
if ( m_LogicalDriveList.IsEmpty()) {
return FALSE;
}
//
// Check each volume to see if it has a sticky drive letter.
//
CLogicalDrive *Drive;
POSITION pos = m_LogicalDriveList.GetHeadPosition();
while (pos) {
Drive = m_LogicalDriveList.GetNext(pos);
if (!Drive->m_IsNTFS) {
return(FALSE);
}
}
return(TRUE);
}
DWORD
CPhysicalDisk::MakeSticky(
CFtInfo *FtInfo
)
/*++
Routine Description:
Attempts to make a disk and all of its partitions have
sticky drive letters.
Arguments:
FtInfo - Supplies the FT information that will be updated.
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
DWORD Status;
if (m_Signature == 0) {
//
// Better not be any information in the registry for a disk
// with no signature.
//
if (m_FtInfo != NULL) {
DISKERR(IDS_GENERAL_FAILURE, ERROR_FILE_NOT_FOUND);
}
//
// There is no signature on this drive. Think one up and
// stamp the drive.
//
HANDLE hDisk = GetPhysicalDriveHandle(GENERIC_READ | GENERIC_WRITE);
PDRIVE_LAYOUT_INFORMATION DriveLayout;
DWORD dwSize;
DWORD NewSignature;
FILETIME CurrentTime;
BOOL success;
if (hDisk == NULL) {
return(GetLastError());
}
//
// Get the current drive layout, change the signature field, and
// set the new drive layout. The new drive layout will be identical
// except for the new signature.
//
if (!ClRtlGetDriveLayoutTable( hDisk, &DriveLayout, &dwSize )) {
Status = GetLastError();
DISKERR(IDS_GENERAL_FAILURE, Status);
CloseHandle(hDisk);
return(Status);
}
GetSystemTimeAsFileTime(&CurrentTime);
NewSignature = CurrentTime.dwLowDateTime;
//
// Make sure this signature is unique.
//
while (FtInfo->FindDiskInfo(NewSignature) != NULL) {
NewSignature++;
}
//
// Finally set the new signature information.
//
DriveLayout->Signature = NewSignature;
success = DeviceIoControl(hDisk,
IOCTL_DISK_SET_DRIVE_LAYOUT,
DriveLayout,
dwSize,
NULL,
0,
&dwSize,
NULL);
LocalFree( DriveLayout );
if ( !success ) {
Status = GetLastError();
DISKERR(IDS_GENERAL_FAILURE, Status);
CloseHandle(hDisk);
return(Status);
}
m_Signature = NewSignature;
}
if (m_FtInfo == NULL) {
//
// There is no existing FT information for this drive.
// Create some FT information based on the drive.
//
m_FtInfo = new CFtInfoDisk(this);
if (m_FtInfo == NULL) {
DISKERR(IDS_GENERAL_FAILURE, ERROR_NOT_ENOUGH_MEMORY);
return ERROR_NOT_ENOUGH_MEMORY;
}
FtInfo->SetDiskInfo(m_FtInfo);
//
// Go through all our partitions and set their FT info.
//
POSITION pos = m_PartitionList.GetHeadPosition();
CPhysicalPartition *Partition;
while (pos) {
Partition = m_PartitionList.GetNext(pos);
Partition->m_FtPartitionInfo = m_FtInfo->GetPartition(Partition->m_Info.StartingOffset,
Partition->m_Info.PartitionLength);
}
}
//
// Go through all the volumes on this drive and make each one
// sticky.
//
CLogicalDrive *Drive;
POSITION pos = m_LogicalDriveList.GetHeadPosition();
while (pos) {
Drive = m_LogicalDriveList.GetNext(pos);
Status = Drive->MakeSticky();
if (Status != ERROR_SUCCESS) {
return(Status);
}
}
return(ERROR_SUCCESS);
}
//
// Functions for the physical disk partition
//
CPhysicalPartition::CPhysicalPartition(
CPhysicalDisk *Disk,
PPARTITION_INFORMATION Info
)
{
m_PhysicalDisk = Disk;
m_Info = *Info;
if (Disk->m_FtInfo) {
m_FtPartitionInfo = Disk->m_FtInfo->GetPartition(m_Info.StartingOffset,
m_Info.PartitionLength);
} else {
m_FtPartitionInfo = NULL;
}
}
//
// Functions for the FT set object
//
BOOL
CFtSet::Initialize(
CDiskConfig *Config,
CFtInfoFtSet *FtInfo
)
{
DWORD MemberCount;
DWORD FoundCount=0;
DWORD Index;
CFtInfoPartition *Partition;
CPhysicalPartition *FoundPartition;
m_FtInfo = FtInfo;
//
// Find the CPhysicalPartition that corresponds to each member of the
// FT set.
//
MemberCount = FtInfo->GetMemberCount();
for (Index=0; Index<MemberCount; Index++) {
Partition = FtInfo->GetMemberByIndex(Index);
if (Partition == NULL) {
break;
}
FoundPartition = Config->FindPartition(Partition);
if (FoundPartition != NULL) {
++FoundCount;
m_Member.AddTail(FoundPartition);
}
}
//
// If we did not find all the required members, fail.
//
switch (FtInfo->GetType()) {
case Stripe:
case VolumeSet:
if (FoundCount != MemberCount) {
return(FALSE);
}
break;
case Mirror:
if (FoundCount == 0) {
return(FALSE);
}
break;
case StripeWithParity:
if (FoundCount < (MemberCount-1)) {
return(FALSE);
}
break;
default:
//
// Don't know what the heck this is supposed to be.
// Ignore it.
//
return(FALSE);
}
//
// If there are any other partitions on any of the drives, create logical
// volumes for them.
//
POSITION MemberPos;
POSITION PartitionPos;
CPhysicalPartition *PhysPartition;
CPhysicalDisk *Disk;
MemberPos = m_Member.GetHeadPosition();
while (MemberPos) {
Disk = m_Member.GetNext(MemberPos)->m_PhysicalDisk;
PartitionPos = Disk->m_PartitionList.GetHeadPosition();
while (PartitionPos) {
PhysPartition = Disk->m_PartitionList.GetNext(PartitionPos);
if ((!(PhysPartition->m_Info.PartitionType & PARTITION_NTFT)) &&
(IsRecognizedPartition(PhysPartition->m_Info.PartitionType))) {
CLogicalDrive *Vol = new CLogicalDrive;
if (Vol == NULL) {
DISKERR(IDS_GENERAL_FAILURE, ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
if (Vol->Initialize(PhysPartition)) {
//
// Add this volume to our list.
//
m_OtherVolumes.AddTail(Vol);
Vol->m_ContainerSet = this;
//
// Update the disk config.
//
Config->m_LogicalDrives[Vol->m_DriveLetter] = Vol;
} else {
delete(Vol);
}
}
}
}
if (Volume.Initialize(m_Member.GetHead())) {
Volume.m_ContainerSet = this;
return(TRUE);
} else {
return(FALSE);
}
}
BOOL
CFtSet::IsSticky()
{
//
// FT sets are, by definition, sticky. Make sure any other volumes on the
// same drive are sticky as well.
//
POSITION pos = m_OtherVolumes.GetHeadPosition();
CLogicalDrive *Volume;
while (pos) {
Volume = m_OtherVolumes.GetNext(pos);
if (!Volume->m_IsSticky) {
return(FALSE);
}
}
return(TRUE);
}
DWORD
CFtSet::MakeSticky()
{
DWORD Status;
//
// FT sets are, by definition, sticky. Make sure any other volumes on the
// same drive are sticky as well.
//
POSITION pos = m_OtherVolumes.GetHeadPosition();
CLogicalDrive *Volume;
while (pos) {
Volume = m_OtherVolumes.GetNext(pos);
Status = Volume->MakeSticky();
if (Status != ERROR_SUCCESS) {
return(Status);
}
}
return(ERROR_SUCCESS);
}
BOOL
CFtSet::IsNTFS()
{
if (!Volume.m_IsNTFS) {
return(FALSE);
}
//
// Check the other volumes to make sure they are NTFS as well.
//
POSITION pos = m_OtherVolumes.GetHeadPosition();
CLogicalDrive *Volume;
while (pos) {
Volume = m_OtherVolumes.GetNext(pos);
if (!Volume->m_IsNTFS) {
return(FALSE);
}
}
return(TRUE);
}
BOOL
CFtSet::IsSCSI()
{
//
// Check the other volumes to make sure they are NTFS as well.
//
POSITION pos = m_Member.GetHeadPosition();
CPhysicalPartition *Partition;
while (pos) {
Partition = m_Member.GetNext(pos);
if (!Partition->m_PhysicalDisk->m_IsSCSI) {
return(FALSE);
}
}
return(TRUE);
}
//
// Functions for the FT disk information
//
CFtInfo::CFtInfo()
{
HKEY hKey;
LONG Status;
//
// for NT5, the DISK info key is no longer maintained by the disk
// system. Clusters still needs a centrally located key such that
// the other members of the cluster can query for the disk config
// of the sponsor's node.
//
Status = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"System\\Disk",
0,
KEY_READ | KEY_WRITE,
&hKey);
if (Status == ERROR_SUCCESS) {
Initialize(hKey, _T("Information"));
RegCloseKey(hKey);
} else {
Initialize();
}
}
CFtInfo::CFtInfo(
HKEY hKey,
LPWSTR lpszValueName
)
{
Initialize(hKey, lpszValueName);
}
CFtInfo::CFtInfo(
PDISK_CONFIG_HEADER Header
)
{
DWORD Length;
Length = Header->FtInformationOffset +
Header->FtInformationSize;
Initialize(Header, Length);
}
CFtInfo::CFtInfo(
CFtInfoFtSet *FtSet
)
/*++
Routine Description:
Constructor for generating a CFtInfo that contains only a
single FT set.
Arguments:
FtSet - Supplies the FT set
Return Value:
None
--*/
{
//
// Initialize an empty FT information.
//
Initialize();
//
// Add the FT set
//
if (FtSet != NULL) {
AddFtSetInfo(FtSet);
}
}
VOID
CFtInfo::Initialize(
HKEY hKey,
LPWSTR lpszValueName
)
{
PDISK_CONFIG_HEADER regHeader;
DWORD Length;
if (GetRegValue(hKey,
lpszValueName,
(LPBYTE *)&regHeader,
&Length)) {
Initialize(regHeader, Length);
LocalFree(regHeader);
} else {
DWORD Status = GetLastError();
if (Status == ERROR_FILE_NOT_FOUND) {
//
// There is no FT information on this machine.
//
Initialize();
} else {
DISKERR(IDS_GENERAL_FAILURE, Status);
}
}
}
VOID
CFtInfo::Initialize(
PDISK_CONFIG_HEADER Header,
DWORD Length
)
{
DWORD i;
DISK_REGISTRY UNALIGNED * diskRegistry;
DISK_DESCRIPTION UNALIGNED * diskDescription;
CFtInfoDisk *DiskInfo;
m_buffer = new BYTE[Length];
if (m_buffer == NULL) {
DISKERR(IDS_GENERAL_FAILURE, ERROR_NOT_ENOUGH_MEMORY);
return; // [REENGINEER] we avoided an AV, but the caller wouldn't know
}
CopyMemory(m_buffer, Header, Length);
m_bufferLength = Length;
//
// Iterate through all the disks and add each one to our list.
//
diskRegistry = (DISK_REGISTRY UNALIGNED *)
(m_buffer + ((PDISK_CONFIG_HEADER)m_buffer)->DiskInformationOffset);
diskDescription = &diskRegistry->Disks[0];
for (i = 0; i < diskRegistry->NumberOfDisks; i++) {
DiskInfo = new CFtInfoDisk(diskDescription);
if (DiskInfo) {
//
// Add this disk information to our list.
//
DiskInfo->SetOffset((DWORD)((PUCHAR)diskDescription - m_buffer));
m_DiskInfo.AddTail(DiskInfo);
} else {
DISKERR(IDS_GENERAL_FAILURE, ERROR_NOT_ENOUGH_MEMORY);
// [REENGINEER] do we need to exit here?
}
//
// Look at the next disk
//
diskDescription = (DISK_DESCRIPTION UNALIGNED *)
&diskDescription->Partitions[diskDescription->NumberOfPartitions];
}
if (((PDISK_CONFIG_HEADER)m_buffer)->FtInformationSize != 0) {
//
// Iterate through all the FT sets and add each one to our list.
//
PFT_REGISTRY ftRegistry;
PFT_DESCRIPTION ftDescription;
CFtInfoFtSet *FtSetInfo;
ftRegistry = (PFT_REGISTRY)
(m_buffer + ((PDISK_CONFIG_HEADER)m_buffer)->FtInformationOffset);
ftDescription = &ftRegistry->FtDescription[0];
for (i=0; i < ftRegistry->NumberOfComponents; i++) {
FtSetInfo = new CFtInfoFtSet;
if (FtSetInfo) {
if (!FtSetInfo->Initialize(this, ftDescription)) {
delete FtSetInfo;
} else {
//
// Add this FT set information to the list.
//
m_FtSetInfo.AddTail(FtSetInfo);
}
} else {
DISKERR(IDS_GENERAL_FAILURE, ERROR_NOT_ENOUGH_MEMORY);
// [REENGINEER] do we need to exit here?
}
ftDescription = (PFT_DESCRIPTION)(&ftDescription->FtMemberDescription[ftDescription->NumberOfMembers]);
}
}
}
VOID
CFtInfo::Initialize(VOID)
{
PDISK_CONFIG_HEADER regHeader;
DISK_REGISTRY UNALIGNED * diskRegistry;
//
// There is no FT information on this machine.
//
m_bufferLength = sizeof(DISK_CONFIG_HEADER) + sizeof(DISK_REGISTRY);
m_buffer = new BYTE[m_bufferLength];
if (m_buffer == NULL) {
DISKERR(IDS_GENERAL_FAILURE, ERROR_NOT_ENOUGH_MEMORY);
return; // [REENGINEER], we avoided an AV, but the caller wouldn't know
}
regHeader = (PDISK_CONFIG_HEADER)m_buffer;
regHeader->Version = DISK_INFORMATION_VERSION;
regHeader->CheckSum = 0;
regHeader->DirtyShutdown = FALSE;
regHeader->DiskInformationOffset = sizeof(DISK_CONFIG_HEADER);
regHeader->DiskInformationSize = sizeof(DISK_REGISTRY)-sizeof(DISK_DESCRIPTION);
regHeader->FtInformationOffset = regHeader->DiskInformationOffset +
regHeader->DiskInformationSize;
regHeader->FtInformationSize = 0;
regHeader->FtStripeWidth = 0;
regHeader->FtPoolSize = 0;
regHeader->NameOffset = 0;
regHeader->NameSize = 0;
diskRegistry = (DISK_REGISTRY UNALIGNED *)
((PUCHAR)regHeader + regHeader->DiskInformationOffset);
diskRegistry->NumberOfDisks = 0;
diskRegistry->ReservedShort = 0;
}
CFtInfo::~CFtInfo()
{
CFtInfoDisk *DiskInfo;
CFtInfoFtSet *FtSetInfo;
POSITION pos = m_DiskInfo.GetHeadPosition();
while (pos) {
DiskInfo = m_DiskInfo.GetNext(pos);
delete(DiskInfo);
}
pos = m_FtSetInfo.GetHeadPosition();
while (pos) {
FtSetInfo = m_FtSetInfo.GetNext(pos);
delete FtSetInfo;
}
delete [] m_buffer;
}
DWORD
CFtInfo::CommitRegistryData()
{
HKEY hKey;
PDISK_CONFIG_HEADER Buffer;
DWORD Size;
DWORD Status = ERROR_SUCCESS;
Status = RegCreateKeyW(HKEY_LOCAL_MACHINE, L"System\\Disk", &hKey);
if (Status != ERROR_SUCCESS) {
DISKERR(IDS_REGISTRY_FAILURE, Status);
return Status;
}
Size = GetSize();
Buffer = (PDISK_CONFIG_HEADER)LocalAlloc(LMEM_FIXED, Size);
if (Buffer == NULL) {
DISKERR(IDS_GENERAL_FAILURE, ERROR_NOT_ENOUGH_MEMORY);
Status = ERROR_NOT_ENOUGH_MEMORY;
} else {
GetData(Buffer);
Status = RegSetValueExW(hKey,
L"Information",
0,
REG_BINARY,
(PBYTE)Buffer,
Size);
if (Status != ERROR_SUCCESS) {
DISKERR(IDS_REGISTRY_FAILURE, Status);
}
LocalFree(Buffer);
}
RegCloseKey(hKey);
return(Status);
}
VOID
CFtInfo::SetDiskInfo(
CFtInfoDisk *NewDisk
)
{
CFtInfoDisk *OldDisk;
//
// See if we already have disk information for this signature
//
OldDisk = FindDiskInfo(NewDisk->m_Signature);
if (OldDisk == NULL) {
DISKLOG(("CFtInfo::SetDiskInfo adding new disk information for %08X\n",NewDisk->m_Signature));
//
// Just add the new disk to our list.
//
m_DiskInfo.AddTail(NewDisk);
} else {
//
// We already have some disk information. If they are the same,
// don't do anything.
//
if (*OldDisk == *NewDisk) {
DISKLOG(("CFtInfo::SetDiskInfo found identical disk information for %08X\n",OldDisk->m_Signature));
delete (NewDisk);
return;
}
//
// We need to replace the old information with the new information.
//
POSITION pos = m_DiskInfo.Find(OldDisk);
if (pos == NULL) {
DISKLOG(("CFtInfo::SetDiskInfo did not find OldDisk %08X\n",OldDisk->m_Signature));
DISKERR(IDS_GENERAL_FAILURE, ERROR_FILE_NOT_FOUND);
m_DiskInfo.AddTail(NewDisk);
} else {
m_DiskInfo.SetAt(pos, NewDisk);
delete(OldDisk);
}
}
}
CFtInfoDisk *
CFtInfo::FindDiskInfo(
IN DWORD Signature
)
{
CFtInfoDisk *RetInfo;
POSITION pos = m_DiskInfo.GetHeadPosition();
while (pos) {
RetInfo = m_DiskInfo.GetNext(pos);
if (RetInfo->m_Signature == Signature) {
return(RetInfo);
}
}
return(NULL);
}
CFtInfoDisk *
CFtInfo::EnumDiskInfo(
IN DWORD Index
)
{
DWORD i=0;
CFtInfoDisk *RetInfo;
POSITION pos = m_DiskInfo.GetHeadPosition();
while (pos) {
RetInfo = m_DiskInfo.GetNext(pos);
if (Index == i) {
return(RetInfo);
}
++i;
}
return(NULL);
}
BOOL
CFtInfo::DeleteDiskInfo(
IN DWORD Signature
)
{
CFtInfoDisk *Info = FindDiskInfo(Signature);
CFtInfoFtSet *OldFtSet=NULL;
if (Info == NULL) {
DISKLOG(("CFtInfo::DeleteDiskInfo: Disk with signature %08X was not found\n",Signature));
return(FALSE);
}
//
// Remove any FT set containing this signature.
//
OldFtSet = FindFtSetInfo(Info->m_Signature);
if (OldFtSet != NULL) {
DeleteFtSetInfo(OldFtSet);
}
POSITION pos = m_DiskInfo.Find(Info);
if (pos == NULL) {
DISKLOG(("CFtInfo::DeleteDiskInfo did not find Info %08X\n",Signature));
DISKERR(IDS_GENERAL_FAILURE, ERROR_FILE_NOT_FOUND);
return(FALSE);
} else {
m_DiskInfo.RemoveAt(pos);
delete(Info);
}
return(TRUE);
}
VOID
CFtInfo::AddFtSetInfo(
CFtInfoFtSet *FtSet,
CFtInfoFtSet *OldFtSet
)
{
DWORD MemberCount;
DWORD i;
CFtInfoPartition *Partition;
CFtInfoPartition *NewPartition;
CFtInfoDisk *Disk;
CFtInfoFtSet *NewFtSet;
USHORT FtGroup;
POSITION pos;
BOOL Success;
if (OldFtSet != NULL) {
CFtInfoFtSet *pSet;
pos = m_FtSetInfo.GetHeadPosition();
for (FtGroup = 1; ; FtGroup++) {
pSet = m_FtSetInfo.GetNext(pos);
if (pSet == NULL) {
OldFtSet = NULL;
break;
}
if (pSet == OldFtSet) {
//
// Reset our position back to point at the OldFtSet
//
pos = m_FtSetInfo.Find(OldFtSet);
break;
}
}
}
if (OldFtSet == NULL) {
FtGroup = (USHORT)m_FtSetInfo.GetCount()+1;
}
//
// Add each disk in the FT set.
//
MemberCount = FtSet->GetMemberCount();
for (i=0; i<MemberCount; i++) {
Partition = FtSet->GetMemberByIndex(i);
DISKASSERT(Partition != NULL);
Disk = new CFtInfoDisk(Partition->m_ParentDisk);
if (Disk == NULL) {
DISKERR(IDS_GENERAL_FAILURE, ERROR_NOT_ENOUGH_MEMORY);
return; // [REENGINEER], caller doesn't know about the problem
}
SetDiskInfo(Disk);
}
//
// Create the empty FT set.
//
NewFtSet = new CFtInfoFtSet;
if (NewFtSet == NULL) {
DISKERR(IDS_GENERAL_FAILURE, ERROR_NOT_ENOUGH_MEMORY);
return; // [REENGINEER], caller doesn't know about the problem
}
Success = NewFtSet->Initialize(FtSet->GetType(), FtSet->GetState());
DISKASSERT(Success);
//
// Add each member to the empty FT set
//
for (i=0; i<MemberCount; i++) {
//
// Find each partition object in our FT information.
//
Partition = FtSet->GetMemberByIndex(i);
NewPartition = FindPartition(Partition->m_ParentDisk->m_Signature,
Partition->m_PartitionInfo->StartingOffset,
Partition->m_PartitionInfo->Length);
DISKASSERT(NewPartition != NULL);
NewFtSet->AddMember(NewPartition,
FtSet->GetMemberDescription(i),
FtGroup);
}
if (OldFtSet != NULL) {
//
// Replace the old FT set information
//
m_FtSetInfo.SetAt(pos, NewFtSet);
delete(OldFtSet);
} else {
//
// Add the new FT set to the FT information
//
m_FtSetInfo.AddTail(NewFtSet);
}
}
CFtInfoFtSet *
CFtInfo::FindFtSetInfo(
IN DWORD Signature
)
{
CFtInfoFtSet *RetInfo;
POSITION pos = m_FtSetInfo.GetHeadPosition();
while (pos) {
RetInfo = m_FtSetInfo.GetNext(pos);
if (RetInfo->GetMemberBySignature(Signature) != NULL) {
return(RetInfo);
}
}
return(NULL);
}
CFtInfoFtSet *
CFtInfo::EnumFtSetInfo(
IN DWORD Index
)
{
DWORD i=0;
CFtInfoFtSet *RetInfo;
POSITION pos = m_FtSetInfo.GetHeadPosition();
while (pos) {
RetInfo = m_FtSetInfo.GetNext(pos);
if (i == Index) {
return(RetInfo);
}
++i;
}
return(NULL);
}
BOOL
CFtInfo::DeleteFtSetInfo(
IN CFtInfoFtSet *FtSet
)
{
POSITION pos = m_FtSetInfo.Find(FtSet);
if (pos == NULL) {
DISKLOG(("CFtInfo::DeleteFtSetInfo did not find Info %08X\n",FtSet));
DISKERR(IDS_GENERAL_FAILURE, ERROR_FILE_NOT_FOUND);
return(FALSE);
} else {
DWORD i;
CFtInfoPartition *FtPartition;
//
// Set the FT group of all this set's members to -1
//
for (i=0; ; i++) {
FtPartition = FtSet->GetMemberByIndex(i);
if (FtPartition == NULL) {
break;
}
FtPartition->m_PartitionInfo->FtGroup = (USHORT)-1;
FtPartition->m_PartitionInfo->FtMember = 0;
FtPartition->m_PartitionInfo->FtType = NotAnFtMember;
}
m_FtSetInfo.RemoveAt(pos);
delete(FtSet);
}
return(TRUE);
}
CFtInfoPartition *
CFtInfo::FindPartition(
DWORD Signature,
LARGE_INTEGER StartingOffset,
LARGE_INTEGER Length
)
{
CFtInfoDisk *Disk;
Disk = FindDiskInfo(Signature);
if (Disk == NULL) {
return(NULL);
}
return(Disk->GetPartition(StartingOffset, Length));
}
CFtInfoPartition *
CFtInfo::FindPartition(
UCHAR DriveLetter
)
{
CFtInfoDisk *Disk;
CFtInfoPartition *Partition;
DWORD DiskIndex;
DWORD PartitionIndex;
for (DiskIndex = 0; ; DiskIndex++) {
Disk = EnumDiskInfo(DiskIndex);
if (Disk == NULL) {
break;
}
for (PartitionIndex = 0; ; PartitionIndex++) {
Partition = Disk->GetPartitionByIndex(PartitionIndex);
if (Partition == NULL) {
break;
}
if (Partition->m_PartitionInfo->AssignDriveLetter &&
(Partition->m_PartitionInfo->DriveLetter == DriveLetter)) {
//
// Found a match.
//
return(Partition);
}
}
}
return(NULL);
}
DWORD
CFtInfo::GetSize()
{
CFtInfoDisk *DiskInfo;
CFtInfoFtSet *FtSetInfo;
DWORD Delta;
//
// Start off with the fixed size header
//
DWORD Size = sizeof(DISK_CONFIG_HEADER);
DISKLOG(("CFtInfo::GetSize headersize = %x\n",Size));
//
// Add in the size of the DISK_REGISTRY header
//
Delta = sizeof(DISK_REGISTRY) - sizeof(DISK_DESCRIPTION);
Size += Delta;
DISKLOG(("CFtInfo::GetSize += DISK_REGISTRY(%x) = %x\n",Delta, Size));
if (!m_DiskInfo.IsEmpty()) {
//
// Add the sizes of each disks partition information
//
POSITION pos = m_DiskInfo.GetHeadPosition();
while (pos) {
DiskInfo = m_DiskInfo.GetNext(pos);
Delta = DiskInfo->GetSize();
Size += Delta;
DISKLOG(("CFtInfo::GetSize += DiskInfo(%x) = %x\n",Delta, Size));
}
if (!m_FtSetInfo.IsEmpty()) {
//
// Add in the size of the FT_REGISTRY header
//
Delta = sizeof(FT_REGISTRY) - sizeof(FT_DESCRIPTION);
Size += Delta;
DISKLOG(("CFtInfo::GetSize += FT_REGISTRY(%x) = %x\n",Delta, Size));
//
// Add the sizes of each FT sets information
//
pos = m_FtSetInfo.GetHeadPosition();
while (pos) {
FtSetInfo = m_FtSetInfo.GetNext(pos);
Delta = FtSetInfo->GetSize();
Size += Delta;
DISKLOG(("CFtInfo::GetSize +=FtSetInfo(%x) = %x\n",Delta, Size));
}
}
}
return(Size);
}
VOID
CFtInfo::GetData(
PDISK_CONFIG_HEADER pDest
)
{
PDISK_CONFIG_HEADER DiskConfigHeader;
PDISK_REGISTRY DiskRegistry;
PDISK_DESCRIPTION DiskDescription;
PFT_REGISTRY FtRegistry;
PFT_DESCRIPTION FtDescription;
DWORD Count;
POSITION pos;
CFtInfoDisk *DiskInfo;
CFtInfoFtSet *FtSetInfo;
//
// Initialize the fixed size header.
//
// Copy the original header, then zero out the fields we might
// change.
//
DiskConfigHeader = pDest;
CopyMemory(DiskConfigHeader, m_buffer, sizeof(DISK_CONFIG_HEADER));
DiskConfigHeader->DiskInformationOffset = sizeof(DISK_CONFIG_HEADER);
DiskConfigHeader->FtInformationOffset = 0;
DiskConfigHeader->FtInformationSize = 0;
//
// Initialize the fixed size DISK_REGISTRY header
//
DiskRegistry = (PDISK_REGISTRY)(DiskConfigHeader + 1);
DiskRegistry->NumberOfDisks = (USHORT)m_DiskInfo.GetCount();
DiskRegistry->ReservedShort = 0;
DiskConfigHeader->DiskInformationSize = sizeof(DISK_REGISTRY) - sizeof(DISK_DESCRIPTION);
if (!m_DiskInfo.IsEmpty()) {
//
// Get each disk's information
//
DiskDescription = &DiskRegistry->Disks[0];
pos = m_DiskInfo.GetHeadPosition();
while (pos) {
DWORD Size;
DiskInfo = m_DiskInfo.GetNext(pos);
DiskInfo->SetOffset((DWORD)((PUCHAR)DiskDescription - (PUCHAR)DiskConfigHeader));
DiskInfo->GetData((PBYTE)DiskDescription);
Size = DiskInfo->GetSize();
DiskConfigHeader->DiskInformationSize += Size;
DiskDescription = (PDISK_DESCRIPTION)((PUCHAR)DiskDescription + Size);
}
//
// Now set the FT information
//
FtRegistry = (PFT_REGISTRY)DiskDescription;
DiskConfigHeader->FtInformationOffset =(DWORD)((PBYTE)FtRegistry - (PBYTE)DiskConfigHeader);
if (!m_FtSetInfo.IsEmpty()) {
//
// Initialize the fixed size FT_REGISTRY header
//
FtRegistry->NumberOfComponents = (USHORT)m_FtSetInfo.GetCount();
FtRegistry->ReservedShort = 0;
DiskConfigHeader->FtInformationSize = sizeof(FT_REGISTRY) - sizeof(FT_DESCRIPTION);
FtDescription = &FtRegistry->FtDescription[0];
pos = m_FtSetInfo.GetHeadPosition();
while (pos) {
DWORD Size;
FtSetInfo = m_FtSetInfo.GetNext(pos);
FtSetInfo->GetData((PBYTE)FtDescription);
Size = FtSetInfo->GetSize();
DiskConfigHeader->FtInformationSize += Size;
FtDescription = (PFT_DESCRIPTION)((PUCHAR)FtDescription + Size);
}
}
}
}
//********************
//
// Implementation of standard partition information
//
//********************
CFtInfoPartition::CFtInfoPartition(
CFtInfoDisk *Disk,
DISK_PARTITION UNALIGNED *Description
)
{
m_ParentDisk = Disk;
m_Modified = TRUE;
m_PartitionInfo = (PDISK_PARTITION)LocalAlloc(LMEM_FIXED, sizeof(DISK_PARTITION));
if (m_PartitionInfo == NULL) {
DISKERR(IDS_GENERAL_FAILURE, ERROR_NOT_ENOUGH_MEMORY);
// [REENGINEER], will AV in a second
}
CopyMemory(m_PartitionInfo, Description, sizeof(DISK_PARTITION));
}
CFtInfoPartition::CFtInfoPartition(
CFtInfoDisk *Disk,
CPhysicalPartition *Partition
)
{
m_ParentDisk = Disk;
m_Modified = TRUE;
m_PartitionInfo = (PDISK_PARTITION)LocalAlloc(LMEM_FIXED, sizeof(DISK_PARTITION));
if (m_PartitionInfo == NULL) {
DISKERR(IDS_GENERAL_FAILURE, ERROR_NOT_ENOUGH_MEMORY);
// [REENGINEER], will AV in a second
}
m_PartitionInfo->FtType = NotAnFtMember;
m_PartitionInfo->FtState = Healthy;
m_PartitionInfo->StartingOffset = Partition->m_Info.StartingOffset;
m_PartitionInfo->Length = Partition->m_Info.PartitionLength;
m_PartitionInfo->FtLength.QuadPart = 0;
m_PartitionInfo->DriveLetter = 0;
m_PartitionInfo->AssignDriveLetter = FALSE;
m_PartitionInfo->LogicalNumber = 0;
m_PartitionInfo->FtGroup = (USHORT)-1;
m_PartitionInfo->FtMember = 0;
m_PartitionInfo->Modified = FALSE;
}
CFtInfoPartition::CFtInfoPartition(
CFtInfoDisk *Disk,
PARTITION_INFORMATION * PartitionInfo
)
{
m_ParentDisk = Disk;
m_Modified = TRUE;
m_PartitionInfo = (PDISK_PARTITION)LocalAlloc(LMEM_FIXED, sizeof(DISK_PARTITION));
if (m_PartitionInfo == NULL) {
DISKERR(IDS_GENERAL_FAILURE, ERROR_NOT_ENOUGH_MEMORY);
// [REENGINEER], will AV in a second
}
m_PartitionInfo->FtType = NotAnFtMember;
m_PartitionInfo->FtState = Healthy;
m_PartitionInfo->StartingOffset = PartitionInfo->StartingOffset;
m_PartitionInfo->Length = PartitionInfo->PartitionLength;
m_PartitionInfo->FtLength.QuadPart = 0;
m_PartitionInfo->DriveLetter = 0;
m_PartitionInfo->AssignDriveLetter = FALSE;
m_PartitionInfo->LogicalNumber = 0;
m_PartitionInfo->FtGroup = (USHORT)-1;
m_PartitionInfo->FtMember = 0;
m_PartitionInfo->Modified = FALSE;
}
CFtInfoPartition::~CFtInfoPartition()
{
if (m_Modified) {
LocalFree(m_PartitionInfo);
}
}
VOID
CFtInfoPartition::GetData(
PDISK_PARTITION pDest
)
{
DISKLOG(("CFtInfoPartition::GetData %12I64X - %12I64X\n",
m_PartitionInfo->StartingOffset.QuadPart,
m_PartitionInfo->Length.QuadPart));
DISKLOG((" %c (%s) %x %x %x\n",
m_PartitionInfo->DriveLetter,
m_PartitionInfo->AssignDriveLetter ? "Sticky" : "Not Sticky",
m_PartitionInfo->LogicalNumber,
m_PartitionInfo->FtGroup,
m_PartitionInfo->FtMember));
CopyMemory(pDest, m_PartitionInfo, sizeof(DISK_PARTITION));
}
DWORD
CFtInfoPartition::GetOffset(
VOID
)
{
DWORD ParentOffset;
ParentOffset = m_ParentDisk->GetOffset();
return(ParentOffset + m_RelativeOffset);
}
VOID
CFtInfoPartition::MakeSticky(
UCHAR DriveLetter
)
{
m_PartitionInfo->DriveLetter = DriveLetter;
//
// if drive letter is being removed, clear the sticky bit
//
m_PartitionInfo->AssignDriveLetter = ( DriveLetter != 0 );
}
//********************
//
// Implementation of standard disk information
//
//********************
CFtInfoDisk::CFtInfoDisk(
DISK_DESCRIPTION UNALIGNED *Description
)
{
DWORD i;
DWORD Offset;
CFtInfoPartition *Partition;
//
// windisk sometimes puts in disk information
// for disks with no partitions. Seems a bit pointless.
//
// DISKASSERT(Description->NumberOfPartitions > 0);
m_PartitionCount = Description->NumberOfPartitions;
m_Signature = Description->Signature;
for (i=0; i<m_PartitionCount; i++) {
Partition = new CFtInfoPartition(this, &Description->Partitions[i]);
if (Partition != NULL) {
Offset = sizeof(DISK_DESCRIPTION) + i*sizeof(DISK_PARTITION) - sizeof(DISK_PARTITION);
Partition->SetOffset(Offset);
m_PartitionInfo.AddTail(Partition);
}
}
}
CFtInfoDisk::CFtInfoDisk(
CPhysicalDisk *Disk
)
{
DISKASSERT(Disk->m_PartitionCount > 0);
m_PartitionCount = Disk->m_PartitionCount;
m_Signature = Disk->m_Signature;
//
// Build up the partition objects
//
CFtInfoPartition *PartitionInfo;
CPhysicalPartition *Partition;
DWORD Offset;
DWORD i=0;
POSITION pos = Disk->m_PartitionList.GetHeadPosition();
while (pos) {
Partition = Disk->m_PartitionList.GetNext(pos);
PartitionInfo = new CFtInfoPartition(this, Partition);
if (PartitionInfo != NULL) {
Offset = sizeof(DISK_DESCRIPTION) + i*sizeof(DISK_PARTITION) - sizeof(DISK_PARTITION);
PartitionInfo->SetOffset(Offset);
m_PartitionInfo.AddTail(PartitionInfo);
++i;
}
}
}
CFtInfoDisk::CFtInfoDisk(
CFtInfoDisk *DiskInfo
)
{
DISKASSERT(DiskInfo->m_PartitionCount > 0);
m_PartitionCount = DiskInfo->m_PartitionCount;
m_Signature = DiskInfo->m_Signature;
//
// Build up the partition objects
//
CFtInfoPartition *PartitionInfo;
CFtInfoPartition *SourcePartitionInfo;
DWORD Offset;
DWORD i=0;
POSITION pos = DiskInfo->m_PartitionInfo.GetHeadPosition();
while (pos) {
SourcePartitionInfo = DiskInfo->m_PartitionInfo.GetNext(pos);
PartitionInfo = new CFtInfoPartition(this, SourcePartitionInfo->m_PartitionInfo);
if (PartitionInfo != NULL) {
Offset = sizeof(DISK_DESCRIPTION) + i*sizeof(DISK_PARTITION) - sizeof(DISK_PARTITION);
PartitionInfo->SetOffset(Offset);
m_PartitionInfo.AddTail(PartitionInfo);
++i;
}
}
}
CFtInfoDisk::CFtInfoDisk(
DRIVE_LAYOUT_INFORMATION *DriveLayout
)
{
DWORD i;
CFtInfoPartition *ftInfoPartition;
m_PartitionCount = 0; // [GN] Bugfix #278913
m_Signature = DriveLayout->Signature;
for (i=0; i < DriveLayout->PartitionCount; i++) {
if (DriveLayout->PartitionEntry[i].RecognizedPartition) {
m_PartitionCount++;
ftInfoPartition = new CFtInfoPartition(this, &DriveLayout->PartitionEntry[i]);
if (ftInfoPartition == NULL) {
DISKERR(IDS_GENERAL_FAILURE, ERROR_NOT_ENOUGH_MEMORY);
// [REENGINEER], we will add a 0 pointer into m_PartitionInfo ... bad
}
m_PartitionInfo.AddTail(ftInfoPartition);
}
}
}
CFtInfoDisk::~CFtInfoDisk()
{
CFtInfoPartition *Partition;
while (!m_PartitionInfo.IsEmpty()) {
Partition = m_PartitionInfo.RemoveHead();
delete(Partition);
}
}
BOOL
CFtInfoDisk::operator==(
const CFtInfoDisk& Disk
)
{
if (m_PartitionCount != Disk.m_PartitionCount) {
DISKLOG(("CFtInfoDisk::operator== partition count %d != %d\n",
m_PartitionCount,
Disk.m_PartitionCount));
return(FALSE);
}
if (m_Signature != Disk.m_Signature) {
DISKLOG(("CFtInfoDisk::operator== signature %08lx != %08lx\n",
m_Signature,
Disk.m_Signature));
return(FALSE);
}
POSITION MyPos, OtherPos;
CFtInfoPartition *MyPartition, *OtherPartition;
MyPos = m_PartitionInfo.GetHeadPosition();
OtherPos = Disk.m_PartitionInfo.GetHeadPosition();
while (MyPos || OtherPos) {
if (!MyPos) {
DISKLOG(("CFtInfoDisk::operator== MyPos is NULL\n"));
return(FALSE);
}
if (!OtherPos) {
DISKLOG(("CFtInfoDisk::operator== OtherPos is NULL\n"));
return(FALSE);
}
MyPartition = m_PartitionInfo.GetNext(MyPos);
OtherPartition = Disk.m_PartitionInfo.GetNext(OtherPos);
if (memcmp(MyPartition->m_PartitionInfo,
OtherPartition->m_PartitionInfo,
sizeof(DISK_PARTITION)) != 0) {
DISKLOG(("CFtInfoDisk::operator== DISK_PARTITIONs don't match\n"));
return(FALSE);
}
}
DISKLOG(("CFtInfoDisk::operator== disk information matches\n"));
return(TRUE);
}
CFtInfoPartition *
CFtInfoDisk::GetPartition(
LARGE_INTEGER StartingOffset,
LARGE_INTEGER Length
)
{
DWORD i;
CFtInfoPartition *Partition;
POSITION pos;
pos = m_PartitionInfo.GetHeadPosition();
while (pos) {
Partition = m_PartitionInfo.GetNext(pos);
if ((Partition->m_PartitionInfo->StartingOffset.QuadPart == StartingOffset.QuadPart) &&
(Partition->m_PartitionInfo->Length.QuadPart == Length.QuadPart)) {
return(Partition);
}
}
return(NULL);
}
CFtInfoPartition *
CFtInfoDisk::GetPartitionByOffset(
DWORD Offset
)
{
CFtInfoPartition *Partition;
POSITION pos;
pos = m_PartitionInfo.GetHeadPosition();
while (pos) {
Partition = m_PartitionInfo.GetNext(pos);
if (Partition->GetOffset() == Offset) {
return(Partition);
}
}
return(NULL);
}
CFtInfoPartition *
CFtInfoDisk::GetPartitionByIndex(
DWORD Index
)
{
DWORD i = 0;
CFtInfoPartition *Partition;
POSITION pos;
pos = m_PartitionInfo.GetHeadPosition();
while (pos) {
Partition = m_PartitionInfo.GetNext(pos);
if (i == Index) {
return(Partition);
}
++i;
}
return(NULL);
}
DWORD
CFtInfoDisk::FtPartitionCount(
VOID
)
/*++
Routine Description:
Returns the number of FT partitions on this disk. This is useful
for determining whether a given FT set shares this disk with another
FT set.
Arguments:
None
Return Value:
The number of FT partitions on this disk
--*/
{
POSITION pos;
CFtInfoPartition *Partition;
DWORD Count = 0;
pos = m_PartitionInfo.GetHeadPosition();
while (pos) {
Partition = m_PartitionInfo.GetNext(pos);
if (Partition->IsFtPartition()) {
++Count;
}
}
return(Count);
}
DWORD
CFtInfoDisk::GetSize(
VOID
)
{
return(sizeof(DISK_DESCRIPTION) +
(m_PartitionCount-1) * sizeof(DISK_PARTITION));
}
VOID
CFtInfoDisk::GetData(
PBYTE pDest
)
{
PDISK_DESCRIPTION Description = (PDISK_DESCRIPTION)pDest;
DWORD i;
CFtInfoPartition *Partition;
DISKLOG(("CFtInfoDisk::GetData signature %08lx has %d partitions\n",m_Signature, m_PartitionCount));
Description->NumberOfPartitions = (USHORT)m_PartitionCount;
Description->ReservedShort = 0;
Description->Signature = m_Signature;
POSITION pos = m_PartitionInfo.GetHeadPosition();
i=0;
while (pos) {
Partition = m_PartitionInfo.GetNext(pos);
Partition->GetData(&Description->Partitions[i]);
++i;
}
}
//********************
//
// Implementation of FT registry information
//
//********************
BOOL
CFtInfoFtSet::Initialize(USHORT Type, FT_STATE FtVolumeState)
{
m_Modified = TRUE;
m_FtDescription = (PFT_DESCRIPTION)LocalAlloc(LMEM_FIXED, sizeof(FT_DESCRIPTION));
DISKASSERT(m_FtDescription);
m_FtDescription->NumberOfMembers = 0;
m_FtDescription->Type = Type;
m_FtDescription->Reserved = 0;
m_FtDescription->FtVolumeState = FtVolumeState;
return(TRUE);
}
BOOL
CFtInfoFtSet::Initialize(
CFtInfo *FtInfo,
PFT_DESCRIPTION Description
)
{
m_FtDescription = Description;
m_Modified = FALSE;
//
// Create the list of members.
//
CFtInfoDisk *Disk;
CFtInfoPartition *Partition;
PFT_MEMBER_DESCRIPTION Member;
DWORD i;
if (Description->NumberOfMembers == 0) {
//
// WINDISK will sometimes put FT sets with zero members in
// the registry after breaking a mirror set. Throw them out,
// seems pretty pointless...
//
DISKLOG(("CFtInfoFtSet::Initialize - FT Set with zero members ignored\n"));
return(FALSE);
}
m_Members.SetSize(Description->NumberOfMembers);
for (i=0; i<Description->NumberOfMembers; i++) {
Member = &Description->FtMemberDescription[i];
//
// Find the disk by its signature
//
Disk = FtInfo->FindDiskInfo(Member->Signature);
if (Disk == NULL) {
DISKLOG(("CFtInfoFtSet::Initialize - Disk signature %08lx not found\n",
Member->Signature));
return(FALSE);
}
//
// Find the partition by its offset.
//
Partition = Disk->GetPartitionByOffset(Member->OffsetToPartitionInfo);
if (Partition == NULL) {
DISKLOG(("CFtInfoFtSet::Initialize - Partition on disk %08lx at offset %08lx not found\n",
Member->Signature,
Member->OffsetToPartitionInfo));
return(FALSE);
}
//
// Add this partition to our list.
//
if (Partition->m_PartitionInfo->FtMember >= Description->NumberOfMembers) {
DISKLOG(("CFtInfoFtSet::Initialize - member %d out of range\n",
Partition->m_PartitionInfo->FtMember));
return(FALSE);
}
if (m_Members[Partition->m_PartitionInfo->FtMember] != NULL) {
DISKLOG(("CFtInfoFtSet::Initialize - Duplicate member %d\n",
Partition->m_PartitionInfo->FtMember));
return(FALSE);
}
m_Members.SetAt(Partition->m_PartitionInfo->FtMember, Partition);
}
return(TRUE);
}
CFtInfoFtSet::~CFtInfoFtSet()
{
if (m_Modified) {
LocalFree(m_FtDescription);
}
}
BOOL
CFtInfoFtSet::operator==(
const CFtInfoFtSet& FtSet1
)
{
DWORD MemberCount;
DWORD i;
CFtInfoDisk *Disk1;
CFtInfoDisk *Disk2;
if (GetType() != FtSet1.GetType()) {
return(FALSE);
}
if (GetState() != FtSet1.GetState()) {
return(FALSE);
}
MemberCount = GetMemberCount();
if (MemberCount != FtSet1.GetMemberCount()) {
return(FALSE);
}
for (i=0; i<MemberCount; i++) {
Disk1 = GetMemberByIndex(i)->m_ParentDisk;
Disk2 = FtSet1.GetMemberByIndex(i)->m_ParentDisk;
if (!(*Disk1 == *Disk2)) {
return(FALSE);
}
}
DISKLOG(("CFtInfoFtSet::operator== FT information matches\n"));
return(TRUE);
}
DWORD
CFtInfoFtSet::GetSize(
VOID
) const
{
return(sizeof(FT_DESCRIPTION) +
(m_FtDescription->NumberOfMembers-1) * sizeof(FT_MEMBER_DESCRIPTION));
}
VOID
CFtInfoFtSet::GetData(
PBYTE pDest
)
{
PFT_DESCRIPTION Description = (PFT_DESCRIPTION)pDest;
DWORD Size = GetSize();
CopyMemory(Description, m_FtDescription, Size);
//
// Now go through the partitions and update the offsets.
//
DWORD i;
CFtInfoPartition *Partition;
for (i=0; i<GetMemberCount(); i++) {
Partition = m_Members[i];
Description->FtMemberDescription[i].OffsetToPartitionInfo = Partition->GetOffset();
}
}
BOOL
CFtInfoFtSet::IsAlone(
VOID
)
/*++
Routine Description:
Returns whether or not this FT set has a disk in common with
any other FT set.
Arguments:
None
Return Value:
TRUE if this FT set does not share any physical disk with any other
FT set
FALSE if there is another FT set sharing a disk as this one.
--*/
{
//
// Go through each member of the FT set and see if any disk has
// more than one partition that is marked as an FT partition.
//
POSITION pos;
CFtInfoPartition *Partition;
CFtInfoDisk *Disk;
DWORD i;
for (i=0; i<GetMemberCount(); i++) {
Partition = m_Members[i];
Disk = Partition->m_ParentDisk;
if (Disk->FtPartitionCount() > 1) {
//
// This disk has more than one FT partition, so there must be
// another set sharing it.
//
return(FALSE);
}
}
return(TRUE);
}
CFtInfoPartition *
CFtInfoFtSet::GetMemberBySignature(
IN DWORD Signature
) const
{
CFtInfoPartition *Partition;
DWORD i;
for (i=0; i<GetMemberCount(); i++) {
Partition = m_Members[i];
if (Partition->m_ParentDisk->m_Signature == Signature) {
return(Partition);
}
}
return(NULL);
}
CFtInfoPartition *
CFtInfoFtSet::GetMemberByIndex(
IN DWORD Index
) const
{
CFtInfoPartition *Partition;
if (Index >= GetMemberCount()) {
return(NULL);
}
return(m_Members[Index]);
}
DWORD
CFtInfoFtSet::AddMember(
CFtInfoPartition *Partition,
PFT_MEMBER_DESCRIPTION Description,
USHORT FtGroup
)
{
DWORD MemberCount;
PFT_DESCRIPTION NewBuff;
PFT_MEMBER_DESCRIPTION NewMember;
MemberCount = GetMemberCount();
if (MemberCount > 0) {
//
// Grow the size of our structure.
//
if (m_Modified) {
NewBuff = (PFT_DESCRIPTION)LocalReAlloc(m_FtDescription,
sizeof(FT_DESCRIPTION) + MemberCount*sizeof(FT_MEMBER_DESCRIPTION),
LMEM_MOVEABLE);
if (NewBuff == NULL) {
return(ERROR_NOT_ENOUGH_MEMORY);
}
m_FtDescription = NewBuff;
} else {
m_Modified = TRUE;
NewBuff = (PFT_DESCRIPTION)LocalAlloc(LMEM_FIXED,
sizeof(FT_DESCRIPTION) + MemberCount*sizeof(FT_MEMBER_DESCRIPTION));
if (NewBuff == NULL) {
return(ERROR_NOT_ENOUGH_MEMORY);
}
CopyMemory(NewBuff,
m_FtDescription,
sizeof(FT_DESCRIPTION) + (MemberCount-1)*sizeof(FT_MEMBER_DESCRIPTION));
m_FtDescription = NewBuff;
}
}
NewMember = &m_FtDescription->FtMemberDescription[MemberCount];
//
// Initialize the member description. Note that the OffsetToPartitionInfo
// will be updated when the user does a GetData.
//
NewMember->State = Description->State;
NewMember->ReservedShort = Description->ReservedShort;
NewMember->Signature = Description->Signature;
NewMember->LogicalNumber = Description->LogicalNumber;
//
// Add the partition to our list.
//
Partition->m_PartitionInfo->FtGroup = FtGroup;
Partition->m_PartitionInfo->FtMember = (USHORT)MemberCount;
m_Members.SetAtGrow(Partition->m_PartitionInfo->FtMember, Partition);
m_FtDescription->NumberOfMembers = (USHORT)GetMemberCount();
return(ERROR_SUCCESS);
}
//
// Some C wrappers used by the FT Set resource DLL
//
extern "C" {
PFT_INFO
DiskGetFtInfo(
VOID
)
{
PFT_INFO FtInfo;
FtInfo = (PFT_INFO)new CFtInfo;
return(FtInfo);
}
VOID
DiskFreeFtInfo(
PFT_INFO FtInfo
)
{
CFtInfo *Info;
Info = (CFtInfo *)FtInfo;
delete Info;
}
DWORD
DiskEnumFtSetSignature(
IN PFULL_FTSET_INFO FtSet,
IN DWORD MemberIndex,
OUT LPDWORD MemberSignature
)
/*++
Routine Description:
Returns the signature of the N'th member of the FT set.
Arguments:
FtSet - Supplies the FT information returned by DiskGetFullFtSetInfo
MemberIndex - Supplies the 0-based index of the member to return.
MemberSignature - Returns the signature of the MemberIndex'th member.
Return Value:
ERROR_SUCCESS if successful
ERROR_NO_MORE_ITEMS if the index is greather than the number of members
--*/
{
CFtInfo *Info;
CFtInfoFtSet *FtSetInfo;
CFtInfoPartition *Partition;
Info = new CFtInfo((PDISK_CONFIG_HEADER)FtSet);
if (Info == NULL) {
DISKERR(IDS_GENERAL_FAILURE, ERROR_NOT_ENOUGH_MEMORY);
return ERROR_NOT_ENOUGH_MEMORY;
}
FtSetInfo = Info->EnumFtSetInfo(0);
if (FtSetInfo == NULL) {
//
// There is no FT set information, so just return the signature of the n'th member
//
CFtInfoDisk *Disk;
Disk = Info->EnumDiskInfo(MemberIndex);
if (Disk == NULL) {
return(ERROR_NO_MORE_ITEMS);
} else {
*MemberSignature = Disk->m_Signature;
return(ERROR_SUCCESS);
}
}
Partition = FtSetInfo->GetMemberByIndex(MemberIndex);
if (Partition == NULL) {
return(ERROR_NO_MORE_ITEMS);
}
*MemberSignature = Partition->m_ParentDisk->m_Signature;
delete Info;
return(ERROR_SUCCESS);
}
PFULL_FTSET_INFO
DiskGetFullFtSetInfo(
IN PFT_INFO FtInfo,
IN LPCWSTR lpszMemberList,
OUT LPDWORD pSize
)
/*++
Routine Description:
Serializes the complete information from an FT set in a form
suitable for saving to a file or the registry. These bits can
be restored with DiskSetFullFtSetInfo.
Arguments:
FtInfo - supplies the FT information
lpszMemberList - Supplies a list of signatures. The list is in the
REG_MULTI_SZ format.
pSize - Returns the size of the FT information bytes.
Return Value:
A pointer to the serializable FT information if successful.
NULL on error
--*/
{
PDISK_CONFIG_HEADER DiskConfig;
DWORD Length;
CFtInfo *OriginalInfo;
CFtInfo *NewInfo;
CFtInfoFtSet *FtSetInfo;
CFtInfoPartition *FtPartitionInfo;
PDISK_PARTITION Member;
DWORD MemberCount;
DWORD i;
DWORD Index;
DWORD Signature;
LPCWSTR lpszSignature;
DWORD MultiSzLength;
WCHAR SignatureString[9];
OriginalInfo = (CFtInfo *)FtInfo;
MultiSzLength = ClRtlMultiSzLength(lpszMemberList);
//
// First, try to find an FT set that has the "identity" of at least one of the
// supplied members. This is tricky because we need to make sure that if multiple
// FT sets are broken and reformed with different members, only one FT resource
// picks up each FT set. We will find a matching FT set if:
// - One of the supplied members is the first member of a mirror or volume set.
// - The supplied members make up N-1 members of an N member SWP.
// - The supplied members make up all the members of a stripe.
//
for (i=0; ; i++) {
lpszSignature = ClRtlMultiSzEnum(lpszMemberList,
MultiSzLength,
i);
if (lpszSignature == NULL) {
DISKLOG(("DiskGetFullFtSetInfo: no FTSET containing members found\n"));
FtSetInfo = NULL;
break;
}
Signature = wcstoul(lpszSignature, NULL, 16);
DISKLOG(("DiskGetFullFtSetInfo: looking for member %08lx\n", Signature));
FtSetInfo = OriginalInfo->FindFtSetInfo(Signature);
if (FtSetInfo == NULL) {
DISKLOG(("DiskGetFullFtSetInfo: member %08lx is not in any FT set\n", Signature));
} else {
//
// Check to see if this is the first member of a volume set or mirror
//
if ((FtSetInfo->GetType() == Mirror) ||
(FtSetInfo->GetType() == VolumeSet)) {
//
// Now check to see if this member is the first member of the set.
//
if (FtSetInfo->GetMemberByIndex(0)->m_ParentDisk->m_Signature == Signature) {
//
// We have found a matching FT set.
//
DISKLOG(("DiskGetFullFtSetInfo: member %08lx found in FT set.\n", Signature));
break;
}
} else if ((FtSetInfo->GetType() == StripeWithParity) ||
(FtSetInfo->GetType() == Stripe)) {
DWORD MaxMissing;
//
// Check to see if the supplied member list makes up N-1 of the members
// of a stripe with parity or all the members of a stripe.
//
if (FtSetInfo->GetType() == StripeWithParity) {
MaxMissing = 1;
} else {
MaxMissing = 0;
}
for (Index = 0; ; Index++) {
FtPartitionInfo = FtSetInfo->GetMemberByIndex(Index);
if (FtPartitionInfo == NULL) {
break;
}
//
// Try to find this signature in the passed in member list.
//
wsprintf(SignatureString, L"%08lX", FtPartitionInfo->m_ParentDisk->m_Signature);
if (ClRtlMultiSzScan(lpszMemberList,SignatureString) == NULL) {
//
// This FT set member is not in the supplied list.
//
DISKLOG(("DiskGetFullFtSetInfo: member %08lx missing from old member list\n",
FtPartitionInfo->m_ParentDisk->m_Signature));
if (MaxMissing == 0) {
FtSetInfo = NULL;
break;
}
--MaxMissing;
}
}
if (FtSetInfo != NULL) {
//
// We have found a matching FT set
//
break;
}
}
}
}
if (FtSetInfo != NULL) {
//
// An FT Set exists that contains one of the supplied members.
// Create a new CFtInfo that contains nothing but the FT set and
// its members.
//
NewInfo = new CFtInfo(FtSetInfo);
if (NewInfo == NULL) {
SetLastError(ERROR_INVALID_DATA);
return(NULL);
}
} else {
//
// No FT Set contains any of the supplied members. Create a new CFtInfo
// that contains disk entries for each of the supplied members, but no
// FT set information. Any members which are members of an FT set will
// be excluded, since they have been assimilated into another set.
//
NewInfo = new CFtInfo((CFtInfoFtSet *)NULL);
if (NewInfo == NULL) {
SetLastError(ERROR_INVALID_DATA);
return(NULL);
}
//
// Find each member in the original FT info and add it to the new
// FT info.
//
for (i=0; ; i++) {
CFtInfoDisk *DiskInfo;
lpszSignature = ClRtlMultiSzEnum(lpszMemberList,
MultiSzLength,
i);
if (lpszSignature == NULL) {
break;
}
Signature = wcstoul(lpszSignature, NULL, 16);
if (OriginalInfo->FindFtSetInfo(Signature) != NULL) {
DISKLOG(("DiskGetFullFtSetInfo: removing member %08lx as it is already a member of another set.\n",Signature));
} else {
DiskInfo = OriginalInfo->FindDiskInfo(Signature);
if (DiskInfo != NULL) {
CFtInfoDisk *NewDisk;
NewDisk = new CFtInfoDisk(DiskInfo);
if ( NewDisk == NULL ) {
SetLastError(ERROR_INVALID_DATA);
return(NULL);
}
DISKLOG(("DiskGetFullFtSetInfo: adding member %08lx to new FT info\n",Signature));
NewInfo->SetDiskInfo(NewDisk);
} else {
DISKLOG(("DiskGetFullFtSetInfo: member %08lx not found in original FT info\n",Signature));
}
}
}
}
//
// Get the FT data
//
*pSize = NewInfo->GetSize();
DiskConfig = (PDISK_CONFIG_HEADER)LocalAlloc(LMEM_FIXED, *pSize);
if (DiskConfig == NULL) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return(NULL);
}
NewInfo->GetData(DiskConfig);
delete NewInfo;
return((PFULL_FTSET_INFO)DiskConfig);
}
PFULL_FTSET_INFO
DiskGetFullFtSetInfoByIndex(
IN PFT_INFO FtInfo,
IN DWORD Index,
OUT LPDWORD pSize
)
/*++
Routine Description:
Serializes the complete information from an FT set in a form
suitable for saving to a file or the registry. These bits can
be restored with DiskSetFullFtSetInfo.
Arguments:
FtInfo - supplies the FT information
Index - Supplies the index
pSize - Returns the size of the FT information bytes.
Return Value:
A pointer to the serializable FT information if successful.
NULL on error
--*/
{
PDISK_CONFIG_HEADER DiskConfig;
DWORD Length;
CFtInfo *OriginalInfo;
CFtInfo *NewInfo;
CFtInfoFtSet *FtSetInfo;
OriginalInfo = (CFtInfo *)FtInfo;
FtSetInfo = OriginalInfo->EnumFtSetInfo(Index);
if (FtSetInfo == NULL) {
return(NULL);
}
//
// Create a new CFtInfo that contains nothing but the FT set and
// its members.
//
NewInfo = new CFtInfo(FtSetInfo);
if (NewInfo == NULL) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return(NULL);
}
//
// Get the FT data
//
*pSize = NewInfo->GetSize();
DiskConfig = (PDISK_CONFIG_HEADER)LocalAlloc(LMEM_FIXED, *pSize);
if (DiskConfig == NULL) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return(NULL);
}
NewInfo->GetData(DiskConfig);
delete NewInfo;
return((PFULL_FTSET_INFO)DiskConfig);
}
BOOL
DiskCheckFtSetLetters(
IN PFT_INFO FtInfo,
IN PFULL_FTSET_INFO Bytes,
OUT WCHAR *Letter
)
/*++
Routine Description:
This routine checks to see if the FT set info conflicts with
any already-defined sticky drive letters on the current system.
If a conflict is found, the conflicting drive letter is returned.
Arguments:
FtInfo - Supplies the FT information
Bytes - Supplies the information returned from DiskGetFullFtSetInfo
Return Value:
TRUE if no conflicts were detected
FALSE if a conflict was detected. If it returns FALSE, *Letter will be
set to the conflicting drive letter.
--*/
{
CFtInfo *RegistryInfo;
CFtInfo *NewInfo;
CFtInfoDisk *Disk;
CFtInfoFtSet *FtSet;
DWORD i;
RegistryInfo = (CFtInfo *)FtInfo;
NewInfo = new CFtInfo((PDISK_CONFIG_HEADER)Bytes);
if (NewInfo == NULL) {
DISKERR(IDS_GENERAL_FAILURE, ERROR_NOT_ENOUGH_MEMORY);
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// Go through each physical disk in the FT set. For each one, see if
// there is a physical disk in the registry information with a different
// signature and the same drive letter. If so, we have a conflict.
//
FtSet = NewInfo->EnumFtSetInfo(0);
DISKASSERT(FtSet != NULL);
for (i=0; ; i++) {
Disk = NewInfo->EnumDiskInfo(i);
if (Disk == NULL) {
break;
}
//
// Go through each partition on this disk and look up the drive letter
// in the registry information.
//
CFtInfoPartition *Partition;
DWORD Index;
for (Index = 0; ; Index++) {
Partition = Disk->GetPartitionByIndex(Index);
if (Partition == NULL) {
break;
}
//
// If this partition has an assigned drive letter,
// check it out.
//
if (Partition->m_PartitionInfo->AssignDriveLetter) {
//
// See if there is an existing partition with this drive
// letter already in the registry information
//
CFtInfoPartition *Existing;
Existing = RegistryInfo->FindPartition(Partition->m_PartitionInfo->DriveLetter);
if (Existing != NULL) {
//
// If the existing partition has a different signature than
// the new partition, we have found a conflict.
//
if (Existing->m_ParentDisk->m_Signature !=
Partition->m_ParentDisk->m_Signature) {
*Letter = (WCHAR)Partition->m_PartitionInfo->DriveLetter;
delete NewInfo;
return(FALSE);
}
}
}
}
}
delete NewInfo;
return(TRUE);
}
DWORD
DiskSetFullFtSetInfo(
IN PFT_INFO FtInfo,
IN PFULL_FTSET_INFO Bytes
)
/*++
Routine Description:
Restores the complete information from an FT set to the DISK
key in the registry. The FT set information must have been
returned from DiskGetFullFtSetInfo.
Arguments:
FtInfo - supplies the FT information
Bytes - Supplies the information returned from DiskGetFullFtSetInfo.
Return Value:
ERROR_SUCCESS if successful.
Win32 error otherwise
--*/
{
CFtInfo *RegistryInfo;
CFtInfo *NewInfo;
CFtInfoFtSet *OldFtSet=NULL;
CFtInfoFtSet *NewFtSet=NULL;
CFtInfoDisk *Disk;
DWORD i;
BOOL Modified = FALSE;
DWORD Status;
RegistryInfo = (CFtInfo *)FtInfo;
NewInfo = new CFtInfo((PDISK_CONFIG_HEADER)Bytes);
if (NewInfo == NULL) {
DISKERR(IDS_GENERAL_FAILURE, ERROR_NOT_ENOUGH_MEMORY);
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// If the new information contains an FT set, merge that into the
// current registry.
//
if (NewInfo->EnumFtSetInfo(0) != NULL) {
//
// Find an FT set in the registry that has a signature
// that is the same as one of those in the restored FT set.
//
NewFtSet = NewInfo->EnumFtSetInfo(0);
DISKASSERT(NewFtSet != NULL);
for (i=0; ; i++) {
Disk = NewInfo->EnumDiskInfo(i);
if (Disk == NULL) {
break;
}
//
// Try and find an existing FT set containing this signature
//
OldFtSet = RegistryInfo->FindFtSetInfo(Disk->m_Signature);
if (OldFtSet != NULL) {
break;
}
}
if (Disk == NULL) {
//
// No matching FT set was found. We can just add this one directly.
//
Modified = TRUE;
RegistryInfo->AddFtSetInfo(NewFtSet);
} else {
if (!(*OldFtSet == *NewFtSet)) {
Modified = TRUE;
RegistryInfo->AddFtSetInfo(NewFtSet, OldFtSet);
}
}
} else {
//
// There is no FT set in the new registry. This will happen if a mirror
// set gets broken. For each member in the new information, delete any
// FT sets that it is a part of and merge it into the registry.
//
for (i=0; ; i++) {
Disk = NewInfo->EnumDiskInfo(i);
if (Disk == NULL) {
break;
}
Modified = TRUE;
//
// Remove any FT sets containing this signature
//
OldFtSet = RegistryInfo->FindFtSetInfo(Disk->m_Signature);
if (OldFtSet != NULL) {
RegistryInfo->DeleteFtSetInfo(OldFtSet);
}
//
// Set the FT information for this member into the registry.
//
CFtInfoDisk *NewDisk;
NewDisk = new CFtInfoDisk(Disk);
if (NewDisk == NULL) {
DISKERR(IDS_GENERAL_FAILURE, ERROR_NOT_ENOUGH_MEMORY);
return ERROR_NOT_ENOUGH_MEMORY;
}
RegistryInfo->SetDiskInfo(NewDisk);
}
}
delete NewInfo;
if (Modified) {
//
// Commit these changes to the Disk key
//
DISKLOG(("DiskSetFullFtSetInfo: committing changes to registry\n"));
Status = RegistryInfo->CommitRegistryData();
} else {
DISKLOG(("DiskSetFullFtSetInfo: no changes detected\n"));
Status = ERROR_SUCCESS;
}
return(Status);
}
DWORD
DiskDeleteFullFtSetInfo(
IN PFT_INFO FtInfo,
IN LPCWSTR lpszMemberList
)
/*++
Routine Description:
Deletes any FT set information for the specified members. This is
used when a mirror set is broken.
Arguments:
FtInfo - supplies the FT information
lpszMemberList - Supplies the list of members whose FT information is to
be deleted.
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
CFtInfo *OriginalInfo;
DWORD Signature;
LPCWSTR lpszSignature;
DWORD MultiSzLength;
CFtInfoFtSet *FtSetInfo;
DWORD i;
BOOL Modified = FALSE;
DWORD Status;
OriginalInfo = (CFtInfo *)FtInfo;
MultiSzLength = ClRtlMultiSzLength(lpszMemberList);
//
// Go through each disk in the MultiSzLength and remove any FT information
// for it.
//
for (i=0; ; i++) {
lpszSignature = ClRtlMultiSzEnum(lpszMemberList,
MultiSzLength,
i);
if (lpszSignature == NULL) {
break;
}
Signature = wcstoul(lpszSignature, NULL, 16);
DISKLOG(("DiskDeleteFullFtSetInfo: deleting member %1!08lx!\n", Signature));
FtSetInfo = OriginalInfo->FindFtSetInfo(Signature);
if (FtSetInfo == NULL) {
DISKLOG(("DiskDeleteFullFtSetInfo: member %08lx is not in any FT set\n", Signature));
} else {
DISKLOG(("DiskDeleteFullFtSetInfo: member %08lx found. \n", Signature));
Modified = TRUE;
OriginalInfo->DeleteFtSetInfo(FtSetInfo);
}
}
if (Modified) {
//
// Commit these changes to the Disk key
//
DISKLOG(("DiskDeleteFullFtSetInfo: committing changes to registry\n"));
Status = OriginalInfo->CommitRegistryData();
} else {
DISKLOG(("DiskDeleteFullFtSetInfo: no changes detected\n"));
Status = ERROR_SUCCESS;
}
return(Status);
}
#if 0
FT_TYPE
DiskFtInfoGetType(
IN PFULL_FTSET_INFO FtSet
)
/*++
Routine Description:
Returns the type of FT set
Arguments:
FtSet - Supplies the FT set information
Return Value:
FT_TYPE of the supplied FT set.
--*/
{
FT_TYPE Type;
CFtInfo *Info;
CFtInfoFtSet *FtSetInfo;
Info = new CFtInfo((PDISK_CONFIG_HEADER)FtSet);
if (Info == NULL) {
DISKERR(IDS_GENERAL_FAILURE, ERROR_NOT_ENOUGH_MEMORY);
// [REENGINEER] will AV in a second
}
FtSetInfo = Info->EnumFtSetInfo(0);
if (FtSetInfo == NULL) {
Type = NotAnFtMember;
} else {
Type = (FT_TYPE)FtSetInfo->GetType();
}
delete Info;
return(Type);
}
#endif
VOID
DiskMarkFullFtSetDirty(
IN PFULL_FTSET_INFO FtSet
)
/*++
Routine Description:
Changes the FT set information so that when it is mounted by FTDISK
the redundant information will be regenerated. This is necessary because
FTDISK only looks at the FT_REGISTRY dirty bit at boot time. By doing
this, we simulate a per-FT Set dirty bit that FT will respect when
sets are brought online after boot.
Magic algorithm from norbertk:
If (and only if) the entire FT set is healthy
If the set is a mirror
set state of second member to SyncRedundantCopy
If the set is a SWP
set state of first member to SyncRedundantCopy
Arguments:
FtSet - Supplies the FT set information returned from DiskGetFullFtSetInfo
Return Value:
None.
--*/
{
DWORD i;
CFtInfo *Info;
CFtInfoFtSet *FtSetInfo;
CFtInfoPartition *Partition;
USHORT FtType;
Info = new CFtInfo((PDISK_CONFIG_HEADER)FtSet);
if (Info == NULL) {
DISKERR(IDS_GENERAL_FAILURE, ERROR_NOT_ENOUGH_MEMORY);
return; // [REENGINEER] we avoided an AV but the caller won't know
}
FtSetInfo = Info->EnumFtSetInfo(0);
if (FtSetInfo != NULL) {
//
// Check all the members to see if they are all healthy.
//
for (i=0; ; i++) {
Partition = FtSetInfo->GetMemberByIndex(i);
if (Partition == NULL) {
break;
} else {
if (Partition->m_PartitionInfo->FtState != Healthy) {
break;
}
}
}
if (Partition == NULL) {
//
// All the members are marked healthy. Set one of them to
// SyncRedundantCopy to force a regen.
//
FtType = FtSetInfo->GetType();
if ((FtType == Mirror) || (FtType == StripeWithParity)) {
if (FtType == Mirror) {
Partition = FtSetInfo->GetMemberByIndex(1);
} else {
Partition = FtSetInfo->GetMemberByIndex(0);
}
if ( Partition != NULL ) {
Partition->m_PartitionInfo->FtState = SyncRedundantCopy;
}
//
// Get the modified FT data
//
Info->GetData((PDISK_CONFIG_HEADER)FtSet);
}
}
}
delete Info;
}
#if 0
BOOL
DiskFtInfoEqual(
IN PFULL_FTSET_INFO Info1,
IN PFULL_FTSET_INFO Info2
)
/*++
Routine Description:
Compares two FT set information structures to see if they are semantically
identical (no change).
Arguments:
Info1 - Supplies the first information (returned from DiskGetFullFtSetInfo)
Info2 - Supplies the second information (returned from DiskGetFullFtSetInfo)
Return Value:
TRUE if the structures are identical (there has been no change)
FALSE if the structures are different.
--*/
{
CFtInfo *FtInfo1;
CFtInfo *FtInfo2;
CFtInfoFtSet *FtSet1;
CFtInfoFtSet *FtSet2;
PDISK_CONFIG_HEADER Config1;
PDISK_CONFIG_HEADER Config2;
BOOL Result;
Config1 = (PDISK_CONFIG_HEADER)Info1;
Config2 = (PDISK_CONFIG_HEADER)Info2;
FtInfo1 = new CFtInfo(Config1);
if (FtInfo1 == NULL) {
DISKERR(IDS_GENERAL_FAILURE, ERROR_NOT_ENOUGH_MEMORY);
// [Unused Code] will AV in a second
}
FtInfo2 = new CFtInfo(Config2);
if (FtInfo2 == NULL) {
DISKERR(IDS_GENERAL_FAILURE, ERROR_NOT_ENOUGH_MEMORY);
// [Unused Code] will AV in a second
}
FtSet1 = FtInfo1->EnumFtSetInfo(0);
FtSet2 = FtInfo2->EnumFtSetInfo(0);
if ((FtSet1 == NULL) || (FtSet2 == NULL)){
//
// If there is no FT set in one of the structures, there must be no
// FT set in both of the structures.
//
if ((FtSet1 != NULL) || (FtSet2 != NULL)) {
Result = FALSE;
} else {
DWORD i;
CFtInfoDisk *Disk1, *Disk2;
//
// Neither structure has an FT Set, so we must compare each of the
// members individually to see if they are equal.
//
for (i=0; ;i++) {
Disk1 = FtInfo1->EnumDiskInfo(i);
Disk2 = FtInfo2->EnumDiskInfo(i);
if ((Disk1 == NULL) && (Disk2 == NULL)) {
//
// Everything compared ok until the end of the list.
//
Result = TRUE;
break;
}
if ((Disk1 == NULL) || (Disk2 == NULL)) {
//
// A different number of members in each list.
//
Result = FALSE;
break;
}
if (!(*Disk1 == *Disk2)) {
Result = FALSE;
break;
}
}
}
} else {
if (*FtSet1 == *FtSet2) {
Result = TRUE;
} else {
Result = FALSE;
}
}
delete(FtInfo1);
delete(FtInfo2);
return(Result);
}
#endif
PFULL_DISK_INFO
DiskGetFullDiskInfo(
IN PFT_INFO DiskInfo,
IN DWORD Signature,
OUT LPDWORD pSize
)
/*++
Routine Description:
Serializes the complete information from a disk in a form
suitable for saving to a file or the registry. These bits can
be restored with DiskSetFullDiskInfo.
Arguments:
DiskInfo - supplies the disk information.
Signature - Supplies the signature.
pSize - Returns the size of the disk information in bytes.
Return Value:
A pointer to the serializable disk information if successful.
NULL on error
--*/
{
PDISK_CONFIG_HEADER DiskConfig;
DWORD Length;
CFtInfo *OriginalInfo;
CFtInfo *NewInfo;
CFtInfoDisk *FtDiskInfo;
CFtInfoDisk *NewDiskInfo;
OriginalInfo = (CFtInfo *)DiskInfo;
//
// First, try to find a disk that matches the supplied signature.
//
DISKLOG(("DiskGetFullDiskInfo: looking for signature %08lx\n", Signature));
FtDiskInfo = OriginalInfo->FindDiskInfo(Signature);
if (FtDiskInfo == NULL) {
DISKLOG(("DiskGetFullDiskInfo: signature %08lx not found\n", Signature));
SetLastError(ERROR_INVALID_DATA);
return(NULL);
}
//
// Create a new CFtInfo that contains no disk information.
//
NewInfo = new CFtInfo((CFtInfoFtSet *)NULL);
if (NewInfo == NULL) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return(NULL);
}
NewDiskInfo = new CFtInfoDisk(FtDiskInfo);
if (NewDiskInfo == NULL) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return(NULL);
}
//
// Disk info already exists. Use that data.
//
NewInfo->SetDiskInfo(NewDiskInfo);
//
// Get the disk data
//
*pSize = NewInfo->GetSize();
DiskConfig = (PDISK_CONFIG_HEADER)LocalAlloc(LMEM_FIXED, *pSize);
if (DiskConfig == NULL) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return(NULL);
}
NewInfo->GetData(DiskConfig);
delete NewInfo;
return((PFULL_DISK_INFO)DiskConfig);
} // DiskGetFullDiskInfo
DWORD
DiskSetFullDiskInfo(
IN PFT_INFO DiskInfo,
IN PFULL_DISK_INFO Bytes
)
/*++
Routine Description:
Restores the complete information from a disk to the DISK
key in the registry. The disk information must have been
returned from DiskGetFullDiskInfo.
Arguments:
DiskInfo - supplies the disk information
Bytes - Supplies the information returned from DiskGetFullDiskInfo.
Return Value:
ERROR_SUCCESS if successful.
Win32 error otherwise
--*/
{
CFtInfo *RegistryInfo;
CFtInfo *NewInfo;
CFtInfoDisk *OldDisk=NULL;
CFtInfoDisk *NewDisk=NULL;
CFtInfoDisk *Disk;
DWORD Status;
RegistryInfo = (CFtInfo *)DiskInfo;
NewInfo = new CFtInfo((PDISK_CONFIG_HEADER)Bytes);
if (NewInfo == NULL) {
DISKERR(IDS_GENERAL_FAILURE, ERROR_NOT_ENOUGH_MEMORY);
return ERROR_NOT_ENOUGH_MEMORY;
}
DISKASSERT(NewInfo->EnumFtSetInfo(0) == NULL); // No FT sets
DISKASSERT(NewInfo->EnumDiskInfo(1) == NULL); // No more than 1 disk
//
// There is no FT set in the new registry. This will happen if a mirror
// set gets broken. For each member in the new information, delete any
// FT sets that it is a part of and merge it into the registry.
//
Disk = NewInfo->EnumDiskInfo(0);
if ( Disk == NULL ) {
DISKLOG(("DiskSetFullDiskInfo: no disks detected\n"));
return(ERROR_SUCCESS);
}
//
// Remove old data containing this signature
//
OldDisk = RegistryInfo->FindDiskInfo(Disk->m_Signature);
if (OldDisk != NULL) {
RegistryInfo->DeleteDiskInfo(Disk->m_Signature);
}
//
// Set the disk information for this disk into the registry.
//
NewDisk = new CFtInfoDisk(Disk);
if (NewDisk == NULL) {
DISKERR(IDS_GENERAL_FAILURE, ERROR_NOT_ENOUGH_MEMORY);
return ERROR_NOT_ENOUGH_MEMORY;
}
RegistryInfo->SetDiskInfo(NewDisk);
delete NewInfo;
//
// Commit these changes to the Disk key
//
DISKLOG(("DiskSetFullDiskInfo: committing changes to registry\n"));
Status = RegistryInfo->CommitRegistryData();
return(Status);
} // DiskSetFullDiskInfo
PFT_INFO
DiskGetFtInfoFromFullDiskinfo(
IN PFULL_DISK_INFO Bytes
)
{
CFtInfo* DiskInfo = new CFtInfo((PDISK_CONFIG_HEADER)Bytes);
if (DiskInfo) {
SetLastError(ERROR_SUCCESS);
} else {
SetLastError(ERROR_OUTOFMEMORY);
}
return reinterpret_cast<PFT_INFO>(DiskInfo);
} // DiskGetFtInfoFromFullDiskinfo //
DWORD
DiskAddDiskInfoEx(
IN PFT_INFO DiskInfo,
IN DWORD DiskIndex,
IN DWORD Signature,
IN DWORD Options
)
/*++
Routine Description:
Adds an NT4 style DISK registry entry for the specified
disk. Used to handle new disks being added to the system
after the cluster service has started. On NT4, windisk would
have been run to configure the disk and generate an entry
in the DISK key. On NT5, the DISK is no longer maintained by
the disk stack; most of the code in this module depends on
windisk maintaining this key. For NT5,
Arguments:
DiskIndex - the physical drive number for the new drive
Signature - the signature of the drive; used for sanity checking
Options - 0 or combination of the following:
DISKRTL_REPLACE_IF_EXISTS: Replace the information for the
disk if it is already exists
DISKRTL_COMMIT: If this flag is set then registry key System\DISK
is updated with a new information
Return Value:
ERROR_SUCCESS if successful.
Win32 error otherwise
--*/
{
DWORD status = ERROR_SUCCESS;
CFtInfo * diskInfo;
CFtInfoDisk * newDisk, * oldDisk;
WCHAR physDriveBuff[100];
HANDLE hDisk;
PDRIVE_LAYOUT_INFORMATION driveLayout;
diskInfo = (CFtInfo *)DiskInfo;
//
// read in the drive layout data for this new drive
//
wsprintf(physDriveBuff, L"\\\\.\\PhysicalDrive%d", DiskIndex);
hDisk = CreateFile(physDriveBuff,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hDisk == INVALID_HANDLE_VALUE) {
return GetLastError();
}
if (ClRtlGetDriveLayoutTable( hDisk, &driveLayout, NULL )) {
//
// make sure signatures line up and that a
// description for this disk doesn't already exist
//
if ( Signature == driveLayout->Signature ) {
oldDisk = diskInfo->FindDiskInfo(Signature);
if (oldDisk != NULL) {
if (Options & DISKRTL_REPLACE_IF_EXISTS) {
diskInfo->DeleteDiskInfo(Signature);
oldDisk = NULL;
}
}
if ( oldDisk == NULL ) {
//
// Pull in a copy of the existing data in the registry
// and initialize a new disk and its associated partitions.
//
newDisk = new CFtInfoDisk( driveLayout );
if ( newDisk != NULL ) {
//
// add the disk to the current database and
// commit the updated database to the registry
//
diskInfo->AddDiskInfo( newDisk );
if (Options & DISKRTL_COMMIT) {
status = diskInfo->CommitRegistryData();
} else {
status = ERROR_SUCCESS;
}
} else {
status = ERROR_OUTOFMEMORY;
}
} else {
status = ERROR_ALREADY_EXISTS;
}
} else {
status = ERROR_INVALID_PARAMETER;
}
LocalFree( driveLayout );
} else {
status = GetLastError();
}
CloseHandle( hDisk );
return status;
} // DiskAddDiskEx
DWORD
DiskAddDriveLetterEx(
IN PFT_INFO DiskInfo,
IN DWORD Signature,
IN LARGE_INTEGER StartingOffset,
IN LARGE_INTEGER PartitionLength,
IN UCHAR DriveLetter,
IN DWORD Options
)
/*++
Routine Description:
Add a drive letter to the specified partition
Arguments:
Signature - the signature of the drive; used for sanity checking
StartingOffset
PartitionLength - describes which partition
DriveLetter - letter to be associated with this partition
Options - if DISKRTL_COMMIT flag is set then registry System\DISK
is immedately updated with a new information
Return Value:
ERROR_SUCCESS if successful.
Win32 error otherwise
--*/
{
DWORD status;
CFtInfo * diskInfo;
CFtInfoPartition * partInfo;
diskInfo = (CFtInfo *)DiskInfo;
partInfo = diskInfo->FindPartition( Signature, StartingOffset, PartitionLength );
if ( partInfo != NULL ) {
partInfo->MakeSticky( DriveLetter );
if (Options & DISKRTL_COMMIT) {
status = diskInfo->CommitRegistryData();
} else {
status = ERROR_SUCCESS;
}
} else {
status = ERROR_INVALID_PARAMETER;
}
return status;
}
DWORD
DiskCommitFtInfo(
IN PFT_INFO FtInfo
)
{
CFtInfo * info = reinterpret_cast<CFtInfo*>( FtInfo );
DWORD status = info->CommitRegistryData();
return status;
}
PFT_DISK_INFO
FtInfo_GetFtDiskInfoBySignature(
IN PFT_INFO FtInfo,
IN DWORD Signature
)
/*++
Routine Description:
Finds an information for a particular drive
Arguments:
DiskInfo - supplies the disk information
Signature - describes which drive
Return Value:
NULL - if not found
CFtInfoDisk - otherwise
--*/
{
CFtInfo* Info = reinterpret_cast<CFtInfo *>(FtInfo);
PFT_DISK_INFO result = reinterpret_cast<PFT_DISK_INFO>(Info->FindDiskInfo(Signature));
if (result == 0) {
SetLastError(ERROR_FILE_NOT_FOUND);
}
return result;
} // FtInfo_GetFtDiskInfoBySignature //
DISK_PARTITION UNALIGNED *
FtDiskInfo_GetPartitionInfoByIndex(
IN PFT_DISK_INFO DiskInfo,
IN DWORD Index
)
/*++
Routine Description:
Get Drive Letter for a partition specified by an offset
Arguments:
DiskInfo - supplies the disk information
index - describes which partition (0 based)
Return Value:
PDISK_PARTITION stucture or NULL
if return value is NULL, SetLastError value is set as follows:
ERROR_FILE_NOT_FOUND: if partition cannot be found
ERROR_INVALID_HANDLE: if partition is found, but m_PartitionInfo is unassigned
--*/
{
CFtInfoDisk* Info = reinterpret_cast<CFtInfoDisk*>(DiskInfo);
CFtInfoPartition* Partition = Info->GetPartitionByIndex( Index );
if (Partition == 0) {
SetLastError(ERROR_FILE_NOT_FOUND);
return NULL;
}
if (Partition->m_PartitionInfo == 0) {
SetLastError(ERROR_INVALID_HANDLE);
return 0;
}
return Partition->m_PartitionInfo;
} // FtDiskInfo_GetDriveLetterByIndex //
DWORD
FtDiskInfo_GetPartitionCount(
IN PFT_DISK_INFO DiskInfo
)
{
CFtInfoDisk* Info = reinterpret_cast<CFtInfoDisk*>(DiskInfo);
return Info->m_PartitionCount;
} // FtDiskInfo_GetPartitionCount //
} // extern C