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

/*++
Copyright (C) 2001 Microsoft Corporation
All rights reserved.
Module Name:
cleanup.cxx
Abstract:
Contains functions which perform clean up when a spooler
resource is deleted, a node is removed as possible owner
for a spooler resource etc.
Author:
Felix Maxa (AMaxa) 11-Sept-2001 Created the file
--*/
#include "precomp.hxx"
#include "cleanup.hxx"
#include "clusinfo.hxx"
/*++
Name:
EnumClusterDirectories
Description:
Enumerates all dirs under system32\spool\drivers. Then it fills in an array
with all the directories which have the names GUIDs. Ex:
Directory of C:\WINDOWS\system32\spool\drivers
08/28/2001 02:44 PM <DIR> .
08/28/2001 02:44 PM <DIR> ..
08/28/2001 03:15 PM <DIR> 0abe4037-88be-4aa1-a714-d6879b4b3a74
08/28/2001 02:44 PM <DIR> 13288f3f-901c-4911-8829-695bc9c16e0c
08/28/2001 12:17 PM <DIR> 40dda7f1-1127-4ca6-817a-9c5d24633bfa
08/28/2001 01:55 PM <DIR> 5c732622-d800-4849-89e1-d5f45d665f30
08/23/2001 06:10 PM <DIR> 8de5c2aa-96fc-493c-ba1b-92dafefdfad9
08/17/2001 04:38 PM <DIR> color
05/22/2001 10:50 AM <DIR> IA64
05/22/2001 10:50 AM <DIR> W32ALPHA
08/17/2001 04:39 PM <DIR> w32x86
05/22/2001 10:50 AM <DIR> WIN40
In this case, the function fills in an array with the following names:
0abe4037-88be-4aa1-a714-d6879b4b3a74
13288f3f-901c-4911-8829-695bc9c16e0c
40dda7f1-1127-4ca6-817a-9c5d24633bfa
5c732622-d800-4849-89e1-d5f45d665f30
8de5c2aa-96fc-493c-ba1b-92dafefdfad9
Arguments:
pArray - pointer to array where to store the strings. We can safely
assume that pArray is always a valid pointer
Return Value:
ERROR_SUCCCESS
Win32 error
--*/
DWORD
EnumClusterDirectories(
IN TStringArray *pArray
)
{
DWORD Error;
WCHAR Scratch[MAX_PATH];
if (GetSystemDirectory(Scratch, MAX_PATH))
{
if ((Error = StrNCatBuff(Scratch,
MAX_PATH,
Scratch,
L"\\spool\\drivers\\*",
NULL)) == ERROR_SUCCESS)
{
HANDLE hFindFile;
WIN32_FIND_DATA FindData;
//
// Enumerate all the files in the directory
//
hFindFile = FindFirstFile(Scratch, &FindData);
if (hFindFile != INVALID_HANDLE_VALUE)
{
do
{
if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if (IsGUIDString(FindData.cFileName))
{
Error = pArray->AddString(FindData.cFileName);
}
}
} while (Error == ERROR_SUCCESS && FindNextFile(hFindFile, &FindData));
FindClose(hFindFile);
}
else
{
Error = GetLastError();
}
}
}
else
{
Error = GetLastError();
}
return Error;
}
/*++
Name:
EnumClusterPrintersDriversKeys
Description:
Enumerates all dirs under system32\spool\drivers. Then it fills in an array
with all the directories which have the names GUIDs. Ex:
Directory of C:\WINDOWS\system32\spool\drivers
08/28/2001 02:44 PM <DIR> .
08/28/2001 02:44 PM <DIR> ..
08/28/2001 03:15 PM <DIR> 0abe4037-88be-4aa1-a714-d6879b4b3a74
08/28/2001 02:44 PM <DIR> 13288f3f-901c-4911-8829-695bc9c16e0c
08/28/2001 12:17 PM <DIR> 40dda7f1-1127-4ca6-817a-9c5d24633bfa
08/28/2001 01:55 PM <DIR> 5c732622-d800-4849-89e1-d5f45d665f30
08/23/2001 06:10 PM <DIR> 8de5c2aa-96fc-493c-ba1b-92dafefdfad9
08/17/2001 04:38 PM <DIR> color
05/22/2001 10:50 AM <DIR> IA64
05/22/2001 10:50 AM <DIR> W32ALPHA
08/17/2001 04:39 PM <DIR> w32x86
05/22/2001 10:50 AM <DIR> WIN40
In this case, the function fills in an array with the following names:
0abe4037-88be-4aa1-a714-d6879b4b3a74
13288f3f-901c-4911-8829-695bc9c16e0c
40dda7f1-1127-4ca6-817a-9c5d24633bfa
5c732622-d800-4849-89e1-d5f45d665f30
8de5c2aa-96fc-493c-ba1b-92dafefdfad9
Arguments:
pArray - pointer to array where to store the strings. We can safely
assume that pArray is always a valid pointer
Return Value:
ERROR_SUCCCESS
Win32 error
--*/
DWORD
EnumClusterPrinterDriversKeys(
IN TStringArray *pArray
)
{
HKEY hKey = NULL;
DWORD Status = ERROR_SUCCESS;
if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
SPLREG_CLUSTER_LOCAL_ROOT_KEY,
0,
KEY_READ,
&hKey)) == ERROR_SUCCESS)
{
LPWSTR pszBuffer = NULL;
DWORD nBufferSize = 0;
Status = GetSubkeyBuffer(hKey, &pszBuffer, &nBufferSize);
if (Status == ERROR_SUCCESS)
{
for (DWORD i = 0; Status == ERROR_SUCCESS ; i++)
{
DWORD nTempSize = nBufferSize;
Status = RegEnumKeyEx(hKey, i, pszBuffer, &nTempSize, 0, 0, 0, 0);
if (Status == ERROR_SUCCESS && IsGUIDString(pszBuffer))
{
Status = pArray->AddString(pszBuffer);
}
}
}
delete [] pszBuffer;
if (Status == ERROR_NO_MORE_ITEMS)
{
Status = ERROR_SUCCESS;
}
RegCloseKey(hKey);
}
return Status;
}
/*++
Name:
EnumClusterSpoolers
Description:
Fill in a list with the IP addresses used by the cluster service running on the local machine.
The function returns S_OK if the cluster service is not running. In that case the list will be
empty.
Arguments:
pClusterIPsList - pointer to list of TStringNodes.
Return Value:
S_OK - success. pClusterIPsList will have 0 or more elements represeting each
an IP address used by cluster resources
any other HRESULT - failure
--*/
DWORD
EnumClusterSpoolers(
IN TStringArray *pArray
)
{
HRESULT hRetval;
HCLUSTER hCluster;
hCluster = OpenCluster(NULL);
hRetval = hCluster ? S_OK : GetLastErrorAsHResult();
if (SUCCEEDED(hRetval))
{
HCLUSENUM hClusEnum;
hClusEnum = ClusterOpenEnum(hCluster, CLUSTER_ENUM_RESOURCE);
hRetval = hClusEnum ? S_OK : GetLastErrorAsHResult();
if (SUCCEEDED(hRetval))
{
BOOL bDone = FALSE;
DWORD Index = 0;
LPWSTR pszName = NULL;
DWORD cchName = 0;
DWORD cchNeeded;
DWORD ResourceType;
cchName = cchNeeded = kBufferAllocHint;
//
// We need to initialize pszName to a valid non NULL memory block, otherwise CluserEnum AV's.
//
pszName = new WCHAR[cchName];
hRetval = pszName ? S_OK : E_OUTOFMEMORY;
for (Index = 0; !bDone && SUCCEEDED(hRetval);)
{
DWORD Error;
cchNeeded = cchName;
Error = ClusterEnum(hClusEnum,
Index,
&ResourceType,
pszName,
&cchNeeded);
switch (Error)
{
case ERROR_SUCCESS:
{
BYTE *pGUID = NULL;
//
// This function allocates memory in pGUID only if pszName is the name
// of a cluster spooler. That's why the if statement below checks for pGUID
//
hRetval = GetSpoolerResourceGUID(hCluster, pszName, &pGUID);
if (pGUID)
{
Error = pArray->AddString((LPCWSTR)pGUID);
hRetval = HRESULT_FROM_WIN32(Error);
delete [] pGUID;
}
Index++;
break;
}
case ERROR_MORE_DATA:
{
delete [] pszName;
//
// cchNeeded returns the number of characters needed, excluding the terminating NULL
//
cchName = cchNeeded + 1;
pszName = new WCHAR[cchName];
if (!pszName)
{
hRetval = E_OUTOFMEMORY;
}
break;
}
case ERROR_NO_MORE_ITEMS:
{
delete [] pszName;
bDone = TRUE;
break;
}
default:
{
delete [] pszName;
hRetval = HRESULT_FROM_WIN32(Error);
}
}
}
ClusterCloseEnum(hClusEnum);
}
CloseCluster(hCluster);
}
return hRetval;
}
/*++
Name:
CleanUnusedClusDriverDirectory
Description:
A cluster spooler was deleted. Then a GUID direcotry was left over
in system32\spooler\drivers. This function takes as argument the name
of a direcotry (a GUID) and deletes recursively all the files in it.
Basically this function deletes
%windir%\system32\spooler\drivers\pszDir
Arguments:
pszDir - name of the directory to delete
Return Value:
ERROR_SUCCESS - the direcotry was deleted or marked for delete on reboot
if files were in use
Win32 error code - otherwise
--*/
DWORD
CleanUnusedClusDriverDirectory(
IN LPCWSTR pszDir
)
{
DWORD Error = ERROR_INVALID_PARAMETER;
if (pszDir && *pszDir)
{
WCHAR Scratch[MAX_PATH];
if (GetSystemDirectory(Scratch, MAX_PATH))
{
if ((Error = StrNCatBuff(Scratch,
MAX_PATH,
Scratch,
L"\\spool\\drivers\\",
pszDir,
NULL)) == ERROR_SUCCESS)
{
Error = DelDirRecursively(Scratch);
}
}
else
{
Error = GetLastError();
}
}
return Error;
}
/*++
Name:
CleanUnusedClusDriverRegistryKey
Description:
A cluster spooler was deleted. Then a GUID registry key was left over
under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Cluster.
This function takes as argument the name of a reg key (a GUID) and deletes recursively
all the keys and values under it.
Basically this function deletes
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Cluster\pszName
Arguments:
pszName - name of the key to delete. The key is relative to
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Cluster
Return Value:
ERROR_SUCCESS - the reg key was deleted
Win32 error code - otherwise
--*/
DWORD
CleanUnusedClusDriverRegistryKey(
IN LPCWSTR pszName
)
{
DWORD Error = ERROR_INVALID_PARAMETER;
if (pszName && *pszName)
{
HKEY hKey = NULL;
if ((Error = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
SPLREG_CLUSTER_LOCAL_ROOT_KEY,
0,
KEY_ALL_ACCESS,
&hKey)) == ERROR_SUCCESS)
{
Error = DeleteKeyRecursive(hKey, pszName);
RegCloseKey(hKey);
}
}
return Error;
}
/*++
Name:
CleanSpoolerUnusedFilesAndKeys
Description:
Cleans up driver files and registry keys which were used by cluster spooler resources which were deleted.
Each cluster spooler keeps driver files under system32\drivers\GUID and a reg key under HKLM\Software\...\Print\Cluster.
This function checks if such "remains" are present on the local node and deletes them.
Arguments:
None
Return Value:
ERROR_SUCCESS - cleanup was done or no clean up was necessary
other Win32 error - an error occurred
--*/
DWORD
CleanSpoolerUnusedFilesAndKeys(
VOID
)
{
DWORD Error = ERROR_SUCCESS;
DWORD i;
//
// Now we enumerate the GUIDS for all the existing cluster spooler resources
//
TStringArray ClusSpoolersArray;
Error = EnumClusterSpoolers(&ClusSpoolersArray);
if (Error == ERROR_SUCCESS)
{
//
// We now enumerate all the directories under system32\spool\drivers. We want to
// isolate the GUID directories. Those are the directories where the cluster spoolers
// keep the printer driver files.
//
TStringArray UnusedDirsArray;
Error = EnumClusterDirectories(&UnusedDirsArray);
if (Error == ERROR_SUCCESS)
{
//
// Now exlcude all existing spooler GUIDs
//
for (i = 0; Error == ERROR_SUCCESS && i < ClusSpoolersArray.Count(); i++)
{
Error = UnusedDirsArray.Exclude(ClusSpoolersArray.StringAt(i));
}
if (Error == ERROR_SUCCESS)
{
//
// Now we have the GUIDs of the unused resources. The unused resources
// are registry keys and file driectories.
//
for (i = 0; i < UnusedDirsArray.Count(); i++)
{
CleanUnusedClusDriverDirectory(UnusedDirsArray.StringAt(i));
}
}
}
//
// Now we enumerate all the keys under SPLREG_CLUSTER_LOCAL_ROOT_KEY
// ("Software\\Microsoft\\Windows NT\\CurrentVersion\\Print\\Cluster")
//
TStringArray UnusedKeysArray;
Error = EnumClusterPrinterDriversKeys(&UnusedKeysArray);
if (Error == ERROR_SUCCESS)
{
//
// Now exlcude all existing spooler GUIDs
//
for (i = 0; Error == ERROR_SUCCESS && i < ClusSpoolersArray.Count(); i++)
{
Error = UnusedKeysArray.Exclude(ClusSpoolersArray.StringAt(i));
}
if (Error == ERROR_SUCCESS)
{
//
// Now we have the GUIDs of the unused resources. The unused resources
// are registry keys and file driectories.
//
for (i = 0; i < UnusedKeysArray.Count(); i++)
{
CleanUnusedClusDriverRegistryKey(UnusedKeysArray.StringAt(i));
}
}
}
}
return Error;
}
/*++
Name:
CleanClusterSpoolerData
Description:
One can configure the nodes which can host the spooler resource. When you remove a node from that list,
all nodes currently up receive a CLUSCTL_RESOURCE_REMOVE_OWNER. So, if a node is up, the spooler resource
DLL needs to preform clean up, i.e. remove the registry key HKLM\Software\...\Print\Cluster. Thus, if you
immediately add back the node as a possible owner for the spooler resource, all the driver files are
going to be copied over from the cluster disk (repository). This addresses a problem where there is
a corruption on the local disk on a node. The admin can remove the node as owner of the spooler resource
and then add it back as possible owner. This procesdure enusres that the driver files are reinstalled on the
local node. (We need the driver files on the local node, otherwise apps loaded printer drivers can hit
in-page errors if they local the drivers directly from the cluster disk)
We have a separate case for a node which is down at the moment when an admin removes it as possible owner
for the spooler resource. That node won't receive a CLUSCTL_RESOURCE_REMOVE_OWNER control code. In that case
we perform the clean up on CLUSCTL_RESOURCE_ADD_OWNER or CLUSCTL_RESOURCE_INSTALL_NODE (we also handle
the case when a node is down as it is evicted).
Arguments:
hResource - handle to the spooler resource
pszNodeName - node node where to perform the clean up
Return Value:
ERROR_SUCCESS - cleanup was done or no clean up was necessary
other Win32 error - an error occurred
--*/
DWORD
CleanClusterSpoolerData(
IN HRESOURCE hResource,
IN LPCWSTR pszNodeName
)
{
DWORD Status = ERROR_INVALID_FUNCTION;
if (hResource && pszNodeName)
{
LPWSTR pszCurrentNode = NULL;
if ((Status = GetCurrentNodeName(&pszCurrentNode)) == ERROR_SUCCESS)
{
if (!ClRtlStrICmp(pszCurrentNode, pszNodeName))
{
//
// Same node, perform clean up
//
LPWSTR pszResourceGuid = NULL;
//
// Retreive the guid of the spooler resource
//
if ((Status = GetIDFromName(hResource, &pszResourceGuid)) == ERROR_SUCCESS)
{
Status = CleanUnusedClusDriverRegistryKey(pszResourceGuid);
//
// We do not care about the result of this function
//
CleanUnusedClusDriverDirectory(pszResourceGuid);
LocalFree(pszResourceGuid);
}
}
delete [] pszCurrentNode;
}
}
return Status;
}
/*++
Routine Name
CleanPrinterDriverRepository
Routine Description:
Thie routine is called when the spooler resource is deleted.
It will remove from the cluster disk the directory where the
spooler keeps the pirnter drivers.
Arguments:
hKey - handle to the Parameters key of the spooler resource
Return Value:
Win32 error code
--*/
DWORD
CleanPrinterDriverRepository(
IN HKEY hKey
)
{
DWORD dwError;
DWORD dwType;
DWORD cbNeeded = 0;
//
// We read the string value (private property) that was written
// by the spooler. This is the directory to delete.
// Note that ClusterRegQueryValue has a bizzare behaviour. If
// you pass NULL for the buffer and the value exists, then it
// doesn't return ERROR_MORE_DATA, but ERROR_SUCCESS
// Should the reg type not be reg_Sz, then we can't do anything
// in this function.
//
if ((dwError = ClusterRegQueryValue(hKey,
SPLREG_CLUSTER_DRIVER_DIRECTORY,
&dwType,
NULL,
&cbNeeded)) == ERROR_SUCCESS &&
dwType == REG_SZ)
{
LPWSTR pszDir;
if (pszDir = (LPWSTR)LocalAlloc(LMEM_FIXED, cbNeeded))
{
if ((dwError = ClusterRegQueryValue(hKey,
SPLREG_CLUSTER_DRIVER_DIRECTORY,
&dwType,
(LPBYTE)pszDir,
&cbNeeded)) == ERROR_SUCCESS)
{
//
// Remove the directory
//
DelDirRecursively(pszDir);
}
LocalFree(pszDir);
}
else
{
dwError = GetLastError();
}
}
return dwError;
}