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.

1866 lines
47 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 2001
  3. Module Name:
  4. disk.c
  5. Abstract:
  6. This utility adds and removes lower filter drivers
  7. for a given disk
  8. Author:
  9. Sidhartha
  10. Environment:
  11. User mode only
  12. Notes:
  13. - the filter is not checked for validity before it is added to the driver
  14. stack; if an invalid filter is added, the device may no longer be
  15. accessible.
  16. - all code works irrespective of character set (ANSI, Unicode, ...)
  17. Revision History:
  18. --*/
  19. #include <stdafx.h>
  20. #include <setupapi.h>
  21. #include <initguid.h>
  22. #include <ntddstor.h>
  23. #include <ntddvol.h>
  24. #include "verifier.h"
  25. #include "disk.h"
  26. #include "VrfUtil.h"
  27. #define GETVOLUMEPATH_MAX_LEN_RETRY 1000
  28. #ifdef __cplusplus
  29. extern "C"
  30. {
  31. #endif //#ifdef __cplusplus
  32. typedef
  33. BOOLEAN
  34. (*DISK_ENUMERATE_CALLBACK)(
  35. PVOID,
  36. HDEVINFO,
  37. SP_DEVINFO_DATA*
  38. );
  39. BOOLEAN
  40. AddFilterDriver(
  41. IN HDEVINFO DeviceInfoSet,
  42. IN PSP_DEVINFO_DATA DeviceInfoData,
  43. IN LPTSTR Filter,
  44. IN BOOLEAN UpperFilter
  45. );
  46. BOOLEAN
  47. RemoveFilterDriver(
  48. IN HDEVINFO DeviceInfoSet,
  49. IN PSP_DEVINFO_DATA DeviceInfoData,
  50. IN LPTSTR Filter,
  51. IN BOOLEAN UpperFilter
  52. );
  53. BOOLEAN
  54. PrintFilters(
  55. IN HDEVINFO DeviceInfoSet,
  56. IN PSP_DEVINFO_DATA DeviceInfoData,
  57. IN BOOLEAN UpperFilters,
  58. IN LPTSTR FilterDriver,
  59. IN OUT LPTSTR *VerifierEnabled
  60. );
  61. LPTSTR
  62. GetFilters(
  63. IN HDEVINFO DeviceInfoSet,
  64. IN PSP_DEVINFO_DATA DeviceInfoData,
  65. IN BOOLEAN UpperFilters
  66. );
  67. BOOLEAN
  68. PrintDeviceName(
  69. IN HDEVINFO DeviceInfoSet,
  70. IN PSP_DEVINFO_DATA DeviceInfoData,
  71. IN OUT LPTSTR *DiskDevicesForDisplay,
  72. IN OUT LPTSTR *DiskDevicesPDOName
  73. );
  74. BOOLEAN
  75. DeviceNameMatches(
  76. IN HDEVINFO DeviceInfoSet,
  77. IN PSP_DEVINFO_DATA DeviceInfoData,
  78. IN LPTSTR DeviceName
  79. );
  80. PBYTE
  81. GetDeviceRegistryProperty(
  82. IN HDEVINFO DeviceInfoSet,
  83. IN PSP_DEVINFO_DATA DeviceInfoData,
  84. IN DWORD Property,
  85. OUT PDWORD PropertyRegDataType
  86. );
  87. BOOLEAN
  88. PrependSzToMultiSz(
  89. IN LPTSTR SzToPrepend,
  90. IN OUT LPTSTR *MultiSz
  91. );
  92. SIZE_T
  93. GetMultiSzLength(
  94. IN LPTSTR MultiSz
  95. );
  96. SIZE_T
  97. MultiSzSearchAndDeleteCaseInsensitive(
  98. IN LPTSTR FindThis,
  99. IN LPTSTR FindWithin,
  100. OUT SIZE_T *NewStringLength
  101. );
  102. BOOLEAN
  103. DiskVerify(
  104. DISK_ENUMERATE_CALLBACK CallBack,
  105. PVOID Context,
  106. LPTSTR deviceName
  107. );
  108. BOOLEAN
  109. DiskEnumerateCallback (
  110. PVOID Context,
  111. HDEVINFO DevInfo,
  112. SP_DEVINFO_DATA *DevInfoData
  113. );
  114. BOOLEAN
  115. AddCallback (
  116. PVOID Context,
  117. HDEVINFO DevInfo,
  118. SP_DEVINFO_DATA *DevInfoData
  119. );
  120. BOOLEAN
  121. DelCallback (
  122. PVOID Context,
  123. HDEVINFO DevInfo,
  124. SP_DEVINFO_DATA *DevInfoData
  125. );
  126. LPTSTR
  127. GetDriveLetters (
  128. IN HDEVINFO DeviceInfoSet,
  129. IN PSP_DEVINFO_DATA DeviceInfoData
  130. );
  131. LPTSTR
  132. GetDriveLettersFromVolume (
  133. IN ULONG DeviceNumber
  134. );
  135. LPTSTR
  136. PrintDriveLetters(
  137. IN PTSTR VolumeName
  138. );
  139. BOOLEAN
  140. StrConcatWithSpace(
  141. IN LPTSTR SzToAppend,
  142. IN OUT LPTSTR *drives
  143. );
  144. typedef struct _DISPLAY_STRUCT{
  145. LPTSTR Filter;
  146. LPTSTR* DiskDevicesForDisplay;
  147. LPTSTR* DiskDevicesPDOName;
  148. LPTSTR* VerifierEnabled;
  149. }DISPLAY_STRUCT,
  150. *PDISPLAY_STRUCT;
  151. typedef struct _ADD_REMOVE_STRUCT{
  152. LPTSTR Filter;
  153. }ADD_REMOVE_STRUCT,
  154. *PADD_REMOVE_STRUCT;
  155. BOOLEAN
  156. DiskEnumerate(
  157. IN LPTSTR Filter,
  158. OUT LPTSTR* DiskDevicesForDisplayP,
  159. OUT LPTSTR* DiskDevicesPDONameP,
  160. OUT LPTSTR* VerifierEnabledP
  161. )
  162. /*++
  163. Routine Description:
  164. This function enumerates all disk drives present on the systen.
  165. Arguments:
  166. Filter - The name of the filter drver whose presence we want to
  167. check on any disk
  168. DiskDevicesForDisplayP - Placeholder for User Friendly Disk names
  169. DiskDevicesPDONameP - Placeholder for PDO device names
  170. VerifierEnabledP - Placeholder for Information regarding presence
  171. of Filter on a particular disk
  172. Return Value:
  173. Returns TRUE if successful, FALSE otherwise
  174. --*/
  175. {
  176. DISPLAY_STRUCT Display;
  177. DISK_ENUMERATE_CALLBACK CallBack;
  178. BOOLEAN Status;
  179. LPTSTR DiskDevicesForDisplay = NULL;
  180. LPTSTR DiskDevicesPDOName = NULL;
  181. LPTSTR VerifierEnabled = NULL;
  182. //
  183. //Initialize the Multisz strings with \0 (i.e. an empty MultiSz String)
  184. //
  185. DiskDevicesForDisplay = (LPTSTR)malloc(sizeof(TCHAR));
  186. if(DiskDevicesForDisplay == NULL ){
  187. VrfErrorResourceFormat( IDS_NOT_ENOUGH_MEMORY );
  188. goto ErrorReturn;
  189. }
  190. DiskDevicesForDisplay[0] = 0;
  191. DiskDevicesPDOName = (LPTSTR)malloc(sizeof(TCHAR));
  192. if( DiskDevicesPDOName == NULL ){
  193. VrfErrorResourceFormat( IDS_NOT_ENOUGH_MEMORY );
  194. goto ErrorReturn;
  195. }
  196. DiskDevicesPDOName[0] = 0;
  197. VerifierEnabled = (LPTSTR)malloc(sizeof(TCHAR));
  198. if( VerifierEnabled == NULL ){
  199. VrfErrorResourceFormat( IDS_NOT_ENOUGH_MEMORY );
  200. goto ErrorReturn;
  201. }
  202. VerifierEnabled[0] = 0;
  203. CallBack = DiskEnumerateCallback;
  204. Display.Filter = Filter;
  205. Display.DiskDevicesForDisplay = &DiskDevicesForDisplay;
  206. Display.DiskDevicesPDOName = &DiskDevicesPDOName;
  207. Display.VerifierEnabled = &VerifierEnabled;
  208. Status = DiskVerify(CallBack,(PVOID) &Display,NULL);
  209. if( !Status ) {
  210. goto ErrorReturn;
  211. }
  212. *DiskDevicesForDisplayP = DiskDevicesForDisplay;
  213. *DiskDevicesPDONameP = DiskDevicesPDOName;
  214. *VerifierEnabledP = VerifierEnabled;
  215. return TRUE;
  216. ErrorReturn:
  217. if(DiskDevicesForDisplay != NULL) {
  218. free( DiskDevicesForDisplay );
  219. }
  220. if(DiskDevicesPDOName != NULL) {
  221. free( DiskDevicesPDOName );
  222. }
  223. if(VerifierEnabled != NULL) {
  224. free(VerifierEnabled);
  225. }
  226. return FALSE;
  227. }
  228. BOOLEAN
  229. DiskEnumerateCallback (
  230. PVOID Context,
  231. HDEVINFO DevInfo,
  232. SP_DEVINFO_DATA *DevInfoData
  233. )
  234. /*++
  235. Routine Description:
  236. This function is a callback to be executed whenever a disk is discovered
  237. Arguments:
  238. Context - Points to all the relevant information needed to succesfully enumerate the disk
  239. DevInfo - The device information set which contains DeviceInfoData
  240. DevInfoData - Information needed to deal with the given device
  241. Return Value:
  242. Returns TRUE if successful, FALSE otherwise
  243. --*/
  244. {
  245. PDISPLAY_STRUCT PDisplay;
  246. BOOLEAN Status;
  247. PDisplay = (PDISPLAY_STRUCT) Context;
  248. //
  249. //Add newly discoverd disks to the list
  250. //
  251. Status = PrintDeviceName(DevInfo,
  252. DevInfoData,
  253. PDisplay->DiskDevicesForDisplay,
  254. PDisplay->DiskDevicesPDOName);
  255. if(!Status) {
  256. return Status;
  257. }
  258. Status = PrintFilters(DevInfo,
  259. DevInfoData,FALSE,
  260. PDisplay->Filter,
  261. PDisplay->VerifierEnabled);
  262. return Status;
  263. }
  264. BOOLEAN
  265. AddFilter(
  266. IN LPTSTR Filter,
  267. IN LPTSTR DiskDevicesPDONameP
  268. )
  269. /*++
  270. Routine Description:
  271. This function adds the filter on the specified disk device
  272. Arguments:
  273. Filter - Name of the filter to be added
  274. DiskDevicesPDONameP - PDO device name of the disk
  275. Return Value:
  276. Returns TRUE if successful, FALSE otherwise
  277. --*/
  278. {
  279. ADD_REMOVE_STRUCT AddRemove;
  280. DISK_ENUMERATE_CALLBACK CallBack;
  281. BOOLEAN Status;
  282. AddRemove.Filter = Filter;
  283. CallBack = AddCallback;
  284. Status = DiskVerify(CallBack,(PVOID) &AddRemove,DiskDevicesPDONameP);
  285. return Status;
  286. }
  287. BOOLEAN
  288. DelFilter(
  289. IN LPTSTR Filter,
  290. IN LPTSTR DiskDevicesPDONameP
  291. )
  292. /*++
  293. Routine Description:
  294. This function removes the filter on the specified disk device
  295. Arguments:
  296. Filter - Name of the filter to be added
  297. DiskDevicesPDONameP - PDO device name of the disk
  298. Return Value:
  299. Returns TRUE if successful, FALSE otherwise
  300. --*/
  301. {
  302. ADD_REMOVE_STRUCT AddRemove;
  303. DISK_ENUMERATE_CALLBACK CallBack;
  304. BOOLEAN Status;
  305. AddRemove.Filter = Filter;
  306. CallBack = DelCallback;
  307. Status = DiskVerify(CallBack,(PVOID) &AddRemove,DiskDevicesPDONameP);
  308. return Status;
  309. }
  310. BOOLEAN
  311. AddCallback (
  312. PVOID Context,
  313. HDEVINFO DevInfo,
  314. SP_DEVINFO_DATA *DevInfoData
  315. )
  316. /*++
  317. Routine Description:
  318. This function is a callback to be executed whenever a disk is matched, and
  319. a filter needs to be added/deleted
  320. Arguments:
  321. Context - Points to all the relevant information needed to succesfully identify the disk
  322. DevInfo - The device information set which contains DeviceInfoData
  323. DevInfoData - Information needed to deal with the given device
  324. Return Value:
  325. Returns TRUE if successful, FALSE otherwise
  326. --*/
  327. {
  328. PADD_REMOVE_STRUCT PAddRemove;
  329. BOOLEAN Status;
  330. PAddRemove = (PADD_REMOVE_STRUCT) Context;
  331. Status = AddFilterDriver(DevInfo,
  332. DevInfoData,
  333. PAddRemove->Filter,
  334. FALSE);
  335. if( !Status ) {
  336. return FALSE;
  337. }
  338. return TRUE;
  339. }
  340. BOOLEAN
  341. DelCallback (
  342. PVOID Context,
  343. HDEVINFO DevInfo,
  344. SP_DEVINFO_DATA *DevInfoData
  345. )
  346. /*++
  347. Routine Description:
  348. This function is a callback to be executed whenever a disk is matched, and
  349. a filter needs to be added/deleted
  350. Arguments:
  351. Context - Points to all the relevant information needed to succesfully identify the disk
  352. DevInfo - The device information set which contains DeviceInfoData
  353. DevInfoData - Information needed to deal with the given device
  354. Return Value:
  355. Returns TRUE if successful, FALSE otherwise
  356. --*/
  357. {
  358. PADD_REMOVE_STRUCT PAddRemove;
  359. BOOLEAN Status;
  360. PAddRemove = (PADD_REMOVE_STRUCT) Context;
  361. Status = RemoveFilterDriver(DevInfo,
  362. DevInfoData,
  363. PAddRemove->Filter,
  364. FALSE);
  365. if( !Status ){
  366. return FALSE;
  367. }
  368. return TRUE;
  369. }
  370. BOOLEAN
  371. DiskVerify(
  372. DISK_ENUMERATE_CALLBACK CallBack,
  373. PVOID Context,
  374. LPTSTR deviceName
  375. )
  376. /*++
  377. Routine Description:
  378. This function enumerates all disk drives. It also is used to add/remove
  379. filter drivers. It triggers callbacks upon detecting a disk.
  380. Arguments:
  381. CallBack - The routine to be executed upon succesfull detection of a disk
  382. Context - Points to all the relevant information needed to succesfully identify the disk
  383. Return Value:
  384. Returns TRUE if successful, FALSE otherwise
  385. --*/
  386. {
  387. //
  388. // these two constants are used to help enumerate through the list of all
  389. // disks and volumes on the system. Adding another GUID should "just work"
  390. //
  391. const GUID * deviceGuids[] = {
  392. &DiskClassGuid,
  393. };
  394. HDEVINFO devInfo = INVALID_HANDLE_VALUE;
  395. SP_DEVINFO_DATA devInfoData;
  396. int deviceIndex;
  397. BOOLEAN upperFilter = FALSE;
  398. BOOLEAN deviceMatch = FALSE;
  399. BOOLEAN Status;
  400. BOOLEAN Sucess;
  401. //
  402. // get a list of devices which support the given interface
  403. //
  404. devInfo = SetupDiGetClassDevs( deviceGuids[0],
  405. NULL,
  406. NULL,
  407. DIGCF_PROFILE |
  408. DIGCF_DEVICEINTERFACE |
  409. DIGCF_PRESENT );
  410. if( devInfo == INVALID_HANDLE_VALUE ) {
  411. VrfErrorResourceFormat( IDS_CANNOT_GET_DEVICES_LIST );
  412. return FALSE;
  413. }
  414. //
  415. // step through the list of devices for this handle
  416. // get device info at index deviceIndex, the function returns FALSE
  417. // when there is no device at the given index.
  418. //
  419. deviceIndex=0;
  420. do{
  421. //
  422. // if a device name was specified, and it doesn't match this one,
  423. // stop. If there is a match (or no name was specified), mark that
  424. // there was a match.
  425. //
  426. devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
  427. Sucess = (SetupDiEnumDeviceInfo( devInfo, deviceIndex++, &devInfoData ) != FALSE);
  428. if( !Sucess ) {
  429. break;
  430. }
  431. if( deviceName != NULL && !DeviceNameMatches( devInfo, &devInfoData, deviceName )) {
  432. continue;
  433. } else {
  434. deviceMatch = TRUE;
  435. }
  436. Status = (*CallBack)(Context,devInfo,&devInfoData);
  437. if( !Status ){
  438. return FALSE;
  439. }
  440. }while(Sucess);
  441. if( devInfo != INVALID_HANDLE_VALUE ) {
  442. Status = ( SetupDiDestroyDeviceInfoList( devInfo ) != FALSE );
  443. }
  444. if( !deviceMatch ) {
  445. VrfErrorResourceFormat( IDS_NO_DEVICES_MATCH_NAME,
  446. deviceName );
  447. return FALSE;
  448. }
  449. return TRUE;
  450. }
  451. BOOLEAN
  452. AddFilterDriver(
  453. IN HDEVINFO DeviceInfoSet,
  454. IN PSP_DEVINFO_DATA DeviceInfoData,
  455. IN LPTSTR Filter,
  456. IN BOOLEAN UpperFilter
  457. )
  458. /*++
  459. Routine Description:
  460. Adds the given filter driver to the list of lower filter drivers for the
  461. disk. Note: The filter is prepended to the list of drivers, which will put
  462. it at the bottom of the filter driver stack
  463. Arguments:
  464. DeviceInfoSet - The device information set which contains DeviceInfoData
  465. DeviceInfoData - Information needed to deal with the given device
  466. Filter - The filter to add
  467. Return Value:
  468. If we are successful in adding the driver, as a lower driver to disk, returns
  469. TRUE, FALSE otherwise
  470. --*/
  471. {
  472. SIZE_T length;
  473. SIZE_T size;
  474. LPTSTR buffer = NULL;
  475. BOOLEAN SetupDiSetDeviceRegistryPropertyReturn;
  476. BOOLEAN Success;
  477. ASSERT(DeviceInfoData != NULL);
  478. ASSERT(Filter != NULL);
  479. Success = TRUE;
  480. length = 0;
  481. size = 0;
  482. buffer = GetFilters( DeviceInfoSet, DeviceInfoData, UpperFilter );
  483. if( buffer == NULL ){
  484. //
  485. // if there is no such value in the registry, then there are no upper
  486. // filter drivers loaded, and we can just put one there
  487. // make room for the string, string null terminator, and multisz null
  488. // terminator
  489. //
  490. length = _tcslen(Filter)+1;
  491. size = (length+1)*sizeof(_TCHAR);
  492. buffer = (LPTSTR)malloc( size );
  493. if( buffer == NULL ){
  494. VrfErrorResourceFormat( IDS_NOT_ENOUGH_MEMORY );
  495. Success = FALSE;
  496. goto Done;
  497. }
  498. memset(buffer, 0, size);
  499. memcpy(buffer, Filter, length*sizeof(_TCHAR));
  500. } else {
  501. LPTSTR buffer2;
  502. //
  503. // remove all instances of filter from driver list
  504. //
  505. MultiSzSearchAndDeleteCaseInsensitive( Filter, buffer, &length );
  506. //
  507. // allocate a buffer large enough to add the new filter
  508. // GetMultiSzLength already includes length of terminating NULL
  509. // determing the new length of the string
  510. //
  511. length = GetMultiSzLength(buffer) + _tcslen(Filter) + 1;
  512. size = length*sizeof(_TCHAR);
  513. buffer2 = (LPTSTR)malloc( size );
  514. if (buffer2 == NULL) {
  515. VrfErrorResourceFormat( IDS_NOT_ENOUGH_MEMORY );
  516. Success = FALSE;
  517. goto Done;
  518. }
  519. memset(buffer2, 0, size);
  520. memcpy(buffer2, buffer, GetMultiSzLength(buffer)*sizeof(_TCHAR));
  521. free(buffer);
  522. buffer = buffer2;
  523. //
  524. // add the driver to the driver list
  525. //
  526. PrependSzToMultiSz(Filter, &buffer);
  527. }
  528. //
  529. // set the new list of filters in place
  530. //
  531. SetupDiSetDeviceRegistryPropertyReturn = (
  532. SetupDiSetDeviceRegistryProperty( DeviceInfoSet,
  533. DeviceInfoData,
  534. (UpperFilter ? SPDRP_UPPERFILTERS : SPDRP_LOWERFILTERS),
  535. (PBYTE)buffer,
  536. ((ULONG) GetMultiSzLength(buffer)*sizeof(_TCHAR))) != FALSE );
  537. if(!SetupDiSetDeviceRegistryPropertyReturn){
  538. TRACE(_T("in AddUpperFilterDriver(): ")
  539. _T("couldn't set registry value! error: %u\n"),
  540. GetLastError());
  541. VrfErrorResourceFormat( IDS_CANNOT_SET_DEVICE_REGISTRY_PROPERTY );
  542. Success = FALSE;
  543. goto Done;
  544. }
  545. Done:
  546. if(buffer != NULL) {
  547. free( buffer );
  548. }
  549. return Success;
  550. }
  551. BOOLEAN
  552. RemoveFilterDriver(
  553. IN HDEVINFO DeviceInfoSet,
  554. IN PSP_DEVINFO_DATA DeviceInfoData,
  555. IN LPTSTR Filter,
  556. IN BOOLEAN UpperFilter
  557. )
  558. /*++
  559. Routine Description:
  560. Removes all instances of the given filter driver from the list of lower
  561. filter drivers for the device.
  562. Arguments:
  563. DeviceInfoSet - The device information set which contains DeviceInfoData
  564. DeviceInfoData - Information needed to deal with the given device
  565. Filter - The filter to remove
  566. Return Value:
  567. returns TRUE if successful, FALSE otherwise
  568. --*/
  569. {
  570. SIZE_T length;
  571. SIZE_T size;
  572. LPTSTR buffer;
  573. BOOL success;
  574. ASSERT(DeviceInfoData != NULL);
  575. ASSERT(Filter != NULL);
  576. success = FALSE;
  577. length = 0;
  578. size = 0;
  579. buffer = GetFilters( DeviceInfoSet, DeviceInfoData, UpperFilter );
  580. if( buffer == NULL ){
  581. return (TRUE);
  582. } else {
  583. //
  584. // remove all instances of filter from driver list
  585. //
  586. MultiSzSearchAndDeleteCaseInsensitive( Filter, buffer, &length );
  587. }
  588. length = GetMultiSzLength(buffer);
  589. ASSERT ( length > 0 );
  590. if( length == 1 ){
  591. //
  592. // if the length of the list is 1, the return value from
  593. // GetMultiSzLength() was just accounting for the trailing '\0', so we can
  594. // delete the registry key, by setting it to NULL.
  595. //
  596. success = SetupDiSetDeviceRegistryProperty( DeviceInfoSet,
  597. DeviceInfoData,
  598. (UpperFilter ? SPDRP_UPPERFILTERS : SPDRP_LOWERFILTERS),
  599. NULL,
  600. 0 );
  601. } else {
  602. //
  603. // set the new list of drivers into the registry
  604. //
  605. size = length*sizeof(_TCHAR);
  606. success = SetupDiSetDeviceRegistryProperty( DeviceInfoSet,
  607. DeviceInfoData,
  608. (UpperFilter ? SPDRP_UPPERFILTERS : SPDRP_LOWERFILTERS),
  609. (PBYTE)buffer,
  610. (ULONG)size );
  611. }
  612. free( buffer );
  613. if( !success ){
  614. TRACE(_T("in RemoveUpperFilterDriver(): ")
  615. _T("couldn't set registry value! error: %i\n"),
  616. GetLastError());
  617. VrfErrorResourceFormat( IDS_CANNOT_SET_DEVICE_REGISTRY_PROPERTY );
  618. return FALSE;
  619. }
  620. return TRUE;
  621. }
  622. BOOLEAN
  623. PrintFilters(
  624. IN HDEVINFO DeviceInfoSet,
  625. IN PSP_DEVINFO_DATA DeviceInfoData,
  626. IN BOOLEAN UpperFilters,
  627. IN LPTSTR FilterDriver,
  628. IN OUT LPTSTR *VerifierEnabled
  629. )
  630. /*++
  631. Routine Description:
  632. Looks at all the lower filters installed for the disk. It tells us if
  633. DiskVerifier is installed for that particular disk.
  634. Arguments:
  635. DeviceInfoSet - The device information set which contains
  636. DeviceInfoData
  637. DeviceInfoData - Information needed to deal with the given device
  638. FilterDriver - The name of the Filter driver for Disk Verifier
  639. DiskDevices - MultiSZ style string listing all devices
  640. VerifierEnabled - MultiSZ style string indicating if Verifier Enabled
  641. Return Value:
  642. Returns TRUE if successful, FALSE otherwise
  643. --*/
  644. {
  645. LPTSTR buffer;
  646. SIZE_T filterPosition;
  647. LPTSTR temp;
  648. int StrCmpReturn;
  649. buffer = GetFilters( DeviceInfoSet, DeviceInfoData, UpperFilters );
  650. if ( buffer != NULL ) {
  651. //
  652. // go through the multisz and print out each driver
  653. //
  654. temp=buffer;
  655. filterPosition=0;
  656. while( *temp != _T('\0') ){
  657. StrCmpReturn = _tcsicmp(FilterDriver,temp);
  658. if(StrCmpReturn == 0 ){
  659. PrependSzToMultiSz( _T("1"),VerifierEnabled);
  660. free( buffer );
  661. return TRUE;
  662. }
  663. temp = temp+_tcslen(temp)+1;
  664. filterPosition++;
  665. }
  666. free( buffer );
  667. }
  668. PrependSzToMultiSz( _T("0"),VerifierEnabled);
  669. return TRUE;
  670. }
  671. BOOLEAN PrintDeviceName(
  672. IN HDEVINFO DeviceInfoSet,
  673. IN PSP_DEVINFO_DATA DeviceInfoData,
  674. IN OUT LPTSTR *DiskDevicesForDisplay,
  675. IN OUT LPTSTR *DiskDevicesPDOName
  676. )
  677. /*++
  678. Routine Description:
  679. Looks up the PDO device name, and user friendly name for disk.
  680. Arguments:
  681. DeviceInfoSet - The device information set which contains
  682. DeviceInfoData
  683. DeviceInfoData - Information needed to deal with the given device
  684. DiskDevicesForDisplay - User Friendly Names for Disk
  685. DiskDevicesPDOName - PDO specified device names for disk
  686. Return Value:
  687. Returns TRUE if successful, FALSE otherwise
  688. --*/
  689. {
  690. DWORD regDataType;
  691. LPTSTR deviceName;
  692. LPTSTR driveLetters;
  693. int StrCmpReturned;
  694. BOOL bResult;
  695. deviceName = (LPTSTR) GetDeviceRegistryProperty( DeviceInfoSet,
  696. DeviceInfoData,
  697. SPDRP_PHYSICAL_DEVICE_OBJECT_NAME,
  698. &regDataType );
  699. if( deviceName == NULL ) {
  700. TRACE(_T("in PrintDeviceName(): registry key is NULL! error: %u\n"),
  701. GetLastError());
  702. VrfErrorResourceFormat( IDS_CANNOT_GET_DEVICE_REGISTRY_PROPERTY );
  703. return FALSE;
  704. }
  705. if( regDataType != REG_SZ ){
  706. TRACE(_T("in PrintDeviceName(): registry key is not an SZ!\n"));
  707. return FALSE;
  708. } else {
  709. //
  710. // if the device name starts with \Device, cut that off (all
  711. // devices will start with it, so it is redundant)
  712. //
  713. StrCmpReturned = _tcsncmp(deviceName, _T("\\Device"), 7);
  714. if( StrCmpReturned == 0 ){
  715. memmove(deviceName,
  716. deviceName+7,
  717. (_tcslen(deviceName)-6)*sizeof(_TCHAR) );
  718. }
  719. PrependSzToMultiSz(deviceName,DiskDevicesPDOName);
  720. free( deviceName );
  721. }
  722. deviceName = (LPTSTR) GetDeviceRegistryProperty( DeviceInfoSet,
  723. DeviceInfoData,
  724. SPDRP_FRIENDLYNAME ,
  725. &regDataType );
  726. if( deviceName == NULL ){
  727. TRACE(_T("in PrintDeviceName(): registry key is NULL! error: %u\n"),
  728. GetLastError());
  729. //
  730. //We could not obtain the friendly name for disk, setting it to Unknown.
  731. //
  732. deviceName = (LPTSTR)malloc(sizeof(TCHAR) * 64);
  733. if ( !deviceName ) {
  734. VrfErrorResourceFormat( IDS_NOT_ENOUGH_MEMORY );
  735. return FALSE;
  736. }
  737. bResult = VrfLoadString(IDS_UNKNOWN,
  738. deviceName,
  739. 64 );
  740. if ( !bResult ) {
  741. VrfErrorResourceFormat( IDS_NOT_ENOUGH_MEMORY );
  742. return FALSE;
  743. }
  744. }
  745. //
  746. // just to make sure we are getting the expected type of buffer
  747. //
  748. if( regDataType != REG_SZ ){
  749. TRACE(_T("in PrintDeviceName(): registry key is not an SZ!\n"));
  750. return FALSE;
  751. }else{
  752. driveLetters = GetDriveLetters( DeviceInfoSet,
  753. DeviceInfoData);
  754. if(driveLetters){
  755. StrConcatWithSpace(driveLetters,&deviceName);
  756. }
  757. PrependSzToMultiSz(deviceName,DiskDevicesForDisplay);
  758. free( deviceName );
  759. }
  760. return TRUE;
  761. }
  762. LPTSTR
  763. GetFilters(
  764. IN HDEVINFO DeviceInfoSet,
  765. IN PSP_DEVINFO_DATA DeviceInfoData,
  766. IN BOOLEAN UpperFilters
  767. )
  768. /*++
  769. Routine Description:
  770. Returns a buffer containing the list of lower filters for the device.The
  771. buffer must be freed by the caller.
  772. Arguments:
  773. DeviceInfoSet - The device information set which contains DeviceInfoData
  774. DeviceInfoData - Information needed to deal with the given device
  775. UpperFilters - Switch to select Upper/Lower filter. Currently we
  776. use Lower.
  777. Return Value:
  778. MultiSz style string containing all lower Filters for the disk is
  779. returned. NULL is returned if there is no buffer, or an error occurs.
  780. --*/
  781. {
  782. DWORD regDataType;
  783. LPTSTR buffer;
  784. buffer = (LPTSTR) GetDeviceRegistryProperty( DeviceInfoSet,
  785. DeviceInfoData,
  786. (UpperFilters ? SPDRP_UPPERFILTERS : SPDRP_LOWERFILTERS),
  787. &regDataType );
  788. if( buffer != NULL && regDataType != REG_MULTI_SZ ){
  789. TRACE(_T("in GetUpperFilters(): ")
  790. _T("registry key is not a MULTI_SZ!\n"));
  791. free( buffer );
  792. return (NULL);
  793. }
  794. return (buffer);
  795. }
  796. BOOLEAN
  797. DeviceNameMatches(
  798. IN HDEVINFO DeviceInfoSet,
  799. IN PSP_DEVINFO_DATA DeviceInfoData,
  800. IN LPTSTR DeviceName
  801. )
  802. /*++
  803. Routine Description:
  804. Searches if DeviceName matches the name of the device specified by
  805. DeviceInfoData
  806. Arguments:
  807. DeviceInfoSet - The device information set which contains DeviceInfoData
  808. DeviceInfoData - Information needed to deal with the given device
  809. DeviceName - the name to try to match
  810. Return Value:
  811. Returns TRUE if successful, FALSE otherwise
  812. --*/
  813. {
  814. BOOLEAN matching = FALSE;
  815. DWORD regDataType;
  816. LPTSTR deviceName;
  817. int StrCmpReturn;
  818. deviceName = (LPTSTR) GetDeviceRegistryProperty( DeviceInfoSet,
  819. DeviceInfoData,
  820. SPDRP_PHYSICAL_DEVICE_OBJECT_NAME,
  821. &regDataType );
  822. if( deviceName != NULL) {
  823. if( regDataType != REG_SZ ){
  824. TRACE(_T("in DeviceNameMatches(): registry key is not an SZ!\n"));
  825. matching = FALSE;
  826. }else{
  827. //
  828. // if the device name starts with \Device, cut that off (all
  829. // devices will start with it, so it is redundant)
  830. //
  831. StrCmpReturn = _tcsncmp(deviceName, _T("\\Device"), 7);
  832. if( StrCmpReturn == 0 ){
  833. memmove(deviceName,
  834. deviceName+7,
  835. (_tcslen(deviceName)-6)*sizeof(_TCHAR) );
  836. }
  837. matching = (_tcscmp(deviceName, DeviceName) == 0);
  838. }
  839. free( deviceName );
  840. } else {
  841. TRACE(_T("in DeviceNameMatches(): registry key is NULL!\n"));
  842. VrfErrorResourceFormat( IDS_CANNOT_GET_DEVICE_REGISTRY_PROPERTY );
  843. matching = FALSE;
  844. }
  845. return (matching);
  846. }
  847. PBYTE
  848. GetDeviceRegistryProperty(
  849. IN HDEVINFO DeviceInfoSet,
  850. IN PSP_DEVINFO_DATA DeviceInfoData,
  851. IN DWORD Property,
  852. OUT PDWORD PropertyRegDataType
  853. )
  854. /*++
  855. Routine Description:
  856. A wrapper around SetupDiGetDeviceRegistryProperty, so that I don't have to
  857. deal with memory allocation anywhere else.
  858. Arguments:
  859. DeviceInfoSet - The device information set which contains DeviceInfoData
  860. DeviceInfoData - Information needed to deal with the given device
  861. Property - which property to get (SPDRP_XXX)
  862. PropertyRegDataType - the type of registry property
  863. Return Value:
  864. Returns buffer for Registery property
  865. --*/
  866. {
  867. DWORD length = 0;
  868. PBYTE buffer = NULL;
  869. BOOL SetupDiGetDeviceRegistryPropertyReturn;
  870. SetupDiGetDeviceRegistryPropertyReturn = SetupDiGetDeviceRegistryProperty(
  871. DeviceInfoSet,
  872. DeviceInfoData,
  873. Property,
  874. NULL,
  875. NULL,
  876. 0,
  877. &length);
  878. if( SetupDiGetDeviceRegistryPropertyReturn ){
  879. //
  880. // we should not be successful at this point, so this call succeeding
  881. // is an error condition
  882. //
  883. TRACE(_T("in GetDeviceRegistryProperty(): ")
  884. _T("call SetupDiGetDeviceRegistryProperty did not fail\n"),
  885. GetLastError());
  886. VrfErrorResourceFormat( IDS_CANNOT_GET_DEVICE_REGISTRY_PROPERTY );
  887. return (NULL);
  888. }
  889. if( GetLastError() != ERROR_INSUFFICIENT_BUFFER ){
  890. //
  891. // this means there are no upper filter drivers loaded, so we can just
  892. // return.
  893. //
  894. return (NULL);
  895. }
  896. //
  897. // since we don't have a buffer yet, it is "insufficient"; we allocate
  898. // one and try again.
  899. //
  900. buffer = (PBYTE)malloc( length );
  901. if( buffer == NULL ) {
  902. VrfErrorResourceFormat( IDS_NOT_ENOUGH_MEMORY );
  903. return (NULL);
  904. }
  905. SetupDiGetDeviceRegistryPropertyReturn = SetupDiGetDeviceRegistryProperty(
  906. DeviceInfoSet,
  907. DeviceInfoData,
  908. Property,
  909. PropertyRegDataType,
  910. buffer,
  911. length,
  912. NULL);
  913. if( !SetupDiGetDeviceRegistryPropertyReturn) {
  914. TRACE(_T("in GetDeviceRegistryProperty(): ")
  915. _T("couldn't get registry property! error: %i\n"),
  916. GetLastError());
  917. VrfErrorResourceFormat( IDS_CANNOT_GET_DEVICE_REGISTRY_PROPERTY );
  918. free( buffer );
  919. return (NULL);
  920. }
  921. return (buffer);
  922. }
  923. BOOLEAN
  924. PrependSzToMultiSz(
  925. IN LPTSTR SzToPrepend,
  926. IN OUT LPTSTR *MultiSz
  927. )
  928. /*++
  929. Routine Description:
  930. Prepends the given string to a MultiSz.Note: This WILL allocate and free
  931. memory, so don't keep pointers to the MultiSz passed in.
  932. Arguments:
  933. SzToPrepend - string to prepend
  934. MultiSz - pointer to a MultiSz which will be prepended-to
  935. Return Value:
  936. Returns true if successful, false if not (will only fail in memory
  937. allocation)
  938. --*/
  939. {
  940. SIZE_T szLen;
  941. SIZE_T multiSzLen;
  942. LPTSTR newMultiSz = NULL;
  943. ASSERT(SzToPrepend != NULL);
  944. ASSERT(MultiSz != NULL);
  945. szLen = (_tcslen(SzToPrepend)+1)*sizeof(_TCHAR);
  946. multiSzLen = GetMultiSzLength(*MultiSz)*sizeof(_TCHAR);
  947. newMultiSz = (LPTSTR)malloc( szLen+multiSzLen );
  948. if( newMultiSz == NULL ){
  949. return (FALSE);
  950. }
  951. //
  952. // recopy the old MultiSz into proper position into the new buffer.
  953. // the (char*) cast is necessary, because newMultiSz may be a wchar*, and
  954. // szLen is in bytes.
  955. //
  956. memcpy( ((char*)newMultiSz) + szLen, *MultiSz, multiSzLen );
  957. _tcscpy( newMultiSz, SzToPrepend );
  958. free( *MultiSz );
  959. *MultiSz = newMultiSz;
  960. return (TRUE);
  961. }
  962. SIZE_T
  963. GetMultiSzLength(
  964. IN LPTSTR MultiSz
  965. )
  966. /*++
  967. Routine Description:
  968. Calculates the size of the buffer required to hold a particular MultiSz
  969. Arguments:
  970. MultiSz - the MultiSz to get the length of
  971. Return Value:
  972. Returns the length (in characters) of the buffer required to hold this
  973. MultiSz, INCLUDING the trailing null.
  974. example: GetMultiSzLength("foo\0bar\0") returns 9
  975. note: since MultiSz cannot be null, a number >= 1 will always be returned
  976. --*/
  977. {
  978. SIZE_T len = 0;
  979. SIZE_T totalLen = 0;
  980. ASSERT( MultiSz != NULL );
  981. while( *MultiSz != _T('\0') ){
  982. len = _tcslen(MultiSz)+1;
  983. MultiSz += len;
  984. totalLen += len;
  985. }
  986. return (totalLen+1);
  987. }
  988. SIZE_T
  989. MultiSzSearchAndDeleteCaseInsensitive(
  990. IN LPTSTR FindThis,
  991. IN LPTSTR FindWithin,
  992. OUT SIZE_T *NewLength
  993. )
  994. /*++
  995. Routine Description:
  996. Deletes all instances of a string from within a multi-sz.
  997. Arguments:
  998. FindThis - the string to find and remove
  999. FindWithin - the string having the instances removed
  1000. NewStringLength - the new string length
  1001. Return Value:
  1002. Returns the no. of string occurences deleted from MultiSz
  1003. --*/
  1004. {
  1005. LPTSTR search;
  1006. SIZE_T currentOffset;
  1007. DWORD instancesDeleted;
  1008. SIZE_T searchLen;
  1009. ASSERT(FindThis != NULL);
  1010. ASSERT(FindWithin != NULL);
  1011. ASSERT(NewLength != NULL);
  1012. currentOffset = 0;
  1013. instancesDeleted = 0;
  1014. search = FindWithin;
  1015. *NewLength = GetMultiSzLength(FindWithin);
  1016. //
  1017. // loop while the multisz null terminator is not found
  1018. //
  1019. while ( *search != _T('\0') ){
  1020. //
  1021. // length of string + null char; used in more than a couple places
  1022. //
  1023. searchLen = _tcslen(search) + 1;
  1024. if( _tcsicmp(search, FindThis) == 0 ){
  1025. //
  1026. // they match, shift the contents of the multisz, to overwrite the
  1027. // string (and terminating null), and update the length
  1028. //
  1029. instancesDeleted++;
  1030. *NewLength -= searchLen;
  1031. memmove( search,
  1032. search + searchLen,
  1033. (*NewLength - currentOffset) * sizeof(TCHAR) );
  1034. } else {
  1035. currentOffset += searchLen;
  1036. search += searchLen;
  1037. }
  1038. }
  1039. return instancesDeleted;
  1040. }
  1041. BOOLEAN
  1042. FreeDiskMultiSz(
  1043. IN LPTSTR MultiSz
  1044. )
  1045. {
  1046. ASSERT( MultiSz != NULL );
  1047. free( MultiSz );
  1048. return TRUE;
  1049. }
  1050. LPTSTR
  1051. GetDriveLetters (
  1052. IN HDEVINFO DeviceInfoSet,
  1053. IN PSP_DEVINFO_DATA DeviceInfoData
  1054. )
  1055. /*++
  1056. Routine Description:
  1057. Looks up the drive letters for the specified disk. Finds the
  1058. device number of the disk, and passes it on to the volume code
  1059. Arguments:
  1060. DeviceInfoSet - The device information set which contains
  1061. DeviceInfoData
  1062. DeviceInfoData - Information needed to deal with the given device
  1063. Return Value:
  1064. Returns the list of drives present on the disk if successful,
  1065. NULL otherwise
  1066. --*/
  1067. {
  1068. SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;
  1069. ULONG cbDetail;
  1070. PSP_DEVICE_INTERFACE_DETAIL_DATA pDetail;
  1071. BOOL Status;
  1072. HANDLE hDisk;
  1073. STORAGE_DEVICE_NUMBER devNumber;
  1074. DWORD cbBytes;
  1075. DeviceInterfaceData.cbSize = sizeof(DeviceInterfaceData);
  1076. cbDetail = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) +
  1077. MAX_PATH * sizeof(WCHAR);
  1078. pDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LMEM_FIXED, cbDetail);
  1079. if (pDetail == NULL) {
  1080. return NULL;
  1081. }
  1082. pDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
  1083. Status = SetupDiEnumDeviceInterfaces (DeviceInfoSet,
  1084. DeviceInfoData,
  1085. &DiskClassGuid,
  1086. 0,
  1087. &DeviceInterfaceData);
  1088. if (! Status) {
  1089. LocalFree(pDetail);
  1090. return NULL;
  1091. }
  1092. Status = SetupDiGetDeviceInterfaceDetail(DeviceInfoSet,
  1093. &DeviceInterfaceData,
  1094. pDetail,
  1095. cbDetail,
  1096. NULL,
  1097. NULL);
  1098. if (! Status) {
  1099. LocalFree(pDetail);
  1100. return NULL;
  1101. }
  1102. hDisk = CreateFile(pDetail->DevicePath,
  1103. GENERIC_READ,
  1104. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1105. NULL,
  1106. OPEN_EXISTING,
  1107. FILE_ATTRIBUTE_NORMAL,
  1108. NULL);
  1109. if (hDisk == INVALID_HANDLE_VALUE) {
  1110. LocalFree(pDetail);
  1111. return NULL;
  1112. }
  1113. Status = DeviceIoControl(hDisk,
  1114. IOCTL_STORAGE_GET_DEVICE_NUMBER,
  1115. NULL,
  1116. 0,
  1117. &devNumber,
  1118. sizeof(devNumber),
  1119. &cbBytes,
  1120. NULL);
  1121. if (!Status) {
  1122. LocalFree(pDetail);
  1123. return NULL;
  1124. }
  1125. LocalFree(pDetail);
  1126. return GetDriveLettersFromVolume (devNumber.DeviceNumber);
  1127. }
  1128. LPTSTR
  1129. GetDriveLettersFromVolume (
  1130. IN ULONG DeviceNumber
  1131. )
  1132. /*++
  1133. Routine Description:
  1134. Looks up the drive letter(s) and volume_label(s) for the specified
  1135. disk(device number) by parsing the volumes.
  1136. Arguments:
  1137. DeviceNumber - Unique Number identifying the physical disk
  1138. Return Value:
  1139. Returns the list of drives present on the disk if successful,
  1140. NULL otherwise
  1141. --*/
  1142. {
  1143. HANDLE h = INVALID_HANDLE_VALUE;
  1144. HANDLE hVol;
  1145. TCHAR volumeName[MAX_PATH];
  1146. TCHAR originalVolumeName[MAX_PATH];
  1147. DWORD cbBytes;
  1148. PVOLUME_DISK_EXTENTS PVolDiskExtent;
  1149. LPTSTR drives;
  1150. LPTSTR temp;
  1151. BOOL b;
  1152. BOOL First;
  1153. int maxDisks;
  1154. int i;
  1155. int j;
  1156. size_t tempLen;
  1157. TCHAR OpenParan[] = TEXT(" ( ");
  1158. TCHAR CloseParan[] = TEXT(")");
  1159. drives = NULL;
  1160. First = TRUE;
  1161. StrConcatWithSpace(OpenParan,&drives);
  1162. for (;;) {
  1163. if(First) {
  1164. //
  1165. //Using FindFirstVolumeA, as it is the Non-Unicode version
  1166. //of FindFirstVolume
  1167. //
  1168. h = FindFirstVolume(volumeName, MAX_PATH);
  1169. if (h == INVALID_HANDLE_VALUE) {
  1170. return NULL;
  1171. }
  1172. First = FALSE;
  1173. b = TRUE;
  1174. } else {
  1175. b = FindNextVolume(h, volumeName, MAX_PATH);
  1176. }
  1177. if (!b) {
  1178. break;
  1179. }
  1180. tempLen = _tcslen(volumeName);
  1181. _tcsncpy(originalVolumeName,
  1182. volumeName,
  1183. tempLen - 1);
  1184. _tcscpy(originalVolumeName + tempLen - 1,
  1185. volumeName + tempLen);
  1186. //
  1187. //To open a handle correctly, CreateFile expects the name
  1188. //of the Volume without the trailing \ returned by
  1189. //FindFirstVolume / FindNextVolume
  1190. //
  1191. hVol = CreateFile(originalVolumeName,
  1192. GENERIC_READ,
  1193. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1194. NULL,
  1195. OPEN_EXISTING,
  1196. FILE_ATTRIBUTE_NORMAL,
  1197. NULL);
  1198. if (hVol == INVALID_HANDLE_VALUE) {
  1199. continue;
  1200. }
  1201. PVolDiskExtent = (PVOLUME_DISK_EXTENTS) LocalAlloc(LMEM_FIXED, sizeof(VOLUME_DISK_EXTENTS));
  1202. if(!PVolDiskExtent) {
  1203. continue;
  1204. }
  1205. //
  1206. //This IOCTL has to be called with a minimum of
  1207. //size VOLUME_DISK_EXTENTS. If more entries are present
  1208. //it can be obtained by PVolDiskExtent->NumberOfDiskExtents
  1209. //
  1210. b = DeviceIoControl(hVol,
  1211. IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
  1212. NULL,
  1213. 0,
  1214. PVolDiskExtent,
  1215. sizeof(VOLUME_DISK_EXTENTS),
  1216. &cbBytes,
  1217. NULL);
  1218. if (!b) {
  1219. //
  1220. //Now, we can read how much memory is actually required
  1221. //to read in the disk info
  1222. //
  1223. if(GetLastError() == ERROR_MORE_DATA){
  1224. maxDisks = PVolDiskExtent->NumberOfDiskExtents;
  1225. LocalFree(PVolDiskExtent);
  1226. PVolDiskExtent = (PVOLUME_DISK_EXTENTS) LocalAlloc(LMEM_FIXED, sizeof(VOLUME_DISK_EXTENTS) + (sizeof(DISK_EXTENT) * maxDisks));
  1227. if(!PVolDiskExtent) {
  1228. continue;
  1229. }
  1230. b = DeviceIoControl(hVol,
  1231. IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
  1232. NULL,
  1233. 0,
  1234. PVolDiskExtent,
  1235. sizeof(VOLUME_DISK_EXTENTS) + (sizeof(DISK_EXTENT) * maxDisks),
  1236. &cbBytes,
  1237. NULL);
  1238. if (!b) {
  1239. continue;
  1240. }
  1241. for(j=0;j<maxDisks;j++){
  1242. if(PVolDiskExtent->Extents[j].DiskNumber == DeviceNumber) {
  1243. temp = PrintDriveLetters(volumeName);
  1244. if(temp) {
  1245. StrConcatWithSpace(temp,&drives);
  1246. FreeDiskMultiSz(temp);
  1247. }
  1248. }
  1249. }
  1250. } else {
  1251. continue;
  1252. }
  1253. } else {
  1254. if(PVolDiskExtent->Extents[0].DiskNumber == DeviceNumber) {
  1255. temp = PrintDriveLetters(volumeName);
  1256. if(temp) {
  1257. StrConcatWithSpace(temp,&drives);
  1258. FreeDiskMultiSz(temp);
  1259. }
  1260. }
  1261. }
  1262. CloseHandle(hVol);
  1263. LocalFree(PVolDiskExtent);
  1264. }
  1265. if(h != INVALID_HANDLE_VALUE) {
  1266. FindVolumeClose(h);
  1267. }
  1268. StrConcatWithSpace(CloseParan,&drives);
  1269. return drives;
  1270. }
  1271. LPTSTR
  1272. PrintDriveLetters(
  1273. IN PTSTR VolumeName
  1274. )
  1275. /*++
  1276. Routine Description:
  1277. Looks up the drive letters for the specified Volume
  1278. Arguments:
  1279. VolumeName - Name of the Volume
  1280. Return Value:
  1281. A list of drive letter(s) for the specified volume
  1282. --*/
  1283. {
  1284. BOOL b;
  1285. DWORD len;
  1286. LPTSTR volumePaths, p;
  1287. LPTSTR drives;
  1288. TCHAR volumeName1[MAX_PATH];
  1289. DWORD lpVolumeSerialNumber;
  1290. DWORD lpMaximumComponentLength;
  1291. DWORD lpFileSystemFlags;
  1292. drives = NULL;
  1293. b = GetVolumePathNamesForVolumeName(VolumeName,
  1294. NULL,
  1295. 0,
  1296. &len);
  1297. if (!b) {
  1298. if(GetLastError() != ERROR_MORE_DATA) {
  1299. return NULL;
  1300. }
  1301. }
  1302. volumePaths = (LPTSTR) LocalAlloc(LMEM_FIXED, len*sizeof(TCHAR));
  1303. if (!volumePaths) {
  1304. return NULL;
  1305. }
  1306. b = GetVolumePathNamesForVolumeName(VolumeName,
  1307. volumePaths,
  1308. len,
  1309. NULL);
  1310. if (!b ) {
  1311. if( GetLastError() != ERROR_MORE_DATA) {
  1312. LocalFree(volumePaths);
  1313. return NULL;
  1314. } else {
  1315. //
  1316. //Warning - This is a hack. For some reason the non-unicode
  1317. //version of GetVolumePathNamesForVolumeNameA does not return
  1318. //the correct length to be used. So we hope it will not be
  1319. //greater then GETVOLUMEPATH_MAX_LEN_RETRY. But if the correct len is
  1320. //returned, we used that previosly
  1321. //
  1322. LocalFree(volumePaths);
  1323. len = GETVOLUMEPATH_MAX_LEN_RETRY;
  1324. volumePaths = (LPTSTR) LocalAlloc(LMEM_FIXED, GETVOLUMEPATH_MAX_LEN_RETRY*sizeof(TCHAR));
  1325. if (!volumePaths) {
  1326. return NULL;
  1327. }
  1328. b = GetVolumePathNamesForVolumeName(VolumeName,
  1329. volumePaths,
  1330. len,
  1331. NULL);
  1332. if (!b ) {
  1333. LocalFree(volumePaths);
  1334. return NULL;
  1335. }
  1336. }
  1337. }
  1338. if (!volumePaths[0]) {
  1339. return NULL;
  1340. }
  1341. p = volumePaths;
  1342. for (;;) {
  1343. if(_tcslen(p) > 2) {
  1344. p[_tcslen(p) - 1] = _T(' ');
  1345. }
  1346. StrConcatWithSpace(p,&drives);
  1347. while (*p++);
  1348. //
  1349. //The drive letters returned are a collection of strings,
  1350. //and the end is marked with \0\0. If we reached the end,
  1351. //stop, else traverse the string list
  1352. //
  1353. if (*p == 0) {
  1354. break;
  1355. }
  1356. }
  1357. LocalFree(volumePaths);
  1358. return drives;
  1359. }
  1360. BOOLEAN
  1361. StrConcatWithSpace(
  1362. IN LPTSTR SzToAppend,
  1363. IN OUT LPTSTR *drives
  1364. )
  1365. /*++
  1366. Routine Description:
  1367. Concatenates the given string to the drive list. Note: This WILL
  1368. allocate and free memory, so don't keep pointers to the drives
  1369. passed in. Do no pass uninitialized pointers. *drives should
  1370. be NULL if empty
  1371. Arguments:
  1372. SzToAppend - string to prepend
  1373. drives - pointer to the existing drive list
  1374. Return Value:
  1375. Returns true if successful, false if not (will only fail in memory
  1376. allocation)
  1377. --*/
  1378. {
  1379. SIZE_T szLen;
  1380. SIZE_T driveLen;
  1381. LPTSTR newdrives = NULL;
  1382. ASSERT(SzToAppend != NULL);
  1383. ASSERT(drives != NULL);
  1384. szLen = (_tcslen(SzToAppend))*sizeof(_TCHAR);
  1385. if(*drives == NULL) {
  1386. driveLen = sizeof(_TCHAR) ;
  1387. } else {
  1388. driveLen = (_tcslen(*drives) + 1)*sizeof(_TCHAR);
  1389. }
  1390. newdrives = (LPTSTR)malloc(szLen+driveLen);
  1391. if( newdrives == NULL ){
  1392. return (FALSE);
  1393. }
  1394. if(*drives == NULL){
  1395. _tcscpy( newdrives, SzToAppend);
  1396. } else {
  1397. _tcscpy(newdrives, *drives);
  1398. //_tcscat(newdrives, _T(" "));
  1399. _tcscat(newdrives, SzToAppend);
  1400. }
  1401. free( *drives );
  1402. *drives = newdrives;
  1403. return (TRUE);
  1404. }
  1405. #ifdef __cplusplus
  1406. }; //extern "C"
  1407. #endif //#ifdef __cplusplus