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.

511 lines
15 KiB

  1. /*
  2. Note: This program creates the following files in %systemroot%\system32\config:
  3. system.sc0 Backup at start of operations
  4. system.scc Backup if compression is on
  5. system.scb Backup after removing devnodes
  6. system.scn Temporary during replacement of system (should go away)
  7. system Updated system hive (should match system.scb)
  8. Modification History:
  9. 10/3/2000 original version from jasconc
  10. 10/4/2000 put back compression option, version 0.9
  11. 10/10/2000 if compression (no removals), save backup as .scc, else as .sc0
  12. 10/13/2000 Add version check (Win2K only, not NT4, not Whistler, etc.)
  13. 10/20/2000 Return DevicesRemoved to help with diskpart in scripts
  14. */
  15. //#include "scrubber.h"
  16. #include <windows.h>
  17. #include <setupapi.h>
  18. #include <cfgmgr32.h>
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <initguid.h>
  22. #include <devguid.h>
  23. #include <winioctl.h>
  24. CONST GUID *ClassesToClean[2] = {
  25. &GUID_DEVCLASS_DISKDRIVE,
  26. &GUID_DEVCLASS_VOLUME
  27. };
  28. CONST GUID *DeviceInterfacesToClean[5] = {
  29. &DiskClassGuid,
  30. &PartitionClassGuid,
  31. &WriteOnceDiskClassGuid,
  32. &VolumeClassGuid,
  33. &StoragePortClassGuid
  34. };
  35. void PERR(void);
  36. BOOL
  37. IsUserAdmin(
  38. VOID
  39. )
  40. /*++
  41. Routine Description:
  42. This routine returns TRUE if the caller's process is a
  43. member of the Administrators local group.
  44. Caller is NOT expected to be impersonating anyone and IS
  45. expected to be able to open their own process and process
  46. token.
  47. Arguments:
  48. None.
  49. Return Value:
  50. TRUE - Caller has Administrators local group.
  51. FALSE - Caller does not have Administrators local group.
  52. --*/
  53. {
  54. BOOL b;
  55. SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
  56. PSID AdministratorsGroup;
  57. b = AllocateAndInitializeSid(&NtAuthority,
  58. 2,
  59. SECURITY_BUILTIN_DOMAIN_RID,
  60. DOMAIN_ALIAS_RID_ADMINS,
  61. 0,
  62. 0,
  63. 0,
  64. 0,
  65. 0,
  66. 0,
  67. &AdministratorsGroup
  68. );
  69. if (b) {
  70. if (!CheckTokenMembership(NULL,
  71. AdministratorsGroup,
  72. &b
  73. )) {
  74. b = FALSE;
  75. }
  76. FreeSid(AdministratorsGroup);
  77. }
  78. return (b);
  79. }
  80. int
  81. __cdecl
  82. main(
  83. IN int argc,
  84. IN char *argv[]
  85. )
  86. {
  87. HDEVINFO DeviceInfoSet;
  88. HDEVINFO InterfaceDeviceInfoSet;
  89. SP_DEVINFO_DATA DeviceInfoData;
  90. SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;
  91. int DevicesRemoved = 0;
  92. int i, InterfaceIndex;
  93. int MemberIndex, InterfaceMemberIndex;
  94. BOOL bDoRemove = TRUE, bDoCompress = FALSE;
  95. DWORD Status, Problem;
  96. CONFIGRET cr;
  97. TCHAR DeviceInstanceId[MAX_DEVICE_ID_LEN];
  98. TCHAR DirBuff[MAX_PATH], DirBuffX[MAX_PATH], BackupDir[MAX_PATH];
  99. UINT uPathLen;
  100. HKEY hKey;
  101. DWORD dwMessage;
  102. TOKEN_PRIVILEGES tp;
  103. HANDLE hToken;
  104. LUID luid;
  105. LPTSTR MachineName=NULL; // pointer to machine name
  106. DWORD dwSubKeyIndex=0; // index into key
  107. DWORD dwSubKeyLength=_MAX_FNAME; // length of SubKey buffer
  108. OSVERSIONINFO osvi;
  109. //
  110. // enable backup privilege at least
  111. //
  112. printf("SCRUBBER 0.93 Storage Device Node Cleanup\nCopyright (c) 2000 Microsoft Corp. All rights reserved.\n");
  113. //
  114. // parse parameters.
  115. //
  116. for (i = 1; i < argc; i++)
  117. {
  118. //
  119. // Check for help
  120. //
  121. if ( (lstrcmpi(argv[i], TEXT("-?")) == 0) ||
  122. (lstrcmpi(argv[i], TEXT("/?")) == 0) ){
  123. printf("\nSCRUBBER will remove phantom storage device nodes from this machine.\n\n");
  124. printf("Usage: scrubber [/n] [/c]\n");
  125. printf("\twhere /n displays but does not remove the phantom devnodes.\n");
  126. printf("\t and /c will compress the registry hive even if no changes are made.\n");
  127. printf("\nBackup and Restore privileges are required to run this utility.\n");
  128. printf("A copy of the registry will saved in %%systemroot%%\\system32\\config\\system.sc0\n");
  129. return 0;
  130. }
  131. //
  132. // Check for -n which means just list the devices that
  133. // we will remove.
  134. //
  135. if ( (lstrcmpi(argv[i], TEXT("-n")) == 0) ||
  136. (lstrcmpi(argv[i], TEXT("/n")) == 0) ) {
  137. bDoRemove = FALSE;
  138. }
  139. //
  140. // Force compress mode?
  141. //
  142. if ( (lstrcmpi(argv[i], TEXT("-c")) == 0) ||
  143. (lstrcmpi(argv[i], TEXT("/c")) == 0) ){
  144. bDoCompress = TRUE;
  145. }
  146. }
  147. //
  148. // Only run on Windows 2000 (not Whistler, etc.) Initialize the OSVERSIONINFOEX structure.
  149. //
  150. ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
  151. osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  152. if (!GetVersionEx(&osvi))
  153. {
  154. fprintf(stderr, "SCRUBBER: Unable to verify Windows version, exiting...\n");
  155. return -1;
  156. }
  157. if ( (osvi.dwMajorVersion != 5) || (osvi.dwMinorVersion != 0) )
  158. {
  159. fprintf(stderr, "SCRUBBER: This utility is only designed to run on Windows 2000\n");
  160. return -1;
  161. }
  162. //
  163. // The process must have admin credentials.
  164. //
  165. if (!IsUserAdmin()) {
  166. fprintf(stderr, "SCRUBBER: You must be an administrator to run this utility.\n");
  167. return -1;
  168. }
  169. //
  170. // see if we can do the task, need backup privelege.
  171. //
  172. if(!OpenProcessToken(GetCurrentProcess(),
  173. TOKEN_ADJUST_PRIVILEGES,
  174. &hToken ))
  175. {
  176. fprintf(stderr, "SCRUBBER: Unable to obtain process token.\nCheck privileges.\n");
  177. return -1;
  178. }
  179. if(!LookupPrivilegeValue(MachineName, SE_BACKUP_NAME, &luid))
  180. {
  181. fprintf(stderr, "SCRUBBER: Backup Privilege is required to save the registry.\n"
  182. "Please rerun from a privileged account\n");
  183. return -1;
  184. }
  185. tp.PrivilegeCount = 1;
  186. tp.Privileges[0].Luid = luid;
  187. tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  188. if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES),
  189. NULL, NULL ))
  190. {
  191. fprintf(stderr, "SCRUBBER: Unable to set Backup Privilege.\n");
  192. return -1;
  193. }
  194. // Backup the file if we aren't doing a dry run
  195. if ( bDoCompress || bDoRemove)
  196. {
  197. if(!LookupPrivilegeValue(MachineName, SE_RESTORE_NAME, &luid))
  198. {
  199. fprintf(stderr, "SCRUBBER: Restore Privilege is required to make changes to the registry.\n"
  200. "Please rerun from a privileged account\n");
  201. return -1;
  202. }
  203. tp.Privileges[0].Luid = luid;
  204. AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES),
  205. NULL, NULL );
  206. if (GetLastError() != ERROR_SUCCESS)
  207. {
  208. fprintf(stderr, "SCRUBBER: Unable to set Restore Privilege.\n");
  209. return -1;
  210. }
  211. if (RegOpenKey(HKEY_LOCAL_MACHINE, "System", &hKey) == ERROR_SUCCESS)
  212. {
  213. uPathLen = GetSystemDirectory(DirBuffX, sizeof(DirBuffX));
  214. if (uPathLen < MAX_PATH - strlen("\\config\\system.scx") - 1)
  215. {
  216. strcat(DirBuffX, "\\config\\system.scx");
  217. dwMessage = RegSaveKey(hKey, DirBuffX, NULL);
  218. if (dwMessage == ERROR_ALREADY_EXISTS)
  219. {
  220. DeleteFile(DirBuffX);
  221. dwMessage = RegSaveKey(hKey, DirBuffX, NULL);
  222. }
  223. RegCloseKey(hKey);
  224. if (dwMessage != ERROR_SUCCESS)
  225. {
  226. fprintf(stderr, "Unable to save a backup copy of the system hive.\n"
  227. "No changes have been made.\n\n");
  228. return -1;
  229. }
  230. }
  231. }
  232. }
  233. for (i=0; (i<sizeof(ClassesToClean)) && (bDoRemove || !bDoCompress); i++) {
  234. DeviceInfoSet = SetupDiGetClassDevs(ClassesToClean[i],
  235. NULL,
  236. NULL,
  237. 0
  238. );
  239. if (DeviceInfoSet != INVALID_HANDLE_VALUE) {
  240. DeviceInfoData.cbSize = sizeof(DeviceInfoData);
  241. MemberIndex = 0;
  242. while (SetupDiEnumDeviceInfo(DeviceInfoSet,
  243. MemberIndex++,
  244. &DeviceInfoData
  245. )) {
  246. //
  247. // Check if this device is a Phantom
  248. //
  249. cr = CM_Get_DevNode_Status(&Status,
  250. &Problem,
  251. DeviceInfoData.DevInst,
  252. 0
  253. );
  254. if ((cr == CR_NO_SUCH_DEVINST) ||
  255. (cr == CR_NO_SUCH_VALUE)) {
  256. //
  257. // This is a phantom. Now get the DeviceInstanceId so we
  258. // can display this as output.
  259. //
  260. if (CM_Get_Device_ID(DeviceInfoData.DevInst,
  261. DeviceInstanceId,
  262. sizeof(DeviceInstanceId) / sizeof(TCHAR),
  263. 0) == CR_SUCCESS) {
  264. if (bDoRemove) {
  265. printf("SCRUBBER: %s will be removed.\n",
  266. DeviceInstanceId
  267. );
  268. //
  269. // Since DeviceRemoval doesn't always clean out all
  270. // of the device's interfaces (this is the case for
  271. // RAW devnodes) then we need to manually build up
  272. // a list of device interfaces that we care about
  273. // that are associated with this DeviceInfoData and
  274. // manually remove them.
  275. //
  276. for (InterfaceIndex = 0;
  277. InterfaceIndex < sizeof(DeviceInterfacesToClean);
  278. InterfaceIndex++) {
  279. //
  280. // Build up a list of the interfaces for this specific
  281. // device.
  282. //
  283. InterfaceDeviceInfoSet =
  284. SetupDiGetClassDevs(DeviceInterfacesToClean[InterfaceIndex],
  285. DeviceInstanceId,
  286. NULL,
  287. DIGCF_DEVICEINTERFACE
  288. );
  289. if (InterfaceDeviceInfoSet != INVALID_HANDLE_VALUE) {
  290. //
  291. // Enumerate through the interfaces that we just
  292. // built up.
  293. //
  294. DeviceInterfaceData.cbSize = sizeof(DeviceInterfaceData);
  295. InterfaceMemberIndex = 0;
  296. while (SetupDiEnumDeviceInterfaces(InterfaceDeviceInfoSet,
  297. NULL,
  298. DeviceInterfacesToClean[InterfaceIndex],
  299. InterfaceMemberIndex++,
  300. &DeviceInterfaceData
  301. )) {
  302. //
  303. // Remove this Interface from the registry.
  304. //
  305. SetupDiRemoveDeviceInterface(InterfaceDeviceInfoSet,
  306. &DeviceInterfaceData
  307. );
  308. }
  309. //
  310. // Destroy the list of Interfaces that we built up.
  311. //
  312. SetupDiDestroyDeviceInfoList(InterfaceDeviceInfoSet);
  313. }
  314. }
  315. //
  316. // Call DIF_REMOVE to remove the device's hardware
  317. // and software registry keys.
  318. //
  319. if (SetupDiCallClassInstaller(DIF_REMOVE,
  320. DeviceInfoSet,
  321. &DeviceInfoData
  322. )) {
  323. DevicesRemoved++;
  324. } else {
  325. fprintf(stderr, "SCRUBBER: Error 0x%X removing phantom\n",
  326. GetLastError());
  327. }
  328. } else {
  329. printf("SCRUBBER: %s would have been removed.\n",
  330. DeviceInstanceId
  331. );
  332. }
  333. }
  334. }
  335. }
  336. SetupDiDestroyDeviceInfoList(DeviceInfoSet);
  337. }
  338. }
  339. // compress registry now
  340. if (DevicesRemoved || bDoCompress)
  341. {
  342. uPathLen = GetSystemDirectory(DirBuff, sizeof(DirBuff));
  343. SetLastError(0);
  344. if (uPathLen < MAX_PATH - strlen("\\config\\system.scn") - 1)
  345. {
  346. // Rename our backup copy
  347. if (!DevicesRemoved)
  348. {
  349. strcat(DirBuff, "\\config\\system.scc");
  350. }
  351. else
  352. {
  353. strcat(DirBuff, "\\config\\system.sc0");
  354. }
  355. DeleteFile(DirBuff);
  356. if (rename(DirBuffX, DirBuff))
  357. {
  358. fprintf(stderr, "SCRUBBER: Failed to rename backup file (system.scx)\n");
  359. }
  360. else
  361. {
  362. printf("System hive backup saved in %s\n", DirBuff);
  363. }
  364. }
  365. else
  366. {
  367. fprintf(stderr, "SCRUBBER: Path name too long. Registry not compressed.\n");
  368. }
  369. if (RegOpenKey(HKEY_LOCAL_MACHINE, "System", &hKey) == ERROR_SUCCESS)
  370. {
  371. // Make an additional copy now because it gets blown away on replace
  372. uPathLen = GetSystemDirectory(DirBuff, sizeof(DirBuff));
  373. strcat(DirBuff, "\\config\\system.scn");
  374. dwMessage = RegSaveKey(hKey, DirBuff, NULL);
  375. if (dwMessage == ERROR_ALREADY_EXISTS)
  376. {
  377. DeleteFile(DirBuff);
  378. dwMessage = RegSaveKey(hKey, DirBuff, NULL);
  379. }
  380. if (dwMessage == ERROR_SUCCESS)
  381. {
  382. if (bDoCompress) {
  383. TCHAR *tcPtr;
  384. sprintf(BackupDir, DirBuff);
  385. tcPtr = strstr(BackupDir, ".scn");
  386. strcpy(tcPtr, ".scb");
  387. if (!DeleteFile(BackupDir))
  388. {
  389. dwMessage = GetLastError();
  390. }
  391. dwMessage = RegReplaceKey(hKey, NULL, DirBuff, BackupDir);
  392. if (dwMessage != ERROR_SUCCESS)
  393. PERR();
  394. else
  395. printf("Saved new system hive.\n");
  396. }
  397. }
  398. else
  399. {
  400. PERR();
  401. }
  402. RegCloseKey(hKey);
  403. }
  404. }
  405. else
  406. DeleteFile(DirBuffX);
  407. return DevicesRemoved;
  408. }
  409. void PERR(void)
  410. {
  411. LPVOID lpMsgBuf;
  412. FormatMessage(
  413. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  414. FORMAT_MESSAGE_FROM_SYSTEM |
  415. FORMAT_MESSAGE_IGNORE_INSERTS,
  416. NULL,
  417. GetLastError(),
  418. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  419. (LPTSTR) &lpMsgBuf,
  420. 0,
  421. NULL);
  422. fprintf(stderr, lpMsgBuf);
  423. LocalFree(lpMsgBuf);
  424. }