Source code of Windows XP (NT5)
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.

1620 lines
42 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 1998 - 1999
  3. Module Name:
  4. RedBook.c
  5. Abstract:
  6. This command line utility adds and removes redbook
  7. for a given drive.
  8. Author:
  9. Henry Gabryjelski (henrygab)
  10. Environment:
  11. user mode only
  12. Notes:
  13. Revision History:
  14. 07-30-98 : Created
  15. --*/
  16. #include <propp.h>
  17. #include <initguid.h>
  18. #include <ntddredb.h>
  19. #define _NTSCSI_USER_MODE_ // prevents all the kernel-mode stuff
  20. #include <scsi.h>
  21. #include "setupapi.h"
  22. #include "resource.h" // strings
  23. #include "storprop.h" // exported function definitions
  24. //
  25. // redefine these to do what i want them to.
  26. // allows the appearance of structured c++ with
  27. // the performance of c.
  28. //
  29. #ifdef TRY
  30. #undef TRY
  31. #endif
  32. #ifdef LEAVE
  33. #undef LEAVE
  34. #endif
  35. #ifdef FINALLY
  36. #undef FINALLY
  37. #endif
  38. #define TRY
  39. #define LEAVE goto __label;
  40. #define FINALLY __label:
  41. //
  42. // just to give out unique errors
  43. //
  44. #define ERROR_REDBOOK_FILTER 0x80ff00f0L
  45. #define ERROR_REDBOOK_PASS_THROUGH 0x80ff00f1L
  46. //
  47. // we initiate a pass-through request for capabilities
  48. // to determine if the drive can support dae
  49. //
  50. #define SPT_MAX_DATA_SIZE 512
  51. typedef struct _SCSI_PASS_THROUGH_WITH_BUFFERS {
  52. SCSI_PASS_THROUGH PassThrough;
  53. ULONG_PTR Reserved; // realign buffers
  54. UCHAR SenseBuffer[32];
  55. UCHAR DataBuffer[SPT_MAX_DATA_SIZE];
  56. } SCSI_PASS_THROUGH_WITH_BUFFERS, *PSCSI_PASS_THROUGH_WITH_BUFFERS;
  57. //
  58. // easier to understand than != NULL
  59. //
  60. #ifndef ARGUMENT_PRESENT
  61. #define ARGUMENT_PRESENT(ArgumentPointer) (\
  62. (CHAR *)(ArgumentPointer) != (CHAR *)(NULL) )
  63. #endif // ARGUMENT_PRESENT
  64. #if DBG
  65. #ifdef UNICODE
  66. #define DbgPrintAllMultiSz DbgPrintAllMultiSzW
  67. #else
  68. #define DbgPrintAllMultiSz DbgPrintAllMultiSzA
  69. #endif // UNICODE
  70. VOID DbgPrintAllMultiSzW(WCHAR *String)
  71. {
  72. ULONG i = 0;
  73. while(*String != UNICODE_NULL) {
  74. DebugPrint((1, "StorProp => MultiSz %3d: %ws\n", i++, String));
  75. while (*String != UNICODE_NULL) {
  76. *String++;
  77. }
  78. *String++; // go past the first NULL
  79. }
  80. }
  81. VOID DbgPrintAllMultiSzA(CHAR *String)
  82. {
  83. ULONG i = 0;
  84. while(*String != ANSI_NULL) {
  85. DebugPrint((1, "StorProp => MultiSz %3d: %ws\n", i++, String));
  86. while (*String != ANSI_NULL) {
  87. *String++;
  88. }
  89. *String++; // go past the first NULL
  90. }
  91. }
  92. #else // !DBG
  93. #define DbgPrintAllMultiSz
  94. #define DbgPrintAllMultiSz
  95. #endif // DBG
  96. ////////////////////////////////////////////////////////////////////////////////
  97. // Local prototypes, not exported anywhere
  98. BOOL IsUserAdmin();
  99. LONG
  100. RedbookpUpperFilterRegDelete(
  101. DEVINST DevInst
  102. );
  103. LONG
  104. RedbookpUpperFilterRegInstall(
  105. DEVINST DevInst
  106. );
  107. BOOLEAN
  108. UtilpIsSingleSzOfMultiSzInMultiSz(
  109. IN LPTSTR FindOneOfThese,
  110. IN LPTSTR WithinThese
  111. );
  112. DWORD
  113. UtilpMultiSzSearchAndDeleteCaseInsensitive(
  114. LPTSTR FindThis,
  115. LPTSTR FindWithin,
  116. DWORD *NewStringLength
  117. );
  118. ////////////////////////////////////////////////////////////////////////////////
  119. ////////////////////////////////////////////////////////////////////////////////
  120. //
  121. // the actual callbacks should do very little, codewise
  122. //
  123. ////////////////////////////////////////////////////////////////////////////////
  124. DWORD
  125. CdromCddaInfo(
  126. IN HDEVINFO HDevInfo,
  127. IN PSP_DEVINFO_DATA DevInfoData,
  128. OUT PREDBOOK_DIGITAL_AUDIO_EXTRACTION_INFO CddaInfo,
  129. IN OUT PULONG BufferSize
  130. )
  131. /*++
  132. Routine Description:
  133. Returns whether the drive is a 'known good' drive.
  134. Returns whether the drive supports CDDA at all.
  135. Returns whether the drive supports accurate CDDA for only some read sizes.
  136. ...
  137. Arguments:
  138. CDDAInfo must point to a pre-allocated buffer for this info
  139. BufferSize will give size of this buffer, to allow for more fields
  140. to be added later in a safe manner.
  141. Return Value:
  142. will return ERROR_SUCCESS/STATUS_SUCCESS (both zero)
  143. Notes:
  144. If cannot open these registry keys, will default to FALSE,
  145. since the caller will most likely not have the ability to enable
  146. redbook anyways.
  147. --*/
  148. {
  149. HKEY enumHandle = INVALID_HANDLE_VALUE;
  150. HKEY subkeyHandle = INVALID_HANDLE_VALUE;
  151. REDBOOK_DIGITAL_AUDIO_EXTRACTION_INFO info;
  152. ULONG i;
  153. DWORD dataType;
  154. DWORD dataSize;
  155. LONG error;
  156. error = ERROR_SUCCESS;
  157. if ((*BufferSize == 0) || (CddaInfo == NULL)) {
  158. *BufferSize = sizeof(REDBOOK_DIGITAL_AUDIO_EXTRACTION_INFO);
  159. return ERROR_INSUFFICIENT_BUFFER;
  160. }
  161. RtlZeroMemory(CddaInfo, *BufferSize);
  162. RtlZeroMemory(&info, sizeof(REDBOOK_DIGITAL_AUDIO_EXTRACTION_INFO));
  163. info.Version = REDBOOK_DIGITAL_AUDIO_EXTRACTION_INFO_VERSION;
  164. TRY {
  165. enumHandle = SetupDiOpenDevRegKey(HDevInfo,
  166. DevInfoData,
  167. DICS_FLAG_GLOBAL,
  168. 0,
  169. DIREG_DEV,
  170. KEY_READ);
  171. if (enumHandle == INVALID_HANDLE_VALUE) {
  172. DebugPrint((1, "StorProp.CddaInfo => unable to open dev key\n"));
  173. error = ERROR_OUT_OF_PAPER;
  174. LEAVE;
  175. }
  176. error = RegOpenKey(enumHandle, TEXT("DigitalAudio"), &subkeyHandle);
  177. if (error != ERROR_SUCCESS) {
  178. DebugPrint((1, "StorProp.CddaInfo => unable to open subkey\n"));
  179. LEAVE;
  180. }
  181. for (i=0; i<3; i++) {
  182. PBYTE buffer;
  183. TCHAR * keyName;
  184. if (i == 0) {
  185. keyName = TEXT("CDDAAccurate");
  186. buffer = (PBYTE)(&info.Accurate);
  187. } else if (i == 1) {
  188. keyName = TEXT("CDDASupported");
  189. buffer = (PBYTE)(&info.Supported);
  190. } else if (i == 2) {
  191. keyName = TEXT("ReadSizesSupported");
  192. buffer = (PBYTE)(&info.AccurateMask0);
  193. #if DBG
  194. } else {
  195. DebugPrint((0, "StorProp.CddaInfo => Looping w/o handling\n"));
  196. DebugBreak();
  197. #endif
  198. }
  199. dataSize = sizeof(DWORD);
  200. error = RegQueryValueEx(subkeyHandle,
  201. keyName,
  202. NULL,
  203. &dataType,
  204. buffer,
  205. &dataSize);
  206. if (error != ERROR_SUCCESS) {
  207. DebugPrint((1, "StorProp.CddaInfo => unable to query %ws %x\n",
  208. keyName, error));
  209. LEAVE;
  210. }
  211. if (dataType != REG_DWORD) {
  212. DebugPrint((1, "StorProp.CddaInfo => %ws wrong data type (%x)\n",
  213. keyName, dataType));
  214. error = ERROR_INVALID_DATA;
  215. LEAVE;
  216. }
  217. DebugPrint((1, "StorProp.CddaInfo => %ws == %x\n",
  218. keyName, *buffer));
  219. }
  220. } FINALLY {
  221. if (subkeyHandle != INVALID_HANDLE_VALUE) {
  222. RegCloseKey(subkeyHandle);
  223. }
  224. if (enumHandle != INVALID_HANDLE_VALUE) {
  225. RegCloseKey(enumHandle);
  226. }
  227. if (error == ERROR_SUCCESS) {
  228. //
  229. // everything succeeded -- copy only the amount they requested
  230. // and don't care about it being aligned on any particular buffer size.
  231. // this is the only other place the user buffer should be modified
  232. //
  233. if (*BufferSize > sizeof(REDBOOK_DIGITAL_AUDIO_EXTRACTION_INFO)) {
  234. *BufferSize = sizeof(REDBOOK_DIGITAL_AUDIO_EXTRACTION_INFO);
  235. }
  236. DebugPrint((2, "StorProp.CddaInfo => everything worked\n"));
  237. RtlCopyMemory(CddaInfo, &info, *BufferSize);
  238. } else {
  239. DebugPrint((2, "StorProp.CddaInfo => something failed\n"));
  240. *BufferSize = 0;
  241. }
  242. }
  243. return error;
  244. }
  245. BOOL
  246. CdromKnownGoodDigitalPlayback(
  247. IN HDEVINFO HDevInfo,
  248. IN PSP_DEVINFO_DATA DevInfoData
  249. )
  250. /*++
  251. Routine Description:
  252. Returns whether this drive is a 'known good' drive.
  253. Arguments:
  254. Return Value:
  255. Notes:
  256. default to FALSE, since if fails, caller probably does not
  257. have ability to enable redbook anyways.
  258. this routine is outdated -- callers should call CdromCddaInfo()
  259. directly for more exact information.
  260. --*/
  261. {
  262. REDBOOK_DIGITAL_AUDIO_EXTRACTION_INFO CddaInfo;
  263. ULONG bufferSize;
  264. DWORD error;
  265. bufferSize = sizeof(REDBOOK_DIGITAL_AUDIO_EXTRACTION_INFO);
  266. #if DBG
  267. DbgPrint("\n\nOutdated call to CdromKnownGoodDigitalPlayback(), "
  268. "should be calling CdromCddaInfo()\n\n");
  269. #endif // DBG
  270. error = CdromCddaInfo(HDevInfo, DevInfoData, &CddaInfo, &bufferSize);
  271. if (error != ERROR_SUCCESS) {
  272. return FALSE;
  273. }
  274. if (bufferSize <= sizeof(REDBOOK_DIGITAL_AUDIO_EXTRACTION_INFO)) {
  275. return FALSE;
  276. }
  277. if (CddaInfo.Accurate) {
  278. return TRUE;
  279. }
  280. if (CddaInfo.Supported && CddaInfo.AccurateMask0) {
  281. return TRUE;
  282. }
  283. return FALSE;
  284. }
  285. LONG
  286. CdromEnableDigitalPlayback(
  287. IN HDEVINFO HDevInfo,
  288. IN PSP_DEVINFO_DATA DevInfoData,
  289. IN BOOLEAN ForceUnknown
  290. )
  291. /*++
  292. Routine Description:
  293. Enables redbook
  294. 1) add redbook to filter list (if not there)
  295. 2) if not on stack (via test of guid) re-start stack
  296. 3) if still not on stack, error
  297. 4) set wmi guid item enabled
  298. Arguments:
  299. DevInfo - the device to enable it on
  300. DevInfoData -
  301. ForceUnknown - will set a popup if not a known good drive and this is false
  302. Return Value:
  303. ERROR_XXX value
  304. --*/
  305. {
  306. LONG status;
  307. SP_DEVINSTALL_PARAMS devInstallParameters;
  308. REDBOOK_DIGITAL_AUDIO_EXTRACTION_INFO digitalInfo;
  309. ULONG digitalInfoSize;
  310. BOOLEAN enableIt;
  311. //
  312. // restrict to administrator ???
  313. //
  314. if (!IsUserAdmin()) {
  315. DebugPrint((1, "StorProp.Enable => you need to be administrator to "
  316. "enable redbook\n"));
  317. return ERROR_ACCESS_DENIED;
  318. }
  319. digitalInfoSize = sizeof(REDBOOK_DIGITAL_AUDIO_EXTRACTION_INFO);
  320. RtlZeroMemory(&digitalInfo, digitalInfoSize);
  321. status = CdromCddaInfo(HDevInfo, DevInfoData,
  322. &digitalInfo, &digitalInfoSize);
  323. if (status != ERROR_SUCCESS) {
  324. DebugPrint((1, "StorProp.Enable => not success getting info %x\n",
  325. status));
  326. //
  327. // fake some info
  328. //
  329. digitalInfo.Version = REDBOOK_DIGITAL_AUDIO_EXTRACTION_INFO_VERSION;
  330. digitalInfo.Accurate = 0;
  331. digitalInfo.Supported = 1;
  332. digitalInfo.AccurateMask0 = -1;
  333. digitalInfoSize = sizeof(REDBOOK_DIGITAL_AUDIO_EXTRACTION_INFO);
  334. }
  335. if (digitalInfoSize < sizeof(REDBOOK_DIGITAL_AUDIO_EXTRACTION_INFO)) {
  336. DebugPrint((3, "StorProp.Enable => returned %x bytes? (not %x)\n",
  337. digitalInfoSize,
  338. sizeof(REDBOOK_DIGITAL_AUDIO_EXTRACTION_INFO)
  339. ));
  340. return ERROR_ACCESS_DENIED;
  341. }
  342. if (!digitalInfo.Supported) {
  343. DebugPrint((1, "StorProp.Enable => This drive will never "
  344. "support redbook\n"));
  345. // return ERROR_INVALID_FUNCTION; // log an error here?
  346. }
  347. //
  348. // if it's not accurate AND we don't have the compensating info AND
  349. // they didn't force it to install, then popup a dialog.
  350. //
  351. if (!(digitalInfo.Accurate) &&
  352. !(digitalInfo.AccurateMask0) &&
  353. !(ForceUnknown)
  354. ) {
  355. BOOLEAN okToProceed = FALSE;
  356. TCHAR buffer[MAX_PATH+1];
  357. TCHAR bufferTitle[MAX_PATH+1];
  358. buffer[0] = '\0';
  359. bufferTitle[0] = '\0';
  360. buffer[MAX_PATH] = '\0';
  361. bufferTitle[MAX_PATH] = '\0';
  362. //
  363. // not forced, and not known good. pop up a box asking permission
  364. //
  365. LoadString(ModuleInstance,
  366. REDBOOK_UNKNOWN_DRIVE_CONFIRM,
  367. buffer,
  368. MAX_PATH);
  369. LoadString(ModuleInstance,
  370. REDBOOK_UNKNOWN_DRIVE_CONFIRM_TITLE,
  371. bufferTitle,
  372. MAX_PATH);
  373. if (MessageBox(GetDesktopWindow(),
  374. buffer,
  375. bufferTitle,
  376. MB_YESNO | // ok and cancel buttons
  377. MB_ICONQUESTION | // question icon
  378. MB_DEFBUTTON2 | // cancel is default
  379. MB_SYSTEMMODAL // must respond to this box
  380. ) == IDYES) {
  381. okToProceed = TRUE;
  382. }
  383. if (!okToProceed) {
  384. DebugPrint((3, "StorProp.Enable => User did not force installation "
  385. "on unknown drive\n"));
  386. return ERROR_REDBOOK_FILTER;
  387. }
  388. }
  389. //
  390. // ensure it is in the filter list
  391. //
  392. RedbookpUpperFilterRegInstall(DevInfoData->DevInst);
  393. //
  394. // restart the device to load redbook
  395. //
  396. if (!UtilpRestartDevice(HDevInfo, DevInfoData)) {
  397. DebugPrint((1, "StorProp.Enable => Restart failed\n"));
  398. } else {
  399. DebugPrint((1, "StorProp.Enable => Restart succeeded\n"));
  400. }
  401. return ERROR_SUCCESS;
  402. }
  403. LONG
  404. CdromDisableDigitalPlayback(
  405. IN HDEVINFO HDevInfo,
  406. IN PSP_DEVINFO_DATA DevInfoData
  407. )
  408. {
  409. //
  410. // restrict to administrator
  411. //
  412. if (!IsUserAdmin()) {
  413. DebugPrint((1, "StorProp.Disable => User is not administrator\n"));
  414. return ERROR_ACCESS_DENIED;
  415. }
  416. //
  417. // delete it from the filter list regardless
  418. //
  419. RedbookpUpperFilterRegDelete(DevInfoData->DevInst);
  420. //
  421. // restart the device to remove redbook from the stack
  422. //
  423. if (!UtilpRestartDevice(HDevInfo, DevInfoData)) {
  424. DebugPrint((1, "StorProp.Disable => Restart failed\n"));
  425. } else {
  426. DebugPrint((1, "StorProp.Disable => restart succeeded\n"));
  427. }
  428. return ERROR_SUCCESS;
  429. }
  430. LONG
  431. CdromIsDigitalPlaybackEnabled(
  432. IN HDEVINFO HDevInfo,
  433. IN PSP_DEVINFO_DATA DevInfoData,
  434. OUT PBOOLEAN Enabled
  435. )
  436. {
  437. //
  438. // check if it's on the filters list.
  439. //
  440. CONFIGRET cr = CR_DEFAULT;
  441. TCHAR *buffer = NULL;
  442. DWORD size = 0;
  443. *Enabled = FALSE;
  444. TRY {
  445. cr = CM_Get_DevNode_Registry_Property(DevInfoData->DevInst,
  446. CM_DRP_UPPERFILTERS,
  447. NULL,
  448. NULL,
  449. &size,
  450. 0);
  451. if (cr == CR_NO_SUCH_VALUE) {
  452. DebugPrint((1, "StorProp.IsEnabled => no upper filters\n"));
  453. LEAVE;
  454. }
  455. if (cr != CR_BUFFER_SMALL) {
  456. DebugPrint((1, "StorProp.IsEnabled => Other error: %x\n", cr));
  457. LEAVE;
  458. }
  459. buffer = LocalAlloc(LPTR, size);
  460. if (buffer == NULL) {
  461. DebugPrint((1, "StorProp.IsEnabled => Not enough mem\n"));
  462. cr = CR_OUT_OF_MEMORY;
  463. LEAVE;
  464. }
  465. cr = CM_Get_DevNode_Registry_Property(DevInfoData->DevInst,
  466. CM_DRP_UPPERFILTERS,
  467. NULL,
  468. buffer,
  469. &size,
  470. 0);
  471. if (cr != CR_SUCCESS) {
  472. DebugPrint((1, "StorProp.IsEnabled => Cannot get upper filter %x\n",
  473. cr));
  474. LEAVE;
  475. }
  476. if (UtilpIsSingleSzOfMultiSzInMultiSz(TEXT("redbook\0"), buffer)) {
  477. DebugPrint((1, "StorProp.IsEnabled => redbook enabled\n"));
  478. //
  479. // further verification needed? maybe a wmi query?
  480. //
  481. *Enabled = TRUE;
  482. cr = CR_SUCCESS;
  483. } else {
  484. DebugPrint((1, "StorProp.IsEnabled => redbook NOT enabled\n"));
  485. }
  486. } FINALLY {
  487. if (buffer != NULL) {
  488. LocalFree(buffer);
  489. }
  490. }
  491. if (cr == CR_SUCCESS) {
  492. DebugPrint((1, "StorProp.IsEnabled => returning %x\n", *Enabled));
  493. return CR_SUCCESS;
  494. }
  495. //
  496. // if we couldn't query the filters key, try WMI...
  497. //
  498. {
  499. WMIHANDLE wmiHandle = INVALID_HANDLE_VALUE;
  500. HANDLE hEnum = INVALID_HANDLE_VALUE;
  501. PREDBOOK_WMI_STD_DATA wmiData = NULL;
  502. PWNODE_SINGLE_INSTANCE wmiInstance = NULL;
  503. DWORD dataSize = 0;
  504. DWORD status = ERROR_INVALID_FUNCTION;
  505. TCHAR *instanceName;
  506. DWORD nameChars;
  507. TCHAR buffer[2];
  508. instanceName = NULL;
  509. DebugPrint((1, "StorProp.IsEnabled => Using alternate method\n"));
  510. hEnum = UtilpGetDeviceHandle(HDevInfo, DevInfoData,
  511. (LPGUID) &CdRomClassGuid,
  512. GENERIC_READ | GENERIC_WRITE);
  513. if (hEnum == INVALID_HANDLE_VALUE) {
  514. DebugPrint((3, "StorProp.IsEnabled => Busy drive\n"));
  515. goto FinalExit;
  516. }
  517. //
  518. // get a handle to redbook's wmi block by passing its guid in
  519. //
  520. status = WmiOpenBlock((LPGUID) &MSRedbook_DriverInformationGuid,
  521. 0,
  522. &wmiHandle);
  523. if (status != ERROR_SUCCESS) {
  524. DebugPrint((3, "StorProp.IsEnabled => WmiOpenBlock "
  525. "returned %x\n", status));
  526. goto FinalExit;
  527. }
  528. //
  529. // on the first try, figure out the length of the wmi name
  530. //
  531. nameChars = 2;
  532. status = WmiFileHandleToInstanceName(wmiHandle,
  533. hEnum,
  534. &nameChars,
  535. buffer);
  536. if (status != ERROR_SUCCESS && status != ERROR_INSUFFICIENT_BUFFER) {
  537. DebugPrint((3, "StorProp.IsEnabled => Can't get instance "
  538. "name %x\n", status));
  539. status = ERROR_WMI_GUID_NOT_FOUND;
  540. goto FinalExit;
  541. }
  542. //
  543. // allocate just enough memory for this name
  544. //
  545. instanceName = (TCHAR *)LocalAlloc(LPTR, nameChars*sizeof(TCHAR));
  546. if (instanceName == NULL) {
  547. DebugPrint((3, "StorProp.IsEnabled => insufficient memory\n"));
  548. status = ERROR_NOT_ENOUGH_MEMORY;
  549. goto FinalExit;
  550. }
  551. //
  552. // get the wmi instance name of the device associated with this handle
  553. //
  554. status = WmiFileHandleToInstanceName(wmiHandle,
  555. hEnum,
  556. &nameChars,
  557. instanceName);
  558. if (status != ERROR_SUCCESS) {
  559. DebugPrint((3, "StorProp.IsEnabled => Can't get instance "
  560. " name2 %x\n", status));
  561. status = ERROR_WMI_GUID_NOT_FOUND;
  562. goto FinalExit;
  563. }
  564. //
  565. // query the data block -- returns buffer size needed in BYTES
  566. //
  567. status = WmiQuerySingleInstance(wmiHandle,
  568. instanceName,
  569. &dataSize,
  570. NULL);
  571. if (dataSize == 0) {
  572. DebugPrint((3, "StorProp.IsEnabled => No data for "
  573. "redbook instance? %x\n", status));
  574. goto FinalExit;
  575. }
  576. wmiInstance = LocalAlloc(LPTR, dataSize);
  577. if (wmiInstance == NULL) {
  578. DebugPrint((3, "StorProp.IsEnabled !! No Memory for wmiNode\n"));
  579. status = ERROR_NOT_ENOUGH_MEMORY;
  580. goto FinalExit;
  581. }
  582. status = WmiQuerySingleInstance(wmiHandle,
  583. instanceName,
  584. &dataSize,
  585. wmiInstance);
  586. if (status == ERROR_SUCCESS) {
  587. DebugPrint((3, "StorProp.IsEnabled => Redbook is currently on the "
  588. "device stack\n"));
  589. *Enabled = TRUE;
  590. } else {
  591. DebugPrint((3, "StorProp.IsEnabled => Redbook query failed %x\n",
  592. status));
  593. }
  594. goto FinalExit;
  595. ////////////////////////////////////////////////////////////////////////
  596. // Deallocate all resources here
  597. FinalExit:
  598. if (wmiInstance != NULL) { LocalFree(wmiInstance); }
  599. if (hEnum != INVALID_HANDLE_VALUE) { CloseHandle(hEnum); }
  600. if (wmiHandle != INVALID_HANDLE_VALUE) { WmiCloseBlock(wmiHandle); }
  601. if (instanceName != NULL) { LocalFree(instanceName); }
  602. }
  603. DebugPrint((1, "StorProp.IsEnabled => returning(2) %x\n", *Enabled));
  604. return cr;
  605. }
  606. ////////////////////////////////////////////////////////////////////////////////
  607. //
  608. // The support routines do all the work....
  609. //
  610. ////////////////////////////////////////////////////////////////////////////////
  611. HANDLE
  612. UtilpGetDeviceHandle(
  613. HDEVINFO DevInfo,
  614. PSP_DEVINFO_DATA DevInfoData,
  615. LPGUID ClassGuid,
  616. DWORD DesiredAccess
  617. )
  618. /*++
  619. Routine Description:
  620. gets a handle for a device
  621. Arguments:
  622. the name of the device to open
  623. Return Value:
  624. handle to the device opened, which must be later closed by the caller.
  625. Notes:
  626. this function is also in the class installer (syssetup.dll)
  627. so please propogate fixes there as well
  628. --*/
  629. {
  630. BOOL status;
  631. ULONG i;
  632. HANDLE fileHandle = INVALID_HANDLE_VALUE;
  633. SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
  634. HDEVINFO devInfoWithInterface = NULL;
  635. PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData = NULL;
  636. PTSTR deviceInstanceId = NULL;
  637. ULONG deviceInterfaceDetailDataSize;
  638. ULONG deviceInstanceIdSize;
  639. TRY {
  640. //
  641. // get the ID for this device
  642. //
  643. for (i=deviceInstanceIdSize=0; i<2; i++) {
  644. if (deviceInstanceIdSize != 0) {
  645. //
  646. // deviceInstanceIdSize is returned in CHARACTERS
  647. // by SetupDiGetDeviceInstanceId(), so must allocate
  648. // returned size * sizeof(TCHAR)
  649. //
  650. deviceInstanceId =
  651. LocalAlloc(LPTR, deviceInstanceIdSize * sizeof(TCHAR));
  652. if (deviceInstanceId == NULL) {
  653. DebugPrint((1, "StorProp.GetDeviceHandle => Unable to "
  654. "allocate for deviceInstanceId\n"));
  655. LEAVE;
  656. }
  657. }
  658. status = SetupDiGetDeviceInstanceId(DevInfo,
  659. DevInfoData,
  660. deviceInstanceId,
  661. deviceInstanceIdSize,
  662. &deviceInstanceIdSize
  663. );
  664. }
  665. if (!status) {
  666. DebugPrint((1, "StorProp.GetDeviceHandle => Unable to get "
  667. "Device IDs\n"));
  668. LEAVE;
  669. }
  670. //
  671. // Get all the cdroms in the system
  672. //
  673. devInfoWithInterface = SetupDiGetClassDevs(ClassGuid,
  674. deviceInstanceId,
  675. NULL,
  676. DIGCF_DEVICEINTERFACE
  677. );
  678. if (devInfoWithInterface == NULL) {
  679. DebugPrint((1, "StorProp.GetDeviceHandle => Unable to get "
  680. "list of CdRom's in system\n"));
  681. LEAVE;
  682. }
  683. memset(&deviceInterfaceData, 0, sizeof(SP_DEVICE_INTERFACE_DATA));
  684. deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
  685. status = SetupDiEnumDeviceInterfaces(devInfoWithInterface,
  686. NULL,
  687. ClassGuid,
  688. 0,
  689. &deviceInterfaceData
  690. );
  691. if (!status) {
  692. DebugPrint((1, "StorProp.GetDeviceHandle => Unable to get "
  693. "SP_DEVICE_INTERFACE_DATA\n"));
  694. LEAVE;
  695. }
  696. for (i=deviceInterfaceDetailDataSize=0; i<2; i++) {
  697. if (deviceInterfaceDetailDataSize != 0) {
  698. //
  699. // deviceInterfaceDetailDataSize is returned in BYTES
  700. // by SetupDiGetDeviceInstanceId(), so must allocate
  701. // returned size only
  702. //
  703. deviceInterfaceDetailData =
  704. LocalAlloc (LPTR, deviceInterfaceDetailDataSize);
  705. if (deviceInterfaceDetailData == NULL) {
  706. DebugPrint((1, "StorProp.GetDeviceHandle => Unable to "
  707. "allocate for deviceInterfaceDetailData\n"));
  708. LEAVE;
  709. }
  710. deviceInterfaceDetailData->cbSize =
  711. sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
  712. }
  713. status = SetupDiGetDeviceInterfaceDetail(devInfoWithInterface,
  714. &deviceInterfaceData,
  715. deviceInterfaceDetailData,
  716. deviceInterfaceDetailDataSize,
  717. &deviceInterfaceDetailDataSize,
  718. NULL);
  719. }
  720. if (!status) {
  721. DebugPrint((1, "StorProp.GetDeviceHandle => Unable to get "
  722. "DeviceInterfaceDetail\n"));
  723. LEAVE;
  724. }
  725. if (deviceInterfaceDetailDataSize <=
  726. FIELD_OFFSET(SP_DEVICE_INTERFACE_DETAIL_DATA, DevicePath)) {
  727. DebugPrint((1, "StorProp.GetDeviceHandle => No device path\n"));
  728. status = ERROR_PATH_NOT_FOUND;
  729. LEAVE;
  730. }
  731. //
  732. // no need to memcpy it, just use the path returned to us.
  733. //
  734. fileHandle = CreateFile(deviceInterfaceDetailData->DevicePath,
  735. DesiredAccess,
  736. FILE_SHARE_READ | FILE_SHARE_WRITE,
  737. NULL,
  738. OPEN_EXISTING,
  739. 0,
  740. NULL);
  741. if (fileHandle == INVALID_HANDLE_VALUE) {
  742. DebugPrint((1, "StorProp.GetDeviceHandle => Final CreateFile() "
  743. "failed\n"));
  744. LEAVE;
  745. }
  746. DebugPrint((3, "StorProp.GetDeviceHandle => handle %x opened\n",
  747. fileHandle));
  748. } FINALLY {
  749. if (devInfoWithInterface != NULL) {
  750. SetupDiDestroyDeviceInfoList(devInfoWithInterface);
  751. }
  752. if (deviceInterfaceDetailData != NULL) {
  753. LocalFree (deviceInterfaceDetailData);
  754. }
  755. }
  756. return fileHandle;
  757. }
  758. BOOLEAN
  759. UtilpRestartDevice(
  760. IN HDEVINFO HDevInfo,
  761. IN PSP_DEVINFO_DATA DevInfoData
  762. )
  763. /*++
  764. Routine Description:
  765. Arguments:
  766. Return Value:
  767. --*/
  768. {
  769. SP_PROPCHANGE_PARAMS parameters;
  770. SP_DEVINSTALL_PARAMS installParameters;
  771. BOOLEAN succeeded = FALSE;
  772. RtlZeroMemory(&parameters, sizeof(SP_PROPCHANGE_PARAMS));
  773. RtlZeroMemory(&installParameters, sizeof(SP_DEVINSTALL_PARAMS));
  774. //
  775. // Initialize the SP_CLASSINSTALL_HEADER struct at the beginning of the
  776. // SP_PROPCHANGE_PARAMS struct. this allows SetupDiSetClassInstallParams
  777. // to work.
  778. //
  779. parameters.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
  780. parameters.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
  781. //
  782. // Initialize SP_PROPCHANGE_PARAMS such that the device will be STOPPED
  783. //
  784. parameters.Scope = DICS_FLAG_CONFIGSPECIFIC;
  785. parameters.HwProfile = 0; // current profile
  786. //
  787. // prepare for the call to SetupDiCallClassInstaller (to stop the device)
  788. //
  789. parameters.StateChange = DICS_STOP;
  790. if (!SetupDiSetClassInstallParams(HDevInfo,
  791. DevInfoData,
  792. (PSP_CLASSINSTALL_HEADER)&parameters,
  793. sizeof(SP_PROPCHANGE_PARAMS))) {
  794. DebugPrint((1, "UtilpRestartDevice => Couldn't stop the device (%x)\n",
  795. GetLastError()));
  796. goto FinishRestart;
  797. }
  798. //
  799. // actually stop the device
  800. //
  801. if (!SetupDiCallClassInstaller(DIF_PROPERTYCHANGE,
  802. HDevInfo,
  803. DevInfoData)) {
  804. DebugPrint((1, "UtilpRestartDevice => call to class installer "
  805. "(STOP) failed (%x)\n", GetLastError()));
  806. goto FinishRestart;
  807. }
  808. //
  809. // prepare for the call to SetupDiCallClassInstaller (to start the device)
  810. //
  811. parameters.StateChange = DICS_START;
  812. if (!SetupDiSetClassInstallParams(HDevInfo,
  813. DevInfoData,
  814. (PSP_CLASSINSTALL_HEADER)&parameters,
  815. sizeof(SP_PROPCHANGE_PARAMS))) {
  816. DebugPrint((1, "UtilpRestartDevice => Couldn't stop the device (%x)\n",
  817. GetLastError()));
  818. goto FinishRestart;
  819. }
  820. //
  821. // actually start the device
  822. //
  823. if (!SetupDiCallClassInstaller(DIF_PROPERTYCHANGE,
  824. HDevInfo,
  825. DevInfoData)) {
  826. DebugPrint((1, "UtilpRestartDevice => call to class installer "
  827. "(STOP) failed (%x)\n", GetLastError()));
  828. goto FinishRestart;
  829. }
  830. succeeded = TRUE;
  831. FinishRestart:
  832. //
  833. // this call will succeed, but we should still check the status
  834. //
  835. if (!SetupDiGetDeviceInstallParams(HDevInfo,
  836. DevInfoData,
  837. &installParameters)) {
  838. DebugPrint((1, "UtilpRestartDevice => Couldn't get the device install "
  839. "paramters (%x)\n", GetLastError()));
  840. return FALSE;
  841. }
  842. if (TEST_FLAG(installParameters.Flags, DI_NEEDREBOOT)) {
  843. DebugPrint((1, "UtilpRestartDevice => Device needs a reboot.\n"));
  844. return FALSE;
  845. }
  846. if (TEST_FLAG(installParameters.Flags, DI_NEEDRESTART)) {
  847. DebugPrint((1, "UtilpRestartDevice => Device needs a restart(!).\n"));
  848. return FALSE;
  849. }
  850. if (succeeded) {
  851. DebugPrint((1, "UtilpRestartDevice => Device successfully stopped and "
  852. "restarted.\n"));
  853. return TRUE;
  854. }
  855. SET_FLAG(installParameters.Flags, DI_NEEDRESTART);
  856. DebugPrint((1, "UtilpRestartDevice => Device needs to be restarted.\n"));
  857. SetupDiSetDeviceInstallParams(HDevInfo, DevInfoData, &installParameters);
  858. return FALSE;
  859. }
  860. LONG
  861. RedbookpUpperFilterRegDelete(
  862. DEVINST DevInst
  863. )
  864. /*++
  865. Routine Description:
  866. Opens the UpperFilters registry key for this device instance
  867. and deletes all instances of redbook.
  868. Arguments:
  869. the device instance
  870. Return Value:
  871. ERROR_SUCCESS if deleted
  872. ERROR_SUCCESS if it wasn't there in the first place
  873. --*/
  874. {
  875. CONFIGRET cr = CR_SUCCESS;
  876. TCHAR *buffer;
  877. DWORD size;
  878. buffer = NULL;
  879. size = 0;
  880. TRY {
  881. size = 0;
  882. //
  883. // CM_Get_DevNode_Registry_Property() returns the number
  884. // of actual bytes required, not number of chars
  885. //
  886. cr = CM_Get_DevNode_Registry_Property(DevInst,
  887. CM_DRP_UPPERFILTERS,
  888. NULL,
  889. NULL,
  890. &size,
  891. 0);
  892. if (cr == CR_NO_SUCH_VALUE) {
  893. DebugPrint((1, "StorProp.RegDelete => Redbook not on filter list\n"));
  894. LEAVE;
  895. }
  896. if (cr != CR_BUFFER_SMALL) {
  897. DebugPrint((1, "StorProp.RegDelete => Other error: %x\n", cr));
  898. LEAVE;
  899. }
  900. buffer = LocalAlloc(LPTR, size);
  901. if (buffer == NULL) {
  902. DebugPrint((1, "StorProp.RegDelete => Not enough mem\n"));
  903. LEAVE;
  904. }
  905. cr = CM_Get_DevNode_Registry_Property(DevInst,
  906. CM_DRP_UPPERFILTERS,
  907. NULL,
  908. buffer,
  909. &size,
  910. 0);
  911. if (cr != CR_SUCCESS) {
  912. DebugPrint((1, "StorProp.RegDelete => Cannot get upper filter %x\n",
  913. cr));
  914. LEAVE;
  915. }
  916. //
  917. // DELETE: Check and see if redbook is already there
  918. //
  919. DebugPrint((1, "StorProp.RegDelete => Registry currently contains %x "
  920. "bytes in the multi-sz\n", size));
  921. if (UtilpMultiSzSearchAndDeleteCaseInsensitive(TEXT("redbook"),
  922. buffer,
  923. &size)) {
  924. DebugPrint((1,"StorProp.RegDelete => Attempting to remove redbook "
  925. "as an upper filter, %x bytes remain at %p\n",
  926. size, buffer));
  927. if (size == 0) {
  928. cr = CM_Set_DevNode_Registry_Property(DevInst,
  929. CM_DRP_UPPERFILTERS,
  930. NULL,
  931. size,
  932. 0);
  933. } else {
  934. cr = CM_Set_DevNode_Registry_Property(DevInst,
  935. CM_DRP_UPPERFILTERS,
  936. buffer,
  937. size,
  938. 0);
  939. }
  940. if (cr != CR_SUCCESS) {
  941. DebugPrint((1,"StorProp.RegDelete !! Could not access upper "
  942. "filters %x\n", cr));
  943. LEAVE;
  944. }
  945. } else {
  946. //
  947. // optimization -- why write an unmodified value?
  948. //
  949. DebugPrint((1,"StorProp.RegDelete => Redbook is not installed as "
  950. "an upper filter\n"));
  951. }
  952. //
  953. // the new list is now in place
  954. //
  955. } FINALLY {
  956. if (buffer != NULL) {
  957. LocalFree(buffer);
  958. buffer = NULL;
  959. }
  960. }
  961. return ERROR_SUCCESS;
  962. }
  963. LONG
  964. RedbookpUpperFilterRegInstall(
  965. DEVINST DevInst
  966. )
  967. /*++
  968. Routine Description:
  969. Opens the UpperFilters registry key for this device instance
  970. and adds one instance of redbook as an upper filter
  971. Arguments:
  972. the device instance
  973. Return Value:
  974. ERROR_SUCCESS if installed
  975. ERROR_SUCCESS if was already there
  976. --*/
  977. {
  978. HKEY hDeviceKey;
  979. CONFIGRET cr = CR_SUCCESS;
  980. TCHAR *buffer = NULL;
  981. TCHAR *currentLocation;
  982. DWORD length = 0;
  983. DWORD newLength = 0;
  984. //
  985. // Open the device key for the source device instance
  986. // length is returned in BYTES by CM_Get_DevNode_Registry_Property()
  987. //
  988. cr = CM_Get_DevNode_Registry_Property(DevInst,
  989. CM_DRP_UPPERFILTERS,
  990. NULL,
  991. buffer,
  992. &length,
  993. 0);
  994. //
  995. // allocate only the memory required for this
  996. //
  997. newLength = _tcslen(TEXT("redbook")) + 1;
  998. newLength *= sizeof(TCHAR);
  999. if (length == 0) {
  1000. newLength += sizeof(TCHAR); // extra NULL to double-NULL terminate string
  1001. } else {
  1002. newLength += length;
  1003. }
  1004. buffer = LocalAlloc(LPTR, newLength);
  1005. if (buffer == NULL) {
  1006. return ERROR_REDBOOK_FILTER;
  1007. }
  1008. if (cr == CR_NO_SUCH_VALUE) {
  1009. assert( length == 0 );
  1010. } else {
  1011. DWORD temp;
  1012. cr = CM_Get_DevNode_Registry_Property(DevInst,
  1013. CM_DRP_UPPERFILTERS,
  1014. NULL,
  1015. buffer,
  1016. &length,
  1017. 0);
  1018. if (cr != CR_SUCCESS) {
  1019. //
  1020. // don't want to wipe existing values if this fails
  1021. //
  1022. LocalFree(buffer);
  1023. return ERROR_REDBOOK_FILTER;
  1024. }
  1025. //
  1026. // succeed if already is on the stack.
  1027. // length will contain the number of bytes of
  1028. // usable data at this point
  1029. //
  1030. DbgPrintAllMultiSz(buffer);
  1031. //
  1032. // if already on the stack (send Multi_Sz "redbook\0"), return
  1033. //
  1034. if (UtilpIsSingleSzOfMultiSzInMultiSz(TEXT("redbook\0"), buffer)) {
  1035. LocalFree(buffer);
  1036. return ERROR_SUCCESS;
  1037. }
  1038. //
  1039. // get the length of the sz's already as upper filters
  1040. // this may be different than above, as above may have removed
  1041. // multiple instances of redbook from the multisz
  1042. //
  1043. temp = 0;
  1044. while ( _tcscmp(buffer+temp, TEXT("")) != 0 ) {
  1045. //
  1046. // don't forget your pointer arithmetic
  1047. //
  1048. temp += (_tcslen(buffer+temp) + 1); // this string
  1049. }
  1050. //
  1051. // assert the size of the upper filters has not changed
  1052. // between the first and second query
  1053. //
  1054. if (temp * sizeof(TCHAR) != length) {
  1055. DebugPrint((1, "StorProp.RegInstall !! Size of upper filters has "
  1056. "changed since last query -- aborting install\n"));
  1057. LocalFree(buffer);
  1058. return ERROR_REDBOOK_FILTER;
  1059. }
  1060. //
  1061. // move all existing text ahead by N bytes, where N is
  1062. // the sizeof the new filter to add
  1063. //
  1064. temp = _tcslen(TEXT("redbook")) + 1;
  1065. RtlMoveMemory(buffer + temp, // destination (pointer arithmetic)
  1066. buffer, // source
  1067. length); // number of bytes of old filters
  1068. }
  1069. //
  1070. // now have the old values in buffer (offset enough to insert redbook)
  1071. // newLength specifies total memory we have alloc'd
  1072. // length is size of existing registry entries retrieved OR
  1073. // sizeof(TCHAR) if none were in the original registry
  1074. //
  1075. //
  1076. // now fill that new space with the new filter addition
  1077. // this includes the NULL for the filter being added
  1078. //
  1079. _tcscpy(buffer, TEXT("redbook"));
  1080. //
  1081. // set the new multisz as the new filter
  1082. //
  1083. cr = CM_Set_DevNode_Registry_Property(DevInst,
  1084. CM_DRP_UPPERFILTERS,
  1085. buffer,
  1086. newLength,
  1087. 0);
  1088. if (cr != CR_SUCCESS) {
  1089. DebugPrint((1, "StorProp.RegInstall !! Could not edit Redbook's "
  1090. "filter status %x\n", cr));
  1091. LocalFree(buffer);
  1092. return ERROR_REDBOOK_FILTER;
  1093. } else {
  1094. DebugPrint((1, "StorProp.RegInstall => Redbook now installed as "
  1095. "an upper filter\n"));
  1096. LocalFree(buffer);
  1097. return ERROR_SUCCESS;
  1098. }
  1099. }
  1100. BOOLEAN
  1101. UtilpIsSingleSzOfMultiSzInMultiSz(
  1102. IN LPTSTR FindOneOfThese,
  1103. IN LPTSTR WithinThese
  1104. )
  1105. /*++
  1106. Routine Description:
  1107. Deletes all instances of a string from within a multi-sz.
  1108. automagically operates on either unicode or ansi or ??
  1109. Arguments:
  1110. FindOneOfThese - multisz to search with
  1111. WithinThese - multisz to search in
  1112. Return Value:
  1113. 1/20 of one cent, or the number of strings deleted, rounded down.
  1114. Notes:
  1115. expect small inputs, so n*m is acceptable run time.
  1116. --*/
  1117. {
  1118. LPTSTR searchFor;
  1119. LPTSTR within;
  1120. //
  1121. // loop through all strings in FindOneOfThese
  1122. //
  1123. searchFor = FindOneOfThese;
  1124. while ( _tcscmp(searchFor, TEXT("\0")) ) {
  1125. //
  1126. // loop through all strings in WithinThese
  1127. //
  1128. within = WithinThese;
  1129. while ( _tcscmp(within, TEXT("\0"))) {
  1130. //
  1131. // if the are equal, return TRUE
  1132. //
  1133. if ( !_tcscmp(searchFor, within) ) {
  1134. return TRUE;
  1135. }
  1136. within += _tcslen(within) + 1;
  1137. } // end of WithinThese loop
  1138. searchFor += _tcslen(searchFor) + 1;
  1139. } // end of FindOneOfThese loop
  1140. return FALSE;
  1141. }
  1142. DWORD
  1143. UtilpMultiSzSearchAndDeleteCaseInsensitive(
  1144. LPTSTR FindThis,
  1145. LPTSTR FindWithin,
  1146. DWORD *NewStringLength
  1147. )
  1148. /*++
  1149. Routine Description:
  1150. Deletes all instances of a string from within a multi-sz.
  1151. automagically operates on either unicode or ansi or ??
  1152. Arguments:
  1153. NewStringLength is in BYTES, not number of chars
  1154. Return Value:
  1155. 1/20 of one cent, or the number of strings deleted, rounded down.
  1156. --*/
  1157. {
  1158. LPTSTR search;
  1159. DWORD charOffset;
  1160. DWORD instancesDeleted;
  1161. if ((*NewStringLength) % sizeof(TCHAR)) {
  1162. assert(!"String must be in bytes, does not divide by sizeof(TCHAR)\n");
  1163. return 0;
  1164. }
  1165. if ((*NewStringLength) < sizeof(TCHAR)*2) {
  1166. assert(!"String must be multi-sz, which requires at least two chars\n");
  1167. return 0;
  1168. }
  1169. charOffset = 0;
  1170. instancesDeleted = 0;
  1171. search = FindWithin;
  1172. //
  1173. // loop while there string length is not zero
  1174. // couldn't find a TNULL, or i'd just compare.
  1175. //
  1176. while (_tcsicmp(search, TEXT("\0")) != 0) {
  1177. //
  1178. // if this string matches...
  1179. //
  1180. if (_tcsicmp(search, FindThis) == 0) {
  1181. //
  1182. // the new length is smaller
  1183. // remove the string (and terminating null)
  1184. //
  1185. instancesDeleted++;
  1186. *NewStringLength -= (_tcslen(search) + 1) * sizeof(TCHAR);
  1187. RtlMoveMemory(search,
  1188. search + _tcslen(search) + 1,
  1189. *NewStringLength - (charOffset * sizeof(TCHAR))
  1190. );
  1191. } else {
  1192. //
  1193. // move current search pointer
  1194. // increment current offset (in CHARS)
  1195. //
  1196. charOffset += _tcslen(search) + 1;
  1197. search += _tcslen(search) + 1;
  1198. }
  1199. //
  1200. // it's that simple
  1201. //
  1202. }
  1203. //
  1204. // if deleted all strings, set to double-null
  1205. //
  1206. if (*NewStringLength == sizeof(TCHAR)) {
  1207. FindWithin = TEXT("\0");
  1208. *NewStringLength = 0;
  1209. }
  1210. return instancesDeleted;
  1211. }