/*++ 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 #include #include #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; iPartitionCount; 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; IndexGetMemberByIndex(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 *)®Header, &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; iGetMemberByIndex(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; iGetMemberByIndex(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; iPartitions[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; iNumberOfMembers; 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; im_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; iFtMemberDescription[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; im_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; im_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(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( 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(FtInfo); PFT_DISK_INFO result = reinterpret_cast(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(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(DiskInfo); return Info->m_PartitionCount; } // FtDiskInfo_GetPartitionCount // } // extern C