/*++ Copyright (c) 1993-1999 Microsoft Corporation Module Name: syspart.c Abstract: Routines to determine the system partition on x86 machines. Author: Ted Miller (tedm) 30-June-1994 Revision History: Copied from winnt32 to risetup by ChuckL 12-May-1999 --*/ #include "pch.h" #pragma hdrstop #include DEFINE_MODULE("SysPart"); #define MALLOC(_size) TraceAlloc(LPTR,(_size)) #define FREE(_p) TraceFree(_p) UINT MyGetDriveType( IN TCHAR Drive ); BOOL IsNEC98( VOID ); #define WINNT_DONT_MATCH_PARTITION 0 #define WINNT_MATCH_PARTITION_NUMBER 1 #define WINNT_MATCH_PARTITION_STARTING_OFFSET 2 DWORD FindSystemPartitionSignature( IN LPCTSTR AdapterKeyName, OUT LPTSTR Signature ); DWORD GetSystemVolumeGUID( IN LPTSTR Signature, OUT LPTSTR SysVolGuid ); BOOL DoDiskSignaturesCompare( IN LPCTSTR Signature, IN LPCTSTR DriveName, IN OUT PVOID Compare, IN DWORD Action ); BOOL x86DetermineSystemPartition( IN HWND ParentWindow, OUT PTCHAR SysPartDrive ) /*++ Routine Description: Determine the system partition on x86 machines. On Win95, we always return C:. For NT, read on. The system partition is the primary partition on the boot disk. Usually this is the active partition on disk 0 and usually it's C:. However the user could have remapped drive letters and generally determining the system partition with 100% accuracy is not possible. With there being differences in the IO system mapping and introduction of Volumes for NT 50 this has now become complicated. Listed below are the algorithms NT 5.0 Beta 2 and above : 1. Get the signature from the registry. Located at HKLM\Hardware\Description\System\\\DiskController\0\DiskPeripheral\0\Identifier 2. Go Through all of the volumes in the system with FindFirstVolume/FindNextVolume/FindVolumeClose. 3. Take off the trailing backslash to the name returne to get \\?\Volume{guid}. 4. IOCTL_STORAGE_GET_DEVICE_NUMBER with \\?\Volume{guid} => Check for FILE_DEVICE_DISK. Remember the partition number. Goto 6 5. If IOCTL_STORAGE_GET_DEVICE_NUMBER fails then use IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS which returns a list of harddisks. For each harddisk remember the starting offset and goto 6. 6. Check Harddisk # by using \\.\PhysicalDrive# with IOCTL_DISK_GET_DRIVE_LAYOUT. If the signature matches then this is the disk we boot from. 7. To find the partition that we boot from we look for boot indicator. If we used 2 we try to match the partition number stored in 6 else if 3 we try to match the starting offset.Then you have a \\?\Volume{guid}\ name for the SYSTEM volume. 8. Call GetVolumeNameForVolumeMountPoint with A:\, B:\, C:\, ... and check the result of the form \\?\Volume{guid}\ against your match to see what the drive letter is. Important: Since the *Volume* APIs are post Beta2 we do dynamic loading of kernel32.dll based on the build number returned. Versions below NT 5.0 Beta 2 1. Get the signature from the registry. Located at HKLM\Hardware\Description\System\\\DiskController\0\DiskPeripheral\0\Identifier 2. Enumerate the \?? directory and look for all entries that begin with PhysicalDrive#. 3. For each of the Disks look for a match with the signature above and if they match then find out the partition number used to boot using IOCTL_DISK_GET_DRIVE_LAYOUT and the BootIndicator bit. 4. On finding the Boot partition create a name of the form \Device\Harddisk#\Partition# 5. Then go through c:,d:...,z: calling QueryDosDeviceName and look for a match. That would be your system partition drive letter Arguments: ParentWindow - supplies window handle for window to be the parent for any dialogs, etc. SysPartDrive - if successful, receives drive letter of system partition. Return Value: Boolean value indicating whether SysPartDrive has been filled in. If FALSE, the user will have been infomred about why. --*/ { TCHAR DriveName[4]; BOOL GotIt=FALSE; TCHAR Buffer[512]; TCHAR Drive; BOOL b; TCHAR SysPartSig[20], PartitionNum[MAX_PATH], SysVolGuid[MAX_PATH]; TCHAR DriveVolGuid[MAX_PATH]; if(IsNEC98()) { if (!GetWindowsDirectory(Buffer,sizeof(Buffer)/sizeof(TCHAR))) { return(FALSE); } *SysPartDrive = Buffer[0]; return(TRUE); } //Get signature from registry - Step 1 listed above if( (FindSystemPartitionSignature(TEXT("Hardware\\Description\\System\\EisaAdapter"),SysPartSig) != ERROR_SUCCESS ) && (FindSystemPartitionSignature(TEXT("Hardware\\Description\\System\\MultiFunctionAdapter"),SysPartSig) != ERROR_SUCCESS ) ){ GotIt = FALSE; goto c0; } //Get the SystemVolumeGUID - steps 2 through 7 listed above ( Beta 2 and after ) if( GetSystemVolumeGUID( SysPartSig, SysVolGuid ) != ERROR_SUCCESS ){ GotIt = FALSE; goto c0; } DriveName[1] = TEXT(':'); // // Enumerate all drive letters and compare their device names // for(Drive=TEXT('A'); Drive<=TEXT('Z'); Drive++) { if(MyGetDriveType(Drive) == DRIVE_FIXED) { DriveName[0] = Drive; DriveName[2] = '\\'; DriveName[3] = 0; if((*GetVolumeNameForVolumeMountPoint)((LPWSTR)DriveName, (LPWSTR)DriveVolGuid, MAX_PATH*sizeof(TCHAR))){ if(!lstrcmp(DriveVolGuid, SysVolGuid) ){ GotIt = TRUE; // Found it break; } } } } // This helps for some builds ~1500 < buildnum < 1877 that are in a tough spot if(!GotIt) { // // Strange case, just assume C: // GotIt = TRUE; Drive = TEXT('C'); } c0: if(GotIt) { *SysPartDrive = Drive; } return(GotIt); } DWORD GetSystemVolumeGUID( IN LPTSTR Signature, OUT LPTSTR SysVolGuid ) /*++ Routine Description: This routine enumerates all the volumes and if successful returns the \\?\Volume{guid} name for the system partition. Arguments: Signature - supplies a disk signature of the Boot disk so that it can be compared against. The details to getting this value are detailed in the comments for x86DetermineSystemPartition. SysVolGuid - If successful, will contain a name of form \\?\Volume{guid} for the System Partition (the one we use to boot) Return Value: Returns NO_ERROR if successful, otherwise it contains the error code. --*/ { HANDLE hVolume, h; TCHAR VolumeName[MAX_PATH]; TCHAR driveName[30]; BOOL b,ret, DoExtent, MatchFound; STORAGE_DEVICE_NUMBER number; DWORD Err = NO_ERROR; DWORD cnt; PVOLUME_DISK_EXTENTS Extent = NULL; PDISK_EXTENT Start, i; DWORD ExtentSize, bytes; PVOID p; ULONG PartitionNumToCompare; LARGE_INTEGER StartingOffToCompare; //Enuberate all volumes hVolume = (*FindFirstVolume)( (LPWSTR)VolumeName, MAX_PATH ); if( hVolume == INVALID_HANDLE_VALUE ){ return GetLastError(); } MatchFound = FALSE; do{ //Remove trailing backslash DoExtent = FALSE; if( wcsrchr( VolumeName,TEXT('\\') ) ){ *wcsrchr( VolumeName,TEXT('\\') ) = 0; }else{ continue; } //Open the volume h = CreateFile(VolumeName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); if (h == INVALID_HANDLE_VALUE) { continue; // Move on to next volume } //Get the disk number ret = DeviceIoControl(h, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &number, sizeof(number), &bytes, NULL); if( !ret ){ // Try using IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS if the above failed Extent = (PVOLUME_DISK_EXTENTS)MALLOC(1024); ExtentSize = 1024; if(!Extent) { CloseHandle( h ); Err = ERROR_NOT_ENOUGH_MEMORY; goto cleanup; } retry: ret = DeviceIoControl( h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL,0,(PVOID)Extent,ExtentSize,&bytes,NULL); if(!ret) { if((Err=GetLastError()) == ERROR_INSUFFICIENT_BUFFER) { ExtentSize += 1024; FREE(Extent); if(Extent = (PVOLUME_DISK_EXTENTS)MALLOC(ExtentSize)) { ; } else { CloseHandle( h ); Err = ERROR_NOT_ENOUGH_MEMORY; goto cleanup; } goto retry; } else { CloseHandle( h ); continue; } }else{ DoExtent = TRUE; } } // Done with the handle this time around CloseHandle( h ); if( !DoExtent ){ // // Check to see if this is a disk and not CDROM etc. // if( number.DeviceType == FILE_DEVICE_DISK ){ // Remember the partition number PartitionNumToCompare = number.PartitionNumber; wsprintf( driveName, TEXT("\\\\.\\PhysicalDrive%lu"), number.DeviceNumber ); if(DoDiskSignaturesCompare( Signature, driveName, (PVOID)&PartitionNumToCompare, WINNT_MATCH_PARTITION_NUMBER ) ){ MatchFound = TRUE; Err = NO_ERROR; lstrcpy( SysVolGuid, VolumeName ); SysVolGuid[lstrlen(VolumeName)]=TEXT('\\'); SysVolGuid[lstrlen(VolumeName)+1]=0; break; } } // Move on .. continue; }else{ // Go through all disks and try for match Start = Extent->Extents; cnt = 0; for( i = Start; cnt < Extent->NumberOfDiskExtents; i++ ){ cnt++; // Remember the starting offset StartingOffToCompare = i->StartingOffset; wsprintf( driveName, TEXT("\\\\.\\PhysicalDrive%lu"), i->DiskNumber ); if(DoDiskSignaturesCompare( Signature, driveName, (PVOID)&StartingOffToCompare, WINNT_MATCH_PARTITION_STARTING_OFFSET ) ){ MatchFound = TRUE; Err = NO_ERROR; lstrcpy( SysVolGuid, VolumeName ); SysVolGuid[lstrlen(VolumeName)]=TEXT('\\'); SysVolGuid[lstrlen(VolumeName)+1]=0; break; } } } if( MatchFound ) break; }while( (*FindNextVolume)( hVolume, (LPWSTR)VolumeName, MAX_PATH )); cleanup: if( hVolume != INVALID_HANDLE_VALUE ) (*FindVolumeClose)( hVolume ); if( Extent != NULL ) { FREE(Extent); } return Err; } BOOL DoDiskSignaturesCompare( IN LPCTSTR Signature, IN LPCTSTR DriveName, IN OUT PVOID Compare, IN DWORD Action ) /*++ Routine Description: This routine compares the given disk signature with the one for the specified physical disk. Arguments: Signature - supplies a disk signature of the Boot disk so that it can be compared against. The details to getting this value are detailed in the comments for x86DetermineSystemPartition. DriveName - Physical Drive name of the form \\.\PhysicalDrive# Compare - A pointer to a storage variable. The type depends on the value of Action Action - Should be one of the following WINNT_DONT_MATCH_PARTITION - Once disk signatures match it returns the boot partition number in Compare. Compare should be a PULONG. WINNT_MATCH_PARTITION_NUMBER - Once disk signatures match it tries to match the boot partition number with the number passed in through Compare. Compare should be PULONG. WINNT_MATCH_PARTITION_STARTING_OFFSET - Once disk signatures match it tries to match the boot partition starting offset with the starting offset number passed in through Compare. Compare should be PLARGE_INTEGER. Return Value: Returns TRUE if successful in getting a match. --*/ { TCHAR temp[30]; BOOL b,Found = FALSE; PLARGE_INTEGER Starting_Off; PPARTITION_INFORMATION Start, i; HANDLE hDisk; PDRIVE_LAYOUT_INFORMATION DriveLayout = NULL; DWORD DriveLayoutSize; DWORD cnt; DWORD DataSize; PULONG Disk_Num; ULONG Sig; if(!Compare ) return FALSE; if( (Action==WINNT_MATCH_PARTITION_STARTING_OFFSET) && Compare ) Starting_Off = (PLARGE_INTEGER) Compare; else Disk_Num = (PULONG) Compare; // On any failure return FALSE // // Get drive layout info for this physical disk. // If we can't do this something is very wrong. // hDisk = CreateFile( DriveName, FILE_READ_ATTRIBUTES | FILE_READ_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if(hDisk == INVALID_HANDLE_VALUE) { return FALSE; } // // Get partition information. // DriveLayout = (PDRIVE_LAYOUT_INFORMATION)MALLOC(1024); DriveLayoutSize = 1024; if(!DriveLayout) { goto cleanexit; } retry: b = DeviceIoControl( hDisk, IOCTL_DISK_GET_DRIVE_LAYOUT, NULL, 0, (PVOID)DriveLayout, DriveLayoutSize, &DataSize, NULL ); if(!b) { if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) { DriveLayoutSize += 1024; FREE(DriveLayout); if(DriveLayout = (PDRIVE_LAYOUT_INFORMATION)MALLOC(DriveLayoutSize)) { ; } else { goto cleanexit; } goto retry; } else { goto cleanexit; } }else{ // Now walk the Drive_Layout to find the boot partition Start = DriveLayout->PartitionEntry; cnt = 0; /* _ultot( DriveLayout->Signature, temp, 16 ); if( lstrcmpi( temp, Signature ) ) goto cleanexit; */ Sig = wcstoul( Signature, NULL, 16 ); if( Sig != DriveLayout->Signature ) goto cleanexit; for( i = Start; cnt < DriveLayout->PartitionCount; i++ ){ cnt++; if( i->BootIndicator == TRUE ){ if( Action == WINNT_DONT_MATCH_PARTITION ){ *Disk_Num = i->PartitionNumber; Found = TRUE; goto cleanexit; } if( Action == WINNT_MATCH_PARTITION_NUMBER ){ if( *Disk_Num == i->PartitionNumber ){ Found = TRUE; goto cleanexit; } }else{ if( Starting_Off->QuadPart == i->StartingOffset.QuadPart ){ Found = TRUE; goto cleanexit; } } break; } } } cleanexit: if( hDisk != INVALID_HANDLE_VALUE ) CloseHandle( hDisk ); if( DriveLayout != NULL ) { FREE(DriveLayout); } return Found; } DWORD FindSystemPartitionSignature( IN LPCTSTR AdapterKeyName, OUT LPTSTR Signature ) /*++ Routine Description: This routine fetches the disk signature for the first disk that the BIOS sees. This has to be the disk that we boot from on x86s. It is at location \\DiskController\0\DiskPeripheral\0\Identifier Arguments: Signature - If successful will contain the signature of the disk we boot off from in Hex. Return Value: Returns ERROR_SUCCESS if successful, otherwise it contains the error code. --*/ { DWORD Err, dwSize; HKEY hkey, BusKey, Controller, SystemDiskKey; int busnumber; TCHAR BusString[20], Identifier[100]; Err = RegOpenKeyEx( HKEY_LOCAL_MACHINE, AdapterKeyName, 0, KEY_READ, &hkey ); if( Err != ERROR_SUCCESS ) return Err; // Start enumerating the buses for( busnumber=0; ;busnumber++){ wsprintf( BusString, TEXT("%d"), busnumber ); Err = RegOpenKeyEx( hkey, BusString, 0, KEY_READ, &BusKey ); if( Err != ERROR_SUCCESS ){ RegCloseKey( hkey ); return Err; } Err = RegOpenKeyEx( BusKey, TEXT("DiskController"), 0, KEY_READ, &Controller ); RegCloseKey(BusKey); // Not needed anymore if( Err != ERROR_SUCCESS ) // Move on to next bus continue; RegCloseKey( hkey ); // Not needed anymore Err = RegOpenKeyEx( Controller, TEXT("0\\DiskPeripheral\\0"), 0, KEY_READ, &SystemDiskKey ); if( Err != ERROR_SUCCESS ){ RegCloseKey( Controller ); return Err; } RegCloseKey( Controller ); // Not needed anymore dwSize = sizeof(Identifier); Err = RegQueryValueEx( SystemDiskKey, TEXT("Identifier"), NULL, NULL, (PBYTE) Identifier, &dwSize); if( Err != ERROR_SUCCESS ){ RegCloseKey( SystemDiskKey ); return Err; } if( Identifier && (lstrlen(Identifier) > 9 ) ){ PWCHAR p; lstrcpy( Signature,Identifier+9); p = wcsrchr( Signature,TEXT('-') ); if( p ) { *p = 0; } RegCloseKey( SystemDiskKey ); return ERROR_SUCCESS; } else{ RegCloseKey( SystemDiskKey ); return Err; } } // Should never get here RegCloseKey( hkey ); return ERROR_PATH_NOT_FOUND; } UINT MyGetDriveType( IN TCHAR Drive ) /*++ Routine Description: Same as GetDriveType() Win32 API except on NT returns DRIVE_FIXED for removeable hard drives. Arguments: Drive - supplies drive letter whose type is desired. Return Value: Same as GetDriveType(). --*/ { TCHAR DriveNameNt[] = TEXT("\\\\.\\?:"); TCHAR DriveName[] = TEXT("?:\\"); HANDLE hDisk; BOOL b; UINT rc; DWORD DataSize; DISK_GEOMETRY MediaInfo; // // First, get the win32 drive type. If it tells us DRIVE_REMOVABLE, // then we need to see whether it's a floppy or hard disk. Otherwise // just believe the api. // // DriveName[0] = Drive; rc = GetDriveType(DriveName); if((rc != DRIVE_REMOVABLE) || (Drive < L'C')) { return(rc); } // // DRIVE_REMOVABLE on NT. // // // Disallow use of removable media (e.g. Jazz, Zip, ...). // DriveNameNt[4] = Drive; hDisk = CreateFile( DriveNameNt, FILE_READ_ATTRIBUTES | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if(hDisk != INVALID_HANDLE_VALUE) { b = DeviceIoControl( hDisk, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &MediaInfo, sizeof(MediaInfo), &DataSize, NULL ); // // It's really a hard disk if the media type is removable. // if(b && (MediaInfo.MediaType == RemovableMedia)) { rc = DRIVE_FIXED; } CloseHandle(hDisk); } return(rc); } BOOL IsNEC98( VOID ) { static BOOL Checked = FALSE; static BOOL Is98; if(!Checked) { Is98 = ((GetKeyboardType(0) == 7) && ((GetKeyboardType(1) & 0xff00) == 0x0d00)); Checked = TRUE; } return(Is98); }