/*++ Copyright (C) 2000 Microsoft Corporation Module Name: ftcomp.cpp Abstract: This compatibility dll is used by winnt32.exe in order to decide whether the installation process should be aborted because of FT sets present in the system. Author: Cristian Teodorescu (cristiat) 6-July-2000 Environment: compatibility dll for winnt32.exe Notes: Revision History: --*/ #include #include #include #include #include "ftcomp.h" #include "ftcomprc.h" HINSTANCE g_hinst; WCHAR g_FTCOMP50_ERROR_HTML_FILE[] = L"compdata\\ftcomp1.htm"; WCHAR g_FTCOMP50_ERROR_TEXT_FILE[] = L"compdata\\ftcomp1.txt"; WCHAR g_FTCOMP40_ERROR_HTML_FILE[] = L"compdata\\ftcomp2.htm"; WCHAR g_FTCOMP40_ERROR_TEXT_FILE[] = L"compdata\\ftcomp2.txt"; WCHAR g_FTCOMP40_WARNING_HTML_FILE[] = L"compdata\\ftcomp3.htm"; WCHAR g_FTCOMP40_WARNING_TEXT_FILE[] = L"compdata\\ftcomp3.txt"; extern "C" BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReasonForCall, LPVOID lpReserved ) { BOOL status = TRUE; switch( dwReasonForCall ) { case DLL_PROCESS_ATTACH: g_hinst = hInstance; DisableThreadLibraryCalls(hInstance); break; case DLL_PROCESS_DETACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: break; } return status; } BOOL WINAPI FtCompatibilityCheckError( IN PCOMPAIBILITYCALLBACK CompatibilityCallback, IN LPVOID Context ) /*++ Routine Description: This routine is called by winnt32.exe in order to decide whether the installation process should be aborted because of FT sets present in a Windows 2000 system or later. It also aborts the installation on NT 4.0 systems if the boot/system/pagefile volumes are FT sets Arguments: CompatibilityCallback - Supplies the winnt32 callback Context - Supplies the compatibility context Return Value: FALSE if the installation can continue TRUE if the installation must be aborted --*/ { OSVERSIONINFO osvi; BOOL ftPresent; BOOL result; COMPATIBILITY_ENTRY ce; WCHAR description[100]; osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (!GetVersionEx(&osvi)) { return FALSE; } if (osvi.dwPlatformId != VER_PLATFORM_WIN32_NT || osvi.dwMajorVersion < 4) { return FALSE; } if (osvi.dwMajorVersion == 4) { // // On NT 4.0 look for boot/system/pagefile FT sets // result = FtBootSystemPagefilePresent40(&ftPresent); } else { // // On Windows 2000 or later look for any FT sets. // result = FtPresent50(&ftPresent); } if (result && !ftPresent) { // // The setup can continue. // return FALSE; } // // FT sets are present in the system or a fatal error occured. // Queue the incompatibility error // ZeroMemory((PVOID) &ce, sizeof(COMPATIBILITY_ENTRY)); if (osvi.dwMajorVersion == 4) { if (!LoadString(g_hinst, FTCOMP_STR_ERROR40_DESCRIPTION, description, 100)) { description[0] = 0; } ce.HtmlName = g_FTCOMP40_ERROR_HTML_FILE; ce.TextName = g_FTCOMP40_ERROR_TEXT_FILE; } else { if (!LoadString(g_hinst, FTCOMP_STR_ERROR50_DESCRIPTION, description, 100)) { description[0] = 0; } ce.HtmlName = g_FTCOMP50_ERROR_HTML_FILE; ce.TextName = g_FTCOMP50_ERROR_TEXT_FILE; } ce.Description = description; ce.RegKeyName = NULL; ce.RegValName = NULL; ce.RegValDataSize = 0; ce.RegValData = NULL; ce.SaveValue = NULL; ce.Flags = 0; CompatibilityCallback(&ce, Context); return TRUE; } BOOL WINAPI FtCompatibilityCheckWarning( IN PCOMPAIBILITYCALLBACK CompatibilityCallback, IN LPVOID Context ) /*++ Routine Description: This routine is called by winnt32.exe in order to decide whether the user should be warned about the presence of FT sets in a Windows NT 4.0 system Arguments: CompatibilityCallback - Supplies the winnt32 callback Context - Supplies the compatibility context Return Value: FALSE if the installation can continue TRUE if the installation must be aborted --*/ { OSVERSIONINFO osvi; BOOL ftPresent; BOOL result; COMPATIBILITY_ENTRY ce; WCHAR description[100]; // // This function is supposed to work only on Windows NT 4.0 // osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (!GetVersionEx(&osvi)) { return FALSE; } if (osvi.dwPlatformId != VER_PLATFORM_WIN32_NT || osvi.dwMajorVersion != 4) { return FALSE; } result = FtPresent40(&ftPresent); if (result && !ftPresent) { // // No FT sets are present in the system. The setup can continue. // return FALSE; } // // FT sets are present in the system or a fatal error occured. // Queue the incompatibility warning // if (!LoadString(g_hinst, FTCOMP_STR_WARNING40_DESCRIPTION, description, 100)) { description[0] = 0; } ZeroMemory((PVOID) &ce, sizeof(COMPATIBILITY_ENTRY)); ce.Description = description; ce.HtmlName = g_FTCOMP40_WARNING_HTML_FILE; ce.TextName = g_FTCOMP40_WARNING_TEXT_FILE; ce.RegKeyName = NULL; ce.RegValName = NULL; ce.RegValDataSize = 0; ce.RegValData = NULL; ce.SaveValue = NULL; ce.Flags = 0; CompatibilityCallback(&ce, Context); return TRUE; } BOOL FtPresent50( PBOOL FtPresent ) /*++ Routine Description: This routine looks for FT volumes on a Window 2000 or later system. Arguments: FtPresent - is set to true if FT sets are detected in the system Return Value: TRUE if the function is successful FALSE if some fatal error occured --*/ { HANDLE handle; FT_ENUMERATE_LOGICAL_DISKS_OUTPUT output; BOOL result; DWORD bytes; *FtPresent = FALSE; handle = CreateFile(L"\\\\.\\FtControl", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); if (handle == INVALID_HANDLE_VALUE) { return FALSE; } memset(&output, 0, sizeof(output)); result = DeviceIoControl(handle, FT_ENUMERATE_LOGICAL_DISKS, NULL, 0, &output, sizeof(output), &bytes, NULL); CloseHandle(handle); if (!result && GetLastError() != ERROR_MORE_DATA) { return FALSE; } if (output.NumberOfRootLogicalDisks > 0) { *FtPresent = TRUE; } return TRUE; } BOOL FtPresent40( PBOOL FtPresent ) /*++ Routine Description: This routine looks for NTFT partitions on a Window NT 4.0 system Arguments: FtPresent - is set to true if FT sets are detected in the system Return Value: TRUE if the function is successful FALSE if some fatal error occured --*/ { HKEY hkey; DWORD registrySize; PDISK_CONFIG_HEADER registry; PDISK_REGISTRY diskRegistry; ULONG i; WCHAR devicePath[50]; NTSTATUS status; HANDLE hdev; *FtPresent = FALSE; // // Get the ftdisk database from registry. // Key: HKLM\System\Disk // Value: Information // if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"System\\Disk", 0, KEY_QUERY_VALUE, &hkey) != ERROR_SUCCESS) { return TRUE; } if (RegQueryValueEx(hkey, L"Information", NULL, NULL, NULL, ®istrySize) != ERROR_SUCCESS) { RegCloseKey(hkey); return TRUE; } registry = (PDISK_CONFIG_HEADER) LocalAlloc(0, registrySize); if (!registry) { RegCloseKey(hkey); return FALSE; } if (RegQueryValueEx(hkey, L"Information", NULL, NULL, (LPBYTE) registry, ®istrySize) != ERROR_SUCCESS) { LocalFree(registry); RegCloseKey(hkey); return TRUE; } RegCloseKey(hkey); // // If no FT volume info is present in the registry database we are done // if (registry->FtInformationSize == 0) { LocalFree(registry); return TRUE; } diskRegistry = (PDISK_REGISTRY) ((PUCHAR) registry + registry->DiskInformationOffset); // // Enumerate all disks present in the system by opening \Device\HarddiskX\Partition0 // in sequence starting with disk 0. Stop when getting STATUS_OBJECT_PATH_NOT_FOUND // // for (i = 0;; i++) { // // Open the device // swprintf(devicePath, L"\\Device\\Harddisk%lu\\Partition0", i); status = OpenDevice(devicePath, &hdev); if (status == STATUS_OBJECT_PATH_NOT_FOUND) { break; } if (!NT_SUCCESS(status) || hdev == NULL || hdev == INVALID_HANDLE_VALUE) { // inaccessible device continue; } // // Look for FT partitions on disk // if (!FtPresentOnDisk40(hdev, diskRegistry, FtPresent)) { CloseHandle(hdev); return FALSE; } CloseHandle(hdev); if (*FtPresent) { break; } } LocalFree(registry); return TRUE; } BOOL FtBootSystemPagefilePresent40( PBOOL FtPresent ) /*++ Routine Description: This routine looks for FT sets that are boot/system/pagefile volumes on a NT 4.0 system Arguments: FtPresent - is set to true if boot/system/pagefile FT sets are detected in the system Return Value: TRUE if the function is successful FALSE if some fatal error occured --*/ { HKEY hkey; DWORD registrySize; PDISK_CONFIG_HEADER registry; PDISK_REGISTRY diskRegistry; WCHAR buffer[MAX_PATH + 1]; NTSTATUS status; UCHAR genericBuffer[0x10000]; PSYSTEM_PAGEFILE_INFORMATION pageFileInfo; PWCHAR p; WCHAR bootDL = 0, systemDL = 0; WCHAR dl; *FtPresent = FALSE; // // Get the ftdisk database from registry. // Key: HKLM\System\Disk // Value: Information // if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"System\\Disk", 0, KEY_QUERY_VALUE, &hkey) != ERROR_SUCCESS) { return TRUE; } if (RegQueryValueEx(hkey, L"Information", NULL, NULL, NULL, ®istrySize) != ERROR_SUCCESS) { RegCloseKey(hkey); return TRUE; } registry = (PDISK_CONFIG_HEADER) LocalAlloc(0, registrySize); if (!registry) { RegCloseKey(hkey); return FALSE; } if (RegQueryValueEx(hkey, L"Information", NULL, NULL, (LPBYTE) registry, ®istrySize) != ERROR_SUCCESS) { LocalFree(registry); RegCloseKey(hkey); return TRUE; } RegCloseKey(hkey); // // If no FT volume info is present in the registry database we are done // if (registry->FtInformationSize == 0) { LocalFree(registry); return TRUE; } diskRegistry = (PDISK_REGISTRY) ((PUCHAR) registry + registry->DiskInformationOffset); // // Check the boot volume // if (!GetSystemDirectory(buffer, MAX_PATH)) { goto system; } if (buffer[1] != L':') { goto system; } bootDL = (WCHAR) tolower(buffer[0]); if (IsFtSet40(bootDL, diskRegistry)) { *FtPresent = TRUE; goto exit; } system: // // Check the system volume // if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"System\\Setup", 0, KEY_QUERY_VALUE, &hkey) != ERROR_SUCCESS) { goto paging; } registrySize = MAX_PATH * sizeof(WCHAR); if (RegQueryValueEx(hkey, L"SystemPartition", NULL, NULL, (LPBYTE) buffer, ®istrySize) != ERROR_SUCCESS) { RegCloseKey(hkey); goto paging; } RegCloseKey(hkey); if (!GetDeviceDriveLetter(buffer, &systemDL)) { goto paging; } systemDL = (WCHAR) tolower(systemDL); if (systemDL == bootDL) { // already checked this drive letter goto paging; } if (IsFtSet40(systemDL, diskRegistry)) { *FtPresent = TRUE; goto exit; } paging: // // Check the paging volumes // if (!NT_SUCCESS(NtQuerySystemInformation( SystemPageFileInformation, genericBuffer, sizeof(genericBuffer), NULL))) { goto exit; } pageFileInfo = (PSYSTEM_PAGEFILE_INFORMATION) genericBuffer; while (TRUE) { // // Since the format of the pagefile name generally // looks something like "\DosDevices\x:\pagefile.sys", // just use the first character before the column // and assume that's the drive letter. // for (p = pageFileInfo->PageFileName.Buffer; p < pageFileInfo->PageFileName.Buffer + pageFileInfo->PageFileName.Length && *p != L':'; p++); if (p < pageFileInfo->PageFileName.Buffer + pageFileInfo->PageFileName.Length && p > pageFileInfo->PageFileName.Buffer) { p--; dl = (WCHAR) tolower(*p); if (dl >= L'a' && dl <= L'z') { // // Found the drive letter of a paging volume // if (dl != bootDL && dl != systemDL) { if (IsFtSet40(dl, diskRegistry)) { *FtPresent = TRUE; goto exit; } } } } if (!pageFileInfo->NextEntryOffset) { break; } pageFileInfo = (PSYSTEM_PAGEFILE_INFORMATION)((PCHAR) pageFileInfo + pageFileInfo->NextEntryOffset); } exit: LocalFree(registry); return TRUE; } NTSTATUS OpenDevice( PWSTR DeviceName, PHANDLE Handle ) /*++ Routine Description: This routine opens a device for read Arguments: DeviceName - supplies the device name Handle - returns a handle to the open device Return Value: NTSTATUS --*/ { OBJECT_ATTRIBUTES oa; NTSTATUS status; IO_STATUS_BLOCK statusBlock; UNICODE_STRING unicodeName; int i; status = RtlCreateUnicodeString(&unicodeName, DeviceName); if (!NT_SUCCESS(status)) { return status; } memset(&statusBlock, 0, sizeof(IO_STATUS_BLOCK)); memset(&oa, 0, sizeof(OBJECT_ATTRIBUTES)); oa.Length = sizeof(OBJECT_ATTRIBUTES); oa.ObjectName = &unicodeName; oa.Attributes = OBJ_CASE_INSENSITIVE; // // If a sharing violation occurs, retry it for // max. 10 seconds // for (i = 0; i < 5; i++) { status = NtOpenFile(Handle, SYNCHRONIZE | GENERIC_READ, &oa, &statusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_ALERT); if (status == STATUS_SHARING_VIOLATION) { Sleep(2000); } else { break; } } RtlFreeUnicodeString(&unicodeName); return status; } PDISK_PARTITION FindPartitionInRegistry40( PDISK_REGISTRY DiskRegistry, ULONG Signature, LONGLONG Offset ) /*++ Routine Description: This routine looks for a gicen partition into the NT 4.0 ftdisk registry database Arguments: DiskRegistry - supplies the ftdisk registry database Signature - supplies the signature of the disk where the partition resides Offset - supplies the offset of the partition Return Value: The partition structure in the registry database. NULL if the partition is not there. --*/ { PDISK_DESCRIPTION diskDescription; USHORT i, j; PDISK_PARTITION diskPartition; LONGLONG tmp; diskDescription = &DiskRegistry->Disks[0]; for (i = 0; i < DiskRegistry->NumberOfDisks; i++) { if (diskDescription->Signature == Signature) { for (j = 0; j < diskDescription->NumberOfPartitions; j++) { diskPartition = &diskDescription->Partitions[j]; memcpy(&tmp, &diskPartition->StartingOffset.QuadPart, sizeof(LONGLONG)); if (tmp == Offset) { return diskPartition; } } } diskDescription = (PDISK_DESCRIPTION) &diskDescription-> Partitions[diskDescription->NumberOfPartitions]; } return NULL; } BOOL FtPresentOnDisk40( HANDLE Handle, PDISK_REGISTRY DiskRegistry, PBOOL FtPresent ) /*++ Routine Description: This routine looks for FT partitions on a disk Arguments: Handle - supplies a handle to the open disk DiskRegistry - supplies the ftdisk registry database FtPresent - is set to true if FT partitions are detected on the disk Return Value: TRUE if the function is successful FALSE if some fatal error occured --*/ { PDRIVE_LAYOUT_INFORMATION layout; DWORD layoutSize; NTSTATUS status; IO_STATUS_BLOCK statusBlock; ULONG i; PPARTITION_INFORMATION partInfo; PDISK_PARTITION diskPartition; *FtPresent = FALSE; // // Allocate memory for IOCTL_GET_DRIVE_LAYOUT // layoutSize = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry) + 32 * sizeof(PARTITION_INFORMATION); layout = (PDRIVE_LAYOUT_INFORMATION) LocalAlloc(0, layoutSize); if (!layout) { return FALSE; } // // Read the drive layout // while (1) { status = NtDeviceIoControlFile(Handle, 0, NULL, NULL, &statusBlock, IOCTL_DISK_GET_DRIVE_LAYOUT, NULL, 0, layout, layoutSize); if (status != STATUS_BUFFER_TOO_SMALL) { break; } layoutSize += 32 * sizeof(PARTITION_INFORMATION); if (layout) { LocalFree(layout); } layout = (PDRIVE_LAYOUT_INFORMATION) LocalAlloc(0, layoutSize); if (!layout) { return FALSE; } } if (!NT_SUCCESS(status)) { // inaccessible device. Act like it has no FT volumes LocalFree(layout); return TRUE; } // // Look for FT partitions // for (i = 0; i < layout->PartitionCount; i++) { // // We're looking after recognized partitions marked // with the 0x80 flag // partInfo = &(layout->PartitionEntry[i]); if (!IsFTPartition(partInfo->PartitionType)) { continue; } // // Check whether the partition is marked as a member // of an FT volume in the registry database // diskPartition = FindPartitionInRegistry40( DiskRegistry, layout->Signature, partInfo->StartingOffset.QuadPart); if (!diskPartition) { continue; } if (diskPartition->FtType != NotAnFtMember) { *FtPresent = TRUE; break; } } LocalFree(layout); return TRUE; } BOOL IsFtSet40( WCHAR DriveLetter, PDISK_REGISTRY DiskRegistry ) /*++ Routine Description: This routine cheks whether the given drive letter belongs to an FT set Arguments: DriveLetter - supplies a drive letter DiskRegistry - supplies the ftdisk registry database Return Value: TRUE if the function is the drive letter belongs to an FT set --*/ { HANDLE handle; NTSTATUS status; WCHAR deviceName[20]; PARTITION_INFORMATION partInfo; BOOL b; DWORD bytes; PDISK_DESCRIPTION diskDescription; PDISK_PARTITION diskPartition; USHORT i, j; // // Open the volume and get its "partition" type // If the NTFT flag is not set the volume is not an FT set // wsprintf(deviceName, L"\\DosDevices\\%c:", DriveLetter); status = OpenDevice(deviceName, &handle); if (!NT_SUCCESS(status)) { return FALSE; } b = DeviceIoControl(handle, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &partInfo, sizeof(partInfo), &bytes, NULL); CloseHandle(handle); if (!b) { return FALSE; } if (!IsFTPartition(partInfo.PartitionType)) { return FALSE; } // // Look for the drive letter in the FT registry. See if it belongs // to an FT set // diskDescription = &DiskRegistry->Disks[0]; for (i = 0; i < DiskRegistry->NumberOfDisks; i++) { for (j = 0; j < diskDescription->NumberOfPartitions; j++) { diskPartition = &diskDescription->Partitions[j]; if (diskPartition->AssignDriveLetter && tolower(diskPartition->DriveLetter) == tolower(DriveLetter) && diskPartition->FtType != NotAnFtMember) { return TRUE; } } diskDescription = (PDISK_DESCRIPTION) &diskDescription-> Partitions[diskDescription->NumberOfPartitions]; } return FALSE; } BOOL GetDeviceDriveLetter( PWSTR DeviceName, PWCHAR DriveLetter ) /*++ Routine Description: This routine returns the drive letter (if any) of a device given the device name (like \Device\HarddiskVolume1) Arguments: DeviceName - supplies the device name DriveLetter - returns the drive letter Return Value: TRUE if the device has a drive letter --*/ { DWORD cch; WCHAR dosDevices[4096]; WCHAR target[4096]; PWCHAR dosDevice; *DriveLetter = 0; if (!QueryDosDevice(NULL, dosDevices, 4096)) { return FALSE; } dosDevice = dosDevices; while (*dosDevice) { if (wcslen(dosDevice) == 2 && dosDevice[1] == L':' && QueryDosDevice(dosDevice, target, 4096)) { if (!wcscmp(target, DeviceName)) { *DriveLetter = (WCHAR) tolower(dosDevice[0]); return TRUE; } } while (*dosDevice++); } return FALSE; }