Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

819 lines
22 KiB

  1. /*++
  2. Copyright (c) 1993-1999 Microsoft Corporation
  3. Module Name:
  4. syspart.c
  5. Abstract:
  6. Routines to determine the system partition on x86 machines.
  7. Author:
  8. Ted Miller (tedm) 30-June-1994
  9. Revision History:
  10. Copied from winnt32 to risetup by ChuckL 12-May-1999
  11. --*/
  12. #include "pch.h"
  13. #pragma hdrstop
  14. #include <winioctl.h>
  15. DEFINE_MODULE("SysPart");
  16. #define MALLOC(_size) TraceAlloc(LPTR,(_size))
  17. #define FREE(_p) TraceFree(_p)
  18. UINT
  19. MyGetDriveType(
  20. IN TCHAR Drive
  21. );
  22. #define WINNT_DONT_MATCH_PARTITION 0
  23. #define WINNT_MATCH_PARTITION_NUMBER 1
  24. #define WINNT_MATCH_PARTITION_STARTING_OFFSET 2
  25. DWORD
  26. FindSystemPartitionSignature(
  27. IN LPCTSTR AdapterKeyName,
  28. OUT LPTSTR Signature
  29. );
  30. DWORD
  31. GetSystemVolumeGUID(
  32. IN LPTSTR Signature,
  33. OUT LPTSTR SysVolGuid
  34. );
  35. BOOL
  36. DoDiskSignaturesCompare(
  37. IN LPCTSTR Signature,
  38. IN LPCTSTR DriveName,
  39. IN OUT PVOID Compare,
  40. IN DWORD Action
  41. );
  42. BOOL
  43. x86DetermineSystemPartition(
  44. IN HWND ParentWindow,
  45. OUT PTCHAR SysPartDrive
  46. )
  47. /*++
  48. Routine Description:
  49. Determine the system partition on x86 machines.
  50. On Win95, we always return C:. For NT, read on.
  51. The system partition is the primary partition on the boot disk.
  52. Usually this is the active partition on disk 0 and usually it's C:.
  53. However the user could have remapped drive letters and generally
  54. determining the system partition with 100% accuracy is not possible.
  55. With there being differences in the IO system mapping and introduction of Volumes for NT 50
  56. this has now become complicated. Listed below are the algorithms
  57. NT 5.0 Beta 2 and above :
  58. 1. Get the signature from the registry. Located at
  59. HKLM\Hardware\Description\System\<MultifunctionAdapter or EisaAdapter>\<some Bus No.>\DiskController\0\DiskPeripheral\0\Identifier
  60. 2. Go Through all of the volumes in the system with FindFirstVolume/FindNextVolume/FindVolumeClose.
  61. 3. Take off the trailing backslash to the name returne to get \\?\Volume{guid}.
  62. 4. IOCTL_STORAGE_GET_DEVICE_NUMBER with \\?\Volume{guid} => Check for FILE_DEVICE_DISK. Remember the partition number. Goto 6
  63. 5. If IOCTL_STORAGE_GET_DEVICE_NUMBER fails then use IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS which returns a list of harddisks.
  64. For each harddisk remember the starting offset and goto 6.
  65. 6. Check Harddisk # by using \\.\PhysicalDrive# with IOCTL_DISK_GET_DRIVE_LAYOUT. If the signature matches then this is the disk we boot from.
  66. 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
  67. else if 3 we try to match the starting offset.Then you have a \\?\Volume{guid}\ name for the SYSTEM volume.
  68. 8. Call GetVolumeNameForVolumeMountPoint with A:\, B:\, C:\, ... and check the result of the form \\?\Volume{guid}\ against your match
  69. to see what the drive letter is.
  70. Important: Since the *Volume* APIs are post Beta2 we do dynamic loading of kernel32.dll based on the build number returned.
  71. Versions below NT 5.0 Beta 2
  72. 1. Get the signature from the registry. Located at
  73. HKLM\Hardware\Description\System\<MultifunctionAdapter or EisaAdapter>\<some Bus No.>\DiskController\0\DiskPeripheral\0\Identifier
  74. 2. Enumerate the \?? directory and look for all entries that begin with PhysicalDrive#.
  75. 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
  76. using IOCTL_DISK_GET_DRIVE_LAYOUT and the BootIndicator bit.
  77. 4. On finding the Boot partition create a name of the form \Device\Harddisk#\Partition#
  78. 5. Then go through c:,d:...,z: calling QueryDosDeviceName and look for a match. That would be your system partition drive letter
  79. Arguments:
  80. ParentWindow - supplies window handle for window to be the parent for
  81. any dialogs, etc.
  82. SysPartDrive - if successful, receives drive letter of system partition.
  83. Return Value:
  84. Boolean value indicating whether SysPartDrive has been filled in.
  85. If FALSE, the user will have been infomred about why.
  86. --*/
  87. {
  88. TCHAR DriveName[4];
  89. BOOL GotIt=FALSE;
  90. TCHAR Drive = TEXT('C');
  91. TCHAR SysPartSig[20], SysVolGuid[MAX_PATH];
  92. TCHAR DriveVolGuid[MAX_PATH];
  93. UNREFERENCED_PARAMETER(ParentWindow);
  94. //Get signature from registry - Step 1 listed above
  95. if( (FindSystemPartitionSignature(TEXT("Hardware\\Description\\System\\EisaAdapter"),SysPartSig) != ERROR_SUCCESS )
  96. && (FindSystemPartitionSignature(TEXT("Hardware\\Description\\System\\MultiFunctionAdapter"),SysPartSig) != ERROR_SUCCESS ) ){
  97. GotIt = FALSE;
  98. goto c0;
  99. }
  100. //Get the SystemVolumeGUID - steps 2 through 7 listed above ( Beta 2 and after )
  101. if( GetSystemVolumeGUID( SysPartSig, SysVolGuid ) != ERROR_SUCCESS ){
  102. GotIt = FALSE;
  103. goto c0;
  104. }
  105. DriveName[1] = TEXT(':');
  106. //
  107. // Enumerate all drive letters and compare their device names
  108. //
  109. for(Drive=TEXT('A'); Drive<=TEXT('Z'); Drive++) {
  110. if(MyGetDriveType(Drive) == DRIVE_FIXED) {
  111. DriveName[0] = Drive;
  112. DriveName[2] = '\\';
  113. DriveName[3] = 0;
  114. if((*GetVolumeNameForVolumeMountPoint)((LPWSTR)DriveName, (LPWSTR)DriveVolGuid, ARRAYSIZE(DriveVolGuid))){
  115. if(!lstrcmp(DriveVolGuid, SysVolGuid) ){
  116. GotIt = TRUE; // Found it
  117. break;
  118. }
  119. }
  120. }
  121. }
  122. // This helps for some builds ~1500 < buildnum < 1877 that are in a tough spot
  123. if(!GotIt) {
  124. //
  125. // Strange case, just assume C:
  126. //
  127. GotIt = TRUE;
  128. Drive = TEXT('C');
  129. }
  130. c0:
  131. if(GotIt) {
  132. *SysPartDrive = Drive;
  133. }
  134. return(GotIt);
  135. }
  136. DWORD
  137. GetSystemVolumeGUID(
  138. IN LPTSTR Signature,
  139. OUT LPTSTR SysVolGuid
  140. )
  141. /*++
  142. Routine Description:
  143. This routine enumerates all the volumes and if successful returns the \\?\Volume{guid} name for the system partition.
  144. Arguments:
  145. Signature - supplies a disk signature of the Boot disk so that it can be compared against. The details
  146. to getting this value are detailed in the comments for x86DetermineSystemPartition.
  147. SysVolGuid - If successful, will contain a name of form \\?\Volume{guid} for the System Partition (the one we use to boot)
  148. Return Value:
  149. Returns NO_ERROR if successful, otherwise it contains the error code.
  150. --*/
  151. {
  152. HANDLE hVolume, h;
  153. TCHAR VolumeName[MAX_PATH];
  154. TCHAR driveName[30];
  155. BOOL ret, DoExtent, MatchFound;
  156. STORAGE_DEVICE_NUMBER number;
  157. DWORD Err = NO_ERROR;
  158. DWORD cnt;
  159. PVOLUME_DISK_EXTENTS Extent = NULL;
  160. PDISK_EXTENT Start, i;
  161. DWORD ExtentSize, bytes;
  162. ULONG PartitionNumToCompare;
  163. LARGE_INTEGER StartingOffToCompare;
  164. //Enuberate all volumes
  165. hVolume = (*FindFirstVolume)( (LPWSTR)VolumeName, MAX_PATH );
  166. if( hVolume == INVALID_HANDLE_VALUE ){
  167. return GetLastError();
  168. }
  169. MatchFound = FALSE;
  170. do{
  171. //Remove trailing backslash
  172. DoExtent = FALSE;
  173. if( wcsrchr( VolumeName,TEXT('\\') ) ){
  174. *wcsrchr( VolumeName,TEXT('\\') ) = 0;
  175. }else{
  176. continue;
  177. }
  178. //Open the volume
  179. h = CreateFile(VolumeName, GENERIC_READ, FILE_SHARE_READ |
  180. FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
  181. FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE);
  182. if (h == INVALID_HANDLE_VALUE) {
  183. continue; // Move on to next volume
  184. }
  185. //Get the disk number
  186. ret = DeviceIoControl(h, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0,
  187. &number, sizeof(number), &bytes, NULL);
  188. if( !ret ){
  189. // Try using IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS if the above failed
  190. Extent = (PVOLUME_DISK_EXTENTS)MALLOC(1024);
  191. ExtentSize = 1024;
  192. if(!Extent) {
  193. CloseHandle( h );
  194. Err = ERROR_NOT_ENOUGH_MEMORY;
  195. goto cleanup;
  196. }
  197. retry:
  198. ret = DeviceIoControl( h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
  199. NULL,0,(PVOID)Extent,ExtentSize,&bytes,NULL);
  200. if(!ret) {
  201. if((Err=GetLastError()) == ERROR_INSUFFICIENT_BUFFER) {
  202. ExtentSize += 1024;
  203. FREE(Extent);
  204. Extent = (PVOLUME_DISK_EXTENTS)MALLOC(ExtentSize);
  205. if( !Extent ) {
  206. CloseHandle( h );
  207. Err = ERROR_NOT_ENOUGH_MEMORY;
  208. goto cleanup;
  209. }
  210. goto retry;
  211. } else {
  212. CloseHandle( h );
  213. continue;
  214. }
  215. }else{
  216. DoExtent = TRUE;
  217. }
  218. }
  219. // Done with the handle this time around
  220. CloseHandle( h );
  221. if( !DoExtent ){
  222. //
  223. // Check to see if this is a disk and not CDROM etc.
  224. //
  225. if( number.DeviceType == FILE_DEVICE_DISK ){
  226. // Remember the partition number
  227. PartitionNumToCompare = number.PartitionNumber;
  228. wsprintf( driveName, TEXT("\\\\.\\PhysicalDrive%lu"), number.DeviceNumber );
  229. if(DoDiskSignaturesCompare( Signature, driveName, (PVOID)&PartitionNumToCompare, WINNT_MATCH_PARTITION_NUMBER ) ){
  230. MatchFound = TRUE;
  231. Err = NO_ERROR;
  232. lstrcpy( SysVolGuid, VolumeName );
  233. SysVolGuid[lstrlen(VolumeName)]=TEXT('\\');
  234. SysVolGuid[lstrlen(VolumeName)+1]=0;
  235. break;
  236. }
  237. }
  238. // Move on ..
  239. continue;
  240. }else{
  241. // Go through all disks and try for match
  242. Start = Extent->Extents;
  243. cnt = 0;
  244. for( i = Start; cnt < Extent->NumberOfDiskExtents; i++ ){
  245. cnt++;
  246. // Remember the starting offset
  247. StartingOffToCompare = i->StartingOffset;
  248. wsprintf( driveName, TEXT("\\\\.\\PhysicalDrive%lu"), i->DiskNumber );
  249. if(DoDiskSignaturesCompare( Signature, driveName, (PVOID)&StartingOffToCompare, WINNT_MATCH_PARTITION_STARTING_OFFSET ) ){
  250. MatchFound = TRUE;
  251. Err = NO_ERROR;
  252. lstrcpy( SysVolGuid, VolumeName );
  253. SysVolGuid[lstrlen(VolumeName)]=TEXT('\\');
  254. SysVolGuid[lstrlen(VolumeName)+1]=0;
  255. break;
  256. }
  257. }
  258. }
  259. if( MatchFound )
  260. break;
  261. }while( (*FindNextVolume)( hVolume, (LPWSTR)VolumeName, MAX_PATH ));
  262. cleanup:
  263. if( hVolume != INVALID_HANDLE_VALUE )
  264. (*FindVolumeClose)( hVolume );
  265. if( Extent != NULL ) {
  266. FREE(Extent);
  267. }
  268. return Err;
  269. }
  270. BOOL
  271. DoDiskSignaturesCompare(
  272. IN LPCTSTR Signature,
  273. IN LPCTSTR DriveName,
  274. IN OUT PVOID Compare,
  275. IN DWORD Action
  276. )
  277. /*++
  278. Routine Description:
  279. This routine compares the given disk signature with the one for the specified physical disk.
  280. Arguments:
  281. Signature - supplies a disk signature of the Boot disk so that it can be compared against. The details
  282. to getting this value are detailed in the comments for x86DetermineSystemPartition.
  283. DriveName - Physical Drive name of the form \\.\PhysicalDrive#
  284. Compare - A pointer to a storage variable. The type depends on the value of Action
  285. Action - Should be one of the following
  286. WINNT_DONT_MATCH_PARTITION - Once disk signatures match it returns the boot partition number in Compare. Compare should be a PULONG.
  287. WINNT_MATCH_PARTITION_NUMBER - Once disk signatures match it tries to match the boot partition number with the number passed in
  288. through Compare. Compare should be PULONG.
  289. WINNT_MATCH_PARTITION_STARTING_OFFSET - Once disk signatures match it tries to match the boot partition starting offset with the
  290. starting offset number passed in through Compare. Compare should be PLARGE_INTEGER.
  291. Return Value:
  292. Returns TRUE if successful in getting a match.
  293. --*/
  294. {
  295. BOOL b,Found = FALSE;
  296. PLARGE_INTEGER Starting_Off = NULL;
  297. PPARTITION_INFORMATION Start = NULL, i = NULL;
  298. HANDLE hDisk;
  299. PDRIVE_LAYOUT_INFORMATION DriveLayout = NULL;
  300. DWORD DriveLayoutSize;
  301. DWORD cnt;
  302. DWORD DataSize;
  303. PULONG Disk_Num = NULL;
  304. ULONG Sig;
  305. if(!Compare )
  306. return FALSE;
  307. if( (Action==WINNT_MATCH_PARTITION_STARTING_OFFSET) && Compare ) {
  308. Starting_Off = (PLARGE_INTEGER) Compare;
  309. } else {
  310. Disk_Num = (PULONG) Compare;
  311. }
  312. // On any failure return FALSE
  313. //
  314. // Get drive layout info for this physical disk.
  315. // If we can't do this something is very wrong.
  316. //
  317. hDisk = CreateFile(
  318. DriveName,
  319. FILE_READ_ATTRIBUTES | FILE_READ_DATA,
  320. FILE_SHARE_READ | FILE_SHARE_WRITE,
  321. NULL,
  322. OPEN_EXISTING,
  323. 0,
  324. NULL
  325. );
  326. if(hDisk == INVALID_HANDLE_VALUE) {
  327. return FALSE;
  328. }
  329. //
  330. // Get partition information.
  331. //
  332. DriveLayout = (PDRIVE_LAYOUT_INFORMATION)MALLOC(1024);
  333. DriveLayoutSize = 1024;
  334. if(!DriveLayout) {
  335. goto cleanexit;
  336. }
  337. retry:
  338. b = DeviceIoControl(
  339. hDisk,
  340. IOCTL_DISK_GET_DRIVE_LAYOUT,
  341. NULL,
  342. 0,
  343. (PVOID)DriveLayout,
  344. DriveLayoutSize,
  345. &DataSize,
  346. NULL
  347. );
  348. if(!b) {
  349. if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
  350. DriveLayoutSize += 1024;
  351. FREE(DriveLayout);
  352. DriveLayout = (PDRIVE_LAYOUT_INFORMATION)MALLOC(DriveLayoutSize);
  353. if( !DriveLayout ) {
  354. goto cleanexit;
  355. }
  356. goto retry;
  357. } else {
  358. goto cleanexit;
  359. }
  360. }else{
  361. // Now walk the Drive_Layout to find the boot partition
  362. Start = DriveLayout->PartitionEntry;
  363. cnt = 0;
  364. /*
  365. _ultot( DriveLayout->Signature, temp, 16 );
  366. if( lstrcmpi( temp, Signature ) )
  367. goto cleanexit;
  368. */
  369. Sig = wcstoul( Signature, NULL, 16 );
  370. if( Sig != DriveLayout->Signature )
  371. goto cleanexit;
  372. for( i = Start; cnt < DriveLayout->PartitionCount; i++ ){
  373. cnt++;
  374. if( i->BootIndicator == TRUE ){
  375. if( (Disk_Num) && (Action == WINNT_DONT_MATCH_PARTITION) ){
  376. *Disk_Num = i->PartitionNumber;
  377. Found = TRUE;
  378. goto cleanexit;
  379. }
  380. if( (Disk_Num) && (Action == WINNT_MATCH_PARTITION_NUMBER) ){
  381. if( *Disk_Num == i->PartitionNumber ){
  382. Found = TRUE;
  383. goto cleanexit;
  384. }
  385. }else{
  386. if( (Starting_Off) && (Starting_Off->QuadPart == i->StartingOffset.QuadPart) ){
  387. Found = TRUE;
  388. goto cleanexit;
  389. }
  390. }
  391. break;
  392. }
  393. }
  394. }
  395. cleanexit:
  396. if( hDisk != INVALID_HANDLE_VALUE )
  397. CloseHandle( hDisk );
  398. if( DriveLayout != NULL ) {
  399. FREE(DriveLayout);
  400. }
  401. return Found;
  402. }
  403. DWORD
  404. FindSystemPartitionSignature(
  405. IN LPCTSTR AdapterKeyName,
  406. OUT LPTSTR Signature
  407. )
  408. /*++
  409. Routine Description:
  410. 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.
  411. It is at location <AdapterKeyName>\<some Bus No.>\DiskController\0\DiskPeripheral\0\Identifier
  412. Arguments:
  413. Signature - If successful will contain the signature of the disk we boot off from in Hex.
  414. Return Value:
  415. Returns ERROR_SUCCESS if successful, otherwise it contains the error code.
  416. --*/
  417. {
  418. DWORD Err, dwSize;
  419. HKEY hkey, BusKey, Controller, SystemDiskKey;
  420. int busnumber;
  421. TCHAR BusString[20], Identifier[100];
  422. Err = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  423. AdapterKeyName,
  424. 0,
  425. KEY_READ,
  426. &hkey );
  427. if( Err != ERROR_SUCCESS )
  428. return Err;
  429. // Start enumerating the buses
  430. for( busnumber=0; ;busnumber++){
  431. wsprintf( BusString, TEXT("%d"), busnumber );
  432. Err = RegOpenKeyEx( hkey,
  433. BusString,
  434. 0,
  435. KEY_READ,
  436. &BusKey );
  437. if( Err != ERROR_SUCCESS ){
  438. RegCloseKey( hkey );
  439. return Err;
  440. }
  441. Err = RegOpenKeyEx( BusKey,
  442. TEXT("DiskController"),
  443. 0,
  444. KEY_READ,
  445. &Controller );
  446. RegCloseKey(BusKey); // Not needed anymore
  447. if( Err != ERROR_SUCCESS ) // Move on to next bus
  448. continue;
  449. RegCloseKey( hkey ); // Not needed anymore
  450. Err = RegOpenKeyEx( Controller,
  451. TEXT("0\\DiskPeripheral\\0"),
  452. 0,
  453. KEY_READ,
  454. &SystemDiskKey );
  455. if( Err != ERROR_SUCCESS ){
  456. RegCloseKey( Controller );
  457. return Err;
  458. }
  459. RegCloseKey( Controller ); // Not needed anymore
  460. dwSize = sizeof(Identifier);
  461. Err = RegQueryValueEx( SystemDiskKey,
  462. TEXT("Identifier"),
  463. NULL,
  464. NULL,
  465. (PBYTE) Identifier,
  466. &dwSize);
  467. if( Err != ERROR_SUCCESS ){
  468. RegCloseKey( SystemDiskKey );
  469. return Err;
  470. }
  471. if( Identifier && (lstrlen(Identifier) > 9 ) ){
  472. PWCHAR p;
  473. lstrcpy( Signature,Identifier+9);
  474. p = wcsrchr( Signature,TEXT('-') );
  475. if( p ) {
  476. *p = 0;
  477. }
  478. RegCloseKey( SystemDiskKey );
  479. return ERROR_SUCCESS;
  480. }
  481. else{
  482. RegCloseKey( SystemDiskKey );
  483. return Err;
  484. }
  485. }
  486. }
  487. UINT
  488. MyGetDriveType(
  489. IN TCHAR Drive
  490. )
  491. /*++
  492. Routine Description:
  493. Same as GetDriveType() Win32 API except on NT returns
  494. DRIVE_FIXED for removeable hard drives.
  495. Arguments:
  496. Drive - supplies drive letter whose type is desired.
  497. Return Value:
  498. Same as GetDriveType().
  499. --*/
  500. {
  501. TCHAR DriveNameNt[] = TEXT("\\\\.\\?:");
  502. TCHAR DriveName[] = TEXT("?:\\");
  503. HANDLE hDisk;
  504. BOOL b;
  505. UINT rc;
  506. DWORD DataSize;
  507. DISK_GEOMETRY MediaInfo;
  508. //
  509. // First, get the win32 drive type. If it tells us DRIVE_REMOVABLE,
  510. // then we need to see whether it's a floppy or hard disk. Otherwise
  511. // just believe the api.
  512. //
  513. //
  514. DriveName[0] = Drive;
  515. rc = GetDriveType(DriveName);
  516. if((rc != DRIVE_REMOVABLE) || (Drive < L'C')) {
  517. return(rc);
  518. }
  519. //
  520. // DRIVE_REMOVABLE on NT.
  521. //
  522. //
  523. // Disallow use of removable media (e.g. Jazz, Zip, ...).
  524. //
  525. DriveNameNt[4] = Drive;
  526. hDisk = CreateFile(
  527. DriveNameNt,
  528. FILE_READ_ATTRIBUTES | SYNCHRONIZE,
  529. FILE_SHARE_READ | FILE_SHARE_WRITE,
  530. NULL,
  531. OPEN_EXISTING,
  532. 0,
  533. NULL
  534. );
  535. if(hDisk != INVALID_HANDLE_VALUE) {
  536. b = DeviceIoControl(
  537. hDisk,
  538. IOCTL_DISK_GET_DRIVE_GEOMETRY,
  539. NULL,
  540. 0,
  541. &MediaInfo,
  542. sizeof(MediaInfo),
  543. &DataSize,
  544. NULL
  545. );
  546. //
  547. // It's really a hard disk if the media type is removable.
  548. //
  549. if(b && (MediaInfo.MediaType == RemovableMedia)) {
  550. rc = DRIVE_FIXED;
  551. }
  552. CloseHandle(hDisk);
  553. }
  554. return(rc);
  555. }