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.

724 lines
21 KiB

  1. /*++
  2. Copyright (C) 2001 Microsoft Corporation
  3. All rights reserved.
  4. Module Name:
  5. cleanup.cxx
  6. Abstract:
  7. Contains functions which perform clean up when a spooler
  8. resource is deleted, a node is removed as possible owner
  9. for a spooler resource etc.
  10. Author:
  11. Felix Maxa (AMaxa) 11-Sept-2001 Created the file
  12. --*/
  13. #include "precomp.hxx"
  14. #include "cleanup.hxx"
  15. #include "clusinfo.hxx"
  16. /*++
  17. Name:
  18. EnumClusterDirectories
  19. Description:
  20. Enumerates all dirs under system32\spool\drivers. Then it fills in an array
  21. with all the directories which have the names GUIDs. Ex:
  22. Directory of C:\WINDOWS\system32\spool\drivers
  23. 08/28/2001 02:44 PM <DIR> .
  24. 08/28/2001 02:44 PM <DIR> ..
  25. 08/28/2001 03:15 PM <DIR> 0abe4037-88be-4aa1-a714-d6879b4b3a74
  26. 08/28/2001 02:44 PM <DIR> 13288f3f-901c-4911-8829-695bc9c16e0c
  27. 08/28/2001 12:17 PM <DIR> 40dda7f1-1127-4ca6-817a-9c5d24633bfa
  28. 08/28/2001 01:55 PM <DIR> 5c732622-d800-4849-89e1-d5f45d665f30
  29. 08/23/2001 06:10 PM <DIR> 8de5c2aa-96fc-493c-ba1b-92dafefdfad9
  30. 08/17/2001 04:38 PM <DIR> color
  31. 05/22/2001 10:50 AM <DIR> IA64
  32. 05/22/2001 10:50 AM <DIR> W32ALPHA
  33. 08/17/2001 04:39 PM <DIR> w32x86
  34. 05/22/2001 10:50 AM <DIR> WIN40
  35. In this case, the function fills in an array with the following names:
  36. 0abe4037-88be-4aa1-a714-d6879b4b3a74
  37. 13288f3f-901c-4911-8829-695bc9c16e0c
  38. 40dda7f1-1127-4ca6-817a-9c5d24633bfa
  39. 5c732622-d800-4849-89e1-d5f45d665f30
  40. 8de5c2aa-96fc-493c-ba1b-92dafefdfad9
  41. Arguments:
  42. pArray - pointer to array where to store the strings. We can safely
  43. assume that pArray is always a valid pointer
  44. Return Value:
  45. ERROR_SUCCCESS
  46. Win32 error
  47. --*/
  48. DWORD
  49. EnumClusterDirectories(
  50. IN TStringArray *pArray
  51. )
  52. {
  53. DWORD Error;
  54. WCHAR Scratch[MAX_PATH];
  55. if (GetSystemDirectory(Scratch, MAX_PATH))
  56. {
  57. if ((Error = StrNCatBuff(Scratch,
  58. MAX_PATH,
  59. Scratch,
  60. L"\\spool\\drivers\\*",
  61. NULL)) == ERROR_SUCCESS)
  62. {
  63. HANDLE hFindFile;
  64. WIN32_FIND_DATA FindData;
  65. //
  66. // Enumerate all the files in the directory
  67. //
  68. hFindFile = FindFirstFile(Scratch, &FindData);
  69. if (hFindFile != INVALID_HANDLE_VALUE)
  70. {
  71. do
  72. {
  73. if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  74. {
  75. if (IsGUIDString(FindData.cFileName))
  76. {
  77. Error = pArray->AddString(FindData.cFileName);
  78. }
  79. }
  80. } while (Error == ERROR_SUCCESS && FindNextFile(hFindFile, &FindData));
  81. FindClose(hFindFile);
  82. }
  83. else
  84. {
  85. Error = GetLastError();
  86. }
  87. }
  88. }
  89. else
  90. {
  91. Error = GetLastError();
  92. }
  93. return Error;
  94. }
  95. /*++
  96. Name:
  97. EnumClusterPrintersDriversKeys
  98. Description:
  99. Enumerates all dirs under system32\spool\drivers. Then it fills in an array
  100. with all the directories which have the names GUIDs. Ex:
  101. Directory of C:\WINDOWS\system32\spool\drivers
  102. 08/28/2001 02:44 PM <DIR> .
  103. 08/28/2001 02:44 PM <DIR> ..
  104. 08/28/2001 03:15 PM <DIR> 0abe4037-88be-4aa1-a714-d6879b4b3a74
  105. 08/28/2001 02:44 PM <DIR> 13288f3f-901c-4911-8829-695bc9c16e0c
  106. 08/28/2001 12:17 PM <DIR> 40dda7f1-1127-4ca6-817a-9c5d24633bfa
  107. 08/28/2001 01:55 PM <DIR> 5c732622-d800-4849-89e1-d5f45d665f30
  108. 08/23/2001 06:10 PM <DIR> 8de5c2aa-96fc-493c-ba1b-92dafefdfad9
  109. 08/17/2001 04:38 PM <DIR> color
  110. 05/22/2001 10:50 AM <DIR> IA64
  111. 05/22/2001 10:50 AM <DIR> W32ALPHA
  112. 08/17/2001 04:39 PM <DIR> w32x86
  113. 05/22/2001 10:50 AM <DIR> WIN40
  114. In this case, the function fills in an array with the following names:
  115. 0abe4037-88be-4aa1-a714-d6879b4b3a74
  116. 13288f3f-901c-4911-8829-695bc9c16e0c
  117. 40dda7f1-1127-4ca6-817a-9c5d24633bfa
  118. 5c732622-d800-4849-89e1-d5f45d665f30
  119. 8de5c2aa-96fc-493c-ba1b-92dafefdfad9
  120. Arguments:
  121. pArray - pointer to array where to store the strings. We can safely
  122. assume that pArray is always a valid pointer
  123. Return Value:
  124. ERROR_SUCCCESS
  125. Win32 error
  126. --*/
  127. DWORD
  128. EnumClusterPrinterDriversKeys(
  129. IN TStringArray *pArray
  130. )
  131. {
  132. HKEY hKey = NULL;
  133. DWORD Status = ERROR_SUCCESS;
  134. if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  135. SPLREG_CLUSTER_LOCAL_ROOT_KEY,
  136. 0,
  137. KEY_READ,
  138. &hKey)) == ERROR_SUCCESS)
  139. {
  140. LPWSTR pszBuffer = NULL;
  141. DWORD nBufferSize = 0;
  142. Status = GetSubkeyBuffer(hKey, &pszBuffer, &nBufferSize);
  143. if (Status == ERROR_SUCCESS)
  144. {
  145. for (DWORD i = 0; Status == ERROR_SUCCESS ; i++)
  146. {
  147. DWORD nTempSize = nBufferSize;
  148. Status = RegEnumKeyEx(hKey, i, pszBuffer, &nTempSize, 0, 0, 0, 0);
  149. if (Status == ERROR_SUCCESS && IsGUIDString(pszBuffer))
  150. {
  151. Status = pArray->AddString(pszBuffer);
  152. }
  153. }
  154. }
  155. delete [] pszBuffer;
  156. if (Status == ERROR_NO_MORE_ITEMS)
  157. {
  158. Status = ERROR_SUCCESS;
  159. }
  160. RegCloseKey(hKey);
  161. }
  162. return Status;
  163. }
  164. /*++
  165. Name:
  166. EnumClusterSpoolers
  167. Description:
  168. Fill in a list with the IP addresses used by the cluster service running on the local machine.
  169. The function returns S_OK if the cluster service is not running. In that case the list will be
  170. empty.
  171. Arguments:
  172. pClusterIPsList - pointer to list of TStringNodes.
  173. Return Value:
  174. S_OK - success. pClusterIPsList will have 0 or more elements represeting each
  175. an IP address used by cluster resources
  176. any other HRESULT - failure
  177. --*/
  178. DWORD
  179. EnumClusterSpoolers(
  180. IN TStringArray *pArray
  181. )
  182. {
  183. HRESULT hRetval;
  184. HCLUSTER hCluster;
  185. hCluster = OpenCluster(NULL);
  186. hRetval = hCluster ? S_OK : GetLastErrorAsHResult();
  187. if (SUCCEEDED(hRetval))
  188. {
  189. HCLUSENUM hClusEnum;
  190. hClusEnum = ClusterOpenEnum(hCluster, CLUSTER_ENUM_RESOURCE);
  191. hRetval = hClusEnum ? S_OK : GetLastErrorAsHResult();
  192. if (SUCCEEDED(hRetval))
  193. {
  194. BOOL bDone = FALSE;
  195. DWORD Index = 0;
  196. LPWSTR pszName = NULL;
  197. DWORD cchName = 0;
  198. DWORD cchNeeded;
  199. DWORD ResourceType;
  200. cchName = cchNeeded = kBufferAllocHint;
  201. //
  202. // We need to initialize pszName to a valid non NULL memory block, otherwise CluserEnum AV's.
  203. //
  204. pszName = new WCHAR[cchName];
  205. hRetval = pszName ? S_OK : E_OUTOFMEMORY;
  206. for (Index = 0; !bDone && SUCCEEDED(hRetval);)
  207. {
  208. DWORD Error;
  209. cchNeeded = cchName;
  210. Error = ClusterEnum(hClusEnum,
  211. Index,
  212. &ResourceType,
  213. pszName,
  214. &cchNeeded);
  215. switch (Error)
  216. {
  217. case ERROR_SUCCESS:
  218. {
  219. BYTE *pGUID = NULL;
  220. //
  221. // This function allocates memory in pGUID only if pszName is the name
  222. // of a cluster spooler. That's why the if statement below checks for pGUID
  223. //
  224. hRetval = GetSpoolerResourceGUID(hCluster, pszName, &pGUID);
  225. if (pGUID)
  226. {
  227. Error = pArray->AddString((LPCWSTR)pGUID);
  228. hRetval = HRESULT_FROM_WIN32(Error);
  229. delete [] pGUID;
  230. }
  231. Index++;
  232. break;
  233. }
  234. case ERROR_MORE_DATA:
  235. {
  236. delete [] pszName;
  237. //
  238. // cchNeeded returns the number of characters needed, excluding the terminating NULL
  239. //
  240. cchName = cchNeeded + 1;
  241. pszName = new WCHAR[cchName];
  242. if (!pszName)
  243. {
  244. hRetval = E_OUTOFMEMORY;
  245. }
  246. break;
  247. }
  248. case ERROR_NO_MORE_ITEMS:
  249. {
  250. delete [] pszName;
  251. bDone = TRUE;
  252. break;
  253. }
  254. default:
  255. {
  256. delete [] pszName;
  257. hRetval = HRESULT_FROM_WIN32(Error);
  258. }
  259. }
  260. }
  261. ClusterCloseEnum(hClusEnum);
  262. }
  263. CloseCluster(hCluster);
  264. }
  265. return hRetval;
  266. }
  267. /*++
  268. Name:
  269. CleanUnusedClusDriverDirectory
  270. Description:
  271. A cluster spooler was deleted. Then a GUID direcotry was left over
  272. in system32\spooler\drivers. This function takes as argument the name
  273. of a direcotry (a GUID) and deletes recursively all the files in it.
  274. Basically this function deletes
  275. %windir%\system32\spooler\drivers\pszDir
  276. Arguments:
  277. pszDir - name of the directory to delete
  278. Return Value:
  279. ERROR_SUCCESS - the direcotry was deleted or marked for delete on reboot
  280. if files were in use
  281. Win32 error code - otherwise
  282. --*/
  283. DWORD
  284. CleanUnusedClusDriverDirectory(
  285. IN LPCWSTR pszDir
  286. )
  287. {
  288. DWORD Error = ERROR_INVALID_PARAMETER;
  289. if (pszDir && *pszDir)
  290. {
  291. WCHAR Scratch[MAX_PATH];
  292. if (GetSystemDirectory(Scratch, MAX_PATH))
  293. {
  294. if ((Error = StrNCatBuff(Scratch,
  295. MAX_PATH,
  296. Scratch,
  297. L"\\spool\\drivers\\",
  298. pszDir,
  299. NULL)) == ERROR_SUCCESS)
  300. {
  301. Error = DelDirRecursively(Scratch);
  302. }
  303. }
  304. else
  305. {
  306. Error = GetLastError();
  307. }
  308. }
  309. return Error;
  310. }
  311. /*++
  312. Name:
  313. CleanUnusedClusDriverRegistryKey
  314. Description:
  315. A cluster spooler was deleted. Then a GUID registry key was left over
  316. under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Cluster.
  317. This function takes as argument the name of a reg key (a GUID) and deletes recursively
  318. all the keys and values under it.
  319. Basically this function deletes
  320. HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Cluster\pszName
  321. Arguments:
  322. pszName - name of the key to delete. The key is relative to
  323. HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Cluster
  324. Return Value:
  325. ERROR_SUCCESS - the reg key was deleted
  326. Win32 error code - otherwise
  327. --*/
  328. DWORD
  329. CleanUnusedClusDriverRegistryKey(
  330. IN LPCWSTR pszName
  331. )
  332. {
  333. DWORD Error = ERROR_INVALID_PARAMETER;
  334. if (pszName && *pszName)
  335. {
  336. HKEY hKey = NULL;
  337. if ((Error = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  338. SPLREG_CLUSTER_LOCAL_ROOT_KEY,
  339. 0,
  340. KEY_ALL_ACCESS,
  341. &hKey)) == ERROR_SUCCESS)
  342. {
  343. Error = DeleteKeyRecursive(hKey, pszName);
  344. RegCloseKey(hKey);
  345. }
  346. }
  347. return Error;
  348. }
  349. /*++
  350. Name:
  351. CleanSpoolerUnusedFilesAndKeys
  352. Description:
  353. Cleans up driver files and registry keys which were used by cluster spooler resources which were deleted.
  354. Each cluster spooler keeps driver files under system32\drivers\GUID and a reg key under HKLM\Software\...\Print\Cluster.
  355. This function checks if such "remains" are present on the local node and deletes them.
  356. Arguments:
  357. None
  358. Return Value:
  359. ERROR_SUCCESS - cleanup was done or no clean up was necessary
  360. other Win32 error - an error occurred
  361. --*/
  362. DWORD
  363. CleanSpoolerUnusedFilesAndKeys(
  364. VOID
  365. )
  366. {
  367. DWORD Error = ERROR_SUCCESS;
  368. DWORD i;
  369. //
  370. // Now we enumerate the GUIDS for all the existing cluster spooler resources
  371. //
  372. TStringArray ClusSpoolersArray;
  373. Error = EnumClusterSpoolers(&ClusSpoolersArray);
  374. if (Error == ERROR_SUCCESS)
  375. {
  376. //
  377. // We now enumerate all the directories under system32\spool\drivers. We want to
  378. // isolate the GUID directories. Those are the directories where the cluster spoolers
  379. // keep the printer driver files.
  380. //
  381. TStringArray UnusedDirsArray;
  382. Error = EnumClusterDirectories(&UnusedDirsArray);
  383. if (Error == ERROR_SUCCESS)
  384. {
  385. //
  386. // Now exlcude all existing spooler GUIDs
  387. //
  388. for (i = 0; Error == ERROR_SUCCESS && i < ClusSpoolersArray.Count(); i++)
  389. {
  390. Error = UnusedDirsArray.Exclude(ClusSpoolersArray.StringAt(i));
  391. }
  392. if (Error == ERROR_SUCCESS)
  393. {
  394. //
  395. // Now we have the GUIDs of the unused resources. The unused resources
  396. // are registry keys and file driectories.
  397. //
  398. for (i = 0; i < UnusedDirsArray.Count(); i++)
  399. {
  400. CleanUnusedClusDriverDirectory(UnusedDirsArray.StringAt(i));
  401. }
  402. }
  403. }
  404. //
  405. // Now we enumerate all the keys under SPLREG_CLUSTER_LOCAL_ROOT_KEY
  406. // ("Software\\Microsoft\\Windows NT\\CurrentVersion\\Print\\Cluster")
  407. //
  408. TStringArray UnusedKeysArray;
  409. Error = EnumClusterPrinterDriversKeys(&UnusedKeysArray);
  410. if (Error == ERROR_SUCCESS)
  411. {
  412. //
  413. // Now exlcude all existing spooler GUIDs
  414. //
  415. for (i = 0; Error == ERROR_SUCCESS && i < ClusSpoolersArray.Count(); i++)
  416. {
  417. Error = UnusedKeysArray.Exclude(ClusSpoolersArray.StringAt(i));
  418. }
  419. if (Error == ERROR_SUCCESS)
  420. {
  421. //
  422. // Now we have the GUIDs of the unused resources. The unused resources
  423. // are registry keys and file driectories.
  424. //
  425. for (i = 0; i < UnusedKeysArray.Count(); i++)
  426. {
  427. CleanUnusedClusDriverRegistryKey(UnusedKeysArray.StringAt(i));
  428. }
  429. }
  430. }
  431. }
  432. return Error;
  433. }
  434. /*++
  435. Name:
  436. CleanClusterSpoolerData
  437. Description:
  438. One can configure the nodes which can host the spooler resource. When you remove a node from that list,
  439. all nodes currently up receive a CLUSCTL_RESOURCE_REMOVE_OWNER. So, if a node is up, the spooler resource
  440. DLL needs to preform clean up, i.e. remove the registry key HKLM\Software\...\Print\Cluster. Thus, if you
  441. immediately add back the node as a possible owner for the spooler resource, all the driver files are
  442. going to be copied over from the cluster disk (repository). This addresses a problem where there is
  443. a corruption on the local disk on a node. The admin can remove the node as owner of the spooler resource
  444. and then add it back as possible owner. This procesdure enusres that the driver files are reinstalled on the
  445. local node. (We need the driver files on the local node, otherwise apps loaded printer drivers can hit
  446. in-page errors if they local the drivers directly from the cluster disk)
  447. We have a separate case for a node which is down at the moment when an admin removes it as possible owner
  448. for the spooler resource. That node won't receive a CLUSCTL_RESOURCE_REMOVE_OWNER control code. In that case
  449. we perform the clean up on CLUSCTL_RESOURCE_ADD_OWNER or CLUSCTL_RESOURCE_INSTALL_NODE (we also handle
  450. the case when a node is down as it is evicted).
  451. Arguments:
  452. hResource - handle to the spooler resource
  453. pszNodeName - node node where to perform the clean up
  454. Return Value:
  455. ERROR_SUCCESS - cleanup was done or no clean up was necessary
  456. other Win32 error - an error occurred
  457. --*/
  458. DWORD
  459. CleanClusterSpoolerData(
  460. IN HRESOURCE hResource,
  461. IN LPCWSTR pszNodeName
  462. )
  463. {
  464. DWORD Status = ERROR_INVALID_FUNCTION;
  465. if (hResource && pszNodeName)
  466. {
  467. LPWSTR pszCurrentNode = NULL;
  468. if ((Status = GetCurrentNodeName(&pszCurrentNode)) == ERROR_SUCCESS)
  469. {
  470. if (!ClRtlStrICmp(pszCurrentNode, pszNodeName))
  471. {
  472. //
  473. // Same node, perform clean up
  474. //
  475. LPWSTR pszResourceGuid = NULL;
  476. //
  477. // Retreive the guid of the spooler resource
  478. //
  479. if ((Status = GetIDFromName(hResource, &pszResourceGuid)) == ERROR_SUCCESS)
  480. {
  481. Status = CleanUnusedClusDriverRegistryKey(pszResourceGuid);
  482. //
  483. // We do not care about the result of this function
  484. //
  485. CleanUnusedClusDriverDirectory(pszResourceGuid);
  486. LocalFree(pszResourceGuid);
  487. }
  488. }
  489. delete [] pszCurrentNode;
  490. }
  491. }
  492. return Status;
  493. }
  494. /*++
  495. Routine Name
  496. CleanPrinterDriverRepository
  497. Routine Description:
  498. Thie routine is called when the spooler resource is deleted.
  499. It will remove from the cluster disk the directory where the
  500. spooler keeps the pirnter drivers.
  501. Arguments:
  502. hKey - handle to the Parameters key of the spooler resource
  503. Return Value:
  504. Win32 error code
  505. --*/
  506. DWORD
  507. CleanPrinterDriverRepository(
  508. IN HKEY hKey
  509. )
  510. {
  511. DWORD dwError;
  512. DWORD dwType;
  513. DWORD cbNeeded = 0;
  514. //
  515. // We read the string value (private property) that was written
  516. // by the spooler. This is the directory to delete.
  517. // Note that ClusterRegQueryValue has a bizzare behaviour. If
  518. // you pass NULL for the buffer and the value exists, then it
  519. // doesn't return ERROR_MORE_DATA, but ERROR_SUCCESS
  520. // Should the reg type not be reg_Sz, then we can't do anything
  521. // in this function.
  522. //
  523. if ((dwError = ClusterRegQueryValue(hKey,
  524. SPLREG_CLUSTER_DRIVER_DIRECTORY,
  525. &dwType,
  526. NULL,
  527. &cbNeeded)) == ERROR_SUCCESS &&
  528. dwType == REG_SZ)
  529. {
  530. LPWSTR pszDir;
  531. if (pszDir = (LPWSTR)LocalAlloc(LMEM_FIXED, cbNeeded))
  532. {
  533. if ((dwError = ClusterRegQueryValue(hKey,
  534. SPLREG_CLUSTER_DRIVER_DIRECTORY,
  535. &dwType,
  536. (LPBYTE)pszDir,
  537. &cbNeeded)) == ERROR_SUCCESS)
  538. {
  539. //
  540. // Remove the directory
  541. //
  542. DelDirRecursively(pszDir);
  543. }
  544. LocalFree(pszDir);
  545. }
  546. else
  547. {
  548. dwError = GetLastError();
  549. }
  550. }
  551. return dwError;
  552. }