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.
10433 lines
328 KiB
10433 lines
328 KiB
/*++
|
|
|
|
Copyright (c) 1990 - 1995 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
driver.c
|
|
|
|
Abstract:
|
|
|
|
This module provides all the public exported APIs relating to the
|
|
Driver-based Spooler Apis for the Local Print Providor
|
|
|
|
LocalAddPrinterDriver
|
|
LocalDeletePrinterDriver
|
|
SplGetPrinterDriver
|
|
LocalGetPrinterDriverDirectory
|
|
LocalEnumPrinterDriver
|
|
|
|
Support Functions in driver.c
|
|
|
|
CopyIniDriverToDriver -- KrishnaG
|
|
GetDriverInfoSize -- KrishnaG
|
|
DeleteDriverIni -- KrishnaG
|
|
WriteDriverIni -- KrishnaG
|
|
|
|
Author:
|
|
|
|
Dave Snipp (DaveSn) 15-Mar-1991
|
|
|
|
Revision History:
|
|
|
|
Felix Maxa (amaxa) 18-Jun-2000
|
|
Modified registry functions to take pIniSpooler
|
|
Added code to propagate drivers to the cluster disk
|
|
|
|
Khaled Sedky (khaleds) 2 Feb 1999
|
|
Modified CompleteDriverUpgrade to enable upgrading v.2 drivers to newer v.2 drivers
|
|
|
|
Ramanathan Venkatapathy (RamanV) 14 Feb 1997
|
|
Modified CreateVersionEntry,CreateDriverEntry, LocalDeletePrinterDriver,
|
|
SplDeletePrinterDriver.
|
|
Added Driver File RefCounting functions, DeletePrinterDriverEx functions.
|
|
|
|
Muhunthan Sivapragasam (MuhuntS) 26 May 1995
|
|
Changes to support DRIVER_INFO_3
|
|
|
|
Matthew A Felton (MattFe) 27 June 1994
|
|
pIniSpooler
|
|
|
|
Matthew A Felton (MattFe) 23 Feb 1995
|
|
CleanUp InternalAddPrinterDriver for win32spl use so it allows copying from non local
|
|
directories.
|
|
|
|
Matthew A Felton (MattFe) 23 Mar 1994
|
|
Added DrvUpgradePrinter calls, changes required to AddPrinterDriver so to save old
|
|
files.
|
|
|
|
--*/
|
|
|
|
#include <precomp.h>
|
|
#include <lm.h>
|
|
#include <offsets.h>
|
|
#include <wingdip.h>
|
|
#include "clusspl.h"
|
|
|
|
|
|
//
|
|
// Private Declarations
|
|
//
|
|
#define COMPATIBLE_SPOOLER_VERSION 2
|
|
|
|
//
|
|
// This definition is duplicated from oak\inc\winddi.h.
|
|
//
|
|
#define DRVQUERY_USERMODE 1
|
|
|
|
extern NET_API_STATUS (*pfnNetShareAdd)();
|
|
extern SHARE_INFO_2 PrintShareInfo;
|
|
extern NET_API_STATUS (*pfnNetShareSetInfo)();
|
|
|
|
|
|
#define MAX_DWORD_LENGTH 11
|
|
|
|
typedef struct _DRVFILE {
|
|
struct _DRVFILE *pnext;
|
|
LPCWSTR pFileName;
|
|
} DRVFILE, *PDRVFILE;
|
|
|
|
DWORD
|
|
CopyICMToClusterDisk(
|
|
IN PINISPOOLER pIniSpooler
|
|
);
|
|
|
|
DWORD
|
|
PropagateMonitorToCluster(
|
|
IN LPCWSTR pszName,
|
|
IN LPCWSTR pszDDLName,
|
|
IN LPCWSTR pszEnvName,
|
|
IN LPCWSTR pszEnvDir,
|
|
IN PINISPOOLER pIniSpooler
|
|
);
|
|
|
|
BOOL
|
|
CheckFilePlatform(
|
|
IN LPWSTR pszFileName,
|
|
IN LPWSTR pszEnvironment
|
|
);
|
|
|
|
LPBYTE
|
|
CopyIniDriverToDriverInfo(
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINIVERSION pIniVersion,
|
|
PINIDRIVER pIniDriver,
|
|
DWORD Level,
|
|
LPBYTE pDriverInfo,
|
|
LPBYTE pEnd,
|
|
LPWSTR lpRemote,
|
|
PINISPOOLER pIniSpooler
|
|
);
|
|
|
|
LPBYTE
|
|
CopyIniDriverToDriverInfoVersion(
|
|
IN PINIENVIRONMENT pIniEnvironment,
|
|
IN PINIVERSION pIniVersion,
|
|
IN PINIDRIVER pIniDriver,
|
|
IN LPBYTE pDriverInfo,
|
|
IN LPBYTE pEnd,
|
|
IN LPWSTR lpRemote,
|
|
IN PINISPOOLER pIniSpooler
|
|
);
|
|
|
|
LPBYTE
|
|
CopyIniDriverFilesToDriverInfo(
|
|
IN LPDRIVER_INFO_VERSION pDriverVersion,
|
|
IN PINIVERSION pIniVersion,
|
|
IN PINIDRIVER pIniDriver,
|
|
IN LPCWSTR pszDriverVersionDir,
|
|
IN LPBYTE pEnd
|
|
);
|
|
|
|
LPBYTE
|
|
FillDriverInfo (
|
|
IN LPDRIVER_INFO_VERSION pDriverFile,
|
|
IN DWORD Index,
|
|
IN PINIVERSION pIniVersion,
|
|
IN LPCWSTR pszPrefix,
|
|
IN LPCWSTR pszFileName,
|
|
IN DRIVER_FILE_TYPE FileType,
|
|
IN LPBYTE pEnd
|
|
);
|
|
|
|
BOOL GetDriverFileCachedVersion(
|
|
IN PINIVERSION pIniVersion,
|
|
IN LPCWSTR pFileName,
|
|
OUT DWORD *pFileVersion
|
|
);
|
|
|
|
BOOL
|
|
DriverAddedOrUpgraded (
|
|
IN PINTERNAL_DRV_FILE pInternalDriverFiles,
|
|
IN DWORD dwFileCount
|
|
);
|
|
|
|
BOOL
|
|
BuildDependentFilesFromDriverInfo (
|
|
IN LPDRIVER_INFO_VERSION pDriverInfo,
|
|
OUT LPWSTR *ppDependentFiles
|
|
);
|
|
|
|
VOID
|
|
UpdateDriverFileVersion(
|
|
IN PINIVERSION pIniVersion,
|
|
IN PINTERNAL_DRV_FILE pInternalDriverFiles,
|
|
IN DWORD FileCount
|
|
);
|
|
|
|
BOOL SaveDriverVersionForUpgrade(
|
|
IN HKEY hDriverKey,
|
|
IN PDRIVER_INFO_VERSION pDriverVersion,
|
|
IN LPWSTR pName,
|
|
IN DWORD dwDriverMoved,
|
|
IN DWORD dwVersion
|
|
);
|
|
|
|
DWORD
|
|
CopyFileToClusterDirectory (
|
|
IN PINISPOOLER pIniSpooler,
|
|
IN PINIENVIRONMENT pIniEnvironment,
|
|
IN PINIVERSION pIniVersion,
|
|
IN PINTERNAL_DRV_FILE pInternalDriverFiles,
|
|
IN DWORD FileCount
|
|
);
|
|
|
|
VOID
|
|
CleanupInternalDriverInfo(
|
|
PINTERNAL_DRV_FILE pInternalDriverFiles,
|
|
DWORD FileCount
|
|
);
|
|
|
|
BOOL
|
|
GetDriverFileVersionsFromNames(
|
|
IN PINTERNAL_DRV_FILE pInternalDriverFiles,
|
|
IN DWORD dwCount
|
|
);
|
|
|
|
BOOL
|
|
GetDriverFileVersions(
|
|
IN LPDRIVER_INFO_VERSION pDriverVersion,
|
|
IN PINTERNAL_DRV_FILE pInternalDriverFiles,
|
|
IN DWORD dwCount
|
|
);
|
|
|
|
BOOL
|
|
GetFileNamesFromDriverVersionInfo (
|
|
IN LPDRIVER_INFO_VERSION pDriverInfo,
|
|
OUT LPWSTR *ppszDriverPath,
|
|
OUT LPWSTR *ppszConfigFile,
|
|
OUT LPWSTR *ppszDataFile,
|
|
OUT LPWSTR *ppszHelpFile
|
|
);
|
|
|
|
BOOL
|
|
WaitRequiredForDriverUnload(
|
|
IN PINISPOOLER pIniSpooler,
|
|
IN PINIENVIRONMENT pIniEnvironment,
|
|
IN PINIVERSION pIniVersion,
|
|
IN PINIDRIVER pIniDriver,
|
|
IN DWORD dwLevel,
|
|
IN LPBYTE pDriverInfo,
|
|
IN DWORD dwFileCopyFlags,
|
|
IN OUT PINTERNAL_DRV_FILE pInternalDriverFiles,
|
|
IN DWORD dwFileCount,
|
|
IN DWORD dwVersion,
|
|
IN BOOL bDriverMoved,
|
|
OUT LPBOOL pbSuccess
|
|
);
|
|
|
|
BOOL FilesUnloaded(
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
LPWSTR pDriverFile,
|
|
LPWSTR pConfigFile,
|
|
DWORD dwDriverAttributes);
|
|
|
|
DWORD StringSizeInBytes(
|
|
LPWSTR pString,
|
|
BOOL bMultiSz);
|
|
|
|
BOOL SaveParametersForUpgrade(
|
|
LPWSTR pName,
|
|
BOOL bDriverMoved,
|
|
DWORD dwLevel,
|
|
LPBYTE pDriverInfo,
|
|
DWORD dwVersion);
|
|
|
|
VOID CleanUpResources(
|
|
LPWSTR pKeyName,
|
|
LPWSTR pSplName,
|
|
PDRIVER_INFO_6 pDriverInfo,
|
|
PINTERNAL_DRV_FILE *pInternalDriverFiles,
|
|
DWORD dwFileCount);
|
|
|
|
BOOL RestoreParametersForUpgrade(
|
|
HKEY hUpgradeKey,
|
|
DWORD dwIndex,
|
|
LPWSTR *pKeyName,
|
|
LPWSTR *pSplName,
|
|
LPDWORD pdwLevel,
|
|
LPDWORD pdwDriverMoved,
|
|
PDRIVER_INFO_6 *ppDriverInfo);
|
|
|
|
VOID CleanUpgradeDirectories();
|
|
|
|
VOID FreeDriverInfo6(
|
|
PDRIVER_INFO_6 pDriver6
|
|
);
|
|
|
|
BOOL RegGetValue(
|
|
HKEY hDriverKey,
|
|
LPWSTR pValueName,
|
|
LPBYTE *pValue
|
|
);
|
|
|
|
BOOL
|
|
WriteDriverIni(
|
|
PINIDRIVER pIniDriver,
|
|
PINIVERSION pIniVersion,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINISPOOLER pIniSpooler
|
|
);
|
|
|
|
BOOL
|
|
DeleteDriverIni(
|
|
PINIDRIVER pIniDriver,
|
|
PINIVERSION pIniVersion,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINISPOOLER pIniSpooler
|
|
);
|
|
|
|
BOOL
|
|
CreateVersionDirectory(
|
|
PINIVERSION pIniVersion,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
BOOL bUpdate,
|
|
PINISPOOLER pIniSpooler
|
|
);
|
|
|
|
DWORD
|
|
GetDriverInfoSize(
|
|
PINIDRIVER pIniDriver,
|
|
DWORD Level,
|
|
PINIVERSION pIniVersion,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
LPWSTR lpRemote,
|
|
PINISPOOLER pIniSpooler
|
|
);
|
|
|
|
BOOL
|
|
DeleteDriverVersionIni(
|
|
PINIVERSION pIniVersion,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINISPOOLER pIniSpooler
|
|
);
|
|
|
|
BOOL
|
|
WriteDriverVersionIni(
|
|
PINIVERSION pIniVersion,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINISPOOLER pIniSpooler
|
|
);
|
|
|
|
PINIDRIVER
|
|
FindDriverEntry(
|
|
PINIVERSION pIniVersion,
|
|
LPWSTR pszName
|
|
);
|
|
|
|
PINIDRIVER
|
|
CreateDriverEntry(
|
|
IN PINIENVIRONMENT pIniEnvironment,
|
|
IN PINIVERSION pIniVersion,
|
|
IN DWORD Level,
|
|
IN LPBYTE pDriverInfo,
|
|
IN DWORD dwFileCopyFlags,
|
|
IN PINISPOOLER pIniSpooler,
|
|
IN OUT PINTERNAL_DRV_FILE pInternalDriverFiles,
|
|
IN DWORD FileCount,
|
|
IN DWORD dwTempDir,
|
|
IN PINIDRIVER pOldIniDriver
|
|
);
|
|
|
|
BOOL
|
|
IsKMPD(
|
|
LPWSTR pDriverName
|
|
);
|
|
|
|
VOID
|
|
CheckDriverAttributes(
|
|
PINISPOOLER pIniSpooler,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINIVERSION pIniVersion,
|
|
PINIDRIVER pIniDriver
|
|
);
|
|
|
|
BOOL
|
|
NotifyDriver(
|
|
PINISPOOLER pIniSpooler,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINIVERSION pIniVersion,
|
|
PINIDRIVER pIniDriver,
|
|
DWORD dwDriverEvent,
|
|
DWORD dwParameter
|
|
);
|
|
|
|
BOOL
|
|
AddTempDriver(
|
|
IN PINISPOOLER pIniSpooler,
|
|
IN PINIENVIRONMENT pIniEnvironment,
|
|
IN PINIVERSION pIniVersion,
|
|
IN DWORD dwLevel,
|
|
IN LPBYTE pDriverInfo,
|
|
IN DWORD dwFileCopyFlags,
|
|
IN OUT PINTERNAL_DRV_FILE pInternalDriverFiles,
|
|
IN DWORD dwFileCount,
|
|
IN DWORD dwVersion,
|
|
IN BOOL bDriverMoved
|
|
);
|
|
|
|
BOOL
|
|
CompleteDriverUpgrade(
|
|
IN PINISPOOLER pIniSpooler,
|
|
IN PINIENVIRONMENT pIniEnvironment,
|
|
IN PINIVERSION pIniVersion,
|
|
IN PINIDRIVER pIniDriver,
|
|
IN DWORD dwLevel,
|
|
IN LPBYTE pDriverInfo,
|
|
IN DWORD dwFileCopyFlags,
|
|
IN PINTERNAL_DRV_FILE pInternalDriverFiles,
|
|
IN DWORD dwFileCount,
|
|
IN DWORD dwVersion,
|
|
IN DWORD dwTempDir,
|
|
IN BOOL bDriverMoved,
|
|
IN BOOL bDriverFileMoved,
|
|
IN BOOL bConfigFileMoved
|
|
);
|
|
|
|
BOOL
|
|
FilesInUse(
|
|
PINIVERSION pIniVersion,
|
|
PINIDRIVER pIniDriver
|
|
);
|
|
|
|
BOOL
|
|
UpdateDriverFileRefCnt(
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINIVERSION pIniVersion,
|
|
PINIDRIVER pIniDriver,
|
|
LPCWSTR pDirectory,
|
|
DWORD dwDeleteFlag,
|
|
BOOL bIncrementFlag
|
|
);
|
|
|
|
PDRVREFCNT
|
|
IncrementFileRefCnt(
|
|
PINIVERSION pIniVersion,
|
|
LPCWSTR szFileName
|
|
);
|
|
|
|
PDRVREFCNT
|
|
DecrementFileRefCnt(
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINIVERSION pIniVersion,
|
|
PINIDRIVER pIniDriver,
|
|
LPCWSTR szFileName,
|
|
LPCWSTR szDirectory,
|
|
DWORD dwDeleteFlag
|
|
);
|
|
|
|
VOID
|
|
RemovePendingUpgradeForDeletedDriver(
|
|
LPWSTR pDriverName,
|
|
DWORD dwVersion,
|
|
PINISPOOLER pIniSpooler
|
|
);
|
|
|
|
VOID
|
|
RemoveDriverTempFiles(
|
|
PINISPOOLER pIniSpooler,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINIVERSION pIniVersion,
|
|
PINIDRIVER pIniDriver
|
|
);
|
|
|
|
VOID
|
|
DeleteDriverEntry(
|
|
PINIVERSION pIniVersion,
|
|
PINIDRIVER pIniDriver
|
|
);
|
|
|
|
PINIVERSION
|
|
CreateVersionEntry(
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
DWORD dwVersion,
|
|
PINISPOOLER pInispooler
|
|
);
|
|
|
|
DWORD
|
|
GetEnvironmentScratchDirectory(
|
|
LPWSTR pDir,
|
|
DWORD MaxLength,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
BOOL Remote
|
|
);
|
|
|
|
VOID
|
|
SetOldDateOnDriverFilesInScratchDirectory(
|
|
PINTERNAL_DRV_FILE pInternalDriverFiles,
|
|
DWORD FileCount,
|
|
PINISPOOLER pIniSpooler
|
|
);
|
|
|
|
BOOL
|
|
CopyFilesToFinalDirectory(
|
|
PINISPOOLER pIniSpooler,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINIVERSION pIniVersion,
|
|
PINTERNAL_DRV_FILE pInternalDriverFiles,
|
|
DWORD dwFileCount,
|
|
DWORD dwFileCopyFlags,
|
|
BOOL bImpersonateOnCreate,
|
|
LPBOOL pbFilesMoved
|
|
);
|
|
|
|
BOOL
|
|
CreateInternalDriverFileArray(
|
|
IN DWORD Level,
|
|
IN LPBYTE pDriverInfo,
|
|
OUT PINTERNAL_DRV_FILE *pInternalDriverFiles,
|
|
OUT LPDWORD pFileCount,
|
|
IN BOOL bUseScratchDir,
|
|
IN PINIENVIRONMENT pIniEnvironment,
|
|
IN BOOL bFileNamesOnly
|
|
);
|
|
|
|
BOOL
|
|
CheckFileCopyOptions(
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINIVERSION pIniVersion,
|
|
PINIDRIVER pIniDriver,
|
|
PINTERNAL_DRV_FILE pInternalDriverFiles,
|
|
DWORD dwFileCount,
|
|
DWORD dwFileCopyFlags,
|
|
LPBOOL pbUpgrade
|
|
);
|
|
|
|
/*++
|
|
|
|
Routine Name
|
|
|
|
FindIndexInDrvFileInfo
|
|
|
|
Routine Description:
|
|
|
|
Checks if a certain driver file is present in an DRIVER_FILE_INFO
|
|
file set. The search is done by the type of the file. The index
|
|
to the first occurence of the file is returned.
|
|
|
|
Arguments:
|
|
|
|
pDrvFileInfo - pointer to DRIVER_FILE_INFO array
|
|
cElements - count of elements in pDrvFileInfo
|
|
kFileType - file type to search for
|
|
pIndex - on success contains the index of the found file in
|
|
the pDrvFileInfo array
|
|
|
|
Return Value:
|
|
|
|
S_OK - the file was found and pIndex is usable
|
|
S_FALSE - the file was not found, pIndex is not usable
|
|
E_INVALIDARG - invalid arguments were passed in
|
|
|
|
--*/
|
|
HRESULT
|
|
FindIndexInDrvFileInfo(
|
|
IN DRIVER_FILE_INFO *pDrvFileInfo,
|
|
IN DWORD cElements,
|
|
IN DRIVER_FILE_TYPE kFileType,
|
|
OUT DWORD *pIndex
|
|
)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
if (pDrvFileInfo && pIndex)
|
|
{
|
|
DWORD i;
|
|
|
|
//
|
|
// Not found
|
|
//
|
|
hr = S_FALSE;
|
|
|
|
for (i = 0; i < cElements; i++)
|
|
{
|
|
if (pDrvFileInfo[i].FileType == kFileType)
|
|
{
|
|
*pIndex = i;
|
|
|
|
hr = S_OK;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
BOOL
|
|
LocalStartSystemRestorePoint(
|
|
IN PCWSTR pszDriverName,
|
|
OUT HANDLE *phRestorePoint
|
|
);
|
|
|
|
/*++
|
|
|
|
Routine Name
|
|
|
|
IsDriverInstalled
|
|
|
|
Routine Description:
|
|
|
|
Checks if a certain driver is already installed.
|
|
|
|
Arguments:
|
|
|
|
pDriver2 - pointer to DRIVER_INFO_2
|
|
pIniSpooler - pointer to spooler structure
|
|
|
|
Return Value:
|
|
|
|
TRUE - driver is installed on the pIniSpooler
|
|
FALSE - driver is not present in pIniSpooler
|
|
|
|
--*/
|
|
BOOL
|
|
IsDriverInstalled(
|
|
DRIVER_INFO_2 *pDriver2,
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
{
|
|
BOOL bReturn = FALSE;
|
|
|
|
if (pIniSpooler &&
|
|
pDriver2 &&
|
|
pDriver2->pName)
|
|
{
|
|
PINIENVIRONMENT pIniEnv;
|
|
PINIVERSION pIniVer;
|
|
|
|
EnterSplSem();
|
|
|
|
if ((pIniEnv = FindEnvironment(pDriver2->pEnvironment && *pDriver2->pEnvironment ?
|
|
pDriver2->pEnvironment : szEnvironment,
|
|
pIniSpooler)) &&
|
|
(pIniVer = FindVersionEntry(pIniEnv, pDriver2->cVersion)) &&
|
|
FindDriverEntry(pIniVer, pDriver2->pName))
|
|
{
|
|
bReturn = TRUE;
|
|
}
|
|
|
|
LeaveSplSem();
|
|
}
|
|
|
|
DBGMSG(DBG_CLUSTER, ("IsDriverInstalled returns %u\n", bReturn));
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
BOOL
|
|
LocalAddPrinterDriver(
|
|
LPWSTR pName,
|
|
DWORD Level,
|
|
LPBYTE pDriverInfo
|
|
)
|
|
{
|
|
return LocalAddPrinterDriverEx( pName,
|
|
Level,
|
|
pDriverInfo,
|
|
APD_COPY_NEW_FILES );
|
|
}
|
|
|
|
BOOL
|
|
LocalAddPrinterDriverEx(
|
|
LPWSTR pName,
|
|
DWORD Level,
|
|
LPBYTE pDriverInfo,
|
|
DWORD dwFileCopyFlags
|
|
)
|
|
{
|
|
PINISPOOLER pIniSpooler;
|
|
BOOL bReturn = TRUE;
|
|
|
|
if (Level == 7)
|
|
{
|
|
bReturn = FALSE;
|
|
SetLastError(ERROR_INVALID_LEVEL);
|
|
}
|
|
else if (dwFileCopyFlags & APD_COPY_TO_ALL_SPOOLERS)
|
|
{
|
|
//
|
|
// Mask flag otherwise SplAddPrinterDriverEx will be fail.
|
|
// This flag is used by Windows Update to update all the
|
|
// drivers for all the spoolers hosted by the local machine
|
|
//
|
|
dwFileCopyFlags = dwFileCopyFlags & ~APD_COPY_TO_ALL_SPOOLERS;
|
|
|
|
for (pIniSpooler = pLocalIniSpooler;
|
|
pIniSpooler && bReturn;
|
|
pIniSpooler = pIniSpooler->pIniNextSpooler)
|
|
{
|
|
//
|
|
// We do not want to add a driver to a pIniSpooler. We want to update
|
|
// an existing driver. That is why we check if the driver is already
|
|
// installed
|
|
//
|
|
if ((pIniSpooler->SpoolerFlags & SPL_TYPE_LOCAL ||
|
|
pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER) &&
|
|
IsDriverInstalled((DRIVER_INFO_2 *)pDriverInfo, pIniSpooler))
|
|
{
|
|
EnterSplSem();
|
|
INCSPOOLERREF(pIniSpooler);
|
|
LeaveSplSem();
|
|
|
|
//
|
|
// The 6th parameter indicates whether to use the scratch
|
|
// directory (TRUE) or not (FALSE)
|
|
//
|
|
bReturn = SplAddPrinterDriverEx(pName,
|
|
Level,
|
|
pDriverInfo,
|
|
dwFileCopyFlags,
|
|
pIniSpooler,
|
|
!(dwFileCopyFlags & APD_COPY_FROM_DIRECTORY),
|
|
IMPERSONATE_USER);
|
|
|
|
DBGMSG(DBG_CLUSTER, ("LocalAddPrinterDriverEx adding driver to "TSTR" bRet %u\n",
|
|
pIniSpooler->pMachineName, bReturn));
|
|
|
|
EnterSplSem();
|
|
DECSPOOLERREF(pIniSpooler);
|
|
LeaveSplSem();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!(pIniSpooler = FindSpoolerByNameIncRef(pName, NULL)))
|
|
{
|
|
return ROUTER_UNKNOWN;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The 6th parameter indicates whether to use the scratch
|
|
// directory (TRUE) or not (FALSE)
|
|
//
|
|
bReturn = SplAddPrinterDriverEx(pName,
|
|
Level,
|
|
pDriverInfo,
|
|
dwFileCopyFlags,
|
|
pIniSpooler,
|
|
!(dwFileCopyFlags & APD_COPY_FROM_DIRECTORY),
|
|
IMPERSONATE_USER);
|
|
}
|
|
|
|
FindSpoolerByNameDecRef(pIniSpooler);
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
BOOL
|
|
SplAddPrinterDriverEx(
|
|
LPWSTR pName,
|
|
DWORD Level,
|
|
LPBYTE pDriverInfo,
|
|
DWORD dwFileCopyFlags,
|
|
PINISPOOLER pIniSpooler,
|
|
BOOL bUseScratchDir,
|
|
BOOL bImpersonateOnCreate
|
|
)
|
|
{
|
|
PINISPOOLER pTempIniSpooler = pIniSpooler;
|
|
|
|
//
|
|
// At this time we do not know if the server name in pName refers to our local
|
|
// machine. We are trying to add the server name to the name cache. The name
|
|
// cache functions decide if the name refers to the local machine and if positive,
|
|
// add an entry for it in the cache.
|
|
//
|
|
CacheAddName(pName);
|
|
|
|
DBGMSG( DBG_TRACE, ("AddPrinterDriver\n"));
|
|
|
|
if (!MyName( pName, pIniSpooler )) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Right now all drivers are global ie they are shared between all IniSpoolers
|
|
// If we want to impersonate the user then lets validate against pLocalIniSpooler
|
|
// whilch causes all the security checking to happen, rather than using the passed
|
|
// in IniSpooler which might not. See win32spl for detail of point and print.
|
|
|
|
if ( bImpersonateOnCreate ) {
|
|
|
|
pTempIniSpooler = pLocalIniSpooler;
|
|
}
|
|
|
|
if ( !ValidateObjectAccess( SPOOLER_OBJECT_SERVER,
|
|
SERVER_ACCESS_ADMINISTER,
|
|
NULL, NULL, pTempIniSpooler)) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return ( InternalAddPrinterDriverEx( pName,
|
|
Level,
|
|
pDriverInfo,
|
|
dwFileCopyFlags,
|
|
pIniSpooler,
|
|
bUseScratchDir,
|
|
bImpersonateOnCreate ) );
|
|
|
|
}
|
|
|
|
/*+
|
|
|
|
Description:
|
|
|
|
This function creates a process for a driver to be installed in and launches an entry point
|
|
from ntprint.dll to install the driver inside that process. This install will take a driver info 7
|
|
struct and do an inf based install with the information in that structure.
|
|
|
|
Arguments:
|
|
|
|
pDriverInfo7 -- driver_info_7 structure
|
|
|
|
Returns:
|
|
|
|
TRUE on success; FALSE otherwise
|
|
The function sets the last error in case of failure
|
|
|
|
Notes:
|
|
|
|
If the driver info pszInfName field is anything other than NULL, this call will fail.
|
|
|
|
--*/
|
|
BOOL
|
|
InternalINFInstallDriver(
|
|
LPDRIVER_INFO_7 pDriverInfo7
|
|
)
|
|
{
|
|
DWORD Error = ERROR_INVALID_PARAMETER;
|
|
LPWSTR pszPipe = NULL;
|
|
DWORD dwCode = ERROR_INVALID_PARAMETER;
|
|
|
|
//
|
|
// Passing with an inf name is not supported.
|
|
//
|
|
if (!pDriverInfo7->pszInfName )
|
|
{
|
|
LPWSTR pszCmdString = NULL;
|
|
LPCWSTR pszRundllName = L"rundll32.exe";
|
|
LPCWSTR pszRundllArgs = L"rundll32.exe ntprint.dll,ServerInstall ";
|
|
LPWSTR pszRundllPath = NULL;
|
|
|
|
Error = StrCatAlloc(&pszCmdString, pszRundllArgs, pDriverInfo7->pszDriverName, NULL);
|
|
|
|
if(Error == ERROR_SUCCESS)
|
|
{
|
|
Error = StrCatSystemPath(pszRundllName, kSystemDir, &pszRundllPath);
|
|
}
|
|
|
|
if(Error == ERROR_SUCCESS)
|
|
{
|
|
Error = RunProcess(pszRundllPath, pszCmdString, INFINITE, &dwCode);
|
|
}
|
|
|
|
if(Error == ERROR_SUCCESS)
|
|
{
|
|
Error = dwCode;
|
|
}
|
|
|
|
if (pszCmdString) FreeSplMem(pszCmdString);
|
|
if (pszRundllPath) FreeSplMem(pszRundllPath);
|
|
if (pszPipe) FreeSplMem(pszPipe);
|
|
}
|
|
|
|
if (Error != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(Error);
|
|
}
|
|
|
|
return Error == ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
BOOL
|
|
BuildTrueDependentFileField(
|
|
LPWSTR pDriverPath,
|
|
LPWSTR pDataFile,
|
|
LPWSTR pConfigFile,
|
|
LPWSTR pHelpFile,
|
|
LPWSTR pInputDependentFiles,
|
|
LPWSTR *ppDependentFiles
|
|
)
|
|
{
|
|
LPWSTR psz, psz2;
|
|
LPCWSTR pszFileNamePart;
|
|
SIZE_T Size;
|
|
|
|
if ( !pInputDependentFiles )
|
|
return TRUE;
|
|
|
|
for ( psz = pInputDependentFiles, Size = 0 ;
|
|
psz && *psz ; psz += wcslen(psz) + 1 ) {
|
|
|
|
pszFileNamePart = FindFileName(psz);
|
|
|
|
if( !pszFileNamePart ){
|
|
break;
|
|
}
|
|
|
|
if ( wstrcmpEx(FindFileName(pDriverPath), pszFileNamePart, FALSE) &&
|
|
wstrcmpEx(FindFileName(pDataFile), pszFileNamePart, FALSE) &&
|
|
wstrcmpEx(FindFileName(pConfigFile), pszFileNamePart, FALSE) &&
|
|
wstrcmpEx(FindFileName(pHelpFile), pszFileNamePart, FALSE) ) {
|
|
|
|
Size += wcslen(psz) + 1;
|
|
}
|
|
}
|
|
|
|
if ( !Size )
|
|
return TRUE;
|
|
|
|
//
|
|
// Increase the Size to accommodate the last \0
|
|
//
|
|
++Size;
|
|
|
|
*ppDependentFiles = AllocSplMem((DWORD)(Size*sizeof(WCHAR)));
|
|
if ( !*ppDependentFiles )
|
|
return FALSE;
|
|
|
|
psz = pInputDependentFiles;
|
|
psz2 = *ppDependentFiles;
|
|
while ( *psz ) {
|
|
|
|
pszFileNamePart = FindFileName(psz);
|
|
|
|
if( !pszFileNamePart ){
|
|
break;
|
|
}
|
|
|
|
if ( wstrcmpEx(FindFileName(pDriverPath), pszFileNamePart, FALSE) &&
|
|
wstrcmpEx(FindFileName(pDataFile), pszFileNamePart, FALSE) &&
|
|
wstrcmpEx(FindFileName(pConfigFile), pszFileNamePart, FALSE) &&
|
|
wstrcmpEx(FindFileName(pHelpFile), pszFileNamePart, FALSE) ) {
|
|
|
|
StrCchCopyMultipleStr(psz2, Size, psz, &psz2, &Size);
|
|
}
|
|
|
|
psz += wcslen(psz) + 1;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
IsCompatibleDriver(
|
|
LPWSTR pszDriverName,
|
|
LPWSTR pszDeviceDriverPath,
|
|
LPWSTR pszEnvironment,
|
|
DWORD dwMajorVersion,
|
|
DWORD *pdwBlockingStatus
|
|
)
|
|
/*++
|
|
Function Description: Call this function to prevent bad drivers from getting installed.
|
|
Check if driver is listed in printupg.inf (lists all known bad driver files ).
|
|
Since printupg.inf contains only driver name, this function should be called
|
|
only for verions 2 drivers.
|
|
Otherwise,it will treat a version 3 driver "DriverName" as bad,
|
|
if it is a bad version 2 driver.
|
|
|
|
Parameters: pszDriverName -- driver name
|
|
pszDeviceDriverPath -- filename for the file that contains the device driver
|
|
pszEnvironment -- environment string for the driver such as "Windows NT x86"
|
|
dwMajorVersion -- major version of the driver
|
|
pdwBlockingStatus -- driver blocking status
|
|
|
|
Return Value: ERROR_SUCCESS if succeeded
|
|
ERROR_INVALID_PARAMETER if invalid parameters
|
|
GetLastError for any other errors
|
|
|
|
--*/
|
|
{
|
|
WIN32_FIND_DATA DeviceDriverData;
|
|
pfPSetupIsCompatibleDriver pfnPSetupIsCompatibleDriver;
|
|
UINT uOldErrMode;
|
|
HANDLE hFileExists = INVALID_HANDLE_VALUE;
|
|
HANDLE hLibrary = NULL;
|
|
DWORD LastError = ERROR_SUCCESS;
|
|
DWORD dwBlockingStatus = BSP_PRINTER_DRIVER_OK;
|
|
|
|
|
|
uOldErrMode = SetErrorMode( SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
|
|
|
|
if( !pszDriverName || !pszDeviceDriverPath || !pszEnvironment ||
|
|
!*pszDriverName || !*pszDeviceDriverPath || !*pszEnvironment || !pdwBlockingStatus) {
|
|
LastError = ERROR_INVALID_PARAMETER;
|
|
goto End;
|
|
}
|
|
|
|
*pdwBlockingStatus = BSP_PRINTER_DRIVER_OK;
|
|
|
|
hFileExists = FindFirstFile( pszDeviceDriverPath, &DeviceDriverData );
|
|
|
|
if (hFileExists == INVALID_HANDLE_VALUE) {
|
|
LastError = GetLastError();
|
|
goto End;
|
|
}
|
|
|
|
if( !(hLibrary = LoadLibrary( TEXT("ntprint.dll"))) ){
|
|
LastError = GetLastError();
|
|
goto End;
|
|
}
|
|
|
|
pfnPSetupIsCompatibleDriver = (pfPSetupIsCompatibleDriver)GetProcAddress( hLibrary, "PSetupIsCompatibleDriver" );
|
|
|
|
if( !pfnPSetupIsCompatibleDriver){
|
|
LastError = GetLastError();
|
|
goto End;
|
|
}
|
|
|
|
//
|
|
// NULL server name is OK since we know this is the local machine.
|
|
// PSetupIsCompatibleDriver uses this to determine the blocking
|
|
// level.
|
|
//
|
|
if ((pfnPSetupIsCompatibleDriver)( NULL,
|
|
pszDriverName,
|
|
pszDeviceDriverPath,
|
|
pszEnvironment,
|
|
dwMajorVersion,
|
|
&DeviceDriverData.ftLastWriteTime,
|
|
&dwBlockingStatus,
|
|
NULL)) {
|
|
*pdwBlockingStatus = dwBlockingStatus;
|
|
} else {
|
|
|
|
LastError = GetLastError();
|
|
}
|
|
|
|
|
|
End:
|
|
|
|
if( hFileExists != INVALID_HANDLE_VALUE ){
|
|
FindClose(hFileExists);
|
|
}
|
|
|
|
if( hLibrary ){
|
|
FreeLibrary( hLibrary );
|
|
}
|
|
|
|
SetErrorMode( uOldErrMode );
|
|
|
|
return LastError;
|
|
|
|
}
|
|
|
|
BOOL
|
|
IsAnICMFile(
|
|
LPCWSTR pszFileName
|
|
)
|
|
|
|
/*++
|
|
Function Description: Checks for ICM extension on the filename
|
|
|
|
Parameters: pszFileName - file name
|
|
|
|
Return Values: TRUE for ICM files; FALSE otherwise
|
|
--*/
|
|
|
|
{
|
|
DWORD dwLen = wcslen(pszFileName);
|
|
LPWSTR psz = (LPWSTR)pszFileName+dwLen-4;
|
|
|
|
if ( dwLen > 3 &&
|
|
( !_wcsicmp(psz, L".ICM") || !_wcsicmp(psz, L".ICC")) )
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
ValidateDriverInfo(
|
|
IN LPBYTE pDriverInfo,
|
|
IN DWORD Level,
|
|
IN DWORD dwFileCopyFlags,
|
|
IN BOOL bCopyFilesToClusterDisk,
|
|
IN PINISPOOLER pIniSpooler
|
|
)
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
ValidateDriverInfo
|
|
|
|
Routine Description:
|
|
|
|
Validates information contained in a buffer depending on level and
|
|
file copy flags.
|
|
|
|
Arguments:
|
|
|
|
pDriverInfo - pointer to a buffer containing DRIVER_INFO_ data.
|
|
Level - 2, 3 ,4 ,6 , 7, DRIVER_INFO_VERSION_LEVEL
|
|
dwFileCopyFlags - file copy flags
|
|
bCopyFilesToClusterDisk - cluster flags
|
|
pIniSpooler - pointer to Spooler structure
|
|
|
|
Return Value:
|
|
|
|
TRUE if the structure is valid.
|
|
|
|
--*/
|
|
{
|
|
BOOL bRetValue = FALSE;
|
|
DWORD LastError = ERROR_SUCCESS;
|
|
LPWSTR pszDriverName = NULL;
|
|
LPWSTR pszDriverPath = NULL;
|
|
LPWSTR pszConfigFile = NULL;
|
|
LPWSTR pszDataFile = NULL;
|
|
LPWSTR pszEnvironment = NULL;
|
|
LPWSTR pszMonitorName = NULL;
|
|
LPWSTR pszDefaultDataType = NULL;
|
|
DWORD dwMajorVersion;
|
|
|
|
PDRIVER_INFO_2 pDriver2 = NULL;
|
|
PDRIVER_INFO_3 pDriver3 = NULL;
|
|
PDRIVER_INFO_VERSION pDriverVersion = NULL;
|
|
|
|
PINIENVIRONMENT pIniEnvironment = NULL;
|
|
PINIMONITOR pIniLangMonitor = NULL;
|
|
|
|
try {
|
|
|
|
if (!pDriverInfo)
|
|
{
|
|
LastError = ERROR_INVALID_PARAMETER;
|
|
leave;
|
|
}
|
|
|
|
switch (Level)
|
|
{
|
|
case 2:
|
|
{
|
|
pDriver2 = (PDRIVER_INFO_2) pDriverInfo;
|
|
pszDriverName = pDriver2->pName;
|
|
pszDriverPath = pDriver2->pDriverPath;
|
|
pszConfigFile = pDriver2->pConfigFile;
|
|
pszDataFile = pDriver2->pDataFile;
|
|
dwMajorVersion = pDriver2->cVersion;
|
|
|
|
if (pDriver2->pEnvironment && *pDriver2->pEnvironment)
|
|
{
|
|
pszEnvironment = pDriver2->pEnvironment;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 3:
|
|
case 4:
|
|
case 6:
|
|
{
|
|
pDriver3 = (PDRIVER_INFO_3) pDriverInfo;
|
|
pszDriverName = pDriver3->pName;
|
|
pszDriverPath = pDriver3->pDriverPath;
|
|
pszConfigFile = pDriver3->pConfigFile;
|
|
pszDataFile = pDriver3->pDataFile;
|
|
dwMajorVersion = pDriver3->cVersion;
|
|
pszMonitorName = pDriver3->pMonitorName;
|
|
pszDefaultDataType = pDriver3->pDefaultDataType;
|
|
|
|
if (pDriver3->pEnvironment && *pDriver3->pEnvironment)
|
|
{
|
|
pszEnvironment = pDriver3->pEnvironment;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 7:
|
|
{
|
|
LPDRIVER_INFO_7 pDriverInfo7 = (LPDRIVER_INFO_7)pDriverInfo;
|
|
|
|
if (!pDriverInfo7 ||
|
|
pDriverInfo7->cbSize < sizeof(DRIVER_INFO_7)||
|
|
!pDriverInfo7->pszDriverName ||
|
|
!*pDriverInfo7->pszDriverName ||
|
|
wcslen(pDriverInfo7->pszDriverName) >= MAX_PATH)
|
|
{
|
|
LastError = ERROR_INVALID_PARAMETER;
|
|
}
|
|
//
|
|
// We don't want to do any more of the validation below, so leave.
|
|
//
|
|
leave;
|
|
break;
|
|
}
|
|
case DRIVER_INFO_VERSION_LEVEL:
|
|
{
|
|
pDriverVersion = (LPDRIVER_INFO_VERSION)pDriverInfo;
|
|
pszDriverName = pDriverVersion->pName;
|
|
|
|
if (!GetFileNamesFromDriverVersionInfo(pDriverVersion,
|
|
&pszDriverPath,
|
|
&pszConfigFile,
|
|
&pszDataFile,
|
|
NULL))
|
|
{
|
|
LastError = ERROR_INVALID_PARAMETER;
|
|
leave;
|
|
}
|
|
|
|
if (pDriverVersion->pEnvironment != NULL &&
|
|
*pDriverVersion->pEnvironment != L'\0')
|
|
{
|
|
pszEnvironment = pDriverVersion->pEnvironment;
|
|
}
|
|
|
|
pszMonitorName = pDriverVersion->pMonitorName;
|
|
pszDefaultDataType = pDriverVersion->pDefaultDataType;
|
|
dwMajorVersion = pDriverVersion->cVersion;
|
|
pszDriverName = pDriverVersion->pName;
|
|
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
LastError = ERROR_INVALID_LEVEL;
|
|
leave;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Validate driver name, driver file, config file and data file.
|
|
//
|
|
if ( !pszDriverName || !*pszDriverName || wcslen(pszDriverName) >= MAX_PATH ||
|
|
!pszDriverPath || !*pszDriverPath || wcslen(pszDriverPath) >= MAX_PATH ||
|
|
!pszConfigFile || !*pszConfigFile || wcslen(pszConfigFile) >= MAX_PATH ||
|
|
!pszDataFile || !*pszDataFile || wcslen(pszDataFile) >= MAX_PATH )
|
|
{
|
|
LastError = ERROR_INVALID_PARAMETER;
|
|
leave;
|
|
}
|
|
|
|
//
|
|
// We don't use Scratch directory when this flag is set.
|
|
// When APD_COPY_FROM_DIRECTORY is set, the temporay directory must
|
|
// be on the local machine.
|
|
// IsLocalFile checks is the file is on the same machine specified by
|
|
// the passed in spooler.
|
|
//
|
|
if (dwFileCopyFlags & APD_COPY_FROM_DIRECTORY)
|
|
{
|
|
if (!IsLocalFile(pszDriverPath, pIniSpooler) ||
|
|
!IsLocalFile(pszConfigFile, pIniSpooler))
|
|
{
|
|
LastError = ERROR_INVALID_PARAMETER;
|
|
leave;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Validate default data type (except for Win95 drivers)
|
|
//
|
|
if ( pszDefaultDataType &&
|
|
*pszDefaultDataType &&
|
|
_wcsicmp(pszEnvironment, szWin95Environment) &&
|
|
!FindDatatype(NULL, pszDefaultDataType))
|
|
{
|
|
LastError = ERROR_INVALID_DATATYPE;
|
|
leave;
|
|
}
|
|
|
|
//
|
|
// Validate monitor name (except for Win95 drivers)
|
|
//
|
|
if ( pszMonitorName &&
|
|
*pszMonitorName &&
|
|
_wcsicmp(pszEnvironment, szWin95Environment))
|
|
{
|
|
//
|
|
// Out driver is not a Win9x driver and it has a language monitor
|
|
//
|
|
if (pIniLangMonitor = FindMonitor(pszMonitorName, pLocalIniSpooler))
|
|
{
|
|
//
|
|
// Check if our pIniSpooler is a cluster spooler and we need to copy the
|
|
// language monitor file to disk. Note that FinEnvironment cannot fail.
|
|
// The environment has been validated by now.
|
|
//
|
|
if (bCopyFilesToClusterDisk &&
|
|
(pIniEnvironment = FindEnvironment(pszEnvironment, pIniSpooler)))
|
|
{
|
|
DBGMSG(DBG_CLUSTER, ("InternalAddPrinterDriverEx pIniLangMonitor = %x\n", pIniLangMonitor));
|
|
|
|
if ((LastError = PropagateMonitorToCluster(pIniLangMonitor->pName,
|
|
pIniLangMonitor->pMonitorDll,
|
|
pIniEnvironment->pName,
|
|
pIniEnvironment->pDirectory,
|
|
pIniSpooler)) != ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// We failed to propagate the montior to the cluster disk. Fail the call
|
|
//
|
|
leave;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBGMSG(DBG_CLUSTER, ("InternalAddPrinterDriverEx pIniLangMonitor = %x Not found\n", pIniLangMonitor));
|
|
LastError = ERROR_UNKNOWN_PRINT_MONITOR;
|
|
leave;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Validate environment.
|
|
//
|
|
SPLASSERT(pszEnvironment != NULL);
|
|
|
|
if (!FindEnvironment(pszEnvironment, pIniSpooler))
|
|
{
|
|
LastError = ERROR_INVALID_ENVIRONMENT;
|
|
leave;
|
|
}
|
|
|
|
} finally {
|
|
|
|
if (LastError != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(LastError);
|
|
}
|
|
else
|
|
{
|
|
bRetValue = TRUE;
|
|
}
|
|
}
|
|
|
|
return bRetValue;
|
|
}
|
|
|
|
BOOL
|
|
InternalAddPrinterDriverEx(
|
|
LPWSTR pName,
|
|
DWORD Level,
|
|
LPBYTE pDriverInfo,
|
|
DWORD dwFileCopyFlags,
|
|
PINISPOOLER pIniSpooler,
|
|
BOOL bUseScratchDir,
|
|
BOOL bImpersonateOnCreate
|
|
)
|
|
/*++
|
|
Function Description: This function adds/upgrades printer drivers. The new files may not be
|
|
used until the old drivers are unloaded. Thus the new functionality
|
|
associated with the new files may take a while to show up; either until
|
|
the DC count in the system goes to 0 or when the machine is rebooted.
|
|
|
|
Parameters: pName -- driver name
|
|
Level -- level of driver_info struct
|
|
pDriverInfo -- driver_info buffer
|
|
dwFileCopyFlags -- file copy options
|
|
pIniSpooler -- pointer to INISPOOLER struct
|
|
bUseScratchDir -- flag indicating location of the driver files
|
|
bImpersonateOnCreate -- flag for impersonating the client on creating and
|
|
moving files
|
|
|
|
Return Value: TRUE on success; FALSE otherwise
|
|
--*/
|
|
{
|
|
DWORD LastError = ERROR_SUCCESS;
|
|
BOOL bReturnValue = FALSE;
|
|
BOOL bDriverMoved = FALSE, bNewIniDriverCreated = FALSE;
|
|
LPWSTR pEnvironment = szEnvironment;
|
|
PINTERNAL_DRV_FILE pInternalDriverFiles = NULL;
|
|
DWORD dwMajorVersion;
|
|
|
|
PINIDRIVER pIniDriver = NULL;
|
|
PINIENVIRONMENT pIniEnvironment;
|
|
PINIVERSION pIniVersion;
|
|
LPWSTR pszDriverPath;
|
|
LPWSTR pszDriverName;
|
|
DWORD dwBlockingStatus = BSP_PRINTER_DRIVER_OK;
|
|
BOOL bCopyFilesToClusterDisk;
|
|
BOOL bBadDriver = FALSE;
|
|
HANDLE hRestorePoint = NULL;
|
|
BOOL bSetSystemRestorePoint = FALSE;
|
|
BOOL bIsSystemRestorePointSet = FALSE;
|
|
DWORD FileCount = 0;
|
|
|
|
//
|
|
// If the pIniSpooler where we add the driver is a cluster type spooler,
|
|
// then besides its normal tasks, it also needs to propagte the driver
|
|
// files to the cluster disk. Thus the driver files will be available
|
|
// on each node where the cluster spooler fails over. SplAddPrinterDriverEx
|
|
// is the function that calls this one. SplAddPrinterDriverEx can be called
|
|
// in 2 types of context:
|
|
// 1) The caller is cluster unaware and wants to add a driver. Then InternalAdd
|
|
// PrinterDriverEX will propagate driver files to the cluster disk, if the
|
|
// pIniSpooler happens to be of cluster type
|
|
// 2) The caller of this function is SplCreateSpooler when pIniSpooler is a
|
|
// cluster spooler. In this case that caller uses the files on the cluster
|
|
// disk and calls the function to add the driver from the cluster disk to the
|
|
// local node. The driver files will be installed on the local machine. They will
|
|
// not be shared with the pLocalIniSpooler. We need the driver files locally.
|
|
// We can't load them off the driver disk. Otherwise, on a fail over, apps
|
|
// who loaded a driver file will get an in page error.
|
|
// The following flag is used to distinguish the case 2). When SplCreateSpooler
|
|
// is the caller of SplAddPrinterDriverEx, then we do not need to copy the files
|
|
// to the disk. It would be redundant.
|
|
//
|
|
bCopyFilesToClusterDisk = pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER &&
|
|
pIniSpooler->SpoolerFlags & SPL_PRINT &&
|
|
!(dwFileCopyFlags & APD_DONT_COPY_FILES_TO_CLUSTER);
|
|
|
|
//
|
|
// We want to set a system restore point unless the installer has already told
|
|
// us that the driver is signed. To really get this working properly, we would
|
|
// have to redesign AddPrinterDriver to be signing aware. For now it is the
|
|
// honor system (it is not obvious why an OEM installer would not want us to
|
|
// check point here though).
|
|
//
|
|
bSetSystemRestorePoint = !bCopyFilesToClusterDisk && !(dwFileCopyFlags & APD_DONT_SET_CHECKPOINT);
|
|
|
|
//
|
|
// We mask APD_DONT_COPY_FILES_TO_CLUSTER. The subsequent uses of dwFilecopyFlags exptect it
|
|
// to have a single bit set. They don't use it bitwise. They compare
|
|
// dwords agains it. The same goes for APD_DRIVER_SIGNATURE_VALID.
|
|
//
|
|
dwFileCopyFlags = dwFileCopyFlags & ~(APD_DONT_COPY_FILES_TO_CLUSTER);
|
|
|
|
DBGMSG(DBG_TRACE, ("InternalAddPrinterDriverEx( %x, %d, %x, %x)\n",
|
|
pName, Level, pDriverInfo, pIniSpooler));
|
|
|
|
try {
|
|
|
|
EnterSplSem();
|
|
|
|
if (!MyName(pName, pIniSpooler) ||
|
|
!ValidateDriverInfo(pDriverInfo,
|
|
Level,
|
|
dwFileCopyFlags,
|
|
bCopyFilesToClusterDisk,
|
|
pIniSpooler))
|
|
{
|
|
leave;
|
|
}
|
|
|
|
if (Level == 7)
|
|
{
|
|
//
|
|
// We can't be inside the semaphore to make this call.
|
|
//
|
|
LeaveSplSem();
|
|
bReturnValue = InternalINFInstallDriver( (DRIVER_INFO_7*)pDriverInfo );
|
|
EnterSplSem();
|
|
leave;
|
|
}
|
|
|
|
pszDriverName = ((DRIVER_INFO_2*)pDriverInfo)->pName;
|
|
|
|
pEnvironment = ((DRIVER_INFO_2*)pDriverInfo)->pEnvironment;
|
|
|
|
//
|
|
// If the driver hasn't gone through our class installer, then we want to
|
|
// create a sysem restore point here. Since Level 7 drivers are by
|
|
// definition signed, we can do this after the InternalINFInstallDriver.
|
|
// Since check-pointing takes from 25-30 seconds, this must take place
|
|
// outside the CS.
|
|
//
|
|
if (bSetSystemRestorePoint)
|
|
{
|
|
LeaveSplSem();
|
|
bIsSystemRestorePointSet = LocalStartSystemRestorePoint(pszDriverName, &hRestorePoint);
|
|
EnterSplSem();
|
|
|
|
//
|
|
// This only fails if something completely unexpected happens in
|
|
// setting the checkpoint. Some skus don't support check points
|
|
// in which case hRestorePoint will be NULL even though the function
|
|
// succeeds.
|
|
//
|
|
if (!bIsSystemRestorePointSet)
|
|
{
|
|
leave;
|
|
}
|
|
}
|
|
|
|
pIniEnvironment = FindEnvironment(pEnvironment, pIniSpooler );
|
|
|
|
if (!CreateInternalDriverFileArray(Level,
|
|
pDriverInfo,
|
|
&pInternalDriverFiles,
|
|
&FileCount,
|
|
bUseScratchDir,
|
|
pIniEnvironment,
|
|
FALSE))
|
|
{
|
|
leave;
|
|
}
|
|
|
|
//
|
|
// For the driver and config files in the scratch directory do a version
|
|
// check else use the version passed in rather than calling
|
|
// GetPrintDriverVersion which will cause a LoadLibrary - possibly
|
|
// over the network.
|
|
// Same for CheckFilePlatform. We shouldn't hit the network for files
|
|
// in Scratch or temporary directory.
|
|
//
|
|
//
|
|
if (bUseScratchDir || dwFileCopyFlags & APD_COPY_FROM_DIRECTORY)
|
|
{
|
|
if (!GetPrintDriverVersion(pInternalDriverFiles[0].pFileName,
|
|
&dwMajorVersion,
|
|
NULL))
|
|
{
|
|
leave;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// ntprint.dll doesn't fill in cVersion. We need to set it correctly
|
|
// just in case we need to call Save/RestoreParametersForUpgrade.
|
|
// For this case we need to have a corect version since no more validation are done.
|
|
//
|
|
((DRIVER_INFO_2*)pDriverInfo)->cVersion = dwMajorVersion;
|
|
}
|
|
|
|
|
|
if (!CheckFilePlatform(pInternalDriverFiles[0].pFileName, pEnvironment) ||
|
|
!CheckFilePlatform(pInternalDriverFiles[1].pFileName, pEnvironment))
|
|
{
|
|
|
|
LastError = ERROR_EXE_MACHINE_TYPE_MISMATCH;
|
|
leave;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
dwMajorVersion = ((DRIVER_INFO_2*)pDriverInfo)->cVersion;
|
|
}
|
|
|
|
LeaveSplSem();
|
|
|
|
LastError = IsCompatibleDriver(pszDriverName,
|
|
pInternalDriverFiles[0].pFileName,
|
|
((DRIVER_INFO_2*)pDriverInfo)->pEnvironment,
|
|
dwMajorVersion,
|
|
&dwBlockingStatus);
|
|
|
|
EnterSplSem();
|
|
|
|
if (LastError != ERROR_SUCCESS)
|
|
{
|
|
leave;
|
|
}
|
|
|
|
|
|
//
|
|
// If the printer driver is blocked, we consider it a bad driver.
|
|
//
|
|
bBadDriver = (dwBlockingStatus & BSP_BLOCKING_LEVEL_MASK) == BSP_PRINTER_DRIVER_BLOCKED;
|
|
if (bBadDriver)
|
|
{
|
|
LastError = ERROR_PRINTER_DRIVER_BLOCKED;
|
|
}
|
|
|
|
//
|
|
// if the driver is not blocked and we are not instructed to install
|
|
// warned driver, check for warned driver.
|
|
//
|
|
if(!bBadDriver && !(dwFileCopyFlags & APD_INSTALL_WARNED_DRIVER))
|
|
{
|
|
bBadDriver = (dwBlockingStatus & BSP_BLOCKING_LEVEL_MASK) == BSP_PRINTER_DRIVER_WARNED;
|
|
if (bBadDriver)
|
|
{
|
|
LastError = ERROR_PRINTER_DRIVER_WARNED;
|
|
}
|
|
}
|
|
|
|
if (bBadDriver)
|
|
{
|
|
//
|
|
// Win2k server does not recognize the new error code so we should
|
|
// returns ERROR_UNKNOWN_PRINTER_DRIVER to get the right error
|
|
// message on win2k and nt4.
|
|
//
|
|
// Client from Whistler or later will set
|
|
// APD_RETURN_BLOCKING_STATUS_CODE before call AddPrinterDrver
|
|
//
|
|
if (!(dwFileCopyFlags & APD_RETURN_BLOCKING_STATUS_CODE))
|
|
{
|
|
LastError = ERROR_UNKNOWN_PRINTER_DRIVER;
|
|
}
|
|
|
|
SplLogEvent(pIniSpooler,
|
|
LOG_ERROR,
|
|
MSG_BAD_OEM_DRIVER,
|
|
TRUE,
|
|
pszDriverName,
|
|
NULL);
|
|
leave;
|
|
}
|
|
|
|
#ifdef _WIN64
|
|
|
|
//
|
|
// Disallow installation of WIN64 KMPD.
|
|
//
|
|
if (pEnvironment &&
|
|
!_wcsicmp(LOCAL_ENVIRONMENT, pEnvironment) &&
|
|
IsKMPD(pInternalDriverFiles[0].pFileName))
|
|
{
|
|
LastError = ERROR_KM_DRIVER_BLOCKED;
|
|
leave;
|
|
}
|
|
#endif
|
|
|
|
pIniVersion = FindVersionEntry( pIniEnvironment, dwMajorVersion );
|
|
|
|
if (pIniVersion == NULL)
|
|
{
|
|
pIniVersion = CreateVersionEntry(pIniEnvironment,
|
|
dwMajorVersion,
|
|
pIniSpooler);
|
|
|
|
if (pIniVersion == NULL)
|
|
{
|
|
leave;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Version exists, try and create directory even if it
|
|
// exists. This is a slight performance hit, but since you
|
|
// install drivers rarely, this is ok. This fixes the problem
|
|
// where the version directory is accidentally deleted.
|
|
//
|
|
if (!CreateVersionDirectory(pIniVersion,
|
|
pIniEnvironment,
|
|
FALSE,
|
|
pIniSpooler))
|
|
{
|
|
leave;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for existing driver
|
|
//
|
|
pIniDriver = FindDriverEntry(pIniVersion, pszDriverName);
|
|
|
|
//
|
|
// Clear this flag since subsequent calls doesn't check bitwise.
|
|
//
|
|
dwFileCopyFlags &= ~(APD_COPY_FROM_DIRECTORY | APD_INSTALL_WARNED_DRIVER | APD_RETURN_BLOCKING_STATUS_CODE | APD_DONT_SET_CHECKPOINT);
|
|
|
|
if (!CheckFileCopyOptions(pIniEnvironment,
|
|
pIniVersion,
|
|
pIniDriver,
|
|
pInternalDriverFiles,
|
|
FileCount,
|
|
dwFileCopyFlags,
|
|
&bReturnValue))
|
|
{
|
|
//
|
|
// We don't need to do anything because either the operation
|
|
// failed (strict upgrade with older src files), or because
|
|
// it's an upgrade and the dest is newer. bReturnValue indicates
|
|
// if the AddPrinterDriver call succeeds.
|
|
//
|
|
leave;
|
|
}
|
|
|
|
//
|
|
// Copy files to the correct directories
|
|
//
|
|
if (!CopyFilesToFinalDirectory(pIniSpooler,
|
|
pIniEnvironment,
|
|
pIniVersion,
|
|
pInternalDriverFiles,
|
|
FileCount,
|
|
dwFileCopyFlags,
|
|
bImpersonateOnCreate,
|
|
&bDriverMoved))
|
|
{
|
|
leave;
|
|
}
|
|
|
|
//
|
|
// If pIniSpooler is a cluster spooler, then copy driver files to cluster disk
|
|
// if the driver is not being installed from the cluster disk (as part of the
|
|
// SplCreatespooler)
|
|
//
|
|
if (bCopyFilesToClusterDisk)
|
|
{
|
|
LastError = CopyFileToClusterDirectory(pIniSpooler,
|
|
pIniEnvironment,
|
|
pIniVersion,
|
|
pInternalDriverFiles,
|
|
FileCount);
|
|
|
|
if (LastError == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Here we propagate the ICM profiles to the cluster disk
|
|
//
|
|
CopyICMFromLocalDiskToClusterDisk(pIniSpooler);
|
|
}
|
|
else
|
|
{
|
|
leave;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check if the drivers need to be unloaded
|
|
// WaitRequiredForDriverUnload returns TRUE if the driver is loaded by Spooler process.
|
|
// If not loaded by Spooler itself, the config file could be loaded by any client app.
|
|
// In this case we move the loaded files in "Old" directory. When reload the confing file,
|
|
// the client apps(WINSPOOL.DRV) will figure that the driver was upgraded and reload the dll.
|
|
// See RefCntLoad and RefCntUnload in Winspool.drv. GDI32.DLL uses the same mechanism for
|
|
// Driver file.
|
|
//
|
|
if (WaitRequiredForDriverUnload(pIniSpooler,
|
|
pIniEnvironment,
|
|
pIniVersion,
|
|
pIniDriver,
|
|
Level,
|
|
pDriverInfo,
|
|
dwFileCopyFlags,
|
|
pInternalDriverFiles,
|
|
FileCount,
|
|
dwMajorVersion,
|
|
bDriverMoved,
|
|
&bReturnValue) &&
|
|
bReturnValue)
|
|
{
|
|
if (pIniDriver)
|
|
{
|
|
//
|
|
// Store information in the registry to complete the call later
|
|
//
|
|
bReturnValue = SaveParametersForUpgrade(pIniSpooler->pMachineName, bDriverMoved,
|
|
Level, pDriverInfo, dwMajorVersion);
|
|
leave;
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Add driver in a temp directory
|
|
//
|
|
bReturnValue = AddTempDriver(pIniSpooler,
|
|
pIniEnvironment,
|
|
pIniVersion,
|
|
Level,
|
|
pDriverInfo,
|
|
dwFileCopyFlags,
|
|
pInternalDriverFiles,
|
|
FileCount,
|
|
dwMajorVersion,
|
|
bDriverMoved
|
|
);
|
|
|
|
leave;
|
|
}
|
|
}
|
|
|
|
} finally {
|
|
|
|
//
|
|
// This code is only for clusters
|
|
//
|
|
if (bReturnValue && pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER)
|
|
{
|
|
SYSTEMTIME SysTime = {0};
|
|
|
|
if (bCopyFilesToClusterDisk)
|
|
{
|
|
//
|
|
// We are in the case where the add printer driver call comes from outside
|
|
// the spooler. We need to get the local time and write the time stamp in the
|
|
// locl regsitry and in the cluster database
|
|
//
|
|
GetLocalTime(&SysTime);
|
|
|
|
//
|
|
// Write timestamp to registry. Doesn't matter if any of them fails
|
|
// The time stamp is for faster cluster spooler initialization
|
|
//
|
|
WriteTimeStamp(pIniSpooler->hckRoot,
|
|
SysTime,
|
|
pIniSpooler->pszRegistryEnvironments,
|
|
pIniEnvironment->pName,
|
|
szDriversKey,
|
|
pIniVersion->pName,
|
|
pszDriverName,
|
|
pIniSpooler);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We are in the case where the add printer driver call came from inside
|
|
// the spooler (SplCreateSpooler). This is the case when our local node
|
|
// doesn't already have the driver installed. We do not need to get a new
|
|
// time stamp. (this would case the time stamp in the cluster db to be updated,
|
|
// and then whenever we fail over the time stamps will be always different)
|
|
// We just get the time stamp from the cluster db and update the local registry
|
|
//
|
|
ReadTimeStamp(pIniSpooler->hckRoot,
|
|
&SysTime,
|
|
pIniSpooler->pszRegistryEnvironments,
|
|
pIniEnvironment->pName,
|
|
szDriversKey,
|
|
pIniVersion->pName,
|
|
pszDriverName,
|
|
pIniSpooler);
|
|
}
|
|
|
|
WriteTimeStamp(HKEY_LOCAL_MACHINE,
|
|
SysTime,
|
|
ipszRegistryClusRepository,
|
|
pIniSpooler->pszClusResID,
|
|
pIniEnvironment->pName,
|
|
pIniVersion->pName,
|
|
pszDriverName,
|
|
NULL);
|
|
}
|
|
|
|
if (!bReturnValue && LastError == ERROR_SUCCESS)
|
|
{
|
|
LastError = GetLastError();
|
|
|
|
//
|
|
// We failed the call because bDriverMoved was FALSE and the driver was loaded
|
|
//
|
|
if(LastError == ERROR_SUCCESS && !bDriverMoved)
|
|
{
|
|
LastError = ERROR_NO_SYSTEM_RESOURCES;
|
|
}
|
|
}
|
|
|
|
if (bUseScratchDir && FileCount)
|
|
{
|
|
SetOldDateOnDriverFilesInScratchDirectory(pInternalDriverFiles,
|
|
FileCount,
|
|
pIniSpooler);
|
|
}
|
|
|
|
LeaveSplSem();
|
|
|
|
if (FileCount)
|
|
{
|
|
CleanupInternalDriverInfo(pInternalDriverFiles, FileCount);
|
|
}
|
|
|
|
CleanUpgradeDirectories();
|
|
|
|
//
|
|
// End the system restore point once everything is done. Cancel it if the
|
|
// function fails.
|
|
//
|
|
if (hRestorePoint)
|
|
{
|
|
(VOID)EndSystemRestorePoint(hRestorePoint, !bReturnValue);
|
|
}
|
|
|
|
if (!bReturnValue)
|
|
{
|
|
DBGMSG( DBG_WARNING, ("InternalAddPrinterDriver Failed %d\n", LastError ));
|
|
SetLastError(LastError);
|
|
}
|
|
}
|
|
|
|
return bReturnValue;
|
|
}
|
|
|
|
BOOL
|
|
AddTempDriver(
|
|
IN PINISPOOLER pIniSpooler,
|
|
IN PINIENVIRONMENT pIniEnvironment,
|
|
IN PINIVERSION pIniVersion,
|
|
IN DWORD dwLevel,
|
|
IN LPBYTE pDriverInfo,
|
|
IN DWORD dwFileCopyFlags,
|
|
IN OUT PINTERNAL_DRV_FILE pInternalDriverFiles,
|
|
IN DWORD dwFileCount,
|
|
IN DWORD dwVersion,
|
|
IN BOOL bDriverMoved
|
|
)
|
|
|
|
/*++
|
|
Function Description: For new drivers which require driver files to be unloaded,
|
|
add the driver into a temp directory and mark it for upgrade on
|
|
reboot OR when the files are unloaded
|
|
|
|
Parameters: pIniSpooler -- pointer to INISPOOLER
|
|
pIniEnvironment -- pointer to INIENVIRONMENT
|
|
pIniVersion -- pointer to INVERSION
|
|
dwLevel -- driver_info level
|
|
pDriverInfo -- pointer to driver_info
|
|
dwFileCopyFlags -- File copy flags that make it to the spooler
|
|
pInternalDriverFiles -- array of INTERNAL_DRV_FILE structures
|
|
dwFileCount -- number of files in file set
|
|
dwVersion -- driver version
|
|
bDriverMoved -- Were any files moved to the Old directory ?
|
|
|
|
Return Values: TRUE if the driver was added;
|
|
FALSE otherwise
|
|
--*/
|
|
|
|
{
|
|
BOOL bReturn = FALSE;
|
|
WCHAR szVersionDir[MAX_PATH], szNewDir[MAX_PATH+5];
|
|
WCHAR szDriverFile[MAX_PATH], szOldFile[MAX_PATH], szNewFile[MAX_PATH];
|
|
WCHAR *pTempDir = NULL;
|
|
DWORD dwIndex, dwTempDir;
|
|
HANDLE hToken = NULL, hFile;
|
|
LPWSTR pFileName;
|
|
|
|
hToken = RevertToPrinterSelf();
|
|
|
|
//
|
|
// get the version directory
|
|
//
|
|
// szVersionDir shouldn't be bigger than MAX_PATH - 5 since is used later
|
|
// to build another file paths.
|
|
//
|
|
if((StrNCatBuff(szVersionDir,
|
|
MAX_PATH - 5,
|
|
pIniSpooler->pDir,
|
|
L"\\drivers\\",
|
|
pIniEnvironment->pDirectory,
|
|
L"\\",
|
|
pIniVersion->szDirectory,
|
|
NULL) != ERROR_SUCCESS))
|
|
{
|
|
goto CleanUp;
|
|
}
|
|
|
|
dwIndex = CreateNumberedTempDirectory((LPWSTR)szVersionDir, &pTempDir);
|
|
|
|
if (dwIndex == -1) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
dwTempDir = dwIndex;
|
|
|
|
StringCchPrintf(szNewDir, COUNTOF(szNewDir), L"%ws\\New", szVersionDir);
|
|
|
|
//
|
|
// copy the files into the temp directory and mark them for deletion on
|
|
// reboot
|
|
//
|
|
for (dwIndex = 0; dwIndex < dwFileCount; dwIndex++) {
|
|
|
|
pFileName = (LPWSTR) FindFileName(pInternalDriverFiles[dwIndex].pFileName);
|
|
|
|
if((StrNCatBuff(szNewFile,MAX_PATH,szNewDir ,L"\\", pFileName, NULL) != ERROR_SUCCESS) ||
|
|
(StrNCatBuff(szOldFile,MAX_PATH,szVersionDir, L"\\", pFileName, NULL) != ERROR_SUCCESS) ||
|
|
(StrNCatBuff(szDriverFile,MAX_PATH,pTempDir, L"\\", pFileName, NULL) != ERROR_SUCCESS))
|
|
{
|
|
goto CleanUp;
|
|
}
|
|
|
|
hFile = CreateFile(szNewFile, GENERIC_READ, 0, NULL, OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
|
|
CopyFile(szOldFile, szDriverFile, FALSE);
|
|
|
|
} else {
|
|
|
|
CloseHandle(hFile);
|
|
hFile = INVALID_HANDLE_VALUE;
|
|
|
|
CopyFile(szNewFile, szDriverFile, FALSE);
|
|
}
|
|
|
|
SplMoveFileEx(szDriverFile, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
|
|
}
|
|
|
|
//
|
|
// Delete the directory on reboot
|
|
//
|
|
SplMoveFileEx(szNewDir, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
|
|
|
|
//
|
|
// Update driver structures and make event call backs and
|
|
// store information in the registry to complete the call later
|
|
//
|
|
bReturn = CompleteDriverUpgrade(pIniSpooler,
|
|
pIniEnvironment,
|
|
pIniVersion,
|
|
NULL,
|
|
dwLevel,
|
|
pDriverInfo,
|
|
dwFileCopyFlags,
|
|
pInternalDriverFiles,
|
|
dwFileCount,
|
|
dwVersion,
|
|
dwTempDir,
|
|
bDriverMoved,
|
|
TRUE,
|
|
TRUE) &&
|
|
|
|
SaveParametersForUpgrade(pIniSpooler->pMachineName,
|
|
bDriverMoved,
|
|
dwLevel,
|
|
pDriverInfo,
|
|
dwVersion);
|
|
|
|
CleanUp:
|
|
|
|
if (hToken) {
|
|
ImpersonatePrinterClient(hToken);
|
|
}
|
|
|
|
FreeSplMem(pTempDir);
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
BOOL
|
|
WaitRequiredForDriverUnload(
|
|
IN PINISPOOLER pIniSpooler,
|
|
IN PINIENVIRONMENT pIniEnvironment,
|
|
IN PINIVERSION pIniVersion,
|
|
IN PINIDRIVER pIniDriver,
|
|
IN DWORD dwLevel,
|
|
IN LPBYTE pDriverInfo,
|
|
IN DWORD dwFileCopyFlags,
|
|
IN OUT PINTERNAL_DRV_FILE pInternalDriverFiles,
|
|
IN DWORD dwFileCount,
|
|
IN DWORD dwVersion,
|
|
IN BOOL bDriverMoved,
|
|
OUT LPBOOL pbSuccess
|
|
)
|
|
/*++
|
|
Function Description: Determine if the driver upgrade has to be defered till the
|
|
dlls can be unloaded. GDI and the client side of the spooler are
|
|
notified to continue the pending upgrade when the dll is unloaded.
|
|
|
|
Parameters: pIniSpooler -- pointer to INISPOOLER
|
|
pIniEnvironment -- pointer to INIENVIRONMENT
|
|
pIniVersion -- pointer to INVERSION
|
|
pIniDriver -- pointer to INIDRIVER
|
|
dwLevel -- driver_info level
|
|
pDriverInfo -- pointer to driver_info
|
|
dwFileCopyFlags -- copy flags for the driver.
|
|
pInternalDriverFiles -- array of INTERNAL_DRV_FILE structures
|
|
dwFileCount -- number of files in file set
|
|
dwVersion -- driver version
|
|
bDriverMoved -- Were any files moved to the Old directory ?
|
|
pbSuccess -- pointer to Success flag
|
|
|
|
Return Values: TRUE if the driver was unloaded and upgraded;
|
|
FALSE if the driver cant be unloaded
|
|
--*/
|
|
|
|
{
|
|
BOOL bUnloaded,bDriverFileMoved, bConfigFileMoved;
|
|
LPWSTR pDriverFile, pConfigFile;
|
|
WCHAR szDriverFile[MAX_PATH], szOldDir[MAX_PATH], szNewDir[MAX_PATH];
|
|
WCHAR szTempFile[MAX_PATH], szCurrDir[MAX_PATH], szConfigFile[MAX_PATH];
|
|
HANDLE hFile, hToken = NULL;
|
|
DWORD dwDriverAttributes = 0;
|
|
|
|
hToken = RevertToPrinterSelf();
|
|
|
|
*pbSuccess = FALSE;
|
|
|
|
//
|
|
// Set up Driver, Old and New directories
|
|
//
|
|
if((StrNCatBuff(szCurrDir,
|
|
MAX_PATH,
|
|
pIniSpooler->pDir,
|
|
L"\\drivers\\",
|
|
pIniEnvironment->pDirectory,
|
|
L"\\",
|
|
pIniVersion->szDirectory,
|
|
NULL) != ERROR_SUCCESS) ||
|
|
(StrNCatBuff(szOldDir,
|
|
MAX_PATH,
|
|
szCurrDir,
|
|
L"\\Old",
|
|
NULL) != ERROR_SUCCESS) ||
|
|
(StrNCatBuff(szNewDir,
|
|
MAX_PATH,
|
|
szCurrDir,
|
|
L"\\New",
|
|
NULL) != ERROR_SUCCESS) ||
|
|
(StrNCatBuff(szDriverFile,
|
|
MAX_PATH,
|
|
szCurrDir,
|
|
L"\\",
|
|
FindFileName(pInternalDriverFiles[0].pFileName),
|
|
NULL) != ERROR_SUCCESS) ||
|
|
(StrNCatBuff(szConfigFile,
|
|
MAX_PATH,
|
|
szCurrDir,
|
|
L"\\",
|
|
FindFileName(pInternalDriverFiles[1].pFileName),
|
|
NULL) != ERROR_SUCCESS) ||
|
|
(StrNCatBuff(szTempFile,
|
|
MAX_PATH,szNewDir,
|
|
L"\\",
|
|
FindFileName(pInternalDriverFiles[0].pFileName),
|
|
NULL) != ERROR_SUCCESS))
|
|
{
|
|
bUnloaded = TRUE;
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Check if the new driver file needs to be copied
|
|
//
|
|
hFile = CreateFile(szTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hFile != INVALID_HANDLE_VALUE) {
|
|
CloseHandle(hFile);
|
|
pDriverFile = szDriverFile;
|
|
|
|
if (pIniDriver) {
|
|
dwDriverAttributes = pIniDriver->dwDriverAttributes;
|
|
} else {
|
|
dwDriverAttributes = IsKMPD(szDriverFile) ? DRIVER_KERNELMODE
|
|
: DRIVER_USERMODE;
|
|
}
|
|
} else {
|
|
pDriverFile = NULL;
|
|
}
|
|
|
|
if((StrNCatBuff(szTempFile,
|
|
MAX_PATH,
|
|
szNewDir,
|
|
L"\\",
|
|
FindFileName(pInternalDriverFiles[1].pFileName), NULL)
|
|
!= ERROR_SUCCESS))
|
|
{
|
|
bUnloaded = TRUE;
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Check if the new config file needs to be copied
|
|
//
|
|
hFile = CreateFile(szTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hFile != INVALID_HANDLE_VALUE) {
|
|
CloseHandle(hFile);
|
|
pConfigFile = szConfigFile;
|
|
} else {
|
|
pConfigFile = NULL;
|
|
}
|
|
|
|
bUnloaded = FilesUnloaded(pIniEnvironment, pDriverFile, pConfigFile,
|
|
dwDriverAttributes);
|
|
|
|
if (bUnloaded) {
|
|
|
|
//
|
|
// Move the driver files from New directory to Version directory
|
|
// and from Version directory to Old directory if in use.
|
|
//
|
|
if (MoveNewDriverRelatedFiles(szNewDir,
|
|
szCurrDir,
|
|
szOldDir,
|
|
pInternalDriverFiles,
|
|
dwFileCount,
|
|
&bDriverFileMoved,
|
|
&bConfigFileMoved)) {
|
|
|
|
//
|
|
// Update driver structures and make event call backs.
|
|
//
|
|
*pbSuccess = CompleteDriverUpgrade(pIniSpooler,
|
|
pIniEnvironment,
|
|
pIniVersion,
|
|
pIniDriver,
|
|
dwLevel,
|
|
pDriverInfo,
|
|
dwFileCopyFlags,
|
|
pInternalDriverFiles,
|
|
dwFileCount,
|
|
dwVersion,
|
|
0,
|
|
bDriverMoved,
|
|
bDriverFileMoved,
|
|
bConfigFileMoved
|
|
);
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// We care if the files are marked to be moved from New to Version directory only if the drivers are loaded
|
|
// and we left the updated files in New directory. Then it is imperative MoveFileEx to have succeeded.
|
|
// Fail the api call if bDriverMoved is FALSE;
|
|
//
|
|
*pbSuccess = bDriverMoved;
|
|
}
|
|
|
|
CleanUp:
|
|
|
|
if (hToken) {
|
|
ImpersonatePrinterClient(hToken);
|
|
}
|
|
|
|
|
|
return (!bUnloaded);
|
|
}
|
|
|
|
BOOL FilesUnloaded(
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
LPWSTR pDriverFile,
|
|
LPWSTR pConfigFile,
|
|
DWORD dwDriverAttributes)
|
|
{
|
|
BOOL bReturn = TRUE;
|
|
fnWinSpoolDrv fnList;
|
|
|
|
//
|
|
// Drivers belonging to other environments are not loaded.
|
|
//
|
|
if (!pIniEnvironment ||
|
|
lstrcmpi(pIniEnvironment->pName, szEnvironment)) {
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
if (pDriverFile) {
|
|
|
|
bReturn = GdiArtificialDecrementDriver(pDriverFile,
|
|
dwDriverAttributes);
|
|
}
|
|
|
|
if (bReturn && pConfigFile && SplInitializeWinSpoolDrv(&fnList)) {
|
|
|
|
bReturn = (* (fnList.pfnForceUnloadDriver))(pConfigFile);
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
DWORD StringSizeInBytes(
|
|
LPWSTR pString,
|
|
BOOL bMultiSz)
|
|
|
|
/*++
|
|
Function Description: Computes the number of bytes in the string
|
|
|
|
Parameters: pString -- string pointer
|
|
bMultiSz -- flag for multi_sz strings
|
|
|
|
Return Values: number of bytes
|
|
--*/
|
|
|
|
{
|
|
DWORD dwReturn = 0, dwLength;
|
|
|
|
if (!pString) {
|
|
return dwReturn;
|
|
}
|
|
|
|
if (!bMultiSz) {
|
|
|
|
dwReturn = (wcslen(pString) + 1) * sizeof(WCHAR);
|
|
|
|
} else {
|
|
|
|
while (dwLength = wcslen(pString)) {
|
|
|
|
pString += (dwLength + 1);
|
|
dwReturn += (dwLength + 1) * sizeof(WCHAR);
|
|
}
|
|
|
|
dwReturn += sizeof(WCHAR);
|
|
}
|
|
|
|
return dwReturn;
|
|
}
|
|
|
|
DWORD LocalRegSetValue(
|
|
HKEY hKey,
|
|
LPWSTR pValueName,
|
|
DWORD dwType,
|
|
LPBYTE pValueData)
|
|
|
|
/*++
|
|
Function Description: This function is a wrapper around RegSetValueEx which puts in
|
|
NULL strings for NULL pointers.
|
|
|
|
Parameters: hKey - handle to registry key
|
|
pValueName - value name
|
|
dwType - type of value data (REG_DWORD , REG_SZ ...)
|
|
pValueData - data buffer
|
|
|
|
Return Values: Last error returned by RegSetValueEx
|
|
--*/
|
|
|
|
{
|
|
DWORD dwSize;
|
|
WCHAR pNull[2];
|
|
LPBYTE pData = pValueData;
|
|
|
|
if (!pValueName) {
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
pNull[0] = pNull[1] = L'\0';
|
|
|
|
switch (dwType) {
|
|
|
|
case REG_DWORD:
|
|
dwSize = sizeof(DWORD);
|
|
break;
|
|
|
|
case REG_SZ:
|
|
if (!pData) {
|
|
pData = (LPBYTE) pNull;
|
|
dwSize = sizeof(WCHAR);
|
|
} else {
|
|
dwSize = StringSizeInBytes((LPWSTR) pData, FALSE);
|
|
}
|
|
break;
|
|
|
|
case REG_MULTI_SZ:
|
|
if (!pData || !*pData) {
|
|
pData = (LPBYTE) pNull;
|
|
dwSize = 2 * sizeof(WCHAR);
|
|
} else {
|
|
dwSize = StringSizeInBytes((LPWSTR) pData, TRUE);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
return RegSetValueEx(hKey, pValueName, 0, dwType, pData, dwSize);
|
|
}
|
|
|
|
BOOL SaveParametersForUpgrade(
|
|
LPWSTR pName,
|
|
BOOL bDriverMoved,
|
|
DWORD dwLevel,
|
|
LPBYTE pDriverInfo,
|
|
DWORD dwVersion)
|
|
|
|
/*++
|
|
Function Description: Saves data for the driver upgrade which has to be
|
|
deferred till the new driver can be loaded
|
|
|
|
Parameters: pName -- pIniSpooler->pName
|
|
bDriverMoved -- Were any of the old driver files moved?
|
|
dwLevel -- Driver_Info level
|
|
pDriverInfo -- Driver_Info pointer
|
|
dwVersion -- Driver version number
|
|
|
|
Return Values: TRUE if successful; FALSE otherwise
|
|
--*/
|
|
|
|
{
|
|
HANDLE hToken = NULL;
|
|
HKEY hRootKey = NULL, hUpgradeKey = NULL, hVersionKey = NULL;
|
|
HKEY hDriverKey = NULL;
|
|
DWORD dwDriverMoved = (DWORD) bDriverMoved;
|
|
BOOL bReturn = FALSE;
|
|
WCHAR Buffer[MAX_PATH];
|
|
PDRIVER_INFO_2 pDriver2;
|
|
PDRIVER_INFO_3 pDriver3;
|
|
PDRIVER_INFO_4 pDriver4;
|
|
PDRIVER_INFO_6 pDriver6;
|
|
|
|
pDriver2 = (PDRIVER_INFO_2) pDriverInfo;
|
|
|
|
//
|
|
// Stop impersonation for modifying the registry
|
|
//
|
|
hToken = RevertToPrinterSelf();
|
|
|
|
//
|
|
// Create the registry keys
|
|
//
|
|
if (!BoolFromHResult(StringCchPrintf(Buffer, COUNTOF(Buffer), L"Version-%d", dwVersion)) ||
|
|
|
|
RegCreateKeyEx(HKEY_LOCAL_MACHINE, szRegistryRoot, 0, NULL, 0, KEY_WRITE, NULL, &hRootKey, NULL) ||
|
|
|
|
RegCreateKeyEx(hRootKey, szPendingUpgrades, 0, NULL, 0, KEY_WRITE, NULL, &hUpgradeKey, NULL) ||
|
|
|
|
RegCreateKeyEx(hUpgradeKey, Buffer, 0, NULL, 0, KEY_WRITE, NULL, &hVersionKey, NULL) ||
|
|
|
|
RegCreateKeyEx(hVersionKey, pDriver2->pName, 0, NULL, 0, KEY_WRITE, NULL, &hDriverKey, NULL)) {
|
|
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (dwLevel == DRIVER_INFO_VERSION_LEVEL) {
|
|
|
|
bReturn = SaveDriverVersionForUpgrade(hDriverKey, (PDRIVER_INFO_VERSION)pDriverInfo,
|
|
pName, bDriverMoved, dwVersion);
|
|
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Add the spooler name and driver info level.
|
|
//
|
|
if (LocalRegSetValue(hDriverKey, L"SplName", REG_SZ, (LPBYTE) pName) ||
|
|
|
|
LocalRegSetValue(hDriverKey, L"Level", REG_DWORD, (LPBYTE) &dwLevel) ||
|
|
|
|
LocalRegSetValue(hDriverKey, L"DriverMoved", REG_DWORD, (LPBYTE) &dwDriverMoved)) {
|
|
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Add Driver_Info_2 data.
|
|
//
|
|
if (LocalRegSetValue(hDriverKey, L"cVersion", REG_DWORD, (LPBYTE) &dwVersion) ||
|
|
|
|
LocalRegSetValue(hDriverKey, L"pName", REG_SZ, (LPBYTE) pDriver2->pName) ||
|
|
|
|
LocalRegSetValue(hDriverKey, L"pEnvironment", REG_SZ, (LPBYTE) pDriver2->pEnvironment) ||
|
|
|
|
LocalRegSetValue(hDriverKey, L"pDriverPath", REG_SZ, (LPBYTE) pDriver2->pDriverPath) ||
|
|
|
|
LocalRegSetValue(hDriverKey, L"pDataFile", REG_SZ, (LPBYTE) pDriver2->pDataFile) ||
|
|
|
|
LocalRegSetValue(hDriverKey, L"pConfigFile", REG_SZ, (LPBYTE) pDriver2->pConfigFile)) {
|
|
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (dwLevel != 2) {
|
|
|
|
pDriver3 = (PDRIVER_INFO_3) pDriverInfo;
|
|
|
|
//
|
|
// Add Driver_Info_3 data.
|
|
//
|
|
if (LocalRegSetValue(hDriverKey, L"pHelpFile", REG_SZ, (LPBYTE) pDriver3->pHelpFile) ||
|
|
|
|
LocalRegSetValue(hDriverKey, L"pDependentFiles", REG_MULTI_SZ,
|
|
(LPBYTE) pDriver3->pDependentFiles) ||
|
|
|
|
LocalRegSetValue(hDriverKey, L"pMonitorName", REG_SZ,
|
|
(LPBYTE) pDriver3->pMonitorName) ||
|
|
|
|
LocalRegSetValue(hDriverKey, L"pDefaultDataType", REG_SZ,
|
|
(LPBYTE) pDriver3->pDefaultDataType)) {
|
|
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (dwLevel == 4 || dwLevel == 6) {
|
|
|
|
pDriver4 = (PDRIVER_INFO_4) pDriverInfo;
|
|
|
|
//
|
|
// Add Driver_Info_4 data.
|
|
//
|
|
if (LocalRegSetValue(hDriverKey, L"pszzPreviousNames", REG_MULTI_SZ, (LPBYTE) pDriver4->pszzPreviousNames))
|
|
{
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
|
|
if (dwLevel == 6) {
|
|
|
|
pDriver6 = (PDRIVER_INFO_6) pDriverInfo;
|
|
|
|
//
|
|
// Add Driver_Info6 data.
|
|
//
|
|
if (RegSetValueEx(hDriverKey, L"ftDriverDate", 0, REG_BINARY, (LPBYTE)&pDriver6->ftDriverDate, sizeof(FILETIME)) ||
|
|
|
|
RegSetValueEx(hDriverKey, L"dwlDriverVersion", 0, REG_BINARY, (LPBYTE)&pDriver6->dwlDriverVersion, sizeof(DWORDLONG)) ||
|
|
|
|
LocalRegSetValue(hDriverKey, L"pszMfgName", REG_SZ, (LPBYTE)pDriver6->pszMfgName) ||
|
|
|
|
LocalRegSetValue(hDriverKey, L"pszOEMUrl", REG_SZ, (LPBYTE)pDriver6->pszOEMUrl) ||
|
|
|
|
LocalRegSetValue(hDriverKey, L"pszHardwareID", REG_SZ, (LPBYTE)pDriver6->pszHardwareID) ||
|
|
|
|
LocalRegSetValue(hDriverKey, L"pszProvider", REG_SZ, (LPBYTE)pDriver6->pszProvider)
|
|
)
|
|
{
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
}
|
|
|
|
bReturn = TRUE;
|
|
|
|
CleanUp:
|
|
|
|
if (hDriverKey) {
|
|
RegCloseKey(hDriverKey);
|
|
}
|
|
|
|
if (hVersionKey) {
|
|
RegCloseKey(hVersionKey);
|
|
}
|
|
|
|
if (hUpgradeKey) {
|
|
RegCloseKey(hUpgradeKey);
|
|
}
|
|
|
|
if (hRootKey) {
|
|
RegCloseKey(hRootKey);
|
|
}
|
|
|
|
if (hToken) {
|
|
ImpersonatePrinterClient(hToken);
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
|
|
BOOL SaveDriverVersionForUpgrade(
|
|
IN HKEY hDriverKey,
|
|
IN PDRIVER_INFO_VERSION pDriverVersion,
|
|
IN LPWSTR pName,
|
|
IN DWORD dwDriverMoved,
|
|
IN DWORD dwVersion
|
|
)
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
SaveDriverVersionForUpgrade
|
|
|
|
Routine Description:
|
|
|
|
Save a DRIVER_INFO_VERSION into registry for pending driver upgrade purposes.
|
|
It is called by SaveParametersForUpgrade.
|
|
For simplicity, it will save it in the same format DRIVER_INFO_6
|
|
is saved in the registry.
|
|
|
|
Arguments:
|
|
|
|
hDriverKey - the registry key where to save data
|
|
pDriverVersion - pointer to DRIVER_INFO_VERSION structure
|
|
pName - driver name
|
|
dwDriverMoved - information about the way files where move between directories
|
|
dwVersion - driver version
|
|
|
|
Return Value:
|
|
|
|
TRUE if success.
|
|
|
|
--*/
|
|
{
|
|
|
|
BOOL bRetValue = FALSE;
|
|
DWORD dwLevel = 6;
|
|
PWSTR pDllFiles = NULL;
|
|
PWSTR pszDriverPath, pszDataFile, pszConfigFile, pszHelpFile, pDependentFiles ;
|
|
|
|
pszDriverPath = pszDataFile = pszConfigFile = pszHelpFile = NULL;
|
|
|
|
|
|
if (!GetFileNamesFromDriverVersionInfo(pDriverVersion,
|
|
&pszDriverPath,
|
|
&pszConfigFile,
|
|
&pszDataFile,
|
|
&pszHelpFile))
|
|
{
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (!BuildDependentFilesFromDriverInfo(pDriverVersion,
|
|
&pDllFiles))
|
|
{
|
|
goto CleanUp;
|
|
}
|
|
|
|
|
|
if (LocalRegSetValue(hDriverKey, L"SplName", REG_SZ, (LPBYTE) pName) ||
|
|
LocalRegSetValue(hDriverKey, L"Level", REG_DWORD, (LPBYTE) &dwLevel) ||
|
|
LocalRegSetValue(hDriverKey, L"DriverMoved", REG_DWORD, (LPBYTE) &dwDriverMoved) ||
|
|
LocalRegSetValue(hDriverKey, L"Level", REG_DWORD, (LPBYTE) &dwLevel) ||
|
|
LocalRegSetValue(hDriverKey, L"cVersion", REG_DWORD, (LPBYTE) &dwVersion) ||
|
|
LocalRegSetValue(hDriverKey, L"pName", REG_SZ, (LPBYTE) pDriverVersion->pName) ||
|
|
LocalRegSetValue(hDriverKey, L"pEnvironment", REG_SZ, (LPBYTE) pDriverVersion->pEnvironment)||
|
|
LocalRegSetValue(hDriverKey, L"pDriverPath", REG_SZ, (LPBYTE) pszDriverPath) ||
|
|
LocalRegSetValue(hDriverKey, L"pDataFile", REG_SZ, (LPBYTE) pszDataFile) ||
|
|
LocalRegSetValue(hDriverKey, L"pConfigFile", REG_SZ, (LPBYTE) pszConfigFile) ||
|
|
LocalRegSetValue(hDriverKey, L"pHelpFile", REG_SZ, (LPBYTE) pszHelpFile) ||
|
|
LocalRegSetValue(hDriverKey, L"pDependentFiles", REG_MULTI_SZ, (LPBYTE) pDllFiles) ||
|
|
LocalRegSetValue(hDriverKey, L"pMonitorName", REG_SZ,
|
|
(LPBYTE) pDriverVersion->pMonitorName) ||
|
|
|
|
LocalRegSetValue(hDriverKey, L"pDefaultDataType", REG_SZ,
|
|
(LPBYTE) pDriverVersion->pDefaultDataType) ||
|
|
|
|
LocalRegSetValue(hDriverKey, L"pszzPreviousNames", REG_MULTI_SZ,
|
|
(LPBYTE) pDriverVersion->pszzPreviousNames) ||
|
|
|
|
RegSetValueEx(hDriverKey, L"ftDriverDate", 0, REG_BINARY,
|
|
(LPBYTE)&pDriverVersion->ftDriverDate, sizeof(FILETIME)) ||
|
|
|
|
RegSetValueEx(hDriverKey, L"dwlDriverVersion", 0, REG_BINARY,
|
|
(LPBYTE)&pDriverVersion->dwlDriverVersion, sizeof(DWORDLONG)) ||
|
|
|
|
LocalRegSetValue(hDriverKey, L"pszMfgName", REG_SZ,
|
|
(LPBYTE)pDriverVersion->pszMfgName) ||
|
|
|
|
LocalRegSetValue(hDriverKey, L"pszOEMUrl", REG_SZ,
|
|
(LPBYTE)pDriverVersion->pszOEMUrl) ||
|
|
|
|
LocalRegSetValue(hDriverKey, L"pszHardwareID", REG_SZ,
|
|
(LPBYTE)pDriverVersion->pszHardwareID) ||
|
|
|
|
LocalRegSetValue(hDriverKey, L"pszProvider", REG_SZ,
|
|
(LPBYTE)pDriverVersion->pszProvider))
|
|
|
|
{
|
|
goto CleanUp;
|
|
}
|
|
|
|
bRetValue = TRUE;
|
|
|
|
CleanUp:
|
|
|
|
FreeSplMem(pDllFiles);
|
|
|
|
return bRetValue;
|
|
}
|
|
|
|
BOOL
|
|
MoveNewDriverRelatedFiles(
|
|
LPWSTR pNewDir,
|
|
LPWSTR pCurrDir,
|
|
LPWSTR pOldDir,
|
|
PINTERNAL_DRV_FILE pInternalDriverFiles,
|
|
DWORD dwFileCount,
|
|
LPBOOL pbDriverFileMoved,
|
|
LPBOOL pbConfigFileMoved)
|
|
|
|
/*++
|
|
Function Description: Moves driver files in the New directory to the correct directory.
|
|
|
|
Parameters: pNewDir -- name of the New (source) directory
|
|
pCurrDir -- name of the destination directory
|
|
pOldDir -- name of the Old (temp) directory
|
|
pInternalDriverFiles -- array of INTERNAL_DRV_FILE structures
|
|
dwFileCount -- number of files in file set
|
|
pbDriverFileMoved -- flag to return if new driver file has been moved;
|
|
We assume entry 0 for driver file in pInternalDriverFiles array
|
|
pbDriverFileMoved should be NULL when this assumption is FALSE
|
|
( see SplCopyNumberOfFiles )
|
|
pbConfigFileMoved -- flag to return if new config file has been moved;
|
|
We assume entry 0 for config file in pInternalDriverFiles array
|
|
pbConfigFileMoved should be NULL when this assumption is FALSE
|
|
( see SplCopyNumberOfFiles )
|
|
|
|
Return Values: NONE
|
|
--*/
|
|
|
|
{
|
|
HANDLE hFile;
|
|
DWORD dwIndex, dwBackupIndex;
|
|
WCHAR szDriverFile[MAX_PATH], szNewFile[MAX_PATH], szOldFile[MAX_PATH];
|
|
WCHAR *pszTempOldDirectory = NULL;
|
|
LPWSTR pFileName;
|
|
BOOL bRetValue = FALSE;
|
|
BOOL bFailedToMove = FALSE;
|
|
|
|
if (pbDriverFileMoved)
|
|
{
|
|
*pbDriverFileMoved = FALSE;
|
|
}
|
|
|
|
if (pbConfigFileMoved)
|
|
{
|
|
*pbConfigFileMoved = FALSE;
|
|
}
|
|
|
|
if (CreateNumberedTempDirectory(pOldDir, &pszTempOldDirectory) != -1) {
|
|
|
|
for (dwIndex = 0; dwIndex < dwFileCount; dwIndex++) {
|
|
|
|
BOOL FileCopied = FALSE;
|
|
|
|
pFileName = (LPWSTR) FindFileName(pInternalDriverFiles[dwIndex].pFileName);
|
|
|
|
if((StrNCatBuff(szNewFile,MAX_PATH,pNewDir, L"\\", pFileName, NULL) == ERROR_SUCCESS) &&
|
|
(StrNCatBuff(szDriverFile,MAX_PATH,pCurrDir, L"\\", pFileName, NULL) == ERROR_SUCCESS) &&
|
|
(StrNCatBuff(szOldFile,MAX_PATH,pszTempOldDirectory, L"\\", pFileName, NULL) == ERROR_SUCCESS))
|
|
{
|
|
hFile = CreateFile(szNewFile, GENERIC_READ, 0, NULL, OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
if (hFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
CloseHandle(hFile);
|
|
|
|
if (!SplMoveFileEx(szDriverFile, szOldFile, MOVEFILE_REPLACE_EXISTING)) {
|
|
|
|
bFailedToMove = TRUE;
|
|
dwBackupIndex = dwIndex;
|
|
break;
|
|
}
|
|
|
|
if (!SplMoveFileEx(szNewFile, szDriverFile, MOVEFILE_REPLACE_EXISTING)) {
|
|
|
|
bFailedToMove = TRUE;
|
|
dwBackupIndex = dwIndex + 1;
|
|
break;
|
|
}
|
|
|
|
FileCopied = TRUE;
|
|
//
|
|
// We could come in here from a pending upgrade
|
|
//
|
|
pInternalDriverFiles[dwIndex].bUpdated = TRUE;
|
|
}
|
|
}
|
|
|
|
switch (dwIndex)
|
|
{
|
|
case 0:
|
|
if (pbDriverFileMoved)
|
|
{
|
|
*pbDriverFileMoved = FileCopied;
|
|
}
|
|
break;
|
|
case 1:
|
|
if (pbConfigFileMoved)
|
|
{
|
|
*pbConfigFileMoved = FileCopied;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( bFailedToMove ) {
|
|
|
|
//
|
|
// Restore the initial file set in version directory.
|
|
// Old\N has the replaced files. Move them back to Version directory.
|
|
//
|
|
for (dwIndex = 0; dwIndex < dwBackupIndex; dwIndex++) {
|
|
|
|
pFileName = (LPWSTR) FindFileName(pInternalDriverFiles[dwIndex].pFileName);
|
|
|
|
if( (StrNCatBuff(szDriverFile,MAX_PATH,pCurrDir, L"\\", pFileName, NULL) == ERROR_SUCCESS) &&
|
|
(StrNCatBuff(szOldFile,MAX_PATH,pszTempOldDirectory, L"\\", pFileName, NULL) == ERROR_SUCCESS)) {
|
|
|
|
SplMoveFileEx(szOldFile, szDriverFile, MOVEFILE_REPLACE_EXISTING);
|
|
}
|
|
|
|
pInternalDriverFiles[dwIndex].bUpdated = FALSE;
|
|
}
|
|
|
|
} else {
|
|
|
|
bRetValue = TRUE;
|
|
}
|
|
}
|
|
|
|
FreeSplMem(pszTempOldDirectory);
|
|
|
|
return bRetValue;
|
|
}
|
|
|
|
BOOL LocalDriverUnloadComplete(
|
|
LPWSTR pDriverFile)
|
|
|
|
/*++
|
|
Function Description: This function is called in response to some driver file
|
|
being unloaded. The spooler tries to complete driver upgrades
|
|
that were waiting for this file to unload.
|
|
|
|
Parameters: pDriverFile -- Driver file which was unloaded
|
|
|
|
Return Values: TRUE
|
|
--*/
|
|
{
|
|
HANDLE hToken = NULL;
|
|
|
|
hToken = RevertToPrinterSelf();
|
|
|
|
PendingDriverUpgrades(pDriverFile);
|
|
|
|
if (hToken) {
|
|
ImpersonatePrinterClient(hToken);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL RestoreVersionKey(
|
|
HKEY hUpgradeKey,
|
|
DWORD dwIndex,
|
|
HKEY *phVersionKey)
|
|
|
|
/*++
|
|
Function Description: Gets the version key from the pending upgrade key
|
|
|
|
Parameters: hUpgradeKey -- upgrade key
|
|
dwIndex -- version index
|
|
phVersionKey -- pointer to buffer for version key
|
|
|
|
Return Values: TRUE if version key is found
|
|
FALSE otherwise
|
|
--*/
|
|
|
|
{
|
|
WCHAR pBuffer[MAX_PATH];
|
|
DWORD dwSize = MAX_PATH;
|
|
|
|
*phVersionKey = NULL;
|
|
|
|
if (RegEnumKeyEx(hUpgradeKey, dwIndex, pBuffer, &dwSize,
|
|
NULL, NULL, NULL, NULL)) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if (RegCreateKeyEx(hUpgradeKey, pBuffer, 0,
|
|
NULL, 0, KEY_READ | DELETE, NULL,
|
|
phVersionKey, NULL)) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID PendingDriverUpgrades(
|
|
LPWSTR pDriverFile)
|
|
|
|
/*++
|
|
Function Description: Loops thru the list of pending upgrades and completes them if
|
|
driver files have been unloaded. This function will try all the
|
|
drivers on spooler startup.
|
|
|
|
Parameters: pDriverFile -- name of the file which was unloaded
|
|
|
|
Return Values: NONE
|
|
--*/
|
|
|
|
{
|
|
DWORD dwIndex, dwLevel, dwDriverMoved, dwFileCount, dwVersion, dwVersionIndex;
|
|
LPWSTR pKeyName, pSplName,pEnvironment;
|
|
PINTERNAL_DRV_FILE pInternalDriverFiles = NULL;
|
|
HKEY hRootKey = NULL, hVersionKey = NULL, hUpgradeKey = NULL;
|
|
WCHAR szDir[MAX_PATH], szDriverFile[MAX_PATH], szConfigFile[MAX_PATH];
|
|
BOOL bSuccess;
|
|
|
|
PDRIVER_INFO_6 pDriverInfo;
|
|
PINISPOOLER pIniSpooler;
|
|
PINIENVIRONMENT pIniEnvironment;
|
|
PINIVERSION pIniVersion;
|
|
PINIDRIVER pIniDriver;
|
|
|
|
//
|
|
// Struct for maintaining keynames to be deleted at the end.
|
|
//
|
|
struct StringList {
|
|
struct StringList *pNext;
|
|
LPWSTR pKeyName;
|
|
DWORD dwVersionIndex;
|
|
} *pStart, *pTemp;
|
|
|
|
pStart = pTemp = NULL;
|
|
|
|
EnterSplSem();
|
|
|
|
//
|
|
// Open the registry key.
|
|
//
|
|
if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, szRegistryRoot, 0,
|
|
NULL, 0, KEY_READ | DELETE, NULL, &hRootKey, NULL) ||
|
|
|
|
RegCreateKeyEx(hRootKey, szPendingUpgrades, 0,
|
|
NULL, 0, KEY_READ | DELETE, NULL, &hUpgradeKey, NULL)) {
|
|
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Loop thru each version entry in the registry.
|
|
//
|
|
for (dwVersionIndex = 0, hVersionKey = NULL;
|
|
|
|
RestoreVersionKey(hUpgradeKey, dwVersionIndex, &hVersionKey);
|
|
|
|
RegCloseKey(hVersionKey), hVersionKey = NULL, ++dwVersionIndex) {
|
|
|
|
//
|
|
// Loop thru each driver upgrade.
|
|
//
|
|
for (dwIndex = 0, dwFileCount = 0, pInternalDriverFiles = NULL;
|
|
|
|
RestoreParametersForUpgrade(hVersionKey,
|
|
dwIndex,
|
|
&pKeyName,
|
|
&pSplName,
|
|
&dwLevel,
|
|
&dwDriverMoved,
|
|
&pDriverInfo);
|
|
|
|
CleanUpResources(pKeyName, pSplName, pDriverInfo,
|
|
&pInternalDriverFiles, dwFileCount),
|
|
++dwIndex, dwFileCount = 0, pInternalDriverFiles = NULL) {
|
|
|
|
//
|
|
// The driver_info struct validity has been checked while updating
|
|
// the registry.
|
|
//
|
|
// Set pIniSpooler to LocalIniSpooler
|
|
//
|
|
if (!(pIniSpooler = pLocalIniSpooler)) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Set pIniEnvironment.
|
|
//
|
|
pEnvironment = szEnvironment;
|
|
if (pDriverInfo->pEnvironment && *(pDriverInfo->pEnvironment)) {
|
|
pEnvironment = pDriverInfo->pEnvironment;
|
|
}
|
|
pIniEnvironment = FindEnvironment(pEnvironment, pIniSpooler);
|
|
if (!pIniEnvironment) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Set pIniVersion.
|
|
//
|
|
dwVersion = pDriverInfo->cVersion;
|
|
pIniVersion = FindVersionEntry(pIniEnvironment, dwVersion);
|
|
if (!pIniVersion) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Set pIniDriver.
|
|
//
|
|
pIniDriver = FindDriverEntry(pIniVersion, pDriverInfo->pName);
|
|
if (!pIniDriver) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check for the file name which was unloaded
|
|
//
|
|
if (pDriverFile) {
|
|
|
|
if((StrNCatBuff(szDir,
|
|
MAX_PATH,
|
|
pIniSpooler->pDir,
|
|
L"\\drivers\\",
|
|
pIniEnvironment->pDirectory,
|
|
L"\\",
|
|
pIniVersion->szDirectory,
|
|
NULL) != ERROR_SUCCESS) ||
|
|
(StrNCatBuff(szDriverFile,
|
|
MAX_PATH,
|
|
szDir,
|
|
L"\\",
|
|
FindFileName(pIniDriver->pDriverFile),
|
|
NULL) != ERROR_SUCCESS) ||
|
|
(StrNCatBuff(szConfigFile,
|
|
MAX_PATH,szDir,
|
|
L"\\",
|
|
FindFileName(pIniDriver->pConfigFile),
|
|
NULL) != ERROR_SUCCESS))
|
|
continue;
|
|
|
|
if (_wcsicmp(pDriverFile, szDriverFile) &&
|
|
_wcsicmp(pDriverFile, szConfigFile)) {
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (!CreateInternalDriverFileArray(dwLevel,
|
|
(LPBYTE)pDriverInfo,
|
|
&pInternalDriverFiles,
|
|
&dwFileCount,
|
|
FALSE,
|
|
pIniEnvironment,
|
|
TRUE))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!WaitRequiredForDriverUnload(pIniSpooler,
|
|
pIniEnvironment,
|
|
pIniVersion,
|
|
pIniDriver,
|
|
dwLevel,
|
|
(LPBYTE) pDriverInfo,
|
|
APD_STRICT_UPGRADE,
|
|
pInternalDriverFiles,
|
|
dwFileCount,
|
|
dwVersion,
|
|
(BOOL) dwDriverMoved,
|
|
&bSuccess) &&
|
|
bSuccess) {
|
|
|
|
//
|
|
// Upgrade has been completed, delete the registry key.
|
|
//
|
|
if (pKeyName && (pTemp = AllocSplMem(sizeof(struct StringList)))) {
|
|
pTemp->pKeyName = pKeyName;
|
|
pTemp->dwVersionIndex = dwVersionIndex;
|
|
pTemp->pNext = pStart;
|
|
pStart = pTemp;
|
|
} else {
|
|
FreeSplMem(pKeyName);
|
|
}
|
|
|
|
pKeyName = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Delete the keys for driver that have completed the upgrade.
|
|
//
|
|
while (pTemp = pStart) {
|
|
pStart = pTemp->pNext;
|
|
|
|
hVersionKey = NULL;
|
|
if (RestoreVersionKey(hUpgradeKey,
|
|
pTemp->dwVersionIndex,
|
|
&hVersionKey)) {
|
|
|
|
RegDeleteKey(hVersionKey, pTemp->pKeyName);
|
|
RegCloseKey(hVersionKey);
|
|
}
|
|
|
|
FreeSplMem(pTemp->pKeyName);
|
|
FreeSplMem(pTemp);
|
|
}
|
|
|
|
CleanUp:
|
|
|
|
LeaveSplSem();
|
|
|
|
if (hUpgradeKey) {
|
|
RegCloseKey(hUpgradeKey);
|
|
}
|
|
if (hRootKey) {
|
|
RegCloseKey(hRootKey);
|
|
}
|
|
|
|
CleanUpgradeDirectories();
|
|
|
|
return;
|
|
}
|
|
|
|
VOID CleanUpResources(
|
|
LPWSTR pKeyName,
|
|
LPWSTR pSplName,
|
|
PDRIVER_INFO_6 pDriverInfo,
|
|
PINTERNAL_DRV_FILE *ppInternalDriverFiles,
|
|
DWORD dwFileCount)
|
|
|
|
/*++
|
|
Function Description: Frees resources allocated for driver upgrades
|
|
|
|
Parameters: pKeyName - registry key name
|
|
pSplName - IniSpooler name
|
|
pDriverInfo - driver info 4 pointer
|
|
pInternalDriverFiles - array of INTERNAL_DRV_FILE structures
|
|
dwFileCount -- number of files in file set
|
|
|
|
Return Values: NONE
|
|
--*/
|
|
|
|
{
|
|
if (pKeyName) {
|
|
FreeSplStr(pKeyName);
|
|
}
|
|
if (pSplName) {
|
|
FreeSplStr(pSplName);
|
|
}
|
|
|
|
FreeDriverInfo6(pDriverInfo);
|
|
|
|
CleanupInternalDriverInfo(*ppInternalDriverFiles, dwFileCount);
|
|
*ppInternalDriverFiles = NULL;
|
|
|
|
return;
|
|
}
|
|
|
|
BOOL RestoreParametersForUpgrade(
|
|
HKEY hUpgradeKey,
|
|
DWORD dwIndex,
|
|
LPWSTR *pKeyName,
|
|
LPWSTR *pSplName,
|
|
LPDWORD pdwLevel,
|
|
LPDWORD pdwDriverMoved,
|
|
PDRIVER_INFO_6 *ppDriverInfo)
|
|
|
|
/*++
|
|
Function Description: Retrieves the parameters for pending driver upgrades
|
|
|
|
Parameters: hUpgradeKey -- Registry key containing the upgrade information
|
|
dwIndex -- Index to enumerate
|
|
pKeyName -- pointer to a string containing the key name
|
|
pSplName -- pIniSpooler->pName
|
|
pdwLevel -- pointer to the driver_info level
|
|
pdwDriverMoved -- pointer to the flag indicating if any of the old driver files
|
|
were moved.
|
|
pDriverInfo -- pointer to driver_info struct
|
|
|
|
Return Values: TRUE if some driver has to be upgraded and the
|
|
parameters can be retrieved;
|
|
FALSE otherwise
|
|
--*/
|
|
|
|
{
|
|
BOOL bReturn = FALSE;
|
|
LPWSTR pDriverName = NULL;
|
|
PDRIVER_INFO_6 pDriver6 = NULL;
|
|
DWORD dwError, dwSize, *pVersion;
|
|
HKEY hDriverKey = NULL;
|
|
|
|
//
|
|
// Initialize pSplName & pKeyName
|
|
//
|
|
*pSplName = NULL;
|
|
*pKeyName = NULL;
|
|
*ppDriverInfo = NULL;
|
|
|
|
dwSize = MAX_PATH+1;
|
|
if (!(pDriver6 = AllocSplMem(sizeof(DRIVER_INFO_6))) ||
|
|
!(pDriverName = AllocSplMem(dwSize*sizeof (WCHAR)))) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
dwError = RegEnumKeyEx(hUpgradeKey, dwIndex, pDriverName, &dwSize,
|
|
NULL, NULL, NULL, NULL);
|
|
|
|
if (dwError == ERROR_MORE_DATA) {
|
|
//
|
|
// Need a bigger buffer
|
|
//
|
|
FreeSplMem(pDriverName);
|
|
|
|
//
|
|
// Make room for last \0
|
|
//
|
|
dwSize++;
|
|
if (!(pDriverName = AllocSplMem(dwSize*sizeof (WCHAR)))) {
|
|
|
|
goto CleanUp;
|
|
}
|
|
|
|
dwError = RegEnumKeyEx(hUpgradeKey, dwIndex, pDriverName, &dwSize,
|
|
NULL, NULL, NULL, NULL);
|
|
}
|
|
|
|
if (dwError) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (RegCreateKeyEx(hUpgradeKey, pDriverName, 0,
|
|
NULL, 0, KEY_READ, NULL, &hDriverKey, NULL) ||
|
|
|
|
!RegGetValue(hDriverKey, L"Level", (LPBYTE *)&pdwLevel) ||
|
|
|
|
!RegGetValue(hDriverKey, L"DriverMoved", (LPBYTE *)&pdwDriverMoved) ||
|
|
|
|
!RegGetValue(hDriverKey, L"SplName", (LPBYTE *)&pSplName)) {
|
|
|
|
goto CleanUp;
|
|
}
|
|
|
|
switch (*pdwLevel) {
|
|
case 6:
|
|
|
|
dwSize = sizeof(FILETIME);
|
|
|
|
if (RegQueryValueEx( hDriverKey,
|
|
L"ftDriverDate",
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&pDriver6->ftDriverDate,
|
|
&dwSize
|
|
)!=ERROR_SUCCESS) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
dwSize = sizeof(DWORDLONG);
|
|
|
|
if (RegQueryValueEx( hDriverKey,
|
|
L"dwlDriverVersion",
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&pDriver6->dwlDriverVersion,
|
|
&dwSize
|
|
)!=ERROR_SUCCESS){
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (!RegGetValue(hDriverKey, L"pszMfgName", (LPBYTE *)&pDriver6->pszMfgName) ||
|
|
|
|
!RegGetValue(hDriverKey, L"pszOEMUrl", (LPBYTE *)&pDriver6->pszOEMUrl) ||
|
|
|
|
!RegGetValue(hDriverKey, L"pszHardwareID", (LPBYTE *)&pDriver6->pszHardwareID) ||
|
|
|
|
!RegGetValue(hDriverKey, L"pszProvider", (LPBYTE *)&pDriver6->pszProvider)
|
|
)
|
|
{
|
|
goto CleanUp;
|
|
}
|
|
|
|
case 4:
|
|
|
|
if (!RegGetValue(hDriverKey, L"pszzPreviousNames",
|
|
(LPBYTE *)&pDriver6->pszzPreviousNames)) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
case 3:
|
|
|
|
if (!RegGetValue(hDriverKey, L"pDefaultDataType",
|
|
(LPBYTE *)&pDriver6->pDefaultDataType) ||
|
|
|
|
!RegGetValue(hDriverKey, L"pMonitorName",
|
|
(LPBYTE *)&pDriver6->pMonitorName) ||
|
|
|
|
!RegGetValue(hDriverKey, L"pDependentFiles",
|
|
(LPBYTE *)&pDriver6->pDependentFiles) ||
|
|
|
|
!RegGetValue(hDriverKey, L"pHelpFile",
|
|
(LPBYTE *)&pDriver6->pHelpFile)) {
|
|
|
|
goto CleanUp;
|
|
}
|
|
|
|
case 2:
|
|
|
|
pVersion = &pDriver6->cVersion;
|
|
|
|
if (!RegGetValue(hDriverKey, L"pConfigFile",
|
|
(LPBYTE *)&pDriver6->pConfigFile) ||
|
|
|
|
!RegGetValue(hDriverKey, L"pDataFile",
|
|
(LPBYTE *)&pDriver6->pDataFile) ||
|
|
|
|
!RegGetValue(hDriverKey, L"pDriverPath",
|
|
(LPBYTE *)&pDriver6->pDriverPath) ||
|
|
|
|
!RegGetValue(hDriverKey, L"pName",
|
|
(LPBYTE *)&pDriver6->pName) ||
|
|
|
|
!RegGetValue(hDriverKey, L"pEnvironment",
|
|
(LPBYTE *)&pDriver6->pEnvironment) ||
|
|
|
|
!RegGetValue(hDriverKey, L"cVersion",
|
|
(LPBYTE *)&pVersion)) {
|
|
|
|
goto CleanUp;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
goto CleanUp;
|
|
}
|
|
|
|
*ppDriverInfo = pDriver6;
|
|
*pKeyName = pDriverName;
|
|
|
|
pDriver6 = NULL;
|
|
pDriverName = NULL;
|
|
|
|
bReturn = TRUE;
|
|
|
|
CleanUp:
|
|
|
|
if (!bReturn) {
|
|
FreeDriverInfo6(pDriver6);
|
|
|
|
FreeSplMem(*pSplName);
|
|
*pSplName = NULL;
|
|
|
|
FreeSplMem(pDriverName);
|
|
}
|
|
|
|
if (hDriverKey) {
|
|
RegCloseKey(hDriverKey);
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
VOID FreeDriverInfo6(
|
|
PDRIVER_INFO_6 pDriver6)
|
|
|
|
/*++
|
|
Function Description: Frees a driver_info_6 struct and the strings inside it.
|
|
|
|
Parameters: pDriver6 -- pointer to the driver_info_6 struct
|
|
|
|
Return Values: NONE
|
|
--*/
|
|
|
|
{
|
|
if (!pDriver6) {
|
|
return;
|
|
}
|
|
|
|
if (pDriver6->pName) {
|
|
FreeSplMem(pDriver6->pName);
|
|
}
|
|
if (pDriver6->pEnvironment) {
|
|
FreeSplMem(pDriver6->pEnvironment);
|
|
}
|
|
if (pDriver6->pDriverPath) {
|
|
FreeSplMem(pDriver6->pDriverPath);
|
|
}
|
|
if (pDriver6->pConfigFile) {
|
|
FreeSplMem(pDriver6->pConfigFile);
|
|
}
|
|
if (pDriver6->pHelpFile) {
|
|
FreeSplMem(pDriver6->pHelpFile);
|
|
}
|
|
if (pDriver6->pDataFile) {
|
|
FreeSplMem(pDriver6->pDataFile);
|
|
}
|
|
if (pDriver6->pDependentFiles) {
|
|
FreeSplMem(pDriver6->pDependentFiles);
|
|
}
|
|
if (pDriver6->pMonitorName) {
|
|
FreeSplMem(pDriver6->pMonitorName);
|
|
}
|
|
if (pDriver6->pDefaultDataType) {
|
|
FreeSplMem(pDriver6->pDefaultDataType);
|
|
}
|
|
if (pDriver6->pszzPreviousNames) {
|
|
FreeSplMem(pDriver6->pszzPreviousNames);
|
|
}
|
|
if (pDriver6->pszMfgName) {
|
|
FreeSplMem(pDriver6->pszMfgName);
|
|
}
|
|
if (pDriver6->pszOEMUrl) {
|
|
FreeSplMem(pDriver6->pszOEMUrl);
|
|
}
|
|
if (pDriver6->pszHardwareID) {
|
|
FreeSplMem(pDriver6->pszHardwareID);
|
|
}
|
|
if (pDriver6->pszProvider) {
|
|
FreeSplMem(pDriver6->pszProvider);
|
|
}
|
|
|
|
FreeSplMem(pDriver6);
|
|
|
|
return;
|
|
}
|
|
|
|
BOOL RegGetValue(
|
|
HKEY hDriverKey,
|
|
LPWSTR pValueName,
|
|
LPBYTE *pValue)
|
|
|
|
/*++
|
|
Function Description: This function retrieves values from the registry. It allocates the
|
|
necessary buffers which should be freed later. The value types are
|
|
DWORD, SZ or MULTI_SZ.
|
|
|
|
Parameters: hDriverKey -- handle to the registry key
|
|
pValueName -- name of the value to be queried
|
|
pValue -- pointer to pointer to store the result
|
|
|
|
Return Values: TRUE if successful; FALSE otherwise
|
|
--*/
|
|
|
|
{
|
|
BOOL bReturn = FALSE;
|
|
DWORD dwError, dwSize = 0, dwType;
|
|
LPBYTE pBuffer = NULL;
|
|
|
|
dwError = RegQueryValueEx(hDriverKey, pValueName, NULL, NULL, NULL, &dwSize);
|
|
|
|
if ((dwError == ERROR_SUCCESS) && (pBuffer = AllocSplMem(dwSize))) {
|
|
|
|
if (dwError = RegQueryValueEx(hDriverKey, pValueName,
|
|
NULL, &dwType, pBuffer, &dwSize)) {
|
|
|
|
goto CleanUp;
|
|
}
|
|
|
|
} else {
|
|
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (dwType == REG_DWORD) {
|
|
//
|
|
// Store DWORD values directly in the location.
|
|
//
|
|
*((LPDWORD)*pValue) = *((LPDWORD)pBuffer);
|
|
FreeSplMem(pBuffer);
|
|
pBuffer = NULL;
|
|
} else {
|
|
//
|
|
// Return pointers for strings and MultiSz strings.
|
|
//
|
|
*((LPBYTE *)pValue) = pBuffer;
|
|
}
|
|
|
|
bReturn = TRUE;
|
|
|
|
CleanUp:
|
|
|
|
if (!bReturn && pBuffer) {
|
|
FreeSplMem(pBuffer);
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
DWORD GetDriverFileVersion(
|
|
PINIVERSION pIniVersion,
|
|
LPWSTR pFileName)
|
|
|
|
/*++
|
|
Function Description: Retrieves the version number of the file
|
|
|
|
Parameters: pIniVersion -- pointer to PINIVERSION
|
|
pFileName -- file name
|
|
|
|
Return Values: file version number
|
|
--*/
|
|
|
|
{
|
|
PDRVREFCNT pdrc;
|
|
DWORD dwReturn = 0;
|
|
|
|
SplInSem();
|
|
|
|
if (!pIniVersion || !pFileName || !(*pFileName)) {
|
|
return dwReturn;
|
|
}
|
|
|
|
for (pdrc = pIniVersion->pDrvRefCnt;
|
|
pdrc;
|
|
pdrc = pdrc->pNext) {
|
|
|
|
if (lstrcmpi(pFileName,pdrc->szDrvFileName) == 0) {
|
|
dwReturn = pdrc->dwVersion;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return dwReturn;
|
|
}
|
|
|
|
BOOL GetDriverFileCachedVersion(
|
|
IN PINIVERSION pIniVersion,
|
|
IN LPCWSTR pFileName,
|
|
OUT DWORD *pFileVersion
|
|
)
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
GetDriverFileCachedVersion
|
|
|
|
Routine Description:
|
|
|
|
This routine returns a file's minor version.
|
|
The file must be an executable( file name ended in .DLL or .EXE )
|
|
pIniVersion keeps a linked list with information about all driver files.
|
|
To avoid service start up delays, the entries in this list aren't initialized
|
|
when Spooler starts. GetPrintDriverVersion loads the executable's data segment
|
|
and this will increase Spooler initialization time.
|
|
If the cache entry isn't initialized, call GetPrintDriverVersion and initialize it.
|
|
Else, return cached information.
|
|
When pIniVersion is NULL, just call GetPrintDriverVersion.
|
|
|
|
Arguments:
|
|
|
|
pIniVersion - pointer to PINIVERSION structure. Can be NULL.
|
|
pFileName - file name
|
|
pFileVersion - retrieve cached file version
|
|
VersionType - specifies which version to return
|
|
|
|
Return Value:
|
|
|
|
TRUE file version was successfully returned.
|
|
|
|
--*/
|
|
{
|
|
PDRVREFCNT pdrc;
|
|
BOOL bRetValue = FALSE;
|
|
BOOL bFound = FALSE;
|
|
|
|
SplInSem();
|
|
|
|
if (pFileVersion && pFileName && *pFileName)
|
|
{
|
|
*pFileVersion = 0;
|
|
//
|
|
// Don't do anything for non-executable files
|
|
//
|
|
if (!IsEXEFile(pFileName))
|
|
{
|
|
bRetValue = TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If pIniVersion is NULL, then we cannot access cached information.
|
|
// This code path was written for calls from SplCopyNumberOfFiles(files.c)
|
|
//
|
|
if (!pIniVersion)
|
|
{
|
|
bRetValue = GetPrintDriverVersion(pFileName,
|
|
NULL,
|
|
pFileVersion);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Search the entry in pIniVersion's list of files
|
|
//
|
|
for (pdrc = pIniVersion->pDrvRefCnt;
|
|
pdrc;
|
|
pdrc = pdrc->pNext)
|
|
{
|
|
LPCWSTR pFile = FindFileName(pFileName);
|
|
|
|
if (pFile && lstrcmpi(pFile, pdrc->szDrvFileName) == 0)
|
|
{
|
|
//
|
|
// Return cached information.
|
|
//
|
|
if(pdrc->bInitialized)
|
|
{
|
|
*pFileVersion = pdrc->dwFileMinorVersion;
|
|
bRetValue = TRUE;
|
|
}
|
|
else if (GetPrintDriverVersion(pFileName,
|
|
&pdrc->dwFileMajorVersion,
|
|
&pdrc->dwFileMinorVersion))
|
|
{
|
|
//
|
|
// Mark the entry as initialized so next time we don't have
|
|
// to do the work of calling GetPrintDriverVersion.
|
|
//
|
|
pdrc->bInitialized = TRUE;
|
|
*pFileVersion = pdrc->dwFileMinorVersion;
|
|
bRetValue = TRUE;
|
|
}
|
|
|
|
//
|
|
// Break the loop when file found.
|
|
//
|
|
bFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bFound)
|
|
{
|
|
bRetValue = TRUE;
|
|
}
|
|
|
|
return bRetValue;
|
|
}
|
|
|
|
VOID IncrementFileVersion(
|
|
PINIVERSION pIniVersion,
|
|
LPCWSTR pFileName)
|
|
|
|
/*++
|
|
Function Description: Increments the version number of the file.
|
|
|
|
Parameters: pIniVersion -- pointer to PINIVERSION
|
|
pFileName -- file name
|
|
|
|
Return Values: NONE
|
|
--*/
|
|
|
|
{
|
|
PDRVREFCNT pdrc;
|
|
|
|
SplInSem();
|
|
|
|
if (!pIniVersion || !pFileName || !(*pFileName)) {
|
|
return;
|
|
}
|
|
|
|
for (pdrc = pIniVersion->pDrvRefCnt;
|
|
pdrc;
|
|
pdrc = pdrc->pNext) {
|
|
|
|
if (lstrcmpi(pFileName,pdrc->szDrvFileName) == 0) {
|
|
pdrc->dwVersion++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
BOOL
|
|
CompleteDriverUpgrade(
|
|
IN PINISPOOLER pIniSpooler,
|
|
IN PINIENVIRONMENT pIniEnvironment,
|
|
IN PINIVERSION pIniVersion,
|
|
IN PINIDRIVER pIniDriver,
|
|
IN DWORD dwLevel,
|
|
IN LPBYTE pDriverInfo,
|
|
IN DWORD dwFileCopyFlags,
|
|
IN PINTERNAL_DRV_FILE pInternalDriverFiles,
|
|
IN DWORD dwFileCount,
|
|
IN DWORD dwVersion,
|
|
IN DWORD dwTempDir,
|
|
IN BOOL bDriverMoved,
|
|
IN BOOL bDriverFileMoved,
|
|
IN BOOL bConfigFileMoved
|
|
)
|
|
/*++
|
|
Function Description: This functions updates the INIDRIVER struct and calls DrvUpgradePrinter
|
|
and DrvDriverEvent. An event for adding printer drivers is logged.
|
|
|
|
Parameters: pIniSpooler -- pointer to INISPOOLER
|
|
pIniEnvironment -- pointer to INIENVIRONMENT
|
|
pIniVersion -- pointer to INIVERSION
|
|
pIniDriver -- pointer to INIDRIVER
|
|
dwLevel -- driver_info level
|
|
pDriverInfo -- pointer to driver_info
|
|
dwFileCopyFlags -- AddPrinterDriver file copy flags.
|
|
pInternalDriverFiles -- array of INTERNAL_DRV_FILE structures
|
|
dwFileCount -- number of files in file set
|
|
dwVersion -- driver version
|
|
dwTempDir -- temp directory number for loaded drivers
|
|
bDriverMoved -- Were any files moved to the Old directory ?
|
|
bDriverFileMoved -- driver file moved ?
|
|
bConfigFileMoved -- config file moved ?
|
|
|
|
Return Values: TRUE if successful; FALSE otherwise
|
|
--*/
|
|
|
|
{
|
|
WCHAR szDirectory[MAX_PATH];
|
|
LPWSTR pOldDir, pTemp, pEnvironment = szEnvironment;
|
|
LPBYTE pDriver4 = NULL, pUpgradeInfo2 = NULL;
|
|
DWORD cbBuf;
|
|
|
|
PINIMONITOR pIniLangMonitor = NULL;
|
|
PINISPOOLER pTempIniSpooler, pIniNextSpooler;
|
|
PINIDRIVER pTempIniDriver = NULL;
|
|
PINIPRINTER pFixUpIniPrinter;
|
|
PINIVERSION pTempIniVersion;
|
|
LPDRIVER_INFO_4 pDrvInfo4 = NULL;
|
|
BOOL bUpdatePrinters = FALSE;
|
|
|
|
//
|
|
// Save the driver_info_4 struct for the old driver. This is passed to the
|
|
// DrvUpgradePrinter call.
|
|
//
|
|
if (pIniDriver && bDriverMoved) {
|
|
|
|
cbBuf = GetDriverInfoSize(pIniDriver, 4, pIniVersion, pIniEnvironment,
|
|
NULL, pIniSpooler);
|
|
|
|
if (pDriver4 = (LPBYTE) AllocSplMem(cbBuf)) {
|
|
|
|
pUpgradeInfo2 = CopyIniDriverToDriverInfo(pIniEnvironment, pIniVersion,
|
|
pIniDriver, 4, pDriver4,
|
|
pDriver4 + cbBuf, NULL, pIniSpooler);
|
|
}
|
|
}
|
|
//
|
|
// Update or create the driver INI structure.
|
|
//
|
|
pIniDriver = CreateDriverEntry(pIniEnvironment,
|
|
pIniVersion,
|
|
dwLevel,
|
|
pDriverInfo,
|
|
dwFileCopyFlags,
|
|
pIniSpooler,
|
|
pInternalDriverFiles,
|
|
dwFileCount,
|
|
dwTempDir,
|
|
pIniDriver);
|
|
//
|
|
// Fail the call if pIniDriver failed
|
|
//
|
|
if (pIniDriver == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Increment version numbers
|
|
//
|
|
if (bDriverFileMoved) {
|
|
IncrementFileVersion(pIniVersion, FindFileName(pInternalDriverFiles[0].pFileName));
|
|
}
|
|
if (bConfigFileMoved) {
|
|
IncrementFileVersion(pIniVersion, FindFileName(pInternalDriverFiles[1].pFileName));
|
|
}
|
|
|
|
pDrvInfo4 = (LPDRIVER_INFO_4) pDriverInfo;
|
|
|
|
if (pDrvInfo4->pEnvironment &&
|
|
*pDrvInfo4->pEnvironment) {
|
|
|
|
pEnvironment = pDrvInfo4->pEnvironment;
|
|
}
|
|
|
|
if ((dwLevel == 3 || dwLevel == 4 || dwLevel ==6) &&
|
|
pDrvInfo4->pMonitorName &&
|
|
*pDrvInfo4->pMonitorName &&
|
|
_wcsicmp(pEnvironment, szWin95Environment)) {
|
|
|
|
pIniLangMonitor = FindMonitor(pDrvInfo4->pMonitorName,
|
|
pLocalIniSpooler);
|
|
}
|
|
|
|
if (pIniLangMonitor &&
|
|
pIniDriver->pIniLangMonitor != pIniLangMonitor) {
|
|
|
|
if (pIniDriver->pIniLangMonitor)
|
|
pIniDriver->pIniLangMonitor->cRef--;
|
|
|
|
if (pIniLangMonitor)
|
|
pIniLangMonitor->cRef++;
|
|
|
|
pIniDriver->pIniLangMonitor = pIniLangMonitor;
|
|
}
|
|
//
|
|
// Increment cRefs for leaving SplSem
|
|
//
|
|
INCSPOOLERREF( pIniSpooler );
|
|
INCDRIVERREF( pIniDriver );
|
|
pIniEnvironment->cRef++;
|
|
//
|
|
// Call DrvDriverEvent in the Driver. Environment and version checks are
|
|
// done inside NotifyDriver.
|
|
NotifyDriver(pIniSpooler,
|
|
pIniEnvironment,
|
|
pIniVersion,
|
|
pIniDriver,
|
|
DRIVER_EVENT_INITIALIZE,
|
|
0);
|
|
|
|
bUpdatePrinters = DriverAddedOrUpgraded(pInternalDriverFiles, dwFileCount);
|
|
|
|
//
|
|
// Call DrvUprgadePrinter if the driver added belongs to this version
|
|
// and environment. And the pIniSpooler is not a cluster spooler
|
|
//
|
|
if (!(pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER) &&
|
|
pThisEnvironment == pIniEnvironment) {
|
|
//
|
|
// Walk through all pIniSpoolers that print.
|
|
//
|
|
INCSPOOLERREF( pLocalIniSpooler );
|
|
|
|
for( pTempIniSpooler = pLocalIniSpooler;
|
|
pTempIniSpooler;
|
|
pTempIniSpooler = pIniNextSpooler ){
|
|
|
|
//
|
|
// Do not touch the driver belonging to cluster spoolers. Cluster spoolers
|
|
// handle thier drivers themselves
|
|
//
|
|
if (pTempIniSpooler->SpoolerFlags & SPL_PRINT && !(pTempIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER)){
|
|
//
|
|
// Walk all the printers and see if anyone is using this driver.
|
|
//
|
|
for ( pFixUpIniPrinter = pTempIniSpooler->pIniPrinter;
|
|
pFixUpIniPrinter != NULL;
|
|
pFixUpIniPrinter = pFixUpIniPrinter->pNext ) {
|
|
//
|
|
// Does this Printer Have this driver ?
|
|
//
|
|
if ( lstrcmpi( pFixUpIniPrinter->pIniDriver->pName,
|
|
pIniDriver->pName ) == STRINGS_ARE_EQUAL ) {
|
|
|
|
pTempIniDriver = FindCompatibleDriver( pIniEnvironment,
|
|
&pTempIniVersion,
|
|
pIniDriver->pName,
|
|
dwVersion,
|
|
FIND_COMPATIBLE_VERSION | DRIVER_UPGRADE);
|
|
|
|
SPLASSERT(pTempIniDriver != NULL);
|
|
|
|
//
|
|
// Does this Printer Has a Newer Driver it should be using ?
|
|
// Note: within the same version, pIniPrinter->pIniDriver
|
|
// does not change (the fields are updated in an upgrade,
|
|
// but the same pIniDriver is used).
|
|
//
|
|
// Version 2 is not compatible with anything else,
|
|
// so the pIniDrivers won't change in SUR.
|
|
//
|
|
if ( pTempIniDriver != pFixUpIniPrinter->pIniDriver ) {
|
|
|
|
DECDRIVERREF( pFixUpIniPrinter->pIniDriver );
|
|
|
|
pFixUpIniPrinter->pIniDriver = pTempIniDriver;
|
|
|
|
INCDRIVERREF( pFixUpIniPrinter->pIniDriver );
|
|
}
|
|
}
|
|
}
|
|
|
|
pOldDir = NULL;
|
|
|
|
if ( !bDriverMoved ) {
|
|
//
|
|
// Use older version of the driver
|
|
//
|
|
pTempIniDriver = FindCompatibleDriver( pIniEnvironment,
|
|
&pTempIniVersion,
|
|
pIniDriver->pName,
|
|
(dwVersion>2)?(dwVersion - 1):dwVersion,
|
|
FIND_ANY_VERSION | DRIVER_UPGRADE);
|
|
|
|
if ( pTempIniDriver != NULL ) {
|
|
|
|
SPLASSERT( pTempIniVersion != NULL );
|
|
|
|
|
|
GetDriverVersionDirectory( szDirectory,
|
|
COUNTOF(szDirectory),
|
|
pIniSpooler,
|
|
pThisEnvironment,
|
|
pTempIniVersion,
|
|
pTempIniDriver,
|
|
NULL );
|
|
|
|
if ( DirectoryExists( szDirectory )) {
|
|
|
|
pOldDir = (LPWSTR) szDirectory;
|
|
}
|
|
|
|
cbBuf = GetDriverInfoSize(pTempIniDriver, 4, pTempIniVersion,
|
|
pIniEnvironment, NULL, pIniSpooler);
|
|
|
|
if (pDriver4 = (LPBYTE) AllocSplMem(cbBuf)) {
|
|
|
|
pUpgradeInfo2 = CopyIniDriverToDriverInfo(pIniEnvironment,
|
|
pTempIniVersion,
|
|
pTempIniDriver,
|
|
4,
|
|
pDriver4,
|
|
pDriver4 + cbBuf,
|
|
NULL,
|
|
pIniSpooler);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
if((StrNCatBuff(szDirectory,
|
|
MAX_PATH,
|
|
pIniSpooler->pDir,
|
|
L"\\drivers\\",
|
|
pIniEnvironment->pDirectory,
|
|
L"\\",
|
|
pIniVersion->szDirectory,
|
|
L"\\Old",
|
|
NULL) == ERROR_SUCCESS))
|
|
{
|
|
pOldDir = (LPWSTR) szDirectory;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Ignore error.
|
|
//
|
|
}
|
|
|
|
}
|
|
|
|
INCDRIVERREF(pIniDriver);
|
|
if( bUpdatePrinters) {
|
|
ForEachPrinterCallDriverDrvUpgrade(pTempIniSpooler,
|
|
pIniDriver,
|
|
pOldDir,
|
|
pInternalDriverFiles,
|
|
dwFileCount,
|
|
pUpgradeInfo2 ? pDriver4
|
|
: NULL);
|
|
}
|
|
DECDRIVERREF(pIniDriver);
|
|
}
|
|
pIniNextSpooler = pTempIniSpooler->pIniNextSpooler;
|
|
|
|
if ( pIniNextSpooler ) {
|
|
INCSPOOLERREF( pIniNextSpooler );
|
|
}
|
|
DECSPOOLERREF( pTempIniSpooler );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Perform driver upgrade if the pIniSpooler is a cluster spooler
|
|
//
|
|
if (pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER &&
|
|
!lstrcmpi(pIniEnvironment->pName, szEnvironment)) {
|
|
|
|
DBGMSG(DBG_CLUSTER, ("CompleteDriverUpgrade searching for cluster spooler printers\n"));
|
|
|
|
//
|
|
// Walk all the printers and see if anyone is using this driver.
|
|
//
|
|
for ( pFixUpIniPrinter = pIniSpooler->pIniPrinter;
|
|
pFixUpIniPrinter != NULL;
|
|
pFixUpIniPrinter = pFixUpIniPrinter->pNext )
|
|
{
|
|
//
|
|
// Does this Printer Have this driver ?
|
|
//
|
|
if (lstrcmpi(pFixUpIniPrinter->pIniDriver->pName, pIniDriver->pName) == STRINGS_ARE_EQUAL)
|
|
{
|
|
pTempIniDriver = FindCompatibleDriver(pIniEnvironment,
|
|
&pTempIniVersion,
|
|
pIniDriver->pName,
|
|
dwVersion,
|
|
FIND_COMPATIBLE_VERSION | DRIVER_UPGRADE);
|
|
|
|
SPLASSERT( pTempIniDriver != NULL );
|
|
|
|
//
|
|
// Does this Printer Has a Newer Driver it should be using ?
|
|
// Note: within the same version, pIniPrinter->pIniDriver
|
|
// does not change (the fields are updated in an upgrade,
|
|
// but the same pIniDriver is used).
|
|
//
|
|
// Version 2 is not compatible with anything else,
|
|
// so the pIniDrivers won't change in SUR.
|
|
//
|
|
|
|
if ( pTempIniDriver != pFixUpIniPrinter->pIniDriver )
|
|
{
|
|
DECDRIVERREF( pFixUpIniPrinter->pIniDriver );
|
|
|
|
pFixUpIniPrinter->pIniDriver = pTempIniDriver;
|
|
|
|
INCDRIVERREF( pFixUpIniPrinter->pIniDriver );
|
|
}
|
|
}
|
|
}
|
|
|
|
pOldDir = NULL;
|
|
|
|
if ( !bDriverMoved )
|
|
{
|
|
//
|
|
// Use older version of the driver
|
|
//
|
|
pTempIniDriver = FindCompatibleDriver( pIniEnvironment,
|
|
&pTempIniVersion,
|
|
pIniDriver->pName,
|
|
(dwVersion>2)?(dwVersion - 1):dwVersion,
|
|
FIND_ANY_VERSION | DRIVER_UPGRADE);
|
|
|
|
if ( pTempIniDriver != NULL )
|
|
{
|
|
SPLASSERT( pTempIniVersion != NULL );
|
|
|
|
GetDriverVersionDirectory( szDirectory,
|
|
COUNTOF(szDirectory),
|
|
pIniSpooler,
|
|
pIniEnvironment,
|
|
pTempIniVersion,
|
|
pTempIniDriver,
|
|
NULL );
|
|
|
|
if ( DirectoryExists( szDirectory ))
|
|
{
|
|
pOldDir = (LPWSTR) szDirectory;
|
|
}
|
|
|
|
cbBuf = GetDriverInfoSize(pTempIniDriver, 4, pTempIniVersion, pIniEnvironment, NULL, pIniSpooler);
|
|
|
|
if (pDriver4 = (LPBYTE) AllocSplMem(cbBuf))
|
|
{
|
|
pUpgradeInfo2 = CopyIniDriverToDriverInfo(pIniEnvironment,
|
|
pTempIniVersion,
|
|
pTempIniDriver,
|
|
4,
|
|
pDriver4,
|
|
pDriver4 + cbBuf,
|
|
NULL,
|
|
pIniSpooler);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if((StrNCatBuff(szDirectory,
|
|
MAX_PATH,
|
|
pIniSpooler->pDir,
|
|
L"\\drivers\\",
|
|
pIniEnvironment->pDirectory,
|
|
L"\\",
|
|
pIniVersion->szDirectory,
|
|
L"\\Old",
|
|
NULL) == ERROR_SUCCESS))
|
|
{
|
|
pOldDir = (LPWSTR) szDirectory;
|
|
}
|
|
}
|
|
|
|
INCDRIVERREF(pIniDriver);
|
|
if( bUpdatePrinters)
|
|
{
|
|
ForEachPrinterCallDriverDrvUpgrade(pIniSpooler,
|
|
pIniDriver,
|
|
pOldDir,
|
|
pInternalDriverFiles,
|
|
dwFileCount,
|
|
pUpgradeInfo2 ? pDriver4 : NULL);
|
|
}
|
|
|
|
DECDRIVERREF(pIniDriver);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (pDriver4) {
|
|
FreeSplMem(pDriver4);
|
|
pDriver4 = NULL;
|
|
}
|
|
|
|
//
|
|
// Log Event - Successfully adding the printer driver.
|
|
//
|
|
// Note we use pLocalIniSpooler here because drivers are currently
|
|
// global accross all spoolers and we always want it logged
|
|
//
|
|
pTemp = BuildFilesCopiedAsAString(pInternalDriverFiles, dwFileCount);
|
|
|
|
SplLogEvent(pLocalIniSpooler,
|
|
LOG_WARNING,
|
|
MSG_DRIVER_ADDED,
|
|
TRUE,
|
|
pIniDriver->pName,
|
|
pIniEnvironment->pName,
|
|
pIniVersion->pName,
|
|
pTemp,
|
|
NULL);
|
|
|
|
FreeSplMem(pTemp);
|
|
//
|
|
// Decrement cRefs after reentering SplSem
|
|
//
|
|
DECSPOOLERREF( pIniSpooler );
|
|
DECDRIVERREF( pIniDriver );
|
|
pIniEnvironment->cRef--;
|
|
|
|
SetPrinterChange(NULL,
|
|
NULL,
|
|
NULL,
|
|
PRINTER_CHANGE_ADD_PRINTER_DRIVER,
|
|
pLocalIniSpooler );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID CleanUpgradeDirectories()
|
|
|
|
/*++
|
|
Function Description: Deletes the Old and New directories if there are
|
|
no pending driver upgrades.
|
|
|
|
Parameters: NONE
|
|
|
|
Return Values: NONE
|
|
--*/
|
|
|
|
{
|
|
DWORD dwError, dwSize, dwVersionIndex;
|
|
BOOL bPendingUpgrade = FALSE;
|
|
HKEY hRootKey = NULL, hUpgradeKey = NULL, hVersionKey = NULL;
|
|
WCHAR pDriverDir[MAX_PATH], pCleanupDir[MAX_PATH];
|
|
PINIENVIRONMENT pIniEnvironment = NULL;
|
|
PINIENVIRONMENT pIniEnvironmentNext = NULL;
|
|
PINISPOOLER pIniSpooler = NULL;
|
|
PINISPOOLER pIniSpoolerNext = NULL;
|
|
PINIVERSION pIniVersion = NULL;
|
|
|
|
//
|
|
// This should always be called outside the CS, check that we are OK.
|
|
//
|
|
SplOutSem();
|
|
|
|
if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, szRegistryRoot, 0,
|
|
NULL, 0, KEY_READ | DELETE, NULL, &hRootKey, NULL) ||
|
|
|
|
RegCreateKeyEx(hRootKey, szPendingUpgrades, 0,
|
|
NULL, 0, KEY_READ | DELETE, NULL, &hUpgradeKey, NULL)) {
|
|
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Loop thru the version entries
|
|
//
|
|
for (dwVersionIndex = 0, hVersionKey = NULL;
|
|
|
|
RestoreVersionKey(hUpgradeKey, dwVersionIndex, &hVersionKey);
|
|
|
|
RegCloseKey(hVersionKey), hVersionKey = NULL, ++dwVersionIndex) {
|
|
|
|
// Search for pending upgrade keys
|
|
dwSize = MAX_PATH;
|
|
dwError = RegEnumKeyEx(hVersionKey, 0, pDriverDir, &dwSize,
|
|
NULL, NULL, NULL, NULL);
|
|
|
|
if (dwError != ERROR_NO_MORE_ITEMS) {
|
|
bPendingUpgrade = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
EnterSplSem();
|
|
|
|
// If there aren't any pending driver upgrades, delete the Old and
|
|
// new directories and the files within.
|
|
if ( pLocalIniSpooler) {
|
|
|
|
INCSPOOLERREF(pLocalIniSpooler);
|
|
|
|
for (pIniSpooler = pLocalIniSpooler;
|
|
pIniSpooler;
|
|
pIniSpooler = pIniSpoolerNext) {
|
|
|
|
if (pIniSpooler->SpoolerFlags & SPL_TYPE_LOCAL ||
|
|
pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER) {
|
|
|
|
//
|
|
// This is a little bogus, since environment reference counts
|
|
// aren't needed, but for consistency with the pIniSpooler code
|
|
// and in case someone actually uses this. I have the ref count
|
|
// correctly.
|
|
//
|
|
if (pIniSpooler->pIniEnvironment) {
|
|
|
|
pIniSpooler->pIniEnvironment->cRef++;
|
|
}
|
|
|
|
for (pIniEnvironment = pIniSpooler->pIniEnvironment;
|
|
pIniEnvironment;
|
|
pIniEnvironment = pIniEnvironmentNext) {
|
|
|
|
for (pIniVersion = pIniEnvironment->pIniVersion;
|
|
pIniVersion;
|
|
pIniVersion = pIniVersion->pNext) {
|
|
|
|
dwError = StrNCatBuff(pDriverDir,
|
|
COUNTOF(pDriverDir),
|
|
pIniSpooler->pDir,
|
|
L"\\drivers\\",
|
|
pIniEnvironment->pDirectory,
|
|
L"\\",
|
|
pIniVersion->szDirectory,
|
|
NULL);
|
|
|
|
//
|
|
// The rest of these operations do not depend on private
|
|
// data held in the inispooler.
|
|
//
|
|
LeaveSplSem();
|
|
|
|
SplOutSem();
|
|
|
|
if (dwError == ERROR_SUCCESS) {
|
|
|
|
dwError = StrNCatBuff(pCleanupDir,
|
|
COUNTOF(pCleanupDir),
|
|
pDriverDir,
|
|
L"\\Old",
|
|
NULL);
|
|
}
|
|
|
|
if (dwError == ERROR_SUCCESS) {
|
|
|
|
DeleteDirectoryRecursively(pCleanupDir, FALSE);
|
|
|
|
dwError = StrNCatBuff(pCleanupDir,
|
|
COUNTOF(pCleanupDir),
|
|
pDriverDir,
|
|
L"\\New",
|
|
NULL);
|
|
}
|
|
|
|
if (dwError == ERROR_SUCCESS && !bPendingUpgrade) {
|
|
|
|
DeleteAllFilesAndDirectory(pCleanupDir, FALSE);
|
|
}
|
|
|
|
EnterSplSem();
|
|
}
|
|
|
|
SplInSem();
|
|
|
|
pIniEnvironmentNext = pIniEnvironment->pNext;
|
|
|
|
if (pIniEnvironmentNext) {
|
|
|
|
pIniEnvironmentNext->cRef++;
|
|
}
|
|
|
|
pIniEnvironment->cRef--;
|
|
}
|
|
}
|
|
|
|
SplInSem();
|
|
|
|
pIniSpoolerNext = pIniSpooler->pIniNextSpooler;
|
|
|
|
if (pIniSpoolerNext) {
|
|
|
|
INCSPOOLERREF(pIniSpoolerNext);
|
|
}
|
|
|
|
//
|
|
// This can result in the pIniSpooler being deleted, which is why we have the stuff
|
|
// with pIniSpooler next on the previous line.
|
|
//
|
|
DECSPOOLERREF(pIniSpooler);
|
|
}
|
|
}
|
|
|
|
LeaveSplSem();
|
|
|
|
CleanUp:
|
|
|
|
SplOutSem();
|
|
|
|
if (hVersionKey) {
|
|
RegCloseKey(hVersionKey);
|
|
}
|
|
|
|
if (hUpgradeKey) {
|
|
RegCloseKey(hUpgradeKey);
|
|
}
|
|
|
|
if (hRootKey) {
|
|
RegCloseKey(hRootKey);
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
CheckFileCopyOptions(
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINIVERSION pIniVersion,
|
|
PINIDRIVER pIniDriver,
|
|
PINTERNAL_DRV_FILE pInternalDriverFiles,
|
|
DWORD dwFileCount,
|
|
DWORD dwFileCopyFlags,
|
|
LPBOOL pbSucceeded
|
|
)
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
CheckFileCopyOptions examines the timestamps of the source and
|
|
target files and determines if strict upgrade/downgrade can fail.
|
|
|
|
Parameters:
|
|
|
|
pIniEnvironment - pointer to a PINIENVIRONMENT structure
|
|
pIniVersion - pointer to a PINIVERSION structure
|
|
pIniDriver - pointer to the old INIDRIVER structure
|
|
pInternalDriverFiles - array of INTERNAL_DRV_FILE structures
|
|
dwFileCount - number of files in file set
|
|
dwFileCopyFlags - file copying options.
|
|
pbSucceeded - flag to indicate the AddPrinterDriver call succeeded.
|
|
|
|
Return Values:
|
|
|
|
TRUE - We need to copy any files. *pbSucceeded is unchanged.
|
|
|
|
FALSE - We don't need to copy, either because the entire call failed
|
|
(e.g., strict upgrade but older source files), or because we don't
|
|
need to do anything. *pbSucceeded indicates if the API call should
|
|
succeed (the latter case).
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL bReturn = FALSE, bInSem = TRUE, bSameMainDriverName = FALSE;
|
|
LPWSTR pDrvDestDir = NULL, pICMDestDir = NULL, pNewDestDir = NULL;
|
|
LPWSTR pTargetFileName = NULL, pFileName;
|
|
DWORD dwCount;
|
|
WIN32_FIND_DATA DestFileData, SourceFileData;
|
|
HANDLE hFileExists;
|
|
DWORD TimeStampComparison;
|
|
enum { Equal, Newer, Older } DriverComparison;
|
|
DWORD dwDriverVersion;
|
|
|
|
|
|
if (!pbSucceeded) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
*pbSucceeded = FALSE;
|
|
|
|
SplInSem();
|
|
|
|
switch (dwFileCopyFlags) {
|
|
|
|
case APD_COPY_ALL_FILES:
|
|
//
|
|
// Nothing to check
|
|
//
|
|
bReturn = TRUE;
|
|
break;
|
|
|
|
case APD_COPY_NEW_FILES:
|
|
//
|
|
// Check if the driver file sets are different
|
|
//
|
|
if (pIniDriver)
|
|
{
|
|
pFileName = wcsrchr(pInternalDriverFiles[0].pFileName, L'\\');
|
|
if (pFileName && pIniDriver->pDriverFile &&
|
|
!_wcsicmp(pFileName+1, pIniDriver->pDriverFile))
|
|
{
|
|
bSameMainDriverName = TRUE;
|
|
}
|
|
}
|
|
|
|
case APD_STRICT_UPGRADE:
|
|
case APD_STRICT_DOWNGRADE:
|
|
//
|
|
// Set up the destination directories
|
|
//
|
|
if (!(pDrvDestDir = AllocSplMem((INTERNET_MAX_HOST_NAME_LENGTH + MAX_PATH + 1) * sizeof(WCHAR))) ||
|
|
!(pTargetFileName = AllocSplMem((INTERNET_MAX_HOST_NAME_LENGTH + MAX_PATH + 1) * sizeof(WCHAR)))) {
|
|
|
|
goto CleanUp;
|
|
}
|
|
//
|
|
// Regular driver directory
|
|
//
|
|
if( !GetEnvironmentScratchDirectory( pDrvDestDir, MAX_PATH, pIniEnvironment, FALSE ) ) {
|
|
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (!BoolFromHResult(StringCchCat(pDrvDestDir, INTERNET_MAX_HOST_NAME_LENGTH + MAX_PATH + 1, L"\\")) ||
|
|
!BoolFromHResult(StringCchCat(pDrvDestDir, INTERNET_MAX_HOST_NAME_LENGTH + MAX_PATH + 1, pIniVersion->szDirectory))) {
|
|
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// New driver files directory where files may be stored temporarily
|
|
//
|
|
if (!BoolFromStatus(StrCatAlloc(&pNewDestDir, pDrvDestDir, L"\\New", NULL))) {
|
|
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (!wcscmp(pIniEnvironment->pName, szWin95Environment)) {
|
|
|
|
if (!BoolFromStatus(StrCatAlloc(&pICMDestDir, pDrvDestDir, L"\\Color", NULL))){
|
|
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
|
|
if (pIniDriver) {
|
|
INCDRIVERREF(pIniDriver);
|
|
}
|
|
LeaveSplSem();
|
|
bInSem = FALSE;
|
|
//
|
|
// Examine the timestamps for the source and the target files.
|
|
//
|
|
for (dwCount = 0; dwCount < dwFileCount; ++dwCount) {
|
|
//
|
|
// Get Source File Date & Time Stamp
|
|
//
|
|
hFileExists = FindFirstFile(pInternalDriverFiles[dwCount].pFileName, &SourceFileData );
|
|
|
|
if (hFileExists == INVALID_HANDLE_VALUE) {
|
|
goto CleanUp;
|
|
} else {
|
|
FindClose(hFileExists);
|
|
}
|
|
|
|
if (!(pFileName = wcsrchr(pInternalDriverFiles[dwCount].pFileName, L'\\'))) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Skip past the backslash.
|
|
//
|
|
++pFileName;
|
|
|
|
if (pICMDestDir && IsAnICMFile(pInternalDriverFiles[dwCount].pFileName)) {
|
|
|
|
//
|
|
// Check in the Color Directory
|
|
//
|
|
StringCchPrintf(pTargetFileName, INTERNET_MAX_HOST_NAME_LENGTH + MAX_PATH + 1, L"%ws\\%ws", pICMDestDir, pFileName);
|
|
|
|
hFileExists = FindFirstFile(pTargetFileName, &DestFileData);
|
|
|
|
} else {
|
|
|
|
LPWSTR pszTestFileName;
|
|
|
|
if ((dwCount == 0) && !bSameMainDriverName && pIniDriver) {
|
|
|
|
//
|
|
// We're processing the main driver file. The server's
|
|
// file doesn't exist on the client, but the client does
|
|
// have a version of this driver.
|
|
//
|
|
// Instead of checking the server's file name on the
|
|
// client, we want to check the client's IniDriver->pDriver
|
|
// to see which is newer.
|
|
//
|
|
// For example, server has rasdd, while client has unidrv.
|
|
// Client does not have rasdd, so we would normally copy
|
|
// rasdd down to the client and change the DRIVER_INFO.
|
|
//
|
|
// Instead, we want to see if the server's unidrv is
|
|
// newer than the client's rasdd. If so, then we
|
|
// need to upgrade.
|
|
//
|
|
// Even if the client did have a new unidrv (even a
|
|
// really new one), we still want to upgrade the
|
|
// client's DRIVER_INFO.
|
|
//
|
|
pszTestFileName = pIniDriver->pDriverFile;
|
|
|
|
} else {
|
|
|
|
pszTestFileName = pFileName;
|
|
}
|
|
//
|
|
// Check in the new directory first
|
|
//
|
|
StringCchPrintf(pTargetFileName, INTERNET_MAX_HOST_NAME_LENGTH + MAX_PATH + 1, L"%ws\\%ws", pNewDestDir, pszTestFileName);
|
|
|
|
hFileExists = FindFirstFile(pTargetFileName, &DestFileData);
|
|
|
|
if (hFileExists == INVALID_HANDLE_VALUE) {
|
|
//
|
|
// Check in the regular driver directory
|
|
//
|
|
StringCchPrintf(pTargetFileName, INTERNET_MAX_HOST_NAME_LENGTH + MAX_PATH + 1, L"%ws\\%ws", pDrvDestDir, pszTestFileName);
|
|
|
|
hFileExists = FindFirstFile(pTargetFileName, &DestFileData);
|
|
}
|
|
}
|
|
|
|
if (hFileExists != INVALID_HANDLE_VALUE) {
|
|
|
|
FindClose(hFileExists);
|
|
|
|
EnterSplSem();
|
|
if (pIniDriver) {
|
|
DECDRIVERREF(pIniDriver);
|
|
}
|
|
bInSem = TRUE;
|
|
|
|
if (!GetDriverFileCachedVersion(pIniVersion, pTargetFileName, &dwDriverVersion)) {
|
|
SetLastError(ERROR_CAN_NOT_COMPLETE);
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (pIniDriver) {
|
|
INCDRIVERREF(pIniDriver);
|
|
}
|
|
LeaveSplSem();
|
|
bInSem = FALSE;
|
|
|
|
DriverComparison = pInternalDriverFiles[dwCount].dwVersion == dwDriverVersion ?
|
|
Equal :
|
|
pInternalDriverFiles[dwCount].dwVersion > dwDriverVersion ?
|
|
Newer :
|
|
Older;
|
|
|
|
if (DriverComparison == Equal) {
|
|
|
|
TimeStampComparison = CompareFileTime( &SourceFileData.ftLastWriteTime,
|
|
&DestFileData.ftLastWriteTime );
|
|
|
|
DriverComparison = TimeStampComparison == 1 ?
|
|
Newer :
|
|
TimeStampComparison == -1 ?
|
|
Older :
|
|
Equal;
|
|
}
|
|
|
|
switch (DriverComparison) {
|
|
|
|
case Newer:
|
|
//
|
|
// Source file newer than the target. Strict downgrade will fail.
|
|
//
|
|
if (dwFileCopyFlags == APD_STRICT_DOWNGRADE) {
|
|
SetLastError(ERROR_CAN_NOT_COMPLETE);
|
|
goto CleanUp;
|
|
}
|
|
break;
|
|
|
|
case Older:
|
|
//
|
|
// Target file newer than the source. Strict upgrade will fail.
|
|
//
|
|
if (dwFileCopyFlags == APD_STRICT_UPGRADE) {
|
|
SetLastError(ERROR_CAN_NOT_COMPLETE);
|
|
goto CleanUp;
|
|
} else {
|
|
|
|
//
|
|
// If we are doing a copy new files (non-strict upgrade),
|
|
// and the main driver files are different, and
|
|
// the driver is already installed, then we want to use
|
|
// the existing driver.
|
|
//
|
|
if ((dwFileCopyFlags == APD_COPY_NEW_FILES) &&
|
|
(dwCount == 0) && !bSameMainDriverName &&
|
|
pIniDriver)
|
|
{
|
|
*pbSucceeded = TRUE;
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// file times are the same
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bReturn = TRUE;
|
|
break;
|
|
|
|
default:
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
break;
|
|
}
|
|
|
|
CleanUp:
|
|
|
|
if (!bInSem) {
|
|
EnterSplSem();
|
|
if (pIniDriver) {
|
|
DECDRIVERREF(pIniDriver);
|
|
}
|
|
}
|
|
if (pDrvDestDir) {
|
|
FreeSplMem(pDrvDestDir);
|
|
}
|
|
if (pTargetFileName) {
|
|
FreeSplMem(pTargetFileName);
|
|
}
|
|
if (pICMDestDir) {
|
|
FreeSplMem(pICMDestDir);
|
|
}
|
|
if (pNewDestDir) {
|
|
FreeSplMem(pNewDestDir);
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
BOOL
|
|
LocalDeletePrinterDriver(
|
|
LPWSTR pName,
|
|
LPWSTR pEnvironment,
|
|
LPWSTR pDriverName
|
|
)
|
|
{
|
|
|
|
BOOL bReturn;
|
|
bReturn = LocalDeletePrinterDriverEx( pName,
|
|
pEnvironment,
|
|
pDriverName,
|
|
0,
|
|
0);
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
BOOL
|
|
LocalDeletePrinterDriverEx(
|
|
LPWSTR pName,
|
|
LPWSTR pEnvironment,
|
|
LPWSTR pDriverName,
|
|
DWORD dwDeleteFlag,
|
|
DWORD dwVersionNum
|
|
)
|
|
{
|
|
PINISPOOLER pIniSpooler;
|
|
BOOL bReturn;
|
|
|
|
pIniSpooler = FindSpoolerByNameIncRef( pName, NULL );
|
|
|
|
if( !pIniSpooler ){
|
|
return ROUTER_UNKNOWN;
|
|
}
|
|
|
|
bReturn = SplDeletePrinterDriverEx( pName,
|
|
pEnvironment,
|
|
pDriverName,
|
|
pIniSpooler,
|
|
dwDeleteFlag,
|
|
dwVersionNum);
|
|
|
|
FindSpoolerByNameDecRef( pIniSpooler );
|
|
return bReturn;
|
|
}
|
|
|
|
BOOL
|
|
SplDeletePrinterDriverEx(
|
|
LPWSTR pName,
|
|
LPWSTR pEnvironment,
|
|
LPWSTR pDriverName,
|
|
PINISPOOLER pIniSpooler,
|
|
DWORD dwDeleteFlag,
|
|
DWORD dwVersionNum
|
|
)
|
|
/*++
|
|
|
|
Function Description: Deletes specific or all versions of a printer driver. Removes unused
|
|
or all files associated with the driver.
|
|
|
|
Parameters: pName - name of the server. NULL implies local machine.
|
|
pEnvironment - string containing the environment of the driver to be deleted.
|
|
NULL implies use local environment.
|
|
pDriverName - string containing the name of the driver.
|
|
pIniSpooler - Pointer to INISPOOLER struct.
|
|
dwDeleteFlag - combination of DPD_DELETE_SPECIFIC_VERSION and
|
|
DPD_DELETE_UNUSED_FILES or DPD_DELETE_ALL_FILES. The defaults
|
|
are delete all versions and dont delete the files.
|
|
dwVersionNum - version number (0-3) of the driver. Used only if dwDeleteFlag
|
|
contains DPD_DELETE_SPECIFIC_VERSION.
|
|
|
|
Return Values: TRUE if deleted.
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
PINIENVIRONMENT pIniEnvironment;
|
|
PINIVERSION pIniVersion;
|
|
PINIDRIVER pIniDriver;
|
|
BOOL bRefCount = FALSE,bEnteredSplSem = FALSE,bReturn = TRUE;
|
|
BOOL bFileRefCount = FALSE;
|
|
BOOL bThisVersion,bSetPrinterChange = FALSE;
|
|
BOOL bFoundDriver = FALSE, bSpecificVersionDeleted = FALSE;
|
|
LPWSTR pIndex;
|
|
WCHAR szDirectory[MAX_PATH];
|
|
HANDLE hImpersonationToken;
|
|
DWORD dwRet;
|
|
|
|
|
|
DBGMSG(DBG_TRACE, ("DeletePrinterDriverEx\n"));
|
|
//
|
|
// Check if the call is for the local machine.
|
|
//
|
|
if ( pName && *pName ) {
|
|
if ( !MyName( pName, pIniSpooler )) {
|
|
bReturn = FALSE;
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
//
|
|
// Invalid Input and Access Checks
|
|
//
|
|
if ( !pDriverName || !*pDriverName ) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
bReturn = FALSE;
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (dwDeleteFlag & ~(DPD_DELETE_SPECIFIC_VERSION
|
|
| DPD_DELETE_ALL_FILES
|
|
| DPD_DELETE_UNUSED_FILES)) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
bReturn = FALSE;
|
|
goto CleanUp;
|
|
}
|
|
|
|
if ( !ValidateObjectAccess( SPOOLER_OBJECT_SERVER,
|
|
SERVER_ACCESS_ADMINISTER,
|
|
NULL, NULL, pIniSpooler )) {
|
|
bReturn = FALSE;
|
|
goto CleanUp;
|
|
}
|
|
|
|
EnterSplSem();
|
|
bEnteredSplSem = TRUE;
|
|
|
|
pIniEnvironment = FindEnvironment(pEnvironment, pIniSpooler);
|
|
|
|
if ( !pIniEnvironment ) {
|
|
SetLastError(ERROR_INVALID_ENVIRONMENT);
|
|
bReturn = FALSE;
|
|
goto CleanUp;
|
|
}
|
|
pIniVersion = pIniEnvironment->pIniVersion;
|
|
|
|
while ( pIniVersion ) {
|
|
|
|
if ((pIniDriver = FindDriverEntry(pIniVersion, pDriverName))) {
|
|
|
|
bFoundDriver = TRUE;
|
|
//
|
|
// bThisVersion indicates if this version is to be deleted.
|
|
//
|
|
bThisVersion = !(dwDeleteFlag & DPD_DELETE_SPECIFIC_VERSION) ||
|
|
(pIniVersion->cMajorVersion == dwVersionNum);
|
|
|
|
if ((pIniDriver->cRef) && bThisVersion) {
|
|
bRefCount = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (bThisVersion &&
|
|
(dwDeleteFlag & DPD_DELETE_ALL_FILES) &&
|
|
FilesInUse(pIniVersion,pIniDriver)) {
|
|
|
|
bFileRefCount = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
pIniVersion = pIniVersion->pNext;
|
|
}
|
|
|
|
if ( !bFoundDriver ) {
|
|
SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER);
|
|
bReturn = FALSE;
|
|
goto CleanUp;
|
|
}
|
|
|
|
if ( bRefCount ) {
|
|
SetLastError( ERROR_PRINTER_DRIVER_IN_USE );
|
|
bReturn = FALSE;
|
|
goto CleanUp;
|
|
}
|
|
|
|
if ( bFileRefCount ) {
|
|
//
|
|
// New error code has to added.
|
|
//
|
|
SetLastError( ERROR_PRINTER_DRIVER_IN_USE );
|
|
bReturn = FALSE;
|
|
goto CleanUp;
|
|
}
|
|
|
|
pIniVersion = pIniEnvironment->pIniVersion;
|
|
|
|
while ( pIniVersion && (!bSpecificVersionDeleted) ) {
|
|
|
|
if ( !(dwDeleteFlag & DPD_DELETE_SPECIFIC_VERSION) ||
|
|
(bSpecificVersionDeleted = (pIniVersion->cMajorVersion == dwVersionNum))) {
|
|
|
|
if (( pIniDriver = FindDriverEntry( pIniVersion, pDriverName ))) {
|
|
|
|
//
|
|
// Remove pending driver upgrades for local environment
|
|
//
|
|
if (!lstrcmpi(pIniEnvironment->pName, szEnvironment)) {
|
|
|
|
RemovePendingUpgradeForDeletedDriver(pDriverName,
|
|
pIniVersion->cMajorVersion,
|
|
pIniSpooler);
|
|
|
|
RemoveDriverTempFiles(pIniSpooler, pIniEnvironment,
|
|
pIniVersion, pIniDriver);
|
|
}
|
|
|
|
if ( !DeleteDriverIni( pIniDriver,
|
|
pIniVersion,
|
|
pIniEnvironment,
|
|
pIniSpooler )) {
|
|
|
|
DBGMSG( DBG_CLUSTER, ("Error - driverini not deleted %d\n", GetLastError()));
|
|
bReturn = FALSE;
|
|
goto CleanUp;
|
|
}
|
|
|
|
bSetPrinterChange = TRUE;
|
|
hImpersonationToken = RevertToPrinterSelf();
|
|
|
|
SPLASSERT(pIniSpooler->pDir!=NULL);
|
|
|
|
dwRet = StrNCatBuff(szDirectory,
|
|
COUNTOF(szDirectory),
|
|
pIniSpooler->pDir,
|
|
L"\\drivers\\",
|
|
pIniEnvironment->pDirectory,
|
|
L"\\",
|
|
pIniVersion->szDirectory,
|
|
L"\\",
|
|
NULL);
|
|
|
|
if (dwRet != ERROR_SUCCESS)
|
|
{
|
|
if (hImpersonationToken)
|
|
{
|
|
ImpersonatePrinterClient(hImpersonationToken);
|
|
}
|
|
bReturn = FALSE;
|
|
SetLastError(dwRet);
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Before we leave for the driver event. Mark this printer driver as
|
|
// pending deletion. This prevents other calls from mistakenly using
|
|
// this driver, even though it is about to be deleted. Drivers should
|
|
// not expect to find any other information about the driver during this
|
|
// call other than what they were presented with.
|
|
//
|
|
pIniDriver->dwDriverFlags |= PRINTER_DRIVER_PENDING_DELETION;
|
|
|
|
//
|
|
// Increment cRefs for leaving SplSem, this prevent SplDeletePrinterDriver
|
|
// from being called twice.
|
|
//
|
|
INCSPOOLERREF( pIniSpooler );
|
|
INCDRIVERREF( pIniDriver );
|
|
pIniEnvironment->cRef++;
|
|
|
|
// Call DrvDriverEvent in the Driver.
|
|
NotifyDriver(pIniSpooler,
|
|
pIniEnvironment,
|
|
pIniVersion,
|
|
pIniDriver,
|
|
DRIVER_EVENT_DELETE,
|
|
dwDeleteFlag);
|
|
|
|
//
|
|
// Decrement cRefs after reentering SplSem
|
|
//
|
|
DECDRIVERREF( pIniDriver );
|
|
DECSPOOLERREF( pIniSpooler );
|
|
pIniEnvironment->cRef--;
|
|
|
|
//
|
|
// Update the file reference counts for the version of the driver that
|
|
// has been deleted.
|
|
//
|
|
UpdateDriverFileRefCnt(pIniEnvironment,pIniVersion,pIniDriver,szDirectory,dwDeleteFlag,FALSE);
|
|
|
|
if (hImpersonationToken) {
|
|
ImpersonatePrinterClient(hImpersonationToken);
|
|
}
|
|
|
|
DeleteDriverEntry( pIniVersion, pIniDriver );
|
|
}
|
|
|
|
}
|
|
|
|
pIniVersion = pIniVersion->pNext;
|
|
}
|
|
|
|
if (bSetPrinterChange) {
|
|
SetPrinterChange( NULL,
|
|
NULL,
|
|
NULL,
|
|
PRINTER_CHANGE_DELETE_PRINTER_DRIVER,
|
|
pIniSpooler );
|
|
}
|
|
|
|
CleanUp:
|
|
|
|
if (bEnteredSplSem) {
|
|
LeaveSplSem();
|
|
}
|
|
|
|
return bReturn;
|
|
|
|
}
|
|
|
|
VOID
|
|
RemoveDriverTempFiles(
|
|
PINISPOOLER pIniSpooler,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINIVERSION pIniVersion,
|
|
PINIDRIVER pIniDriver
|
|
)
|
|
|
|
/*++
|
|
Function Description: Removes temp directory associated with the driver
|
|
|
|
Parameters: pIniSpooler - pointer to INISPOOLER
|
|
pIniEnvironment - pointer to INIENVIRONMENT
|
|
pIniVersion - pointer to INIVERSION
|
|
pIniDriver - pointer to INIDRIVER
|
|
|
|
Return Values: NONE
|
|
--*/
|
|
|
|
{
|
|
WCHAR szDriverDir[MAX_PATH], szDriverFile[MAX_PATH];
|
|
LPCWSTR pszDriverFile, pszConfigFile;
|
|
DWORD DriverFileSize, ConfigFileSize, MaxFileSize;
|
|
fnWinSpoolDrv fnList;
|
|
|
|
pszDriverFile = FindFileName(pIniDriver->pDriverFile);
|
|
pszConfigFile = FindFileName(pIniDriver->pConfigFile);
|
|
|
|
DriverFileSize = pszDriverFile ? wcslen(pszDriverFile) : 0 ;
|
|
|
|
ConfigFileSize = pszConfigFile ? wcslen(pszConfigFile) : 0 ;
|
|
|
|
MaxFileSize = ConfigFileSize > DriverFileSize ?
|
|
ConfigFileSize :
|
|
DriverFileSize;
|
|
|
|
|
|
if (pIniDriver->dwTempDir &&
|
|
GetDriverVersionDirectory(szDriverDir,
|
|
COUNTOF(szDriverDir) - MaxFileSize -1,
|
|
pIniSpooler,
|
|
pIniEnvironment,
|
|
pIniVersion,
|
|
pIniDriver,
|
|
NULL))
|
|
{
|
|
// Unload the driver files if neccessary
|
|
|
|
if( pszDriverFile &&
|
|
StrNCatBuff (szDriverFile,
|
|
COUNTOF(szDriverFile),
|
|
szDriverDir,
|
|
L"\\",
|
|
pszDriverFile,
|
|
NULL) == ERROR_SUCCESS ) {
|
|
|
|
GdiArtificialDecrementDriver(szDriverFile, pIniDriver->dwDriverAttributes);
|
|
}
|
|
|
|
|
|
|
|
if( pszConfigFile &&
|
|
StrNCatBuff (szDriverFile,
|
|
COUNTOF(szDriverFile),
|
|
szDriverDir,
|
|
L"\\",
|
|
pszConfigFile,
|
|
NULL) == ERROR_SUCCESS ) {
|
|
if (SplInitializeWinSpoolDrv(&fnList)) {
|
|
(* (fnList.pfnForceUnloadDriver))(szDriverFile);
|
|
}
|
|
}
|
|
// Delete the files and the directory
|
|
DeleteAllFilesAndDirectory(szDriverDir, FALSE);
|
|
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID RemovePendingUpgradeForDeletedDriver(
|
|
LPWSTR pDriverName,
|
|
DWORD dwVersion,
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
|
|
/*++
|
|
Function Description: Removes pending upgrade keys for deleted drivers.
|
|
|
|
Parameters: pDriverName - driver name (eg. HP LaserJet 5)
|
|
dwVersion - version number being deleted
|
|
|
|
Return Values: NONE
|
|
--*/
|
|
|
|
{
|
|
HKEY hRootKey = NULL, hUpgradeKey = NULL, hVersionKey = NULL;
|
|
HANDLE hToken = NULL;
|
|
WCHAR pDriver[MAX_PATH];
|
|
BOOL bAllocMem = FALSE;
|
|
DWORD dwSize;
|
|
|
|
if (!pDriverName || !*pDriverName) {
|
|
return;
|
|
}
|
|
|
|
hToken = RevertToPrinterSelf();
|
|
|
|
DBGMSG(DBG_CLUSTER, ("RemovePendingUpgradeForDeletedDriver Driver "TSTR"\n", pDriverName));
|
|
|
|
StringCchPrintf(pDriver, COUNTOF(pDriver), L"Version-%d", dwVersion);
|
|
|
|
//
|
|
// The local spooler and cluster spooler have different sets of drivers.
|
|
// The root registry is different.
|
|
//
|
|
if (pIniSpooler->SpoolerFlags & SPL_CLUSTER_REG)
|
|
{
|
|
hRootKey = pIniSpooler->hckRoot;
|
|
}
|
|
else
|
|
{
|
|
SplRegCreateKey(HKEY_LOCAL_MACHINE,
|
|
szRegistryRoot,
|
|
0,
|
|
KEY_READ | DELETE,
|
|
NULL,
|
|
&hRootKey,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
|
|
if (hRootKey &&
|
|
SplRegCreateKey(hRootKey,
|
|
szPendingUpgrades,
|
|
0,
|
|
KEY_READ | DELETE,
|
|
NULL,
|
|
&hUpgradeKey,
|
|
NULL,
|
|
pIniSpooler) == ERROR_SUCCESS &&
|
|
SplRegCreateKey(hUpgradeKey,
|
|
pDriver,
|
|
0,
|
|
KEY_READ | DELETE,
|
|
NULL,
|
|
&hVersionKey,
|
|
NULL,
|
|
pIniSpooler) == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Delete driver subkey, if any (since reg apis are not case sensitive)
|
|
//
|
|
SplRegDeleteKey(hVersionKey, pDriverName, pIniSpooler);
|
|
}
|
|
|
|
if (hVersionKey) {
|
|
SplRegCloseKey(hVersionKey, pIniSpooler);
|
|
}
|
|
|
|
if (hUpgradeKey) {
|
|
SplRegCloseKey(hUpgradeKey, pIniSpooler);
|
|
}
|
|
|
|
//
|
|
// Do not close the root key if the spooler is a cluster spooler
|
|
//
|
|
if (hRootKey && !(pIniSpooler->SpoolerFlags & SPL_CLUSTER_REG)) {
|
|
SplRegCloseKey(hRootKey, pIniSpooler);
|
|
}
|
|
|
|
if (hToken) {
|
|
ImpersonatePrinterClient(hToken);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
BOOL
|
|
NotifyDriver(
|
|
PINISPOOLER pIniSpooler,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINIVERSION pIniVersion,
|
|
PINIDRIVER pIniDriver,
|
|
DWORD dwDriverEvent,
|
|
DWORD dwParameter
|
|
)
|
|
/*++
|
|
Function description: Calls DrvDriverEvent, to allow the driver to cleanup some of it's
|
|
private files. The function is called inside SplSem.
|
|
|
|
Parameters: pIniSpooler - pointer to INISPOOLER struct.
|
|
pIniEnvironment - pointer to INIENVIRONMENT struct.
|
|
pIniVersion - pointer to INIVERSION struct.
|
|
pIniDriver - pointer to the INIDRIVER struct of the driver to be notified.
|
|
dwDriverEvent - the type of Driver Event (delete | initialize)
|
|
dwParameter - LPARAM to pass to DrvDriverEvent. Contains dwDeleteFlag for
|
|
DRIVER_EVENT_DELETE
|
|
|
|
Return Values: TRUE if DrvDriverEvent returns TRUE or if it need not be called.
|
|
FALSE if DrvDriverEvent could not be called or if it returns FALSE.
|
|
|
|
--*/
|
|
{
|
|
WCHAR szDriverLib[MAX_PATH];
|
|
FARPROC pfnDrvDriverEvent;
|
|
HINSTANCE hDrvLib = NULL;
|
|
LPBYTE pDriverInfo = NULL;
|
|
DWORD cbBuf;
|
|
BOOL bReturn = FALSE;
|
|
|
|
SplInSem();
|
|
|
|
//
|
|
// Check if the driver could have been used by the system. Version number should be
|
|
// 2 or 3, Environment should match with the global szEnvironment.
|
|
//
|
|
if (((pIniVersion->cMajorVersion != SPOOLER_VERSION) &&
|
|
(pIniVersion->cMajorVersion != COMPATIBLE_SPOOLER_VERSION)) ||
|
|
lstrcmpi(pIniEnvironment->pName,szEnvironment)) {
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Get the directory where the driver files are stored.
|
|
//
|
|
if( pIniDriver->pConfigFile &&
|
|
GetDriverVersionDirectory(szDriverLib,
|
|
(DWORD)(COUNTOF(szDriverLib) - wcslen(pIniDriver->pConfigFile) - 2),
|
|
pIniSpooler, pIniEnvironment,
|
|
pIniVersion, pIniDriver, NULL)) {
|
|
|
|
|
|
if((StrNCatBuff(szDriverLib,
|
|
COUNTOF(szDriverLib),
|
|
szDriverLib,
|
|
L"\\",
|
|
pIniDriver->pConfigFile,
|
|
NULL) == ERROR_SUCCESS))
|
|
{
|
|
//
|
|
// Load the driver dll for the version being deleted.
|
|
//
|
|
if (hDrvLib = LoadDriver(szDriverLib))
|
|
{
|
|
if (pfnDrvDriverEvent = GetProcAddress(hDrvLib, "DrvDriverEvent")) {
|
|
|
|
//
|
|
// If the DrvDriverEvent is supported Copy pIniDriver Info into a
|
|
// DRIVER_INFO_3 struct and call the DrvDriverEvent Function.
|
|
//
|
|
cbBuf = GetDriverInfoSize( pIniDriver, 3, pIniVersion, pIniEnvironment,
|
|
NULL, pIniSpooler );
|
|
|
|
if (pDriverInfo = (LPBYTE) AllocSplMem(cbBuf)) {
|
|
|
|
if (CopyIniDriverToDriverInfo( pIniEnvironment,
|
|
pIniVersion,
|
|
pIniDriver,
|
|
3,
|
|
pDriverInfo,
|
|
pDriverInfo + cbBuf,
|
|
NULL,
|
|
pIniSpooler )) {
|
|
|
|
//
|
|
// Leave the semaphore before calling into the spooler
|
|
//
|
|
LeaveSplSem();
|
|
SplOutSem();
|
|
|
|
|
|
try {
|
|
|
|
bReturn = (BOOL) pfnDrvDriverEvent(dwDriverEvent,
|
|
3,
|
|
pDriverInfo,
|
|
(LPARAM) dwParameter);
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
SetLastError( GetExceptionCode() );
|
|
DBGMSG(DBG_ERROR,
|
|
("NotifyDriver ExceptionCode %x Driver %ws Error %d\n",
|
|
GetLastError(), szDriverLib, GetLastError() ));
|
|
bReturn = FALSE;
|
|
}
|
|
|
|
EnterSplSem();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pDriverInfo) {
|
|
FreeSplMem(pDriverInfo);
|
|
}
|
|
|
|
if (hDrvLib) {
|
|
UnloadDriver(hDrvLib);
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
|
|
PDRVREFCNT
|
|
DecrementFileRefCnt(
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINIVERSION pIniVersion,
|
|
PINIDRIVER pIniDriver,
|
|
LPCWSTR pszFileName,
|
|
LPCWSTR pszDirectory,
|
|
DWORD dwDeleteFlag
|
|
)
|
|
/*++
|
|
|
|
Function description: Decrements the driver file usage reference counts and deletes unused
|
|
files depending on dwDeleteFlag.
|
|
|
|
Paramaters: pIniEnvironment - pointer to INIENVIRONMENT
|
|
pIniVersion - pointer to INIVERSION struct. This struct contains the ref counts.
|
|
pIniDriver - pointer to INIDRIVER
|
|
szFileName - driver file name whose ref count is to be decremented.
|
|
szDirectory - Directory where the files are located.
|
|
dwDeleteFlag - unused files are deleted if this flag contains
|
|
DPD_DELETE_UNUSED_FILES or DPD_DELETE_ALL_FILES
|
|
|
|
Return Value: pointer to the DRVREFCNT which was decremented
|
|
NULL if memory allocation fails.
|
|
|
|
--*/
|
|
{
|
|
|
|
PDRVREFCNT pdrc,*pprev;
|
|
LPWSTR pszDelFile=NULL;
|
|
WCHAR szTempDir[MAX_PATH+5],szTempFile[MAX_PATH];
|
|
DWORD cbSize;
|
|
PDRVREFCNT pReturn = NULL;
|
|
|
|
SplInSem();
|
|
|
|
pdrc = pIniVersion->pDrvRefCnt;
|
|
pprev = &(pIniVersion->pDrvRefCnt);
|
|
|
|
//
|
|
// Go thru the list of ref count nodes in the Iniversion struct and find the node
|
|
// corresponding to szFileName.
|
|
//
|
|
while (pdrc != NULL) {
|
|
if (lstrcmpi(pszFileName,pdrc->szDrvFileName) == 0) {
|
|
|
|
if (pdrc->refcount == 1 &&
|
|
((dwDeleteFlag & DPD_DELETE_UNUSED_FILES) ||
|
|
(dwDeleteFlag & DPD_DELETE_ALL_FILES)) ) {
|
|
|
|
//
|
|
// Delete the file, the size is needed later when we pass the file
|
|
// to be deleted.
|
|
//
|
|
cbSize = sizeof(pszDirectory[0])*(wcslen(pszDirectory)+1);
|
|
cbSize += sizeof(pszFileName[0])*(wcslen(pszFileName)+1);
|
|
|
|
if (!BoolFromStatus(StrCatAlloc(&pszDelFile, pszDirectory, pszFileName, NULL))) {
|
|
|
|
pReturn = NULL;
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (pIniDriver) {
|
|
|
|
if (!lstrcmpi(pszFileName, pIniDriver->pDriverFile)) {
|
|
|
|
FilesUnloaded(pIniEnvironment, pszDelFile,
|
|
NULL, pIniDriver->dwDriverAttributes);
|
|
}
|
|
else if (!lstrcmpi(pszFileName, pIniDriver->pConfigFile)) {
|
|
|
|
FilesUnloaded(pIniEnvironment, NULL,
|
|
pszDelFile, pIniDriver->dwDriverAttributes);
|
|
}
|
|
}
|
|
|
|
//
|
|
// We are about to delete a driver file. Delete the same file from
|
|
// the cluster disk too (if applicable)
|
|
//
|
|
if (pIniEnvironment->pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER)
|
|
{
|
|
WCHAR szFilePath[MAX_PATH] = {0};
|
|
|
|
//
|
|
// If DeleteFile fails, there isn't much we can do about it.
|
|
// The file will remain on the cluster disk.
|
|
//
|
|
if (StrNCatBuff(szFilePath,
|
|
MAX_PATH,
|
|
pIniEnvironment->pIniSpooler->pszClusResDriveLetter,
|
|
L"\\",
|
|
szClusterDriverRoot,
|
|
L"\\",
|
|
pIniEnvironment->pDirectory,
|
|
L"\\",
|
|
pIniVersion->szDirectory,
|
|
L"\\",
|
|
pszFileName,
|
|
NULL) == ERROR_SUCCESS &&
|
|
SplDeleteFile(szFilePath))
|
|
{
|
|
DBGMSG(DBG_CLUSTER, ("DecrementFilesRefCnt Deleted szFilePath "TSTR" from cluster\n", szFilePath));
|
|
}
|
|
}
|
|
|
|
if (!SplDeleteFile(pszDelFile)) {
|
|
|
|
//
|
|
// Move the file to a temp directory and delete on REBOOT.
|
|
// Create the temp directory and new tempfile.
|
|
//
|
|
StringCchCopy(szTempDir, COUNTOF(szTempDir), pszDirectory);
|
|
StringCchCat(szTempDir, COUNTOF(szTempDir), L"temp");
|
|
|
|
//
|
|
// CreateDirectory will fail, if szTempDir already
|
|
// exists. Since we don't check for any errors, subsequent functions
|
|
// may fail.
|
|
//
|
|
CreateDirectory(szTempDir,NULL);
|
|
|
|
GetTempFileName(szTempDir, pszFileName, 0, szTempFile);
|
|
SplMoveFileEx(pszDelFile, szTempFile, MOVEFILE_REPLACE_EXISTING);
|
|
SplMoveFileEx(szTempFile, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
|
|
}
|
|
|
|
*pprev = pdrc->pNext;
|
|
}
|
|
|
|
//
|
|
// Decrement the ref cnt for the file.
|
|
//
|
|
if (pdrc->refcount > 0) pdrc->refcount--;
|
|
pReturn = pdrc;
|
|
break;
|
|
|
|
}
|
|
pprev = &(pdrc->pNext);
|
|
pdrc = pdrc->pNext;
|
|
}
|
|
|
|
CleanUp:
|
|
|
|
if (pszDelFile) {
|
|
FreeSplMem(pszDelFile);
|
|
}
|
|
return pReturn;
|
|
}
|
|
|
|
|
|
BOOL
|
|
SplGetPrinterDriver(
|
|
HANDLE hPrinter,
|
|
LPWSTR pEnvironment,
|
|
DWORD Level,
|
|
LPBYTE pDriverInfo,
|
|
DWORD cbBuf,
|
|
LPDWORD pcbNeeded
|
|
)
|
|
{
|
|
DWORD dwDontCare;
|
|
|
|
return SplGetPrinterDriverEx(hPrinter,
|
|
pEnvironment,
|
|
Level,
|
|
pDriverInfo,
|
|
cbBuf,
|
|
pcbNeeded,
|
|
0,
|
|
0,
|
|
&dwDontCare,
|
|
&dwDontCare
|
|
);
|
|
}
|
|
|
|
BOOL
|
|
LocalGetPrinterDriverDirectory(
|
|
LPWSTR pName,
|
|
LPWSTR pEnvironment,
|
|
DWORD Level,
|
|
LPBYTE pDriverInfo,
|
|
DWORD cbBuf,
|
|
LPDWORD pcbNeeded
|
|
)
|
|
{
|
|
PINISPOOLER pIniSpooler;
|
|
BOOL bReturn;
|
|
|
|
pIniSpooler = FindSpoolerByNameIncRef( pName, NULL );
|
|
|
|
if( !pIniSpooler ){
|
|
return ROUTER_UNKNOWN;
|
|
}
|
|
|
|
bReturn = SplGetPrinterDriverDirectory( pName,
|
|
pEnvironment,
|
|
Level,
|
|
pDriverInfo,
|
|
cbBuf,
|
|
pcbNeeded,
|
|
pIniSpooler );
|
|
|
|
FindSpoolerByNameDecRef( pIniSpooler );
|
|
return bReturn;
|
|
}
|
|
|
|
BOOL
|
|
SplGetPrinterDriverDirectory(
|
|
LPWSTR pName,
|
|
LPWSTR pEnvironment,
|
|
DWORD Level,
|
|
LPBYTE pDriverInfo,
|
|
DWORD cbBuf,
|
|
LPDWORD pcbNeeded,
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
{
|
|
DWORD cb;
|
|
WCHAR string[MAX_PATH];
|
|
BOOL bRemote=FALSE;
|
|
PINIENVIRONMENT pIniEnvironment;
|
|
HANDLE hImpersonationToken;
|
|
DWORD ParmError;
|
|
SHARE_INFO_1501 ShareInfo1501;
|
|
PSECURITY_DESCRIPTOR pSecurityDescriptor = NULL;
|
|
PSHARE_INFO_2 pShareInfo = (PSHARE_INFO_2)pIniSpooler->pDriversShareInfo;
|
|
|
|
DBGMSG( DBG_TRACE, ("GetPrinterDriverDirectory\n"));
|
|
|
|
if ( pName && *pName ) {
|
|
|
|
if ( !MyName( pName, pIniSpooler )) {
|
|
|
|
return FALSE;
|
|
|
|
} else {
|
|
bRemote = TRUE;
|
|
}
|
|
}
|
|
|
|
if ( !ValidateObjectAccess( SPOOLER_OBJECT_SERVER,
|
|
SERVER_ACCESS_ENUMERATE,
|
|
NULL, NULL, pIniSpooler )) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
EnterSplSem();
|
|
|
|
pIniEnvironment = FindEnvironment( pEnvironment, pIniSpooler );
|
|
|
|
if ( !pIniEnvironment ) {
|
|
|
|
LeaveSplSem();
|
|
SetLastError( ERROR_INVALID_ENVIRONMENT );
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Ensure that the directory exists
|
|
//
|
|
GetDriverDirectory( string, COUNTOF(string), pIniEnvironment, NULL, pIniSpooler );
|
|
|
|
hImpersonationToken = RevertToPrinterSelf();
|
|
|
|
CreateCompleteDirectory( string );
|
|
|
|
if (hImpersonationToken && !ImpersonatePrinterClient( hImpersonationToken ))
|
|
{
|
|
LeaveSplSem();
|
|
return FALSE;
|
|
}
|
|
|
|
cb = GetDriverDirectory( string, COUNTOF(string), pIniEnvironment, bRemote ? pName : NULL, pIniSpooler )
|
|
* sizeof(WCHAR) + sizeof(WCHAR);
|
|
|
|
*pcbNeeded = cb;
|
|
|
|
LeaveSplSem();
|
|
|
|
if (cb > cbBuf) {
|
|
|
|
SetLastError( ERROR_INSUFFICIENT_BUFFER );
|
|
return FALSE;
|
|
}
|
|
|
|
StringCbCopy((PWSTR)pDriverInfo, cbBuf, string);
|
|
|
|
memset( &ShareInfo1501, 0, sizeof ShareInfo1501 );
|
|
|
|
//
|
|
// Also ensure the drivers share exists
|
|
//
|
|
if ( bRemote ) {
|
|
|
|
NET_API_STATUS rc;
|
|
|
|
if ( rc = (*pfnNetShareAdd)(NULL, 2, (LPBYTE)pIniSpooler->pDriversShareInfo, &ParmError )) {
|
|
|
|
DBGMSG( DBG_WARNING, ("NetShareAdd failed: Error %d, Parm %d\n", rc, ParmError));
|
|
}
|
|
|
|
else if (pSecurityDescriptor = CreateDriversShareSecurityDescriptor( )) {
|
|
|
|
ShareInfo1501.shi1501_security_descriptor = pSecurityDescriptor;
|
|
|
|
if (rc = (*pfnNetShareSetInfo)(NULL, pShareInfo->shi2_netname, 1501,
|
|
&ShareInfo1501, &ParmError)) {
|
|
|
|
DBGMSG( DBG_WARNING, ("NetShareSetInfo failed: Error %d, Parm %d\n", rc, ParmError));
|
|
|
|
}
|
|
|
|
LocalFree(pSecurityDescriptor);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
LocalEnumPrinterDrivers(
|
|
LPWSTR pName,
|
|
LPWSTR pEnvironment,
|
|
DWORD Level,
|
|
LPBYTE pDriverInfo,
|
|
DWORD cbBuf,
|
|
LPDWORD pcbNeeded,
|
|
LPDWORD pcReturned
|
|
)
|
|
{
|
|
PINISPOOLER pIniSpooler;
|
|
BOOL bReturn;
|
|
|
|
pIniSpooler = FindSpoolerByNameIncRef( pName, NULL );
|
|
|
|
if( !pIniSpooler ){
|
|
return ROUTER_UNKNOWN;
|
|
}
|
|
|
|
bReturn = SplEnumPrinterDrivers( pName, pEnvironment, Level, pDriverInfo,
|
|
cbBuf, pcbNeeded, pcReturned,
|
|
pIniSpooler);
|
|
|
|
FindSpoolerByNameDecRef( pIniSpooler );
|
|
return bReturn;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name
|
|
|
|
FindDriverInList
|
|
|
|
Routine Description:
|
|
|
|
Finds a certain driver in a list of drivers. None of the arguments
|
|
can or will be null.
|
|
|
|
Arguments:
|
|
pDriverList - array of DRIVER INFO 6 strucutres
|
|
cDrivers - number of drivers in the list
|
|
pszName - name of the driver we are looking for
|
|
pszEnv - environment of the driver we are looking for
|
|
dwVersion - version of the driver we are looking for
|
|
|
|
Return Value:
|
|
|
|
valid pointer to driver info 6 structure if the driver is found
|
|
NULL if the driver was not found
|
|
|
|
--*/
|
|
DRIVER_INFO_6*
|
|
FindDriverInList(
|
|
DRIVER_INFO_6 *pDriverList,
|
|
DWORD cDrivers,
|
|
LPCWSTR pszName,
|
|
LPCWSTR pszEnv,
|
|
DWORD dwVersion
|
|
)
|
|
{
|
|
DWORD uIndex;
|
|
DRIVER_INFO_6 *pDrv6 = NULL;
|
|
|
|
for (pDrv6 = pDriverList, uIndex = 0;
|
|
pDrv6 && uIndex < cDrivers;
|
|
pDrv6++, uIndex++)
|
|
{
|
|
if (!_wcsicmp(pDrv6->pName, pszName) &&
|
|
!_wcsicmp(pDrv6->pEnvironment, pszEnv) &&
|
|
pDrv6->cVersion == dwVersion)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check if driver was found
|
|
//
|
|
return uIndex == cDrivers ? NULL : pDrv6;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name
|
|
|
|
GetBufferSizeForPrinterDrivers
|
|
|
|
Routine Description:
|
|
|
|
Helper function for SplEnumAllClusterPrinterDrivers. Calculates the
|
|
bytes needed to hold all printer driver strucutres on all the spoolers
|
|
hosted by the spooler process. Note that we may ask for more bytes
|
|
than we really need. This is beacuse we enumerate the drivers on the
|
|
local spooler and on cluster spoolers and we count duplicates again.
|
|
In order to count the exact number of bytes needed, we would need to
|
|
loop through the drivers and search each of them in all spoolers. This
|
|
would be too slow.
|
|
|
|
Arguments:
|
|
|
|
pszRemote - NULL if the caller is local on the machine, a string otherwise
|
|
|
|
Return Value:
|
|
|
|
Count of bytes needed to store all the drivers
|
|
|
|
--*/
|
|
DWORD
|
|
GetBufferSizeForPrinterDrivers(
|
|
LPWSTR pszRemote
|
|
)
|
|
{
|
|
PINISPOOLER pIniSpooler;
|
|
PINIENVIRONMENT pIniEnvironment;
|
|
PINIVERSION pIniVersion;
|
|
PINIDRIVER pIniDriver;
|
|
DWORD cbNeeded = 0;
|
|
|
|
SplInSem();
|
|
|
|
for (pIniSpooler = pLocalIniSpooler;
|
|
pIniSpooler;
|
|
pIniSpooler = pIniSpooler->pIniNextSpooler)
|
|
{
|
|
//
|
|
// We want either a pIniSpooler that is not a clusrer, or
|
|
// a pIniSpooler that is a cluster spooler that is not
|
|
// in pending deletion or offline. We could optimize this so it
|
|
// skips win32spl spoolers.
|
|
//
|
|
if (!(pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER) ||
|
|
pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER &&
|
|
!(pIniSpooler->SpoolerFlags & SPL_PENDING_DELETION ||
|
|
pIniSpooler->SpoolerFlags & SPL_OFFLINE))
|
|
{
|
|
for (pIniEnvironment = pIniSpooler->pIniEnvironment;
|
|
pIniEnvironment;
|
|
pIniEnvironment = pIniEnvironment->pNext)
|
|
{
|
|
for (pIniVersion = pIniEnvironment->pIniVersion;
|
|
pIniVersion;
|
|
pIniVersion = pIniVersion->pNext)
|
|
{
|
|
for (pIniDriver = pIniVersion->pIniDriver;
|
|
pIniDriver;
|
|
pIniDriver = pIniDriver->pNext)
|
|
{
|
|
//
|
|
// Omit drivers that are currently in a pending deletion
|
|
// state.
|
|
//
|
|
if (!(pIniDriver->dwDriverFlags & PRINTER_DRIVER_PENDING_DELETION))
|
|
{
|
|
cbNeeded += GetDriverInfoSize(pIniDriver,
|
|
6,
|
|
pIniVersion,
|
|
pIniEnvironment,
|
|
pszRemote,
|
|
pIniSpooler);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return cbNeeded;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name
|
|
|
|
PackClusterPrinterDrivers
|
|
|
|
Routine Description:
|
|
|
|
Helper function for SplEnumAllClusterPrinterDrivers. This function relies on
|
|
its caller to validate the arguments. This function loops through all the
|
|
drivers on all pIniSpooler and stores driver information in a buffer. There
|
|
won't be duplicate drivers in the list. If 2 pIniSpooler have the same driver,
|
|
then the oldest is enumerated.
|
|
|
|
Arguments:
|
|
|
|
pszRemote - NULL if the caller is local on the machine, a string otherwise
|
|
pDriverBuf - buffer to hold the strcutures
|
|
cbBuf - buffer size in bytes
|
|
pcReturned - number of structures returned
|
|
|
|
Return Value:
|
|
|
|
Win32 error code
|
|
|
|
--*/
|
|
DWORD
|
|
PackClusterPrinterDrivers(
|
|
LPWSTR pszRemote,
|
|
LPBYTE pDriverBuf,
|
|
DWORD cbBuf,
|
|
LPDWORD pcReturned
|
|
)
|
|
{
|
|
PINIDRIVER pIniDriver;
|
|
PINIENVIRONMENT pIniEnvironment;
|
|
PINIVERSION pIniVersion;
|
|
PINISPOOLER pIniSpooler;
|
|
DRIVER_INFO_6 *pListHead = (DRIVER_INFO_6 *)pDriverBuf;
|
|
LPBYTE pEnd = pDriverBuf + cbBuf;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
|
|
SplInSem();
|
|
|
|
for (pIniSpooler = pLocalIniSpooler;
|
|
pIniSpooler;
|
|
pIniSpooler = pIniSpooler->pIniNextSpooler)
|
|
{
|
|
//
|
|
// Either pIniSpooler is not a cluster, or it is a cluster and
|
|
// it is not pending deletion or offline. We could optimize this so it
|
|
// skips win32spl spoolers.
|
|
//
|
|
if (!(pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER) ||
|
|
pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER &&
|
|
!(pIniSpooler->SpoolerFlags & SPL_PENDING_DELETION ||
|
|
pIniSpooler->SpoolerFlags & SPL_OFFLINE))
|
|
{
|
|
for (pIniEnvironment = pIniSpooler->pIniEnvironment;
|
|
pIniEnvironment;
|
|
pIniEnvironment = pIniEnvironment->pNext)
|
|
{
|
|
for (pIniVersion = pIniEnvironment->pIniVersion;
|
|
pIniVersion;
|
|
pIniVersion = pIniVersion->pNext)
|
|
{
|
|
for (pIniDriver = pIniVersion->pIniDriver;
|
|
pIniDriver;
|
|
pIniDriver = pIniDriver->pNext)
|
|
{
|
|
//
|
|
// Make sure that we don't enumerate drivers that are pending deletion.
|
|
//
|
|
if (!(pIniDriver->dwDriverFlags & PRINTER_DRIVER_PENDING_DELETION))
|
|
{
|
|
DRIVER_INFO_6 *pDrv6 = NULL;
|
|
|
|
if (pDrv6 = FindDriverInList(pListHead,
|
|
*pcReturned,
|
|
pIniDriver->pName,
|
|
pIniEnvironment->pName,
|
|
pIniDriver->cVersion))
|
|
|
|
{
|
|
//
|
|
// The driver that we are currently enumerating is older than the
|
|
// driver that we have in the list. We need to update the driver
|
|
// time in the list. The list always has the oldest driver.
|
|
//
|
|
if (CompareFileTime(&pDrv6->ftDriverDate, &pIniDriver->ftDriverDate) > 0)
|
|
{
|
|
pDrv6->ftDriverDate = pIniDriver->ftDriverDate;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Add the driver to the driver list
|
|
//
|
|
if (pEnd = CopyIniDriverToDriverInfo(pIniEnvironment,
|
|
pIniVersion,
|
|
pIniDriver,
|
|
6,
|
|
pDriverBuf,
|
|
pEnd,
|
|
pszRemote,
|
|
pIniSpooler))
|
|
{
|
|
pDriverBuf += sizeof(DRIVER_INFO_6);
|
|
|
|
(*pcReturned)++;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Severe error occured
|
|
//
|
|
dwError = ERROR_INSUFFICIENT_BUFFER;
|
|
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CleanUp:
|
|
|
|
return dwError;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name
|
|
|
|
SplEnumAllClusterPrinterDrivers
|
|
|
|
Routine Description:
|
|
|
|
Enumerates the driver on all the spoolers hosted by the spooler process.
|
|
It does not enumerate duplicates. This function is a helper function
|
|
for EnumPrinterDrivers, when the latter is called with "allcluster"
|
|
environment. The only consumer for this is Windows Update. Windows
|
|
update needs to update all the drivers on all the spoolers on a machine,
|
|
and uses EnumPrinterDrivers with "allcluster" environment.
|
|
|
|
Arguments:
|
|
|
|
pszRemote - NULL if the caller is local on the machine, a string otherwise
|
|
Level - must be 6
|
|
pDriverInfo - buffer to hold the strcutures
|
|
cbBuf - buffer size in bytes
|
|
pcbNeeded - pointer to receive the count of bytes needed
|
|
pcReturned - number of structures returned. Must be a valid pointer
|
|
|
|
Return Value:
|
|
|
|
TRUE, if getting the drivers was successful
|
|
FALSE, otherwise. Use GetLastError for error code
|
|
|
|
--*/
|
|
BOOL
|
|
SplEnumAllClusterPrinterDrivers(
|
|
LPWSTR pszRemote,
|
|
DWORD dwLevel,
|
|
LPBYTE pDriverInfo,
|
|
DWORD cbBuf,
|
|
LPDWORD pcbNeeded,
|
|
LPDWORD pcReturned)
|
|
{
|
|
DWORD dwError = ERROR_INVALID_PARAMETER;
|
|
|
|
if (pcbNeeded && pcReturned)
|
|
{
|
|
*pcReturned = 0;
|
|
|
|
if (dwLevel == 6)
|
|
{
|
|
EnterSplSem();
|
|
|
|
//
|
|
// Calculate the bytes needed for our driver structures
|
|
//
|
|
*pcbNeeded = GetBufferSizeForPrinterDrivers(pszRemote);
|
|
|
|
dwError = cbBuf < *pcbNeeded ? ERROR_INSUFFICIENT_BUFFER :
|
|
PackClusterPrinterDrivers(pszRemote,
|
|
pDriverInfo,
|
|
cbBuf,
|
|
pcReturned);
|
|
|
|
LeaveSplSem();
|
|
}
|
|
else
|
|
{
|
|
dwError = ERROR_INVALID_LEVEL;
|
|
}
|
|
}
|
|
|
|
SetLastError(dwError);
|
|
|
|
return dwError == ERROR_SUCCESS;
|
|
}
|
|
|
|
BOOL
|
|
SplEnumPrinterDrivers(
|
|
LPWSTR pName,
|
|
LPWSTR pEnvironment,
|
|
DWORD Level,
|
|
LPBYTE pDriverInfo,
|
|
DWORD cbBuf,
|
|
LPDWORD pcbNeeded,
|
|
LPDWORD pcReturned,
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
{
|
|
PINIDRIVER pIniDriver;
|
|
PINIVERSION pIniVersion;
|
|
BOOL bAllDrivers;
|
|
DWORD cb, cbStruct;
|
|
LPBYTE pEnd;
|
|
LPWSTR lpRemote = NULL;
|
|
PINIENVIRONMENT pIniEnvironment;
|
|
|
|
DBGMSG( DBG_TRACE, ("EnumPrinterDrivers\n"));
|
|
|
|
if ( pName && *pName ) {
|
|
|
|
if ( !MyName( pName, pIniSpooler )) {
|
|
|
|
return FALSE;
|
|
|
|
} else {
|
|
|
|
lpRemote = pName;
|
|
}
|
|
}
|
|
|
|
|
|
if ( !ValidateObjectAccess( SPOOLER_OBJECT_SERVER,
|
|
SERVER_ACCESS_ENUMERATE,
|
|
NULL, NULL, pIniSpooler )) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if (!_wcsicmp(pEnvironment, EPD_ALL_LOCAL_AND_CLUSTER))
|
|
{
|
|
return SplEnumAllClusterPrinterDrivers(lpRemote,
|
|
Level,
|
|
pDriverInfo,
|
|
cbBuf,
|
|
pcbNeeded,
|
|
pcReturned);
|
|
}
|
|
|
|
switch (Level) {
|
|
|
|
case 1:
|
|
cbStruct = sizeof(DRIVER_INFO_1);
|
|
break;
|
|
|
|
case 2:
|
|
cbStruct = sizeof(DRIVER_INFO_2);
|
|
break;
|
|
|
|
case 3:
|
|
cbStruct = sizeof(DRIVER_INFO_3);
|
|
break;
|
|
|
|
case 4:
|
|
cbStruct = sizeof(DRIVER_INFO_4);
|
|
break;
|
|
|
|
case 5:
|
|
cbStruct = sizeof(DRIVER_INFO_5);
|
|
break;
|
|
|
|
case 6:
|
|
cbStruct = sizeof(DRIVER_INFO_6);
|
|
break;
|
|
}
|
|
|
|
*pcReturned=0;
|
|
|
|
cb=0;
|
|
|
|
bAllDrivers = !_wcsicmp(pEnvironment, L"All");
|
|
|
|
EnterSplSem();
|
|
|
|
if ( bAllDrivers )
|
|
pIniEnvironment = pIniSpooler->pIniEnvironment;
|
|
else
|
|
pIniEnvironment = FindEnvironment( pEnvironment, pIniSpooler );
|
|
|
|
if ( !pIniEnvironment ) {
|
|
|
|
LeaveSplSem();
|
|
SetLastError(ERROR_INVALID_ENVIRONMENT);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
do {
|
|
|
|
pIniVersion = pIniEnvironment->pIniVersion;
|
|
|
|
while ( pIniVersion ) {
|
|
|
|
pIniDriver = pIniVersion->pIniDriver;
|
|
|
|
while ( pIniDriver ) {
|
|
|
|
//
|
|
// Don't consider drivers that are pending deletion for enumeration.
|
|
//
|
|
if (!(pIniDriver->dwDriverFlags & PRINTER_DRIVER_PENDING_DELETION))
|
|
{
|
|
DBGMSG( DBG_TRACE, ("Driver found - %ws\n", pIniDriver->pName));
|
|
|
|
cb += GetDriverInfoSize( pIniDriver, Level, pIniVersion,
|
|
pIniEnvironment, lpRemote, pIniSpooler );
|
|
}
|
|
|
|
pIniDriver = pIniDriver->pNext;
|
|
}
|
|
|
|
pIniVersion = pIniVersion->pNext;
|
|
}
|
|
|
|
if ( bAllDrivers )
|
|
pIniEnvironment = pIniEnvironment->pNext;
|
|
else
|
|
break;
|
|
} while ( pIniEnvironment );
|
|
|
|
*pcbNeeded=cb;
|
|
|
|
DBGMSG( DBG_TRACE, ("Required is %d and Available is %d\n", cb, cbBuf));
|
|
|
|
if (cbBuf < cb) {
|
|
|
|
SetLastError( ERROR_INSUFFICIENT_BUFFER );
|
|
LeaveSplSem();
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
DBGMSG( DBG_TRACE, ("Now copying contents into DRIVER_INFO structures\n"));
|
|
|
|
if ( bAllDrivers )
|
|
pIniEnvironment = pIniSpooler->pIniEnvironment;
|
|
|
|
pEnd = pDriverInfo+cbBuf;
|
|
|
|
do {
|
|
|
|
pIniVersion = pIniEnvironment->pIniVersion;
|
|
|
|
while ( pIniVersion ) {
|
|
|
|
pIniDriver = pIniVersion->pIniDriver;
|
|
|
|
while ( pIniDriver ) {
|
|
|
|
//
|
|
// Don't consider printer drivers that are pending deletion for
|
|
// enumeration.
|
|
//
|
|
if (!(pIniDriver->dwDriverFlags & PRINTER_DRIVER_PENDING_DELETION))
|
|
{
|
|
|
|
if (( pEnd = CopyIniDriverToDriverInfo( pIniEnvironment,
|
|
pIniVersion,
|
|
pIniDriver,
|
|
Level,
|
|
pDriverInfo,
|
|
pEnd,
|
|
lpRemote,
|
|
pIniSpooler )) == NULL){
|
|
LeaveSplSem();
|
|
return FALSE;
|
|
}
|
|
|
|
pDriverInfo += cbStruct;
|
|
(*pcReturned)++;
|
|
}
|
|
|
|
pIniDriver = pIniDriver->pNext;
|
|
}
|
|
|
|
pIniVersion = pIniVersion->pNext;
|
|
}
|
|
|
|
if ( bAllDrivers )
|
|
pIniEnvironment = pIniEnvironment->pNext;
|
|
else
|
|
break;
|
|
} while ( pIniEnvironment );
|
|
|
|
LeaveSplSem();
|
|
return TRUE;
|
|
}
|
|
|
|
DWORD
|
|
GetDriverInfoSize(
|
|
PINIDRIVER pIniDriver,
|
|
DWORD Level,
|
|
PINIVERSION pIniVersion,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
LPWSTR lpRemote,
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
{
|
|
DWORD cbDir, cb=0, cchLen;
|
|
WCHAR string[INTERNET_MAX_HOST_NAME_LENGTH + MAX_PATH + 1];
|
|
LPWSTR pStr;
|
|
DWORD cFiles = 0;
|
|
|
|
switch (Level) {
|
|
|
|
case 1:
|
|
cb=sizeof(DRIVER_INFO_1) + wcslen(pIniDriver->pName)*sizeof(WCHAR) +
|
|
sizeof(WCHAR);
|
|
break;
|
|
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
case 5:
|
|
case 6:
|
|
case DRIVER_INFO_VERSION_LEVEL:
|
|
|
|
cbDir = GetDriverVersionDirectory( string, COUNTOF(string), pIniSpooler, pIniEnvironment,
|
|
pIniVersion, pIniDriver, lpRemote) + 1;
|
|
|
|
SPLASSERT(pIniDriver->pDriverFile);
|
|
cb+=wcslen(pIniDriver->pDriverFile) + 1 + cbDir;
|
|
|
|
SPLASSERT(pIniDriver->pDataFile);
|
|
cb+=wcslen(pIniDriver->pDataFile) + 1 + cbDir;
|
|
|
|
SPLASSERT(pIniDriver->pConfigFile);
|
|
cb+=wcslen(pIniDriver->pConfigFile) + 1 + cbDir;
|
|
|
|
cb += wcslen( pIniDriver->pName ) + 1 + wcslen( pIniEnvironment->pName ) + 1;
|
|
|
|
if ((Level == 2) || (Level == 5)) {
|
|
|
|
//
|
|
// For the strings in the struct
|
|
//
|
|
cb *= sizeof(WCHAR);
|
|
if (Level == 2) {
|
|
cb += sizeof( DRIVER_INFO_2 );
|
|
} else {
|
|
//
|
|
// Level 5
|
|
//
|
|
cb += sizeof( DRIVER_INFO_5 );
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Level 3 or 4 or 6.
|
|
//
|
|
if ( pIniDriver->pHelpFile && *pIniDriver->pHelpFile )
|
|
cb += wcslen(pIniDriver->pHelpFile) + cbDir + 1;
|
|
|
|
if ( pIniDriver->pMonitorName && *pIniDriver->pMonitorName )
|
|
cb += wcslen(pIniDriver->pMonitorName) + 1;
|
|
|
|
if ( pIniDriver->pDefaultDataType && *pIniDriver->pDefaultDataType)
|
|
cb += wcslen(pIniDriver->pDefaultDataType) + 1;
|
|
|
|
if ( (pStr=pIniDriver->pDependentFiles) && *pStr ) {
|
|
|
|
//
|
|
// There are 4 distinctive files in the file set
|
|
// (driver, data , config, help).
|
|
//
|
|
cFiles = 4;
|
|
while ( *pStr ) {
|
|
cchLen = wcslen(pStr) + 1;
|
|
cb += cchLen + cbDir;
|
|
pStr += cchLen;
|
|
cFiles++;
|
|
}
|
|
//
|
|
// Make room for final \0
|
|
//
|
|
++cb;
|
|
}
|
|
|
|
if ( (Level == 4 || Level == 6 || Level == DRIVER_INFO_VERSION_LEVEL) &&
|
|
(pStr = pIniDriver->pszzPreviousNames) &&
|
|
*pStr) {
|
|
|
|
while ( *pStr ) {
|
|
|
|
cchLen = wcslen(pStr) + 1;
|
|
cb += cchLen;
|
|
pStr += cchLen;
|
|
}
|
|
|
|
//
|
|
// Make room for final \0
|
|
//
|
|
++cb;
|
|
}
|
|
|
|
if (Level==6 || Level == DRIVER_INFO_VERSION_LEVEL) {
|
|
|
|
if (pIniDriver->pszMfgName && *pIniDriver->pszMfgName)
|
|
cb += wcslen(pIniDriver->pszMfgName) + 1;
|
|
|
|
if (pIniDriver->pszOEMUrl && *pIniDriver->pszOEMUrl)
|
|
cb += wcslen(pIniDriver->pszOEMUrl) + 1;
|
|
|
|
if (pIniDriver->pszHardwareID && *pIniDriver->pszHardwareID)
|
|
cb += wcslen(pIniDriver->pszHardwareID) + 1;
|
|
|
|
if (pIniDriver->pszProvider && *pIniDriver->pszProvider)
|
|
cb += wcslen(pIniDriver->pszProvider) + 1;
|
|
|
|
}
|
|
|
|
cb *= sizeof(WCHAR);
|
|
|
|
switch (Level) {
|
|
case 3:
|
|
cb += sizeof( DRIVER_INFO_3 );
|
|
break;
|
|
case 4:
|
|
cb += sizeof( DRIVER_INFO_4 );
|
|
break;
|
|
case 6:
|
|
cb += sizeof( DRIVER_INFO_6 );
|
|
break;
|
|
case DRIVER_INFO_VERSION_LEVEL:
|
|
cb += sizeof( DRIVER_INFO_VERSION ) +
|
|
cFiles * sizeof(DRIVER_FILE_INFO) +
|
|
sizeof(ULONG_PTR);
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
default:
|
|
DBGMSG(DBG_ERROR,
|
|
("GetDriverInfoSize: level can not be %d", Level) );
|
|
cb = 0;
|
|
break;
|
|
}
|
|
|
|
return cb;
|
|
}
|
|
|
|
|
|
|
|
LPBYTE
|
|
CopyMultiSzFieldToDriverInfo(
|
|
LPWSTR pszz,
|
|
LPBYTE pEnd,
|
|
LPWSTR pszPrefix,
|
|
DWORD cchPrefix
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Copies a multi sz field from IniDriver to DriverInfo structure.
|
|
If a pszPrefix is specified that is appended before each string.
|
|
|
|
Arguments:
|
|
pszz : entry in pIniDriver (this could be dependent files
|
|
ex. PSCRIPT.DLL\0QMS810.PPD\0PSCRPTUI.DLL\0PSPCRIPTUI.HLP\0PSTEST.TXT\0\0
|
|
or previous names
|
|
ex. OldName1\0OldName2\0\0 )
|
|
pEnd : end of buffer to which it needs to be copied
|
|
pszPrefix : Prefix to copy when copying to user buffer. For dependent
|
|
files this will be driver directory path
|
|
cchPrefix : length of prefix
|
|
|
|
Return Value:
|
|
after copying where is the buffer end to copy next field
|
|
|
|
--*/
|
|
{
|
|
LPWSTR pStr1, pStr2;
|
|
DWORD cchSize, cchLen;
|
|
|
|
if ( !pszz || !*pszz )
|
|
return pEnd;
|
|
|
|
pStr1 = pszz;
|
|
cchSize = 0;
|
|
|
|
while ( *pStr1 ) {
|
|
|
|
cchLen = wcslen(pStr1) + 1;
|
|
cchSize += cchPrefix + cchLen;
|
|
pStr1 += cchLen;
|
|
}
|
|
|
|
//
|
|
// Make room for the last \0.
|
|
//
|
|
++cchSize;
|
|
|
|
pEnd -= cchSize * sizeof(WCHAR);
|
|
|
|
pStr1 = pszz;
|
|
pStr2 = (LPWSTR) pEnd;
|
|
|
|
//
|
|
// Here, we assume that the buffer size has been validated up front. The
|
|
// string copying routines are adding nothing here. We just use them
|
|
// because we have to.
|
|
//
|
|
while ( *pStr1 ) {
|
|
|
|
if ( pszPrefix ) {
|
|
|
|
StringCchCopy(pStr2, cchPrefix + 1, pszPrefix);
|
|
pStr2 += cchPrefix;
|
|
}
|
|
|
|
//
|
|
// This should really be rewritten to make this function able to
|
|
// validate buffer sizes before packing the strings. In practice,
|
|
// the buffer is always checked by the spooler APIs first which
|
|
// makes this safe.
|
|
//
|
|
cchLen = wcslen(pStr1) + 1;
|
|
|
|
CopyMemory(pStr2, pStr1, cchLen * sizeof(*pStr2));
|
|
|
|
pStr2 += cchLen;
|
|
pStr1 += cchLen;
|
|
}
|
|
|
|
*pStr2 = '\0';
|
|
|
|
return pEnd;
|
|
}
|
|
|
|
|
|
LPBYTE
|
|
CopyIniDriverToDriverInfo(
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINIVERSION pIniVersion,
|
|
PINIDRIVER pIniDriver,
|
|
DWORD Level,
|
|
LPBYTE pDriverInfo,
|
|
LPBYTE pEnd,
|
|
LPWSTR lpRemote,
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine copies data from the IniDriver structure to
|
|
an DRIVER_INFO_X structure.
|
|
|
|
Arguments:
|
|
|
|
pIniEnvironment pointer to the INIENVIRONMENT structure
|
|
|
|
pIniVersion pointer to the INIVERSION structure.
|
|
|
|
pIniDriver pointer to the INIDRIVER structure.
|
|
|
|
Level Level of the DRIVER_INFO_X structure
|
|
|
|
pDriverInfo Buffer of the DRIVER_INFO_X structure
|
|
|
|
pEnd pointer to the end of the pDriverInfo
|
|
|
|
lpRemote flag which determines whether Remote or Local
|
|
|
|
pIniSpooler pointer to the INISPOOLER structure
|
|
Return Value:
|
|
|
|
if the call is successful, the return value is the updated pEnd value.
|
|
|
|
if the call is unsuccessful, the return value is NULL.
|
|
|
|
|
|
Note:
|
|
|
|
--*/
|
|
{
|
|
LPWSTR *pSourceStrings, *SourceStrings;
|
|
WCHAR string[INTERNET_MAX_HOST_NAME_LENGTH + MAX_PATH + 1];
|
|
DWORD i, j;
|
|
DWORD *pOffsets;
|
|
LPWSTR pTempDriverPath=NULL;
|
|
LPWSTR pTempConfigFile=NULL;
|
|
LPWSTR pTempDataFile=NULL;
|
|
LPWSTR pTempHelpFile=NULL;
|
|
|
|
switch (Level) {
|
|
|
|
case DRIVER_INFO_VERSION_LEVEL:
|
|
|
|
return CopyIniDriverToDriverInfoVersion(pIniEnvironment,
|
|
pIniVersion,
|
|
pIniDriver,
|
|
pDriverInfo,
|
|
pEnd,
|
|
lpRemote,
|
|
pIniSpooler);
|
|
break;
|
|
|
|
case 1:
|
|
pOffsets = DriverInfo1Strings;
|
|
break;
|
|
|
|
case 2:
|
|
case 5:
|
|
pOffsets = DriverInfo2Strings;
|
|
break;
|
|
|
|
case 3:
|
|
case 4:
|
|
pOffsets = DriverInfo3Strings;
|
|
break;
|
|
case 6:
|
|
pOffsets = DriverInfo6Strings;
|
|
break;
|
|
|
|
default:
|
|
SetLastError(ERROR_INVALID_LEVEL);
|
|
return NULL;
|
|
}
|
|
|
|
for (j=0; pOffsets[j] != -1; j++) {
|
|
}
|
|
|
|
SourceStrings = pSourceStrings = AllocSplMem(j * sizeof(LPWSTR));
|
|
|
|
if ( pSourceStrings ) {
|
|
|
|
switch (Level) {
|
|
|
|
case 1:
|
|
*pSourceStrings++=pIniDriver->pName;
|
|
|
|
pEnd = PackStrings(SourceStrings, pDriverInfo, pOffsets, pEnd);
|
|
break;
|
|
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
case 5:
|
|
case 6:
|
|
|
|
i = GetDriverVersionDirectory(string, (DWORD)(COUNTOF(string) - wcslen(pIniDriver->pDriverFile) - 1), pIniSpooler, pIniEnvironment,
|
|
pIniVersion, pIniDriver, lpRemote);
|
|
if(!i) {
|
|
pEnd = NULL;
|
|
goto Fail;
|
|
}
|
|
string[i++] = L'\\';
|
|
|
|
*pSourceStrings++ = pIniDriver->pName;
|
|
|
|
*pSourceStrings++ = pIniEnvironment->pName;
|
|
|
|
StringCchCopy(&string[i], COUNTOF(string) - i, pIniDriver->pDriverFile);
|
|
|
|
if (( pTempDriverPath = AllocSplStr(string) ) == NULL){
|
|
|
|
DBGMSG( DBG_WARNING, ("CopyIniDriverToDriverInfo: AlloSplStr failed\n"));
|
|
pEnd = NULL;
|
|
goto Fail;
|
|
}
|
|
|
|
*pSourceStrings++ = pTempDriverPath;
|
|
|
|
StringCchCopy(&string[i], COUNTOF(string) - i, pIniDriver->pDataFile);
|
|
|
|
if (( pTempDataFile = AllocSplStr(string) ) == NULL){
|
|
|
|
DBGMSG( DBG_WARNING, ("CopyIniDriverToDriverInfo: AlloSplStr failed\n"));
|
|
pEnd = NULL;
|
|
goto Fail;
|
|
}
|
|
|
|
*pSourceStrings++ = pTempDataFile;
|
|
|
|
|
|
if ( pIniDriver->pConfigFile && *pIniDriver->pConfigFile ) {
|
|
|
|
StringCchCopy(&string[i], COUNTOF(string) - i, pIniDriver->pConfigFile );
|
|
|
|
if (( pTempConfigFile = AllocSplStr(string) ) == NULL) {
|
|
|
|
DBGMSG( DBG_WARNING, ("CopyIniDriverToDriverInfo: AlloSplStr failed\n"));
|
|
pEnd = NULL;
|
|
goto Fail;
|
|
}
|
|
|
|
*pSourceStrings++ = pTempConfigFile;
|
|
|
|
} else {
|
|
|
|
*pSourceStrings++=0;
|
|
}
|
|
|
|
if ( Level == 3 || Level == 4 || Level == 6 ) {
|
|
|
|
if ( pIniDriver->pHelpFile && *pIniDriver->pHelpFile ) {
|
|
|
|
StringCchCopy(&string[i], COUNTOF(string) - i, pIniDriver ->pHelpFile);
|
|
|
|
if (( pTempHelpFile = AllocSplStr(string) ) == NULL) {
|
|
DBGMSG(DBG_WARNING,
|
|
("CopyIniDriverToDriverInfo: AlloSplStr failed\n"));
|
|
pEnd = NULL;
|
|
goto Fail;
|
|
}
|
|
*pSourceStrings++ = pTempHelpFile;
|
|
} else {
|
|
|
|
*pSourceStrings++=0;
|
|
}
|
|
|
|
*pSourceStrings++ = pIniDriver->pMonitorName;
|
|
|
|
*pSourceStrings++ = pIniDriver->pDefaultDataType;
|
|
|
|
}
|
|
|
|
|
|
if (Level == 6) {
|
|
|
|
((PDRIVER_INFO_6)pDriverInfo)->ftDriverDate = pIniDriver->ftDriverDate;
|
|
|
|
((PDRIVER_INFO_6)pDriverInfo)->dwlDriverVersion = pIniDriver->dwlDriverVersion;
|
|
|
|
*pSourceStrings++ = pIniDriver->pszMfgName;
|
|
|
|
*pSourceStrings++ = pIniDriver->pszOEMUrl;
|
|
|
|
*pSourceStrings++ = pIniDriver->pszHardwareID;
|
|
|
|
*pSourceStrings++ = pIniDriver->pszProvider;
|
|
}
|
|
|
|
pEnd = PackStrings( SourceStrings, pDriverInfo, pOffsets, pEnd );
|
|
|
|
if ( Level == 3 || Level == 4 || Level == 6 ) {
|
|
|
|
//
|
|
// Dependent files need to be copied till \0\0
|
|
// so need to do it outside PackStirngs
|
|
//
|
|
if ( pIniDriver->cchDependentFiles ) {
|
|
|
|
pEnd = CopyMultiSzFieldToDriverInfo(
|
|
pIniDriver->pDependentFiles,
|
|
pEnd,
|
|
string,
|
|
i);
|
|
((PDRIVER_INFO_3)pDriverInfo)->pDependentFiles = (LPWSTR) pEnd;
|
|
}
|
|
else {
|
|
((PDRIVER_INFO_3)pDriverInfo)->pDependentFiles = NULL;
|
|
}
|
|
|
|
//
|
|
// pszzPreviousNames is multi-sz too
|
|
//
|
|
if ( Level == 4 || Level == 6) {
|
|
|
|
if ( pIniDriver->cchPreviousNames ) {
|
|
|
|
pEnd = CopyMultiSzFieldToDriverInfo(
|
|
pIniDriver->pszzPreviousNames,
|
|
pEnd,
|
|
NULL,
|
|
0);
|
|
((PDRIVER_INFO_4)pDriverInfo)->pszzPreviousNames = (LPWSTR) pEnd;
|
|
} else {
|
|
|
|
((PDRIVER_INFO_4)pDriverInfo)->pszzPreviousNames = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
((PDRIVER_INFO_3)pDriverInfo)->cVersion = pIniDriver->cVersion;
|
|
} else {
|
|
//
|
|
// Level == 2 or Level = 5.
|
|
//
|
|
if (Level == 2) {
|
|
|
|
((PDRIVER_INFO_2)pDriverInfo)->cVersion = pIniDriver->cVersion;
|
|
|
|
} else {
|
|
|
|
PDRIVER_INFO_5 pDriver5;
|
|
|
|
pDriver5 = (PDRIVER_INFO_5) pDriverInfo;
|
|
pDriver5->cVersion = pIniDriver->cVersion;
|
|
|
|
if (!pIniDriver->dwDriverAttributes) {
|
|
|
|
//
|
|
// Driver Attributes has not been initialized as yet; do it now
|
|
//
|
|
CheckDriverAttributes(pIniSpooler, pIniEnvironment,
|
|
pIniVersion, pIniDriver);
|
|
}
|
|
|
|
pDriver5->dwDriverAttributes = pIniDriver->dwDriverAttributes;
|
|
pDriver5->dwConfigVersion = GetDriverFileVersion(pIniVersion,
|
|
pIniDriver->pConfigFile);
|
|
pDriver5->dwDriverVersion = GetDriverFileVersion(pIniVersion,
|
|
pIniDriver->pDriverFile);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
Fail:
|
|
|
|
FreeSplStr( pTempDriverPath );
|
|
FreeSplStr( pTempConfigFile );
|
|
FreeSplStr( pTempDataFile );
|
|
FreeSplStr( pTempHelpFile );
|
|
FreeSplMem( SourceStrings );
|
|
|
|
} else {
|
|
|
|
DBGMSG( DBG_WARNING, ("Failed to alloc driver source strings.\n"));
|
|
pEnd = NULL;
|
|
}
|
|
|
|
return pEnd;
|
|
}
|
|
|
|
LPBYTE
|
|
CopyIniDriverToDriverInfoVersion(
|
|
IN PINIENVIRONMENT pIniEnvironment,
|
|
IN PINIVERSION pIniVersion,
|
|
IN PINIDRIVER pIniDriver,
|
|
IN LPBYTE pDriverInfo,
|
|
IN LPBYTE pEnd,
|
|
IN LPWSTR lpRemote,
|
|
IN PINISPOOLER pIniSpooler
|
|
)
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
CopyIniDriverToDriverInfoVersion
|
|
|
|
Routine Description:
|
|
|
|
This routine copy data from pIniDriver to the pDriverInfo as a DRIVER_INFO_VERSION
|
|
|
|
Arguments:
|
|
|
|
pIniEnvironment pointer to the INIENVIRONMENT structure
|
|
pIniVersion pointer to the INIVERSION structure.
|
|
pIniDriver pointer to the INIDRIVER structure.
|
|
pDriverInfo Buffer big enough to fit a DRIVER_INFO_VERSION and
|
|
the strings that needs to be packed
|
|
pEnd pointer to the end of the pDriverInfo
|
|
lpRemote flag which determines whether Remote or Local
|
|
pIniSpooler pointer to the INISPOOLER structure
|
|
|
|
Return Value:
|
|
|
|
Returns the pointer to the "end" of pDriverInfo if success.
|
|
NULL if it failes.
|
|
|
|
--*/
|
|
{
|
|
LPWSTR *pSourceStrings = NULL;
|
|
LPWSTR *SourceStrings = NULL;
|
|
DRIVER_INFO_VERSION *pDriverVersion;
|
|
WCHAR szDriverVersionDir[INTERNET_MAX_HOST_NAME_LENGTH + MAX_PATH + 1];
|
|
DWORD cStrings;
|
|
LPWSTR pTempDllFile = NULL;
|
|
|
|
pDriverVersion = (DRIVER_INFO_VERSION *)pDriverInfo;
|
|
|
|
if (!GetDriverVersionDirectory(szDriverVersionDir,
|
|
COUNTOF(szDriverVersionDir),
|
|
pIniSpooler,
|
|
pIniEnvironment,
|
|
pIniVersion,
|
|
pIniDriver,
|
|
lpRemote))
|
|
{
|
|
pEnd = NULL;
|
|
}
|
|
else
|
|
{
|
|
for (cStrings=0; DriverInfoVersionStrings[cStrings] != 0xFFFFFFFF; cStrings++);
|
|
|
|
if (!(pSourceStrings = SourceStrings = AllocSplMem(cStrings * sizeof(LPWSTR))))
|
|
{
|
|
DBGMSG( DBG_WARNING, ("Failed to alloc driver source strings.\n"));
|
|
pEnd = NULL;
|
|
}
|
|
else
|
|
{
|
|
*pSourceStrings++ = pIniDriver->pName;
|
|
*pSourceStrings++ = pIniEnvironment->pName;
|
|
*pSourceStrings++ = pIniDriver->pMonitorName;
|
|
*pSourceStrings++ = pIniDriver->pDefaultDataType;
|
|
*pSourceStrings++ = pIniDriver->pszMfgName;
|
|
*pSourceStrings++ = pIniDriver->pszOEMUrl;
|
|
*pSourceStrings++ = pIniDriver->pszHardwareID;
|
|
*pSourceStrings++ = pIniDriver->pszProvider;
|
|
//
|
|
// Pack the strings at the end of pDriverInfo
|
|
//
|
|
pEnd = PackStrings( SourceStrings, pDriverInfo, DriverInfoVersionStrings, pEnd );
|
|
|
|
if (pEnd)
|
|
{
|
|
if (pIniDriver->cchPreviousNames == 0)
|
|
{
|
|
pDriverVersion->pszzPreviousNames = NULL;
|
|
}
|
|
else
|
|
{
|
|
pEnd = CopyMultiSzFieldToDriverInfo(pIniDriver->pszzPreviousNames,
|
|
pEnd,
|
|
NULL,
|
|
0);
|
|
if (pEnd)
|
|
{
|
|
pDriverVersion->pszzPreviousNames = (LPWSTR) pEnd;
|
|
}
|
|
}
|
|
|
|
if (pEnd)
|
|
{
|
|
pDriverVersion->cVersion = pIniDriver->cVersion;
|
|
pDriverVersion->ftDriverDate = pIniDriver->ftDriverDate;
|
|
pDriverVersion->dwlDriverVersion = pIniDriver->dwlDriverVersion;
|
|
pDriverVersion->dwFileCount = 3;
|
|
|
|
if (pIniDriver->pHelpFile && *pIniDriver->pHelpFile)
|
|
{
|
|
pDriverVersion->dwFileCount++;
|
|
}
|
|
|
|
if (pIniDriver->cchDependentFiles)
|
|
{
|
|
for (pTempDllFile = pIniDriver->pDependentFiles;
|
|
*pTempDllFile;
|
|
pTempDllFile += wcslen(pTempDllFile) + 1,
|
|
pDriverVersion->dwFileCount++ );
|
|
}
|
|
|
|
//
|
|
// Pack in the file names and versions in pDriverVersion->pFileInfo.
|
|
//
|
|
pEnd = CopyIniDriverFilesToDriverInfo(pDriverVersion,
|
|
pIniVersion,
|
|
pIniDriver,
|
|
szDriverVersionDir,
|
|
pEnd);
|
|
|
|
//
|
|
// When we are done, the end shoud not be less than the
|
|
// start of the buffer plus the driver info version buffer
|
|
// size. If these have overlapped, we are in serious trouble.
|
|
//
|
|
SPLASSERT(pEnd >= pDriverInfo + sizeof(DRIVER_INFO_VERSION));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FreeSplMem(SourceStrings);
|
|
|
|
return pEnd;
|
|
}
|
|
|
|
|
|
LPBYTE
|
|
CopyIniDriverFilesToDriverInfo(
|
|
IN LPDRIVER_INFO_VERSION pDriverVersion,
|
|
IN PINIVERSION pIniVersion,
|
|
IN PINIDRIVER pIniDriver,
|
|
IN LPCWSTR pszDriverVersionDir,
|
|
IN LPBYTE pEnd
|
|
)
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
CopyIniDriverFilesToDriverInfo
|
|
|
|
Routine Description:
|
|
|
|
This routine copy data from pIniDriver to the pDriverInfo->pFileInfo.
|
|
The number of files is already filled in pDriverInfo->dwFileCount
|
|
|
|
Arguments:
|
|
|
|
pDriverVersion pointer to a DRIVER_INFO_VERSION structure
|
|
pIniVersion pointer to the INIVERSION structure.
|
|
pIniDriver pointer to the INIDRIVER structure.
|
|
pszDriverVersionDir string containing the driver version directory
|
|
pEnd pointer to the end of the pDriverInfo
|
|
|
|
Return Value:
|
|
|
|
Returns the pointer to the "end" of pDriverInfo if success.
|
|
NULL if it failes.
|
|
|
|
--*/
|
|
{
|
|
DWORD dwIndex = 0;
|
|
LPWSTR pTempDllFile = NULL;
|
|
DWORD dwFileSetCount = pDriverVersion->dwFileCount;
|
|
|
|
//
|
|
// Reserve space for DRIVER_FILE_INFO array
|
|
//
|
|
pEnd = (LPBYTE)ALIGN_DOWN(pEnd, ULONG_PTR);
|
|
pEnd -= dwFileSetCount * sizeof(DRIVER_FILE_INFO);
|
|
|
|
pDriverVersion->pFileInfo = (DRIVER_FILE_INFO*)pEnd;
|
|
//
|
|
// For each file call FillDriverInfo and fill in the entry
|
|
// in the array of DRIVER_FILE_INFO.
|
|
//
|
|
if (dwIndex >= pDriverVersion->dwFileCount ||
|
|
!(pEnd = FillDriverInfo(pDriverVersion,
|
|
dwIndex++,
|
|
pIniVersion,
|
|
pszDriverVersionDir,
|
|
pIniDriver->pDriverFile,
|
|
DRIVER_FILE,
|
|
pEnd)))
|
|
{
|
|
goto End;
|
|
}
|
|
|
|
if (dwIndex >= dwFileSetCount ||
|
|
!(pEnd = FillDriverInfo(pDriverVersion,
|
|
dwIndex++,
|
|
pIniVersion,
|
|
pszDriverVersionDir,
|
|
pIniDriver->pConfigFile,
|
|
CONFIG_FILE,
|
|
pEnd)))
|
|
{
|
|
goto End;
|
|
}
|
|
|
|
if (dwIndex >= dwFileSetCount ||
|
|
!(pEnd = FillDriverInfo(pDriverVersion,
|
|
dwIndex++,
|
|
pIniVersion,
|
|
pszDriverVersionDir,
|
|
pIniDriver->pDataFile,
|
|
DATA_FILE,
|
|
pEnd)))
|
|
{
|
|
goto End;
|
|
}
|
|
|
|
if (pIniDriver->pHelpFile && *pIniDriver->pHelpFile)
|
|
{
|
|
if (dwIndex >= dwFileSetCount ||
|
|
!(pEnd = FillDriverInfo(pDriverVersion,
|
|
dwIndex++,
|
|
pIniVersion,
|
|
pszDriverVersionDir,
|
|
pIniDriver->pHelpFile,
|
|
HELP_FILE,
|
|
pEnd)))
|
|
{
|
|
goto End;
|
|
}
|
|
}
|
|
|
|
if (pIniDriver->cchDependentFiles)
|
|
{
|
|
for (pTempDllFile = pIniDriver->pDependentFiles;
|
|
*pTempDllFile;
|
|
pTempDllFile += wcslen(pTempDllFile) + 1)
|
|
{
|
|
if (dwIndex >= dwFileSetCount ||
|
|
!(pEnd = FillDriverInfo(pDriverVersion,
|
|
dwIndex++,
|
|
pIniVersion,
|
|
pszDriverVersionDir,
|
|
pTempDllFile,
|
|
DEPENDENT_FILE,
|
|
pEnd)))
|
|
{
|
|
goto End;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
End:
|
|
|
|
return pEnd;
|
|
}
|
|
|
|
|
|
LPBYTE
|
|
FillDriverInfo (
|
|
LPDRIVER_INFO_VERSION pDriverVersion,
|
|
DWORD Index,
|
|
PINIVERSION pIniVersion,
|
|
LPCWSTR pszPrefix,
|
|
LPCWSTR pszFileName,
|
|
DRIVER_FILE_TYPE FileType,
|
|
LPBYTE pEnd
|
|
)
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
FillDriverInfo
|
|
|
|
Routine Description:
|
|
|
|
This routine copy a file name and version into the pDriverInfo->pFileInfo entry
|
|
|
|
Arguments:
|
|
|
|
pDriverVersion pointer to a DRIVER_INFO_VERSION structure
|
|
Index index in the pDriverInfo->pFileInfo array of
|
|
pIniVersion pointer to the INIVERSION structure.
|
|
pszPrefix prefix string for file name.
|
|
This should be the driver version directory
|
|
pszFileName file name, no path
|
|
FileType file type: Driver, Config, Data, etc
|
|
pEnd pointer to the end of the pDriverInfo
|
|
|
|
Return Value:
|
|
|
|
Returns the pointer to the "end" of pDriverInfo if success.
|
|
NULL if it failes.
|
|
|
|
--*/
|
|
{
|
|
LPWSTR pszTempFilePath = NULL;
|
|
LPBYTE pszNewEnd = NULL;
|
|
DWORD dwRet = ERROR_SUCCESS;
|
|
|
|
if ((dwRet = StrCatAlloc(&pszTempFilePath,
|
|
pszPrefix,
|
|
L"\\",
|
|
pszFileName,
|
|
NULL)) != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(dwRet);
|
|
pszNewEnd = NULL;
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Packs the file name into pDriverInfo
|
|
//
|
|
pszNewEnd = PackStringToEOB(pszTempFilePath, pEnd);
|
|
//
|
|
// Fills in the offset in pDriverVersion where the string was packed.
|
|
// We cannot store pointers because we don't marshall anything else
|
|
// but the structure at the begining of the buffer. We could marshall
|
|
// the array of DRIVER_FILE_INFO but there is no way to update the buffer
|
|
// size between 32 and 64 bits in Win32spl.dll ( UpdateBufferSize ) since we
|
|
// don't know how many files are by that time.
|
|
//
|
|
pDriverVersion->pFileInfo[Index].FileNameOffset = MakeOffset((LPVOID)pszNewEnd, (LPVOID)pDriverVersion);
|
|
|
|
pDriverVersion->pFileInfo[Index].FileVersion = 0;
|
|
|
|
pDriverVersion->pFileInfo[Index].FileType = FileType;
|
|
|
|
if (!GetDriverFileCachedVersion(pIniVersion,
|
|
(LPWSTR)pszNewEnd,
|
|
&pDriverVersion->pFileInfo[Index].FileVersion))
|
|
{
|
|
pszNewEnd = NULL;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
FreeSplMem(pszTempFilePath);
|
|
|
|
return pszNewEnd;
|
|
}
|
|
|
|
BOOL
|
|
WriteDriverIni(
|
|
PINIDRIVER pIniDriver,
|
|
PINIVERSION pIniVersion,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
{
|
|
HKEY hEnvironmentsRootKey, hEnvironmentKey, hDriversKey, hDriverKey;
|
|
HKEY hVersionKey;
|
|
HANDLE hToken;
|
|
DWORD dwLastError=ERROR_SUCCESS;
|
|
PINIDRIVER pUpdateIniDriver;
|
|
|
|
hToken = RevertToPrinterSelf();
|
|
|
|
if ((dwLastError = SplRegCreateKey(pIniSpooler->SpoolerFlags & SPL_CLUSTER_REG ? pIniSpooler->hckRoot : HKEY_LOCAL_MACHINE,
|
|
pIniSpooler->pszRegistryEnvironments,
|
|
0,
|
|
KEY_WRITE,
|
|
NULL,
|
|
&hEnvironmentsRootKey,
|
|
NULL,
|
|
pIniSpooler)) == ERROR_SUCCESS) {
|
|
DBGMSG(DBG_TRACE, ("WriteDriverIni Created key %ws\n", pIniSpooler->pszRegistryEnvironments));
|
|
|
|
if ((dwLastError = SplRegCreateKey(hEnvironmentsRootKey,
|
|
pIniEnvironment->pName,
|
|
0,
|
|
KEY_WRITE,
|
|
NULL,
|
|
&hEnvironmentKey,
|
|
NULL,
|
|
pIniSpooler)) == ERROR_SUCCESS) {
|
|
|
|
DBGMSG(DBG_TRACE, ("WriteDriverIni Created key %ws\n", pIniEnvironment->pName));
|
|
|
|
if ((dwLastError = SplRegCreateKey(hEnvironmentKey,
|
|
szDriversKey,
|
|
0,
|
|
KEY_WRITE,
|
|
NULL,
|
|
&hDriversKey,
|
|
NULL,
|
|
pIniSpooler)) == ERROR_SUCCESS) {
|
|
DBGMSG(DBG_TRACE, ("WriteDriverIni Created key %ws\n", szDriversKey));
|
|
DBGMSG(DBG_TRACE, ("WriteDriverIni Trying to create version key %ws\n", pIniVersion->pName));
|
|
if ((dwLastError = SplRegCreateKey(hDriversKey,
|
|
pIniVersion->pName,
|
|
0,
|
|
KEY_WRITE,
|
|
NULL,
|
|
&hVersionKey,
|
|
NULL,
|
|
pIniSpooler)) == ERROR_SUCCESS) {
|
|
|
|
DBGMSG(DBG_TRACE, ("WriteDriverIni Created key %ws\n", pIniVersion->pName));
|
|
if ((dwLastError = SplRegCreateKey(hVersionKey,
|
|
pIniDriver->pName,
|
|
0,
|
|
KEY_WRITE,
|
|
NULL,
|
|
&hDriverKey,
|
|
NULL,
|
|
pIniSpooler)) == ERROR_SUCCESS) {
|
|
DBGMSG(DBG_TRACE,(" WriteDriverIni Created key %ws\n", pIniDriver->pName));
|
|
|
|
RegSetString(hDriverKey, szConfigurationKey, pIniDriver->pConfigFile, &dwLastError, pIniSpooler);
|
|
|
|
RegSetString(hDriverKey, szDataFileKey, pIniDriver->pDataFile, &dwLastError, pIniSpooler);
|
|
|
|
RegSetString(hDriverKey, szDriverFile, pIniDriver->pDriverFile, &dwLastError, pIniSpooler);
|
|
|
|
RegSetString(hDriverKey, szHelpFile, pIniDriver->pHelpFile, &dwLastError, pIniSpooler);
|
|
|
|
RegSetString(hDriverKey, szMonitor, pIniDriver->pMonitorName, &dwLastError, pIniSpooler);
|
|
|
|
RegSetString(hDriverKey, szDatatype, pIniDriver->pDefaultDataType, &dwLastError, pIniSpooler);
|
|
|
|
RegSetMultiString(hDriverKey, szDependentFiles, pIniDriver->pDependentFiles, pIniDriver->cchDependentFiles, &dwLastError, pIniSpooler);
|
|
|
|
RegSetMultiString(hDriverKey, szPreviousNames, pIniDriver->pszzPreviousNames, pIniDriver->cchPreviousNames, &dwLastError, pIniSpooler);
|
|
|
|
RegSetDWord(hDriverKey, szDriverVersion, pIniDriver->cVersion, &dwLastError, pIniSpooler);
|
|
|
|
RegSetDWord(hDriverKey, szTempDir, pIniDriver->dwTempDir, &dwLastError, pIniSpooler);
|
|
|
|
RegSetDWord(hDriverKey, szAttributes, pIniDriver->dwDriverAttributes, &dwLastError, pIniSpooler);
|
|
|
|
RegSetString(hDriverKey, szMfgName, pIniDriver->pszMfgName, &dwLastError, pIniSpooler);
|
|
|
|
RegSetString(hDriverKey, szOEMUrl, pIniDriver->pszOEMUrl, &dwLastError, pIniSpooler);
|
|
|
|
RegSetString(hDriverKey, szHardwareID, pIniDriver->pszHardwareID, &dwLastError, pIniSpooler);
|
|
|
|
RegSetString(hDriverKey, szProvider, pIniDriver->pszProvider, &dwLastError, pIniSpooler);
|
|
|
|
RegSetBinaryData(hDriverKey,
|
|
szDriverDate,
|
|
(LPBYTE)&pIniDriver->ftDriverDate,
|
|
sizeof(FILETIME),
|
|
&dwLastError,
|
|
pIniSpooler);
|
|
|
|
RegSetBinaryData(hDriverKey,
|
|
szLongVersion,
|
|
(LPBYTE)&pIniDriver->dwlDriverVersion,
|
|
sizeof(DWORDLONG),
|
|
&dwLastError,
|
|
pIniSpooler);
|
|
|
|
SplRegCloseKey(hDriverKey, pIniSpooler);
|
|
|
|
if(dwLastError != ERROR_SUCCESS) {
|
|
|
|
SplRegDeleteKey(hVersionKey, pIniDriver->pName, pIniSpooler);
|
|
}
|
|
}
|
|
|
|
SplRegCloseKey(hVersionKey, pIniSpooler);
|
|
}
|
|
|
|
SplRegCloseKey(hDriversKey, pIniSpooler);
|
|
}
|
|
|
|
SplRegCloseKey(hEnvironmentKey, pIniSpooler);
|
|
}
|
|
|
|
SplRegCloseKey(hEnvironmentsRootKey, pIniSpooler);
|
|
}
|
|
|
|
ImpersonatePrinterClient( hToken );
|
|
|
|
if ( dwLastError != ERROR_SUCCESS ) {
|
|
|
|
SetLastError( dwLastError );
|
|
return FALSE;
|
|
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
DeleteDriverIni(
|
|
PINIDRIVER pIniDriver,
|
|
PINIVERSION pIniVersion,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
{
|
|
HKEY hEnvironmentsRootKey, hEnvironmentKey, hDriversKey;
|
|
HANDLE hToken;
|
|
HKEY hVersionKey;
|
|
DWORD LastError= 0;
|
|
DWORD dwRet = 0;
|
|
|
|
hToken = RevertToPrinterSelf();
|
|
|
|
if ((dwRet = SplRegCreateKey(pIniSpooler->SpoolerFlags & SPL_CLUSTER_REG ? pIniSpooler->hckRoot : HKEY_LOCAL_MACHINE,
|
|
pIniSpooler->pszRegistryEnvironments,
|
|
0,
|
|
KEY_WRITE,
|
|
NULL,
|
|
&hEnvironmentsRootKey,
|
|
NULL,
|
|
pIniSpooler) == ERROR_SUCCESS)) {
|
|
if ((dwRet = SplRegOpenKey(hEnvironmentsRootKey,
|
|
pIniEnvironment->pName,
|
|
KEY_WRITE,
|
|
&hEnvironmentKey,
|
|
pIniSpooler)) == ERROR_SUCCESS) {
|
|
|
|
if ((dwRet = SplRegOpenKey(hEnvironmentKey,
|
|
szDriversKey,
|
|
KEY_WRITE,
|
|
&hDriversKey,
|
|
pIniSpooler)) == ERROR_SUCCESS) {
|
|
if ((dwRet = SplRegOpenKey(hDriversKey,
|
|
pIniVersion->pName,
|
|
KEY_WRITE,
|
|
&hVersionKey,
|
|
pIniSpooler)) == ERROR_SUCCESS) {
|
|
|
|
if ((dwRet = SplRegDeleteKey(hVersionKey, pIniDriver->pName, pIniSpooler)) != ERROR_SUCCESS) {
|
|
LastError = dwRet;
|
|
DBGMSG( DBG_WARNING, ("Error:RegDeleteKey failed with %d\n", dwRet));
|
|
}
|
|
|
|
SplRegCloseKey(hVersionKey, pIniSpooler);
|
|
} else {
|
|
LastError = dwRet;
|
|
DBGMSG( DBG_WARNING, ("Error: RegOpenKeyEx <version> failed with %d\n", dwRet));
|
|
}
|
|
SplRegCloseKey(hDriversKey, pIniSpooler);
|
|
} else {
|
|
LastError = dwRet;
|
|
DBGMSG( DBG_WARNING, ("Error:RegOpenKeyEx <Drivers>failed with %d\n", dwRet));
|
|
}
|
|
SplRegCloseKey(hEnvironmentKey, pIniSpooler);
|
|
} else {
|
|
LastError = dwRet;
|
|
DBGMSG( DBG_WARNING, ("Error:RegOpenKeyEx <Environment> failed with %d\n", dwRet));
|
|
}
|
|
SplRegCloseKey(hEnvironmentsRootKey, pIniSpooler);
|
|
} else {
|
|
LastError = dwRet;
|
|
DBGMSG( DBG_WARNING, ("Error:RegCreateKeyEx <Environments> failed with %d\n", dwRet));
|
|
}
|
|
|
|
ImpersonatePrinterClient( hToken );
|
|
|
|
if (LastError) {
|
|
SetLastError(LastError);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
SetOldDateOnSingleDriverFile(
|
|
LPWSTR pFileName
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
This routine changes the Date / Time of the file.
|
|
|
|
The reason for doing this is that, when AddPrinterDriver is called we move the Driver
|
|
file from the ScratchDiretory to a \version directory. We then want to mark the original
|
|
file for deletion. However Integraphs install program ( an possibly others ) rely on the
|
|
file still being located in the scratch directory. By setting the files date / time
|
|
back to an earlier date / time we will not attemp to copy this file again to the \version
|
|
directory since it will be an older date.
|
|
|
|
It is then marked for deletion at reboot.
|
|
|
|
Arguments:
|
|
|
|
pFileName Just file Name ( not fully qualified )
|
|
|
|
pDir Directory where file to be deleted is located
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Note:
|
|
|
|
--*/
|
|
{
|
|
FILETIME WriteFileTime;
|
|
HANDLE hFile;
|
|
|
|
if ( pFileName ) {
|
|
|
|
DBGMSG( DBG_TRACE,("Attempting to delete file %ws\n", pFileName));
|
|
|
|
hFile = CreateFile(pFileName, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
if ( hFile != INVALID_HANDLE_VALUE ) {
|
|
|
|
DBGMSG( DBG_TRACE, ("CreateFile %ws succeeded\n", pFileName));
|
|
|
|
DosDateTimeToFileTime(0xc3, 0x3000, &WriteFileTime);
|
|
SetFileTime(hFile, &WriteFileTime, &WriteFileTime, &WriteFileTime);
|
|
CloseHandle(hFile);
|
|
|
|
} else {
|
|
DBGMSG( DBG_WARNING, ("CreateFile %ws failed with %d\n", pFileName, GetLastError()));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
SetOldDateOnDriverFilesInScratchDirectory(
|
|
PINTERNAL_DRV_FILE pInternalDriverFiles,
|
|
DWORD FileCount,
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
{
|
|
HANDLE hToken;
|
|
|
|
SPLASSERT(FileCount);
|
|
|
|
//
|
|
// Run as SYSTEM so we don't run into problems
|
|
// Changing the file time or date
|
|
//
|
|
hToken = RevertToPrinterSelf();
|
|
|
|
do {
|
|
SetOldDateOnSingleDriverFile(pInternalDriverFiles[--FileCount].pFileName);
|
|
} while (FileCount);
|
|
|
|
ImpersonatePrinterClient(hToken);
|
|
|
|
}
|
|
|
|
|
|
|
|
PINIVERSION
|
|
FindVersionEntry(
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
DWORD dwVersion
|
|
)
|
|
{
|
|
PINIVERSION pIniVersion;
|
|
|
|
pIniVersion = pIniEnvironment->pIniVersion;
|
|
|
|
while (pIniVersion) {
|
|
if (pIniVersion->cMajorVersion == dwVersion) {
|
|
return pIniVersion;
|
|
} else {
|
|
pIniVersion = pIniVersion->pNext;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
PINIVERSION
|
|
CreateVersionEntry(
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
DWORD dwVersion,
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
{
|
|
PINIVERSION pIniVersion = NULL;
|
|
WCHAR szTempBuffer[MAX_PATH];
|
|
BOOL bSuccess = FALSE;
|
|
|
|
try {
|
|
|
|
pIniVersion = AllocSplMem(sizeof(INIVERSION));
|
|
if ( pIniVersion == NULL ) {
|
|
leave;
|
|
}
|
|
|
|
pIniVersion->signature = IV_SIGNATURE;
|
|
|
|
if (!BoolFromHResult(StringCchPrintf(szTempBuffer, COUNTOF(szTempBuffer), L"Version-%d", dwVersion))) {
|
|
leave;
|
|
}
|
|
|
|
pIniVersion->pName = AllocSplStr( szTempBuffer );
|
|
|
|
if ( pIniVersion->pName == NULL ) {
|
|
leave;
|
|
}
|
|
|
|
if (!BoolFromHResult(StringCchPrintf(szTempBuffer, COUNTOF(szTempBuffer), L"%d", dwVersion))) {
|
|
leave;
|
|
}
|
|
|
|
pIniVersion->szDirectory = AllocSplStr(szTempBuffer);
|
|
|
|
if ( pIniVersion->szDirectory == NULL ) {
|
|
leave;
|
|
}
|
|
|
|
pIniVersion->cMajorVersion = dwVersion;
|
|
|
|
//
|
|
// Initialize the Driver Files Reference count list.
|
|
//
|
|
pIniVersion->pDrvRefCnt = NULL;
|
|
|
|
//
|
|
// Create the version directory. This will write it out to the
|
|
// registry since it will create a new directory.
|
|
//
|
|
if ( !CreateVersionDirectory( pIniVersion,
|
|
pIniEnvironment,
|
|
TRUE,
|
|
pIniSpooler )) {
|
|
|
|
//
|
|
// Something Went Wrong Clean Up Registry Entry
|
|
//
|
|
DeleteDriverVersionIni( pIniVersion, pIniEnvironment, pIniSpooler );
|
|
leave;
|
|
}
|
|
|
|
//
|
|
// insert version entry into version list
|
|
//
|
|
InsertVersionList( &pIniEnvironment->pIniVersion, pIniVersion );
|
|
|
|
bSuccess = TRUE;
|
|
|
|
} finally {
|
|
|
|
if ( !bSuccess && pIniVersion != NULL ) {
|
|
|
|
FreeSplStr( pIniVersion->pName );
|
|
FreeSplStr( pIniVersion->szDirectory );
|
|
FreeSplMem( pIniVersion );
|
|
pIniVersion = NULL;
|
|
}
|
|
}
|
|
|
|
return pIniVersion;
|
|
}
|
|
|
|
|
|
BOOL
|
|
SetDependentFiles(
|
|
IN OUT LPWSTR *ppszDependentFiles,
|
|
IN OUT LPDWORD pcchDependentFiles,
|
|
IN OUT PINTERNAL_DRV_FILE pInternalDriverFiles,
|
|
IN DWORD FileCount,
|
|
IN BOOL bFixICM,
|
|
IN BOOL bMergeDependentFiles
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Sets dependentFiles field in IniDriver
|
|
|
|
Arguments:
|
|
pDependentFiles : copy the field to this (copy file names only, not full path)
|
|
cchDependentFiles : this is the character count (inc. \0\0) of the field
|
|
pInternalDriverFiles: array of INTERNAL_DRV_FILE structures
|
|
FileCount : number of entries in previous array
|
|
bFixICM : For Win95 drivers ICM files should be used as
|
|
Color\<icm-file> in the dependent file list since
|
|
that is how SMB point and print needs it.
|
|
|
|
Return Value:
|
|
TRUE success (memory will be allocated)
|
|
FALSE else
|
|
|
|
--*/
|
|
{
|
|
BOOL bRet = TRUE;
|
|
LPCWSTR pFileName = NULL;
|
|
LPWSTR pStr = NULL;
|
|
LPWSTR pszDependentFiles = NULL;
|
|
DWORD cchDependentFiles = 0;
|
|
DWORD i;
|
|
|
|
SPLASSERT(FileCount);
|
|
|
|
for ( i = cchDependentFiles = 0; i < FileCount && bRet ; ++i ) {
|
|
|
|
pFileName = FindFileName(pInternalDriverFiles[i].pFileName);
|
|
|
|
if (pFileName)
|
|
{
|
|
cchDependentFiles += wcslen(pFileName)+1;
|
|
|
|
if ( bFixICM && IsAnICMFile(pInternalDriverFiles[i].pFileName) )
|
|
cchDependentFiles += 6;
|
|
}
|
|
else
|
|
{
|
|
bRet = FALSE;
|
|
|
|
SetLastError(ERROR_FILE_NOT_FOUND);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make room for the last \0.
|
|
//
|
|
++(cchDependentFiles);
|
|
|
|
if (bRet)
|
|
{
|
|
pszDependentFiles = AllocSplMem(cchDependentFiles*sizeof(WCHAR));
|
|
|
|
bRet = pszDependentFiles != NULL;
|
|
}
|
|
|
|
if (bRet)
|
|
{
|
|
//
|
|
// Use this to count down the amount of buffer space left which copying
|
|
// the string. Subtract one for the final terminating NULL;
|
|
//
|
|
SIZE_T cchBufferLeft = cchDependentFiles - 1;
|
|
|
|
for ( i=0, pStr = pszDependentFiles; i < FileCount && bRet ; ++i ) {
|
|
|
|
pFileName = FindFileName(pInternalDriverFiles[i].pFileName);
|
|
|
|
if (pFileName)
|
|
{
|
|
if ( bFixICM && IsAnICMFile(pInternalDriverFiles[i].pFileName) ) {
|
|
|
|
bRet = BoolFromHResult(StringCchCopyEx(pStr, cchBufferLeft, L"Color\\", &pStr, &cchBufferLeft, 0)) &&
|
|
BoolFromHResult(StrCchCopyMultipleStr(pStr, cchBufferLeft, pFileName, &pStr, &cchBufferLeft));
|
|
|
|
} else {
|
|
|
|
bRet = BoolFromHResult(StrCchCopyMultipleStr(pStr, cchBufferLeft, pFileName, &pStr, &cchBufferLeft));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bRet = FALSE;
|
|
|
|
SetLastError(ERROR_FILE_NOT_FOUND);
|
|
}
|
|
}
|
|
|
|
//
|
|
// The space for this NULL has been reserved up front.
|
|
//
|
|
*pStr = '\0';
|
|
}
|
|
|
|
//
|
|
// If everything succeeded so far, we have two multi-sz strings that
|
|
// represent the old and the new dependent files, what we want to do
|
|
// is to merge the resulting set of files together
|
|
//
|
|
if (bRet && bMergeDependentFiles)
|
|
{
|
|
PWSTR pszNewDependentFiles = pszDependentFiles;
|
|
DWORD cchNewDependentFiles = cchDependentFiles;
|
|
|
|
pszDependentFiles = NULL; cchDependentFiles = 0;
|
|
|
|
bRet = MergeMultiSz(*ppszDependentFiles, *pcchDependentFiles, pszNewDependentFiles, cchNewDependentFiles, &pszDependentFiles, &cchDependentFiles);
|
|
|
|
FreeSplMem(pszNewDependentFiles);
|
|
}
|
|
|
|
if (bRet)
|
|
{
|
|
*ppszDependentFiles = pszDependentFiles;
|
|
|
|
pszDependentFiles = NULL;
|
|
*pcchDependentFiles = cchDependentFiles;
|
|
}
|
|
else
|
|
{
|
|
*pcchDependentFiles = 0;
|
|
*ppszDependentFiles = NULL;
|
|
}
|
|
|
|
FreeSplMem(pszDependentFiles);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
PINIDRIVER
|
|
CreateDriverEntry(
|
|
IN PINIENVIRONMENT pIniEnvironment,
|
|
IN PINIVERSION pIniVersion,
|
|
IN DWORD Level,
|
|
IN LPBYTE pDriverInfo,
|
|
IN DWORD dwFileCopyFlags,
|
|
IN PINISPOOLER pIniSpooler,
|
|
IN OUT PINTERNAL_DRV_FILE pInternalDriverFiles,
|
|
IN DWORD FileCount,
|
|
IN DWORD dwTempDir,
|
|
IN PINIDRIVER pOldIniDriver
|
|
)
|
|
{
|
|
PINIDRIVER pIniDriver;
|
|
PDRIVER_INFO_2 pDriver = (PDRIVER_INFO_2)pDriverInfo;
|
|
PDRIVER_INFO_3 pDriver3 = (PDRIVER_INFO_3)pDriverInfo;
|
|
PDRIVER_INFO_4 pDriver4 = (PDRIVER_INFO_4)pDriverInfo;
|
|
PDRIVER_INFO_6 pDriver6 = (PDRIVER_INFO_6)pDriverInfo;
|
|
PDRIVER_INFO_VERSION pDriverVersion = (PDRIVER_INFO_VERSION)pDriverInfo;
|
|
LPWSTR pszzPreviousNames;
|
|
BOOL bFail = FALSE, bUpdate;
|
|
BOOL bCoreFilesSame = TRUE;
|
|
DWORD dwDepFileIndex, dwDepFileCount, dwLen;
|
|
|
|
bUpdate = pOldIniDriver != NULL;
|
|
|
|
if ( !(pIniDriver = (PINIDRIVER) AllocSplMem(sizeof(INIDRIVER))) ) {
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// If it is an update pIniDriver is just a place holder for strings
|
|
//
|
|
if ( !bUpdate ) {
|
|
|
|
pIniDriver->signature = ID_SIGNATURE;
|
|
pIniDriver->cVersion = pIniVersion->cMajorVersion;
|
|
|
|
} else {
|
|
|
|
UpdateDriverFileRefCnt(pIniEnvironment, pIniVersion, pOldIniDriver, NULL, 0, FALSE);
|
|
CopyMemory(pIniDriver, pOldIniDriver, sizeof(INIDRIVER));
|
|
}
|
|
|
|
//
|
|
// For the core driver files, we want to see if any of them have changed, if
|
|
// they are the same and the behaviour is APD_COPY_NEW_FILES, then we merge
|
|
// the dependent files. This is to handle plugins correctly.
|
|
//
|
|
AllocOrUpdateStringAndTestSame(&pIniDriver->pDriverFile,
|
|
FindFileName(pInternalDriverFiles[0].pFileName),
|
|
bUpdate ? pOldIniDriver->pDriverFile : NULL,
|
|
FALSE,
|
|
&bFail,
|
|
&bCoreFilesSame);
|
|
|
|
AllocOrUpdateStringAndTestSame(&pIniDriver->pConfigFile,
|
|
FindFileName(pInternalDriverFiles[1].pFileName),
|
|
bUpdate ? pOldIniDriver->pConfigFile : NULL,
|
|
FALSE,
|
|
&bFail,
|
|
&bCoreFilesSame);
|
|
|
|
AllocOrUpdateStringAndTestSame(&pIniDriver->pDataFile,
|
|
FindFileName(pInternalDriverFiles[2].pFileName),
|
|
bUpdate ? pOldIniDriver->pDataFile : NULL,
|
|
FALSE,
|
|
&bFail,
|
|
&bCoreFilesSame);
|
|
|
|
pIniDriver->dwTempDir = dwTempDir;
|
|
|
|
switch (Level) {
|
|
case 2:
|
|
AllocOrUpdateString(&pIniDriver->pName,
|
|
pDriver->pName,
|
|
bUpdate ? pOldIniDriver->pName : NULL,
|
|
FALSE,
|
|
&bFail);
|
|
|
|
pIniDriver->pHelpFile = pIniDriver->pDependentFiles
|
|
= pIniDriver->pMonitorName
|
|
= pIniDriver->pDefaultDataType
|
|
= pIniDriver->pszzPreviousNames
|
|
= NULL;
|
|
|
|
pIniDriver->cchDependentFiles = pIniDriver->cchPreviousNames
|
|
= 0;
|
|
break;
|
|
|
|
case 3:
|
|
case 4:
|
|
pIniDriver->pszMfgName = NULL;
|
|
pIniDriver->pszOEMUrl = NULL;
|
|
pIniDriver->pszHardwareID = NULL;
|
|
pIniDriver->pszProvider = NULL;
|
|
|
|
case DRIVER_INFO_VERSION_LEVEL :
|
|
case 6:
|
|
AllocOrUpdateString(&pIniDriver->pName,
|
|
pDriver3->pName,
|
|
bUpdate ? pOldIniDriver->pName : NULL,
|
|
FALSE,
|
|
&bFail);
|
|
|
|
dwDepFileIndex = 3;
|
|
dwDepFileCount = FileCount - 3;
|
|
|
|
//
|
|
// Look for the help file
|
|
//
|
|
{
|
|
LPWSTR pszHelpFile = NULL;
|
|
|
|
if (Level == DRIVER_INFO_VERSION_LEVEL)
|
|
{
|
|
DWORD HelpFileIndex;
|
|
|
|
//
|
|
// Search for the help file in the array of file infos. All inbox
|
|
// drivers have a help file, but IHV printer drivers may not have
|
|
// one. Therefore it is not safe to assume we always have a help file
|
|
//
|
|
if (S_OK == FindIndexInDrvFileInfo(pDriverVersion->pFileInfo,
|
|
pDriverVersion->dwFileCount,
|
|
HELP_FILE,
|
|
&HelpFileIndex))
|
|
{
|
|
pszHelpFile = (LPWSTR)((LPBYTE)pDriverVersion +
|
|
pDriverVersion->pFileInfo[HelpFileIndex].FileNameOffset);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Level is 3,4 or 6
|
|
//
|
|
pszHelpFile = pDriver3->pHelpFile;
|
|
}
|
|
|
|
if (pszHelpFile && *pszHelpFile)
|
|
{
|
|
AllocOrUpdateString(&pIniDriver->pHelpFile,
|
|
FindFileName(pInternalDriverFiles[3].pFileName),
|
|
bUpdate ? pOldIniDriver->pHelpFile : NULL,
|
|
FALSE,
|
|
&bFail);
|
|
|
|
++dwDepFileIndex;
|
|
--dwDepFileCount;
|
|
}
|
|
else
|
|
{
|
|
pIniDriver->pHelpFile = NULL;
|
|
}
|
|
}
|
|
|
|
if ( dwDepFileCount ) {
|
|
|
|
//
|
|
// We want to merge the dependent files if:
|
|
// 1. None of the Core files have changed.
|
|
// 2. The call was made with APD_COPY_NEW_FILES.
|
|
//
|
|
BOOL bMergeDependentFiles = bCoreFilesSame && dwFileCopyFlags & APD_COPY_NEW_FILES;
|
|
|
|
if ( !bFail &&
|
|
!SetDependentFiles(&pIniDriver->pDependentFiles,
|
|
&pIniDriver->cchDependentFiles,
|
|
pInternalDriverFiles+dwDepFileIndex,
|
|
dwDepFileCount,
|
|
!wcscmp(pIniEnvironment->pName, szWin95Environment),
|
|
bMergeDependentFiles) ) {
|
|
bFail = TRUE;
|
|
}
|
|
} else {
|
|
|
|
pIniDriver->pDependentFiles = NULL;
|
|
pIniDriver->cchDependentFiles = 0;
|
|
}
|
|
|
|
AllocOrUpdateString(&pIniDriver->pMonitorName,
|
|
(Level == DRIVER_INFO_VERSION_LEVEL) ?
|
|
pDriverVersion->pMonitorName : pDriver3->pMonitorName,
|
|
bUpdate ? pOldIniDriver->pMonitorName : NULL,
|
|
FALSE,
|
|
&bFail);
|
|
|
|
AllocOrUpdateString(&pIniDriver->pDefaultDataType,
|
|
(Level == DRIVER_INFO_VERSION_LEVEL) ?
|
|
pDriverVersion->pDefaultDataType : pDriver3->pDefaultDataType,
|
|
bUpdate ? pOldIniDriver->pDefaultDataType : NULL,
|
|
FALSE,
|
|
&bFail);
|
|
|
|
pIniDriver->cchPreviousNames = 0;
|
|
|
|
if ( Level == 4 || Level == 6 || Level == DRIVER_INFO_VERSION_LEVEL) {
|
|
|
|
pszzPreviousNames = (Level == DRIVER_INFO_VERSION_LEVEL) ?
|
|
pDriverVersion->pszzPreviousNames :
|
|
pDriver4->pszzPreviousNames;
|
|
|
|
for ( ; pszzPreviousNames && *pszzPreviousNames; pszzPreviousNames += dwLen) {
|
|
|
|
dwLen = wcslen(pszzPreviousNames) + 1;
|
|
|
|
pIniDriver->cchPreviousNames += dwLen;
|
|
}
|
|
|
|
if ( pIniDriver->cchPreviousNames ) {
|
|
|
|
pIniDriver->cchPreviousNames++;
|
|
|
|
if ( !(pIniDriver->pszzPreviousNames
|
|
= AllocSplMem(pIniDriver->cchPreviousNames
|
|
* sizeof(WCHAR))) ) {
|
|
|
|
bFail = TRUE;
|
|
|
|
} else {
|
|
|
|
CopyMemory(
|
|
(LPBYTE)(pIniDriver->pszzPreviousNames),
|
|
(Level == DRIVER_INFO_VERSION_LEVEL) ?
|
|
pDriverVersion->pszzPreviousNames :
|
|
pDriver4->pszzPreviousNames,
|
|
pIniDriver->cchPreviousNames * sizeof(WCHAR));
|
|
}
|
|
|
|
} else {
|
|
|
|
pIniDriver->pszzPreviousNames = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
if (Level == 6 || Level == DRIVER_INFO_VERSION_LEVEL) {
|
|
|
|
AllocOrUpdateString(&pIniDriver->pszMfgName,
|
|
(Level == DRIVER_INFO_VERSION_LEVEL) ?
|
|
pDriverVersion->pszMfgName : pDriver6->pszMfgName,
|
|
bUpdate ? pOldIniDriver->pszMfgName : NULL,
|
|
FALSE,
|
|
&bFail);
|
|
|
|
AllocOrUpdateString(&pIniDriver->pszOEMUrl,
|
|
(Level == DRIVER_INFO_VERSION_LEVEL) ?
|
|
pDriverVersion->pszOEMUrl : pDriver6->pszOEMUrl,
|
|
bUpdate ? pOldIniDriver->pszOEMUrl : NULL,
|
|
FALSE,
|
|
&bFail);
|
|
|
|
AllocOrUpdateString(&pIniDriver->pszHardwareID,
|
|
(Level == DRIVER_INFO_VERSION_LEVEL) ?
|
|
pDriverVersion->pszHardwareID : pDriver6->pszHardwareID,
|
|
bUpdate ? pOldIniDriver->pszHardwareID : NULL,
|
|
FALSE,
|
|
&bFail);
|
|
|
|
AllocOrUpdateString(&pIniDriver->pszProvider,
|
|
(Level == DRIVER_INFO_VERSION_LEVEL) ?
|
|
pDriverVersion->pszProvider : pDriver6->pszProvider,
|
|
bUpdate ? pOldIniDriver->pszProvider : NULL,
|
|
FALSE,
|
|
&bFail);
|
|
|
|
pIniDriver->dwlDriverVersion = (Level == DRIVER_INFO_VERSION_LEVEL) ?
|
|
pDriverVersion->dwlDriverVersion : pDriver6->dwlDriverVersion;
|
|
pIniDriver->ftDriverDate = (Level == DRIVER_INFO_VERSION_LEVEL) ?
|
|
pDriverVersion->ftDriverDate : pDriver6->ftDriverDate;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
DBGMSG(DBG_ERROR,
|
|
("CreateDriverEntry: level can not be %d", Level) );
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Added calls to update driver files ref counts.
|
|
//
|
|
if ( !bFail && UpdateDriverFileRefCnt(pIniEnvironment,pIniVersion,pIniDriver,NULL,0,TRUE) ) {
|
|
|
|
//
|
|
// Update the files minor version
|
|
//
|
|
UpdateDriverFileVersion(pIniVersion, pInternalDriverFiles, FileCount);
|
|
|
|
//
|
|
// UMPD\KMPD detection
|
|
//
|
|
CheckDriverAttributes(pIniSpooler, pIniEnvironment,
|
|
pIniVersion, pIniDriver);
|
|
|
|
if ( WriteDriverIni(pIniDriver, pIniVersion, pIniEnvironment, pIniSpooler)) {
|
|
|
|
if ( bUpdate ) {
|
|
CopyNewOffsets((LPBYTE) pOldIniDriver,
|
|
(LPBYTE) pIniDriver,
|
|
IniDriverOffsets);
|
|
|
|
//
|
|
// Remove temp files and directory, if any.
|
|
//
|
|
if (pOldIniDriver->dwTempDir && (dwTempDir == 0)) {
|
|
|
|
RemoveDriverTempFiles(pIniSpooler,
|
|
pIniEnvironment,
|
|
pIniVersion,
|
|
pOldIniDriver);
|
|
}
|
|
|
|
pOldIniDriver->dwDriverAttributes = pIniDriver->dwDriverAttributes;
|
|
pOldIniDriver->cchDependentFiles = pIniDriver->cchDependentFiles;
|
|
pOldIniDriver->dwTempDir = pIniDriver->dwTempDir;
|
|
pOldIniDriver->cchPreviousNames = pIniDriver->cchPreviousNames;
|
|
|
|
if(Level == 6)
|
|
{
|
|
pOldIniDriver->dwlDriverVersion = pIniDriver->dwlDriverVersion;
|
|
pOldIniDriver->ftDriverDate = pIniDriver->ftDriverDate;
|
|
}
|
|
|
|
FreeSplMem( pIniDriver );
|
|
|
|
return pOldIniDriver;
|
|
} else {
|
|
pIniDriver->pNext = pIniVersion->pIniDriver;
|
|
pIniVersion->pIniDriver = pIniDriver;
|
|
|
|
return pIniDriver;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get here only for failure cases.
|
|
//
|
|
FreeStructurePointers((LPBYTE) pIniDriver,
|
|
(LPBYTE) pOldIniDriver,
|
|
IniDriverOffsets);
|
|
FreeSplMem( pIniDriver );
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
BOOL
|
|
IsKMPD(
|
|
LPWSTR pDriverName
|
|
)
|
|
/*++
|
|
Function Description: Determines if the driver is kernel or user mode. If the dll
|
|
cant be loaded or the required export is not found, the spooler
|
|
assumes that the driver runs in kernel mode.
|
|
|
|
Parameters: pDriverName -- Driver file name
|
|
|
|
Return Values: TRUE if kernel mode;
|
|
FALSE otherwise
|
|
|
|
REMARK: to be rewritten to not make any assumption if something went wrong.
|
|
--*/
|
|
{
|
|
DWORD dwOldErrorMode, dwUserMode, cb;
|
|
HANDLE hInst;
|
|
BOOL bReturn = TRUE;
|
|
BOOL (*pfnDrvQuery)(DWORD, PVOID, DWORD, PDWORD);
|
|
|
|
// Avoid popups from loadlibrary failures
|
|
dwOldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
|
|
|
|
hInst = LoadLibraryExW(pDriverName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
|
|
|
|
if (hInst) {
|
|
|
|
// Check if the printer driver DLL exports DrvQueryDriverInfo entrypoint
|
|
pfnDrvQuery = (BOOL (*)(DWORD, PVOID, DWORD, PDWORD))
|
|
GetProcAddress(hInst, "DrvQueryDriverInfo");
|
|
|
|
if ( pfnDrvQuery ) {
|
|
|
|
try {
|
|
|
|
if ( pfnDrvQuery(DRVQUERY_USERMODE, &dwUserMode,
|
|
sizeof(dwUserMode), &cb) )
|
|
bReturn = (dwUserMode == 0);
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
SetLastError( GetExceptionCode() );
|
|
DBGMSG(DBG_ERROR,
|
|
("IsKMPD ExceptionCode %x Driver %ws Error %d\n",
|
|
GetLastError(), pDriverName, GetLastError() ));
|
|
}
|
|
}
|
|
|
|
FreeLibrary(hInst);
|
|
}
|
|
|
|
SetErrorMode(dwOldErrorMode);
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
BOOL
|
|
IniDriverIsKMPD (
|
|
PINISPOOLER pIniSpooler,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINIVERSION pIniVersion,
|
|
PINIDRIVER pIniDriver
|
|
)
|
|
/*++
|
|
Function Description:
|
|
Determines if the driver is kernel or user mode.
|
|
For Whistler we save pIniDriver->dwDriverAttributes under registry.
|
|
pIniDriver->dwDriverAttributes could be un-initialized at the time we do
|
|
the check to see if a driver is KM or UM.
|
|
|
|
Parameters: pIniSpooler -- pointer to INISPOOLER
|
|
pIniEnvironment -- pointer to INIENVIRONMENT
|
|
pIniVersion -- pointer to INVERSION
|
|
pIniDriver -- pointer to INIDRIVER
|
|
|
|
Return Values: TRUE if kernel mode;
|
|
FALSE otherwise
|
|
--*/
|
|
{
|
|
//
|
|
// Call IsKMPD if dwDriverAttributes is not initialized
|
|
//
|
|
if ( pIniDriver->dwDriverAttributes == 0 ) {
|
|
|
|
CheckDriverAttributes(pIniSpooler, pIniEnvironment, pIniVersion, pIniDriver);
|
|
}
|
|
|
|
return (BOOL)(pIniDriver->dwDriverAttributes & DRIVER_KERNELMODE);
|
|
}
|
|
|
|
VOID
|
|
CheckDriverAttributes(
|
|
PINISPOOLER pIniSpooler,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINIVERSION pIniVersion,
|
|
PINIDRIVER pIniDriver
|
|
)
|
|
/*++
|
|
Function Description: Updates the pIniDriver->dwDriverAttributes field
|
|
|
|
Parameters: pIniSpooler -- pointer to INISPOOLER
|
|
pIniEnvironment -- pointer to INIENVIRONMENT
|
|
pIniVersion -- pointer to INVERSION
|
|
pIniDriver -- pointer to INIDRIVER
|
|
|
|
Return Values: NONE
|
|
--*/
|
|
{
|
|
WCHAR szDriverFile[MAX_PATH];
|
|
PINIDRIVER pUpdateIniDriver;
|
|
|
|
if( GetDriverVersionDirectory( szDriverFile,
|
|
COUNTOF(szDriverFile),
|
|
pIniSpooler,
|
|
pIniEnvironment,
|
|
pIniVersion,
|
|
pIniDriver,
|
|
NULL) &&
|
|
StrNCatBuff(szDriverFile,
|
|
COUNTOF(szDriverFile),
|
|
szDriverFile,
|
|
L"\\",
|
|
FindFileName(pIniDriver->pDriverFile),
|
|
NULL) == ERROR_SUCCESS )
|
|
{
|
|
pIniDriver->dwDriverAttributes = IsKMPD(szDriverFile) ? DRIVER_KERNELMODE
|
|
: DRIVER_USERMODE;
|
|
//
|
|
// Update other pIniDriver structs with the new driver attributes.
|
|
//
|
|
for (pUpdateIniDriver = pIniVersion->pIniDriver;
|
|
pUpdateIniDriver;
|
|
pUpdateIniDriver = pUpdateIniDriver->pNext) {
|
|
|
|
if (pUpdateIniDriver == pIniDriver) {
|
|
//
|
|
// Already updated this driver
|
|
//
|
|
continue;
|
|
}
|
|
|
|
if (!_wcsicmp(FindFileName(pIniDriver->pDriverFile),
|
|
FindFileName(pUpdateIniDriver->pDriverFile))) {
|
|
|
|
pUpdateIniDriver->dwDriverAttributes = pIniDriver->dwDriverAttributes;
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
BOOL
|
|
FileInUse(
|
|
PINIVERSION pIniVersion,
|
|
LPWSTR pFileName
|
|
)
|
|
/*++
|
|
Function Description: Finds if the file specified by pFileName is used by any driver.
|
|
|
|
Parameters: pIniVersion - pointer to INIVERSION struct where the ref counts are
|
|
stored
|
|
pFileName - Name of the driver related file
|
|
|
|
Return Value: TRUE if file is in Use
|
|
FALSE otherwise
|
|
--*/
|
|
{
|
|
PDRVREFCNT pdrc;
|
|
|
|
if (!pFileName || !(*pFileName)) {
|
|
return FALSE;
|
|
}
|
|
|
|
pdrc = pIniVersion->pDrvRefCnt;
|
|
|
|
while (pdrc != NULL) {
|
|
if (_wcsicmp(pFileName,pdrc->szDrvFileName) == 0) {
|
|
return (pdrc->refcount > 1);
|
|
}
|
|
pdrc = pdrc->pNext;
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
BOOL
|
|
FilesInUse(
|
|
PINIVERSION pIniVersion,
|
|
PINIDRIVER pIniDriver
|
|
)
|
|
|
|
/*++
|
|
Function Description: FilesInUse checks if any of the driver files are used by another
|
|
driver
|
|
|
|
Parameters: pIniVersion - pointer to INIVERSION struct where the ref counts are
|
|
stored
|
|
pIniDriver - pointer to INIDRIVER struct where the filenames are stored
|
|
|
|
Return Value: TRUE if any file is in Use
|
|
FALSE otherwise
|
|
--*/
|
|
{
|
|
LPWSTR pIndex;
|
|
|
|
if (FileInUse(pIniVersion,pIniDriver->pDriverFile)) {
|
|
return TRUE;
|
|
}
|
|
if (FileInUse(pIniVersion,pIniDriver->pConfigFile)) {
|
|
return TRUE;
|
|
}
|
|
if (FileInUse(pIniVersion,pIniDriver->pDataFile)) {
|
|
return TRUE;
|
|
}
|
|
if (FileInUse(pIniVersion,pIniDriver->pHelpFile)) {
|
|
return TRUE;
|
|
}
|
|
|
|
pIndex = pIniDriver->pDependentFiles;
|
|
while (pIndex && *pIndex) {
|
|
if (FileInUse(pIniVersion,pIndex)) return TRUE;
|
|
pIndex += wcslen(pIndex) + 1;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
DuplicateFile(
|
|
PDRVFILE *ppfile,
|
|
LPCWSTR pFileName,
|
|
BOOL *pbDuplicate
|
|
)
|
|
/*++
|
|
Function Description: Detects repeated filenames in INIDRIVER struct.
|
|
The function adds nodes to the list of filenames.
|
|
|
|
Parameters: ppfile - pointer to a list of filenames seen till now
|
|
pFileName - name of the file
|
|
pbDuplicate - pointer to flag to indicate duplication
|
|
|
|
Return Values: TRUE - if successful
|
|
FALSE - otherwise
|
|
--*/
|
|
{
|
|
PDRVFILE pfile = *ppfile,pfiletemp;
|
|
|
|
*pbDuplicate = FALSE;
|
|
|
|
if (!pFileName || !(*pFileName)) {
|
|
return TRUE;
|
|
}
|
|
|
|
while (pfile) {
|
|
if (pfile->pFileName && (lstrcmpi(pFileName,pfile->pFileName) == 0)) {
|
|
*pbDuplicate = TRUE;
|
|
return TRUE;
|
|
}
|
|
pfile = pfile->pnext;
|
|
}
|
|
|
|
if (!(pfiletemp = AllocSplMem(sizeof(DRVFILE)))) {
|
|
return FALSE;
|
|
}
|
|
pfiletemp->pnext = *ppfile;
|
|
pfiletemp->pFileName = pFileName;
|
|
*ppfile = pfiletemp;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
InternalIncrement(
|
|
PDRVREFNODE *pNew,
|
|
PDRVFILE *ppfile,
|
|
PINIVERSION pIniVersion,
|
|
LPCWSTR pFileName
|
|
)
|
|
/*++
|
|
Function Description: InternalIncrement calls IncrementFileRefCnt and saves the pointer to
|
|
to the DRVREFCNT in a DRVREFNODE. These pointers are used to readjust
|
|
the ref counts if any intermediate call to IncrementFileRefCnt fails.
|
|
|
|
Parameters: pNew - pointer to a variable which contains a pointer to a DRVREFNODE.
|
|
The new DRVREFNODE is assigned to this variable.
|
|
ppfile - list of filenames seen so far.
|
|
pIniVersion - pointer to INIVERSION struct.
|
|
pFileName - Name of the file whose ref cnt is to be incremented.
|
|
|
|
Return Value: TRUE if memory allocation and call to IncrementFileRefCnt succeeds
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
PDRVREFNODE ptemp;
|
|
BOOL bDuplicate;
|
|
|
|
if (!pFileName || !pFileName[0]) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (!DuplicateFile(ppfile, pFileName, &bDuplicate)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (bDuplicate) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (!(ptemp = AllocSplMem(sizeof(DRVREFNODE)))) {
|
|
return FALSE;
|
|
}
|
|
|
|
ptemp->pNext = *pNew;
|
|
*pNew = ptemp;
|
|
|
|
if ((*pNew)->pdrc = IncrementFileRefCnt(pIniVersion,pFileName)) {
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
InternalDecrement(
|
|
PDRVREFNODE *pNew,
|
|
PDRVFILE *ppfile,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINIVERSION pIniVersion,
|
|
PINIDRIVER pIniDriver,
|
|
LPCWSTR pFileName,
|
|
LPCWSTR pDirectory,
|
|
DWORD dwDeleteFlag
|
|
)
|
|
/*++
|
|
Function Description: InternalDecrement calls DecrementFileRefCnt and saves the pointer to
|
|
to the DRVREFCNT in a DRVREFNODE. These pointers are used to readjust
|
|
the ref counts if any intermediate call to DecrementFileRefCnt fails.
|
|
|
|
Parameters: pNew - pointer to a variable which contains a pointer to a DRVREFNODE.
|
|
The new DRVREFNODE is assigned to this variable.
|
|
ppfile - list of filenames seen so far.
|
|
pIniEnvironment - pointer to INIENVIRONMENT.
|
|
pIniVersion - pointer to INIVERSION struct.
|
|
pIniDriver - pointer to INIDRIVER.
|
|
pFileName - Name of the file whose ref cnt is to be decremented.
|
|
pDirectory - Directory where the files are stored.
|
|
dwDeleteFlag - Flag to delete files.
|
|
|
|
Return Value: TRUE if memory allocation and call to DecrementFileRefCnt succeeds
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDRVREFNODE ptemp;
|
|
BOOL bDuplicate;
|
|
|
|
if( !pFileName || !pFileName[0] ){
|
|
return TRUE;
|
|
}
|
|
|
|
if (!DuplicateFile(ppfile, pFileName, &bDuplicate)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (bDuplicate) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (!(ptemp = AllocSplMem(sizeof(DRVREFNODE)))) {
|
|
return FALSE;
|
|
}
|
|
|
|
ptemp->pNext = *pNew;
|
|
*pNew = ptemp;
|
|
|
|
if ((*pNew)->pdrc = DecrementFileRefCnt(pIniEnvironment,pIniVersion,pIniDriver,pFileName,
|
|
pDirectory,dwDeleteFlag)) {
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
UpdateDriverFileRefCnt(
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINIVERSION pIniVersion,
|
|
PINIDRIVER pIniDriver,
|
|
LPCWSTR pDirectory,
|
|
DWORD dwDeleteFlag,
|
|
BOOL bIncrementFlag
|
|
)
|
|
/*++
|
|
Function Description: UpdateDriverRefCnt calls the functions to increment or decrement
|
|
the ref cnts for the driver related files. If any call fails, the
|
|
ref cnts are returned to their previous values.
|
|
|
|
Parameters: pIniEnvironment : pointer to INIENVIRONMENT
|
|
pIniVersion : pointer to INIVERSION struct which contains the ref cnts.
|
|
pIniDriver : pointer to INIDRIVER struct which contains driver info.
|
|
pDirectory : Directory where the files are stored.
|
|
dwDeleteFlag: Flag to delete the files.
|
|
bIncrementFlag: TRUE if driver added
|
|
FALSE if driver deleted.
|
|
|
|
Return Values: TRUE if success
|
|
FALSE otherwise.
|
|
--*/
|
|
{
|
|
LPWSTR pIndex;
|
|
PDRVREFNODE phead=NULL,ptemp=NULL;
|
|
BOOL bReturn = TRUE;
|
|
PDRVFILE pfile = NULL,pfiletemp;
|
|
PDRVREFCNT pDrvRefCnt, *ppDrvRefCnt;
|
|
|
|
pIndex = pIniDriver->pDependentFiles;
|
|
|
|
if (bIncrementFlag) {
|
|
//
|
|
// Adding driver entry. Increment fileref counts.
|
|
//
|
|
if (!InternalIncrement(&phead,&pfile,pIniVersion,pIniDriver->pDriverFile)
|
|
|| !InternalIncrement(&phead,&pfile,pIniVersion,pIniDriver->pConfigFile)
|
|
|| !InternalIncrement(&phead,&pfile,pIniVersion,pIniDriver->pHelpFile)
|
|
|| !InternalIncrement(&phead,&pfile,pIniVersion,pIniDriver->pDataFile)) {
|
|
|
|
bReturn = FALSE;
|
|
goto CleanUp;
|
|
|
|
}
|
|
|
|
while (pIndex && *pIndex) {
|
|
if (!InternalIncrement(&phead,&pfile,pIniVersion,pIndex)) {
|
|
bReturn = FALSE;
|
|
goto CleanUp;
|
|
}
|
|
pIndex += wcslen(pIndex) + 1;
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Deleting driver entry. Decrement fileref counts.
|
|
//
|
|
if (!InternalDecrement(&phead,&pfile,pIniEnvironment,pIniVersion,pIniDriver,pIniDriver->pDriverFile,
|
|
pDirectory,dwDeleteFlag)
|
|
|| !InternalDecrement(&phead,&pfile,pIniEnvironment,pIniVersion,pIniDriver,pIniDriver->pConfigFile,
|
|
pDirectory,dwDeleteFlag)
|
|
|| !InternalDecrement(&phead,&pfile,pIniEnvironment,pIniVersion,pIniDriver,pIniDriver->pHelpFile,
|
|
pDirectory,dwDeleteFlag)
|
|
|| !InternalDecrement(&phead,&pfile,pIniEnvironment,pIniVersion,pIniDriver,pIniDriver->pDataFile,
|
|
pDirectory,dwDeleteFlag)) {
|
|
|
|
bReturn = FALSE;
|
|
goto CleanUp;
|
|
}
|
|
|
|
while (pIndex && *pIndex) {
|
|
if (!InternalDecrement(&phead,&pfile,pIniEnvironment,pIniVersion,pIniDriver,pIndex,pDirectory,dwDeleteFlag)) {
|
|
bReturn = FALSE;
|
|
goto CleanUp;
|
|
}
|
|
pIndex += wcslen(pIndex) + 1;
|
|
}
|
|
}
|
|
|
|
CleanUp:
|
|
|
|
if (bReturn) {
|
|
//
|
|
// When delete the file, remove the RefCnt nodes with count = 0.
|
|
// We want to keep the node when we don't delete the file because the node
|
|
// contains info about how many times the file was updated (dwVersion).
|
|
// Client apps (WINSPOOL.DRV) rely on this when decide to reload the driver files.
|
|
//
|
|
while (ptemp = phead) {
|
|
if (ptemp->pdrc &&
|
|
ptemp->pdrc->refcount == 0 &&
|
|
(dwDeleteFlag & DPD_DELETE_UNUSED_FILES ||
|
|
dwDeleteFlag & DPD_DELETE_ALL_FILES)) {
|
|
FreeSplStr(ptemp->pdrc->szDrvFileName);
|
|
FreeSplMem(ptemp->pdrc);
|
|
}
|
|
phead = phead->pNext;
|
|
FreeSplMem(ptemp);
|
|
}
|
|
|
|
} else {
|
|
// Adjust the ref counts.
|
|
while (ptemp = phead) {
|
|
if (ptemp->pdrc) {
|
|
if (bIncrementFlag) {
|
|
ptemp->pdrc->refcount--;
|
|
} else {
|
|
ptemp->pdrc->refcount++;
|
|
if (ptemp->pdrc->refcount == 1) {
|
|
ptemp->pdrc->pNext = pIniVersion->pDrvRefCnt;
|
|
pIniVersion->pDrvRefCnt = ptemp->pdrc;
|
|
}
|
|
}
|
|
}
|
|
phead = phead->pNext;
|
|
FreeSplMem(ptemp);
|
|
}
|
|
|
|
//
|
|
// When delete the file, remove the RefCnt nodes with count = 0.
|
|
// We want to keep the node when we don't delete the file because the node
|
|
// contains info about how many times the file was updated (dwVersion).
|
|
// Client apps (WINSPOOL.DRV) rely on this when decide to reload the driver files.
|
|
//
|
|
ppDrvRefCnt = &(pIniVersion->pDrvRefCnt);
|
|
while (pDrvRefCnt = *ppDrvRefCnt) {
|
|
if (pDrvRefCnt->refcount == 0 && dwDeleteFlag) {
|
|
*ppDrvRefCnt = pDrvRefCnt->pNext;
|
|
FreeSplStr(pDrvRefCnt->szDrvFileName);
|
|
FreeSplMem(pDrvRefCnt);
|
|
} else {
|
|
ppDrvRefCnt = &(pDrvRefCnt->pNext);
|
|
}
|
|
}
|
|
}
|
|
|
|
while (pfiletemp = pfile) {
|
|
pfile = pfile->pnext;
|
|
FreeSplMem(pfiletemp);
|
|
}
|
|
|
|
return bReturn;
|
|
|
|
}
|
|
|
|
VOID
|
|
UpdateDriverFileVersion(
|
|
IN PINIVERSION pIniVersion,
|
|
IN PINTERNAL_DRV_FILE pInternalDriverFiles,
|
|
IN DWORD FileCount
|
|
)
|
|
{
|
|
PDRVREFCNT pdrc;
|
|
DWORD dwIndex;
|
|
|
|
SplInSem();
|
|
|
|
if (pInternalDriverFiles && pIniVersion)
|
|
{
|
|
for (dwIndex = 0 ; dwIndex < FileCount ; dwIndex ++)
|
|
{
|
|
//
|
|
// Don't do anything for non-executable files
|
|
//
|
|
if (!IsEXEFile(pInternalDriverFiles[dwIndex].pFileName))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Search the entry in pIniVersion's list of files
|
|
//
|
|
for (pdrc = pIniVersion->pDrvRefCnt;
|
|
pdrc &&
|
|
lstrcmpi(FindFileName(pInternalDriverFiles[dwIndex].pFileName),
|
|
pdrc->szDrvFileName) != 0;
|
|
pdrc = pdrc->pNext);
|
|
|
|
if (pdrc)
|
|
{
|
|
if (pInternalDriverFiles[dwIndex].hFileHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
//
|
|
// We can come in here from a pending upgrade when we don't know the
|
|
// version.
|
|
//
|
|
pdrc->bInitialized = FALSE;
|
|
}
|
|
else if (pInternalDriverFiles[dwIndex].bUpdated)
|
|
{
|
|
pdrc->dwFileMinorVersion = pInternalDriverFiles[dwIndex].dwVersion;
|
|
pdrc->bInitialized = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
PDRVREFCNT
|
|
IncrementFileRefCnt(
|
|
PINIVERSION pIniVersion,
|
|
LPCWSTR pFileName
|
|
)
|
|
/*++
|
|
Function Description: IncrementFileRefCnt increments/initializes to 1 the ref count node
|
|
for pFileName in the IniVersion Struct.
|
|
|
|
Parameters: pIniversion - pointer to the INIVERSION struct.
|
|
pFileName - Name of the file whose ref cnt is to be incremented.
|
|
|
|
Return Values: Pointer to the ref cnt that was incremented
|
|
NULL if memory allocation fails.
|
|
|
|
--*/
|
|
{
|
|
PDRVREFCNT pdrc;
|
|
|
|
SplInSem();
|
|
|
|
if (!pIniVersion || !pFileName || !(*pFileName)) {
|
|
return NULL;
|
|
}
|
|
|
|
pdrc = pIniVersion->pDrvRefCnt;
|
|
|
|
while (pdrc != NULL) {
|
|
|
|
if (lstrcmpi(pFileName,pdrc->szDrvFileName) == 0) {
|
|
pdrc->refcount++;
|
|
return pdrc;
|
|
}
|
|
pdrc = pdrc->pNext;
|
|
}
|
|
|
|
if (!(pdrc = (PDRVREFCNT) AllocSplMem(sizeof(DRVREFCNT)))) return NULL;
|
|
pdrc->refcount = 1;
|
|
pdrc->dwVersion = 0;
|
|
pdrc->dwFileMinorVersion = 0;
|
|
pdrc->dwFileMajorVersion = 0;
|
|
pdrc->bInitialized = 0;
|
|
if (!(pdrc->szDrvFileName = AllocSplStr(pFileName))) {
|
|
FreeSplMem(pdrc);
|
|
return NULL;
|
|
}
|
|
pdrc->pNext = pIniVersion->pDrvRefCnt;
|
|
pIniVersion->pDrvRefCnt = pdrc;
|
|
|
|
return pdrc;
|
|
}
|
|
|
|
|
|
DWORD
|
|
GetEnvironmentScratchDirectory(
|
|
LPWSTR pDir,
|
|
DWORD MaxLength,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
BOOL Remote
|
|
)
|
|
{
|
|
PINISPOOLER pIniSpooler = pIniEnvironment->pIniSpooler;
|
|
|
|
if (Remote) {
|
|
|
|
if( StrNCatBuff( pDir,
|
|
MaxLength,
|
|
pIniSpooler->pMachineName,
|
|
L"\\",
|
|
pIniSpooler->pszDriversShare,
|
|
L"\\",
|
|
pIniEnvironment->pDirectory,
|
|
NULL) != ERROR_SUCCESS )
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
if( StrNCatBuff( pDir,
|
|
MaxLength,
|
|
pIniSpooler->pDir,
|
|
L"\\",
|
|
szDriverDir,
|
|
L"\\",
|
|
pIniEnvironment->pDirectory,
|
|
NULL) != ERROR_SUCCESS ) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return wcslen(pDir);
|
|
|
|
}
|
|
|
|
|
|
BOOL
|
|
CreateVersionDirectory(
|
|
PINIVERSION pIniVersion,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
BOOL bUpdate,
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a version directory if necessary for the environment.
|
|
If a version number file exists instead of a directory, a tmp
|
|
directory is created, and pIniVersion is updated appropriately.
|
|
|
|
We will update the registry if we need to create a directory by
|
|
re-writing the entire version entry. This is how the version
|
|
entry in the registry is initially created.
|
|
|
|
Arguments:
|
|
|
|
pIniVersion - Version of drivers that the directory will hold.
|
|
If the directory already exists, we will modify
|
|
pIniVersion->szDirectory to a temp name and write
|
|
it to the registry.
|
|
|
|
pIniEnvironment - Environment to use.
|
|
|
|
bUpdate - Indicates whether we should write out the IniVersion
|
|
registry entries. We need to do this if we just alloced
|
|
the pIniVersion, or if we have changed directories.
|
|
|
|
pIniSpooler
|
|
|
|
Return Value:
|
|
|
|
BOOL - TRUE = Version directory and registry created/updated.
|
|
FALSE = Failure, call GetLastError().
|
|
|
|
--*/
|
|
{
|
|
WCHAR ParentDir[MAX_PATH];
|
|
WCHAR Directory[MAX_PATH];
|
|
DWORD dwParentLen=0;
|
|
DWORD dwAttributes = 0;
|
|
BOOL bCreateDirectory = FALSE;
|
|
BOOL bReturn = TRUE;
|
|
HANDLE hToken;
|
|
|
|
if((StrNCatBuff ( ParentDir,
|
|
COUNTOF(ParentDir),
|
|
pIniSpooler->pDir,
|
|
L"\\drivers\\" ,
|
|
pIniEnvironment->pDirectory,
|
|
NULL) != ERROR_SUCCESS ) ||
|
|
(StrNCatBuff ( Directory,
|
|
COUNTOF(Directory),
|
|
pIniSpooler->pDir,
|
|
L"\\drivers\\",
|
|
pIniEnvironment->pDirectory,
|
|
L"\\",
|
|
pIniVersion->szDirectory,
|
|
NULL) != ERROR_SUCCESS ) )
|
|
{
|
|
bReturn = FALSE;
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
goto End;
|
|
}
|
|
|
|
DBGMSG( DBG_TRACE, ("The name of the version directory is %ws\n", Directory));
|
|
dwAttributes = GetFileAttributes( Directory );
|
|
|
|
hToken = RevertToPrinterSelf();
|
|
|
|
if (dwAttributes == 0xffffffff) {
|
|
|
|
bCreateDirectory = TRUE;
|
|
|
|
} else if (!(dwAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
|
|
LPWSTR pszOldDirectory = pIniVersion->szDirectory;
|
|
|
|
DBGMSG(DBG_WARNING, ("CreateVersionDirectory: a file <not a dir> exists by the name of %ws\n", Directory));
|
|
|
|
GetTempFileName(ParentDir, L"SPL", 0, Directory);
|
|
|
|
//
|
|
// GetTempFileName creates the file. (Small window where someone
|
|
// else could grab our file name.)
|
|
//
|
|
SplDeleteFile(Directory);
|
|
|
|
//
|
|
// We created a new dir, so modify the string.
|
|
//
|
|
dwParentLen = wcslen(ParentDir);
|
|
pIniVersion->szDirectory = AllocSplStr(&Directory[dwParentLen+1]);
|
|
|
|
if (!pIniVersion->szDirectory) {
|
|
|
|
pIniVersion->szDirectory = pszOldDirectory;
|
|
|
|
//
|
|
// Memory allocation failed, just revert back to old and
|
|
// let downwind code handle failure case.
|
|
//
|
|
bReturn = FALSE;
|
|
|
|
} else {
|
|
|
|
FreeSplStr(pszOldDirectory);
|
|
bCreateDirectory = TRUE;
|
|
}
|
|
}
|
|
|
|
if( bCreateDirectory ){
|
|
|
|
if( CreateCompleteDirectory( Directory )){
|
|
|
|
//
|
|
// Be sure to update the registry entries.
|
|
//
|
|
bUpdate = TRUE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Fail the operation since we couldn't create the directory.
|
|
//
|
|
bReturn = FALSE;
|
|
}
|
|
}
|
|
|
|
if( bUpdate ){
|
|
|
|
//
|
|
// Directory exists, update registry.
|
|
//
|
|
bReturn = WriteDriverVersionIni( pIniVersion,
|
|
pIniEnvironment,
|
|
pIniSpooler);
|
|
}
|
|
|
|
ImpersonatePrinterClient( hToken );
|
|
|
|
End:
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
|
|
BOOL
|
|
WriteDriverVersionIni(
|
|
PINIVERSION pIniVersion,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Writes out the driver version registry entries.
|
|
|
|
Note: assumes we are running in the system context; callee must
|
|
call RevertToPrinterSelf()!
|
|
|
|
Arguments:
|
|
|
|
pIniVersion - version to write out
|
|
|
|
pIniEnvironment - environment the version belongs to
|
|
|
|
pIniSpooler
|
|
|
|
Return Value:
|
|
|
|
TRUE = success
|
|
FALSE = failure, call GetLastError()
|
|
|
|
--*/
|
|
{
|
|
HKEY hEnvironmentsRootKey = NULL;
|
|
HKEY hEnvironmentKey = NULL;
|
|
HKEY hDriversKey = NULL;
|
|
HKEY hVersionKey = NULL;
|
|
DWORD dwLastError = ERROR_SUCCESS;
|
|
BOOL bReturnValue;
|
|
|
|
try {
|
|
|
|
//
|
|
// The local spooler and cluster spoolers do not share the same resgirty location
|
|
// for environments, drivers, processors etc.
|
|
//
|
|
if ( !PrinterCreateKey( pIniSpooler->SpoolerFlags & SPL_CLUSTER_REG ? pIniSpooler->hckRoot : HKEY_LOCAL_MACHINE,
|
|
(LPWSTR)pIniSpooler->pszRegistryEnvironments,
|
|
&hEnvironmentsRootKey,
|
|
&dwLastError,
|
|
pIniSpooler )) {
|
|
|
|
leave;
|
|
}
|
|
|
|
if ( !PrinterCreateKey( hEnvironmentsRootKey,
|
|
pIniEnvironment->pName,
|
|
&hEnvironmentKey,
|
|
&dwLastError,
|
|
pIniSpooler )) {
|
|
|
|
leave;
|
|
}
|
|
|
|
if ( !PrinterCreateKey( hEnvironmentKey,
|
|
szDriversKey,
|
|
&hDriversKey,
|
|
&dwLastError,
|
|
pIniSpooler )) {
|
|
|
|
|
|
leave;
|
|
}
|
|
|
|
if ( !PrinterCreateKey( hDriversKey,
|
|
pIniVersion->pName,
|
|
&hVersionKey,
|
|
&dwLastError,
|
|
pIniSpooler )) {
|
|
|
|
leave;
|
|
}
|
|
|
|
RegSetString( hVersionKey, szDirectory, pIniVersion->szDirectory, &dwLastError, pIniSpooler );
|
|
RegSetDWord( hVersionKey, szMajorVersion, pIniVersion->cMajorVersion, &dwLastError, pIniSpooler );
|
|
RegSetDWord( hVersionKey, szMinorVersion, pIniVersion->cMinorVersion ,&dwLastError, pIniSpooler );
|
|
|
|
} finally {
|
|
|
|
if (hVersionKey)
|
|
SplRegCloseKey(hVersionKey, pIniSpooler);
|
|
|
|
if (hDriversKey)
|
|
SplRegCloseKey(hDriversKey, pIniSpooler);
|
|
|
|
if (hEnvironmentKey)
|
|
SplRegCloseKey(hEnvironmentKey, pIniSpooler);
|
|
|
|
if (hEnvironmentsRootKey)
|
|
SplRegCloseKey(hEnvironmentsRootKey, pIniSpooler);
|
|
|
|
if (dwLastError != ERROR_SUCCESS) {
|
|
|
|
SetLastError(dwLastError);
|
|
bReturnValue = FALSE;
|
|
|
|
} else {
|
|
|
|
bReturnValue = TRUE;
|
|
}
|
|
|
|
}
|
|
return bReturnValue;
|
|
}
|
|
|
|
BOOL
|
|
DeleteDriverVersionIni(
|
|
PINIVERSION pIniVersion,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
{
|
|
HKEY hEnvironmentsRootKey, hEnvironmentKey, hDriversKey;
|
|
HANDLE hToken;
|
|
HKEY hVersionKey;
|
|
BOOL bReturnValue = FALSE;
|
|
DWORD Status;
|
|
|
|
hToken = RevertToPrinterSelf();
|
|
|
|
if ( RegCreateKeyEx( HKEY_LOCAL_MACHINE, pIniSpooler->pszRegistryEnvironments, 0,
|
|
NULL, 0, KEY_WRITE, NULL, &hEnvironmentsRootKey, NULL) == ERROR_SUCCESS) {
|
|
|
|
if ( RegOpenKeyEx( hEnvironmentsRootKey, pIniEnvironment->pName, 0,
|
|
KEY_WRITE, &hEnvironmentKey) == ERROR_SUCCESS) {
|
|
|
|
if ( RegOpenKeyEx( hEnvironmentKey, szDriversKey, 0,
|
|
KEY_WRITE, &hDriversKey) == ERROR_SUCCESS) {
|
|
|
|
Status = RegDeleteKey( hDriversKey, pIniVersion->pName );
|
|
|
|
if ( Status == ERROR_SUCCESS ) {
|
|
|
|
bReturnValue = TRUE;
|
|
|
|
} else {
|
|
|
|
DBGMSG( DBG_WARNING, ( "DeleteDriverVersionIni failed RegDeleteKey %x %ws error %d\n",
|
|
hDriversKey,
|
|
pIniVersion->pName,
|
|
Status ));
|
|
}
|
|
|
|
RegCloseKey(hDriversKey);
|
|
}
|
|
|
|
RegCloseKey(hEnvironmentKey);
|
|
}
|
|
|
|
RegCloseKey(hEnvironmentsRootKey);
|
|
}
|
|
|
|
ImpersonatePrinterClient( hToken );
|
|
|
|
return bReturnValue;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
SplGetPrinterDriverEx(
|
|
HANDLE hPrinter,
|
|
LPWSTR pEnvironment,
|
|
DWORD Level,
|
|
LPBYTE pDriverInfo,
|
|
DWORD cbBuf,
|
|
LPDWORD pcbNeeded,
|
|
DWORD dwClientMajorVersion,
|
|
DWORD dwClientMinorVersion,
|
|
PDWORD pdwServerMajorVersion,
|
|
PDWORD pdwServerMinorVersion
|
|
)
|
|
{
|
|
PINIDRIVER pIniDriver=NULL;
|
|
PINIVERSION pIniVersion=NULL;
|
|
PINIENVIRONMENT pIniEnvironment;
|
|
DWORD cb;
|
|
LPBYTE pEnd;
|
|
PSPOOL pSpool = (PSPOOL)hPrinter;
|
|
PINISPOOLER pIniSpooler;
|
|
LPWSTR psz;
|
|
|
|
if ((dwClientMajorVersion == (DWORD)-1) && (dwClientMinorVersion == (DWORD)-1)) {
|
|
dwClientMajorVersion = dwMajorVersion;
|
|
dwClientMinorVersion = dwMinorVersion;
|
|
}
|
|
|
|
EnterSplSem();
|
|
|
|
if (!ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER )) {
|
|
|
|
LeaveSplSem();
|
|
return FALSE;
|
|
}
|
|
|
|
pIniSpooler = pSpool->pIniSpooler;
|
|
|
|
if (!(pIniEnvironment = FindEnvironment(pEnvironment, pIniSpooler))) {
|
|
LeaveSplSem();
|
|
SetLastError(ERROR_INVALID_ENVIRONMENT);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// If the printer handle is remote or a non-native driver is asked for,
|
|
// then return back a compatible driver; Else return pIniPrinter->pIniDriver
|
|
//
|
|
if ( (pSpool->TypeofHandle & PRINTER_HANDLE_REMOTE_CALL) ||
|
|
lstrcmpi(szEnvironment, pIniEnvironment->pName) ) {
|
|
|
|
pIniDriver = FindCompatibleDriver(pIniEnvironment,
|
|
&pIniVersion,
|
|
pSpool->pIniPrinter->pIniDriver->pName,
|
|
dwClientMajorVersion,
|
|
FIND_COMPATIBLE_VERSION | DRIVER_SEARCH);
|
|
|
|
//
|
|
// For Windows 9x drivers if no driver with same name is found
|
|
// then we look for a driver with name in the pszzPreviousNames field
|
|
//
|
|
if ( !pIniDriver &&
|
|
!wcscmp(pIniEnvironment->pName, szWin95Environment) &&
|
|
(psz = pSpool->pIniPrinter->pIniDriver->pszzPreviousNames) ) {
|
|
|
|
for ( ; !pIniDriver && *psz ; psz += wcslen(psz) + 1 )
|
|
pIniDriver = FindCompatibleDriver(pIniEnvironment,
|
|
&pIniVersion,
|
|
psz,
|
|
0,
|
|
FIND_COMPATIBLE_VERSION | DRIVER_SEARCH);
|
|
|
|
if (!pIniDriver && Level == 1) {
|
|
|
|
//
|
|
// SMB code calls GetPrinterDriver level 1 to findout which
|
|
// driver name to send to Win9x client in GetPrinter info
|
|
// If we do not have Win9x printer driver installed and previous
|
|
// names field is not NULL our best guess is the first one in
|
|
// the pszzPreviousNames. This is expected to be the popular
|
|
// driver on Win9x. If client already has the driver they can
|
|
// print
|
|
//
|
|
psz = pSpool->pIniPrinter->pIniDriver->pszzPreviousNames;
|
|
*pcbNeeded = ( wcslen(psz) + 1 ) * sizeof(WCHAR)
|
|
+ sizeof(DRIVER_INFO_1);
|
|
if ( *pcbNeeded > cbBuf ) {
|
|
|
|
LeaveSplSem();
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return FALSE;
|
|
}
|
|
|
|
((LPDRIVER_INFO_1)pDriverInfo)->pName = (LPWSTR)(pDriverInfo + sizeof(DRIVER_INFO_1));
|
|
|
|
StringCbCopy(((LPDRIVER_INFO_1)pDriverInfo)->pName, cbBuf, psz);
|
|
|
|
LeaveSplSem();
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if ( !pIniDriver ) {
|
|
|
|
LeaveSplSem();
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
|
|
pIniDriver = pSpool->pIniPrinter->pIniDriver;
|
|
|
|
pIniVersion = FindVersionForDriver(pIniEnvironment, pIniDriver);
|
|
}
|
|
|
|
cb = GetDriverInfoSize( pIniDriver, Level, pIniVersion,pIniEnvironment,
|
|
pSpool->TypeofHandle & PRINTER_HANDLE_REMOTE_CALL ?
|
|
pSpool->pFullMachineName : NULL,
|
|
pSpool->pIniSpooler );
|
|
*pcbNeeded=cb;
|
|
|
|
if (cb > cbBuf) {
|
|
LeaveSplSem();
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return FALSE;
|
|
}
|
|
pEnd = pDriverInfo+cbBuf;
|
|
if (!CopyIniDriverToDriverInfo(pIniEnvironment, pIniVersion, pIniDriver,
|
|
Level, pDriverInfo, pEnd,
|
|
pSpool->TypeofHandle & PRINTER_HANDLE_REMOTE_CALL ?
|
|
pSpool->pFullMachineName : NULL,
|
|
pIniSpooler)) {
|
|
LeaveSplSem();
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
return FALSE;
|
|
}
|
|
LeaveSplSem();
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
PINIVERSION
|
|
FindCompatibleVersion(
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
DWORD dwMajorVersion,
|
|
int FindAnyVersion
|
|
)
|
|
{
|
|
PINIVERSION pIniVersion;
|
|
|
|
if (!pIniEnvironment) {
|
|
return NULL;
|
|
}
|
|
|
|
for ( pIniVersion = pIniEnvironment->pIniVersion;
|
|
pIniVersion != NULL;
|
|
pIniVersion = pIniVersion->pNext ) {
|
|
|
|
if ( (FindAnyVersion & DRIVER_UPGRADE) ?
|
|
(pIniVersion->cMajorVersion >= dwMajorVersion) :
|
|
(pIniVersion->cMajorVersion <= dwMajorVersion))
|
|
{
|
|
|
|
//
|
|
// Pre version 2 is not comparable with version 2 or newer
|
|
//
|
|
if ( dwMajorVersion >= 2 &&
|
|
pIniVersion->cMajorVersion < 2 &&
|
|
((FindAnyVersion & FIND_ANY_VERSION)==FIND_COMPATIBLE_VERSION) &&
|
|
lstrcmpi(pIniEnvironment->pName, szWin95Environment) ) {
|
|
|
|
return NULL;
|
|
}
|
|
|
|
return pIniVersion;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
PINIDRIVER
|
|
FindCompatibleDriver(
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINIVERSION * ppIniVersion,
|
|
LPWSTR pDriverName,
|
|
DWORD dwMajorVersion,
|
|
int FindAnyDriver
|
|
)
|
|
{
|
|
PINIVERSION pIniVersion;
|
|
PINIDRIVER pIniDriver = NULL;
|
|
|
|
try {
|
|
|
|
*ppIniVersion = NULL;
|
|
|
|
if (!pIniEnvironment) {
|
|
leave;
|
|
}
|
|
|
|
pIniVersion = FindCompatibleVersion( pIniEnvironment, dwMajorVersion, FindAnyDriver );
|
|
|
|
if ( pIniVersion == NULL) {
|
|
leave;
|
|
}
|
|
|
|
while (pIniVersion){
|
|
|
|
//
|
|
// Pre version 2 is not comparable with version 2 or newer
|
|
//
|
|
if ( dwMajorVersion >= 2 &&
|
|
((FindAnyDriver & FIND_ANY_VERSION) == FIND_COMPATIBLE_VERSION) &&
|
|
pIniVersion->cMajorVersion < 2 ) {
|
|
|
|
break;
|
|
}
|
|
|
|
if ( pIniDriver = FindDriverEntry( pIniVersion, pDriverName ) ) {
|
|
|
|
//
|
|
// We found the driver. Break the loop and return succesfully.
|
|
//
|
|
*ppIniVersion = pIniVersion;
|
|
leave;
|
|
}
|
|
|
|
pIniVersion = pIniVersion->pNext;
|
|
}
|
|
|
|
} finally {
|
|
|
|
if ( pIniDriver == NULL ) {
|
|
|
|
SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER);
|
|
}
|
|
}
|
|
|
|
return pIniDriver;
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
InsertVersionList(
|
|
PINIVERSION* ppIniVersionHead,
|
|
PINIVERSION pIniVersion
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Insert a version entry into the verions linked list.
|
|
|
|
Versions are stored in decending order (2, 1, 0) so that
|
|
when a version is needed, we get the highest first.
|
|
|
|
Arguments:
|
|
|
|
ppIniVersionHead - Pointer to the head of the pIniVersion head.
|
|
|
|
pIniVersion - Version structure we want to add.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
SplInSem();
|
|
|
|
//
|
|
// Insert into single-linked list code. We take the address of
|
|
// the head pointer so that we can avoid special casing the
|
|
// insert into empty list case.
|
|
//
|
|
for( ; *ppIniVersionHead; ppIniVersionHead = &(*ppIniVersionHead)->pNext ){
|
|
|
|
//
|
|
// If the major version of the pIniVersion we're inserting
|
|
// is > the next pIniVersion on the list, insert it before
|
|
// that one.
|
|
//
|
|
// 4 3 2 1
|
|
// ^
|
|
// New '3' gets inserted here. (Note: duplicate versions should
|
|
// never be added.)
|
|
//
|
|
if( pIniVersion->cMajorVersion > (*ppIniVersionHead)->cMajorVersion ){
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Link up the new version.
|
|
//
|
|
pIniVersion->pNext = *ppIniVersionHead;
|
|
*ppIniVersionHead = pIniVersion;
|
|
}
|
|
|
|
|
|
|
|
PINIDRIVER
|
|
FindDriverEntry(
|
|
PINIVERSION pIniVersion,
|
|
LPWSTR pszName
|
|
)
|
|
{
|
|
PINIDRIVER pIniDriver;
|
|
|
|
if (!pIniVersion) {
|
|
return NULL;
|
|
}
|
|
|
|
if (!pszName || !*pszName) {
|
|
DBGMSG( DBG_WARNING, ("Passing a Null Printer Driver Name to FindDriverEntry\n"));
|
|
return NULL;
|
|
}
|
|
|
|
pIniDriver = pIniVersion->pIniDriver;
|
|
|
|
//
|
|
// Only return the driver if it is not pending deletion.
|
|
//
|
|
while (pIniDriver) {
|
|
if (!lstrcmpi(pIniDriver->pName, pszName) &&
|
|
!(pIniDriver->dwDriverFlags & PRINTER_DRIVER_PENDING_DELETION)) {
|
|
return pIniDriver;
|
|
}
|
|
pIniDriver = pIniDriver->pNext;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
VOID
|
|
DeleteDriverEntry(
|
|
PINIVERSION pIniVersion,
|
|
PINIDRIVER pIniDriver
|
|
)
|
|
{ PINIDRIVER pPrev, pCurrent;
|
|
if (!pIniVersion) {
|
|
return;
|
|
}
|
|
|
|
if (!pIniVersion->pIniDriver) {
|
|
return;
|
|
}
|
|
pPrev = pCurrent = NULL;
|
|
pCurrent = pIniVersion->pIniDriver;
|
|
|
|
while (pCurrent) {
|
|
if (pCurrent == pIniDriver) {
|
|
if (pPrev == NULL) {
|
|
pIniVersion->pIniDriver = pCurrent->pNext;
|
|
} else{
|
|
pPrev->pNext = pCurrent->pNext;
|
|
}
|
|
//
|
|
// Free all the entries in the entry
|
|
//
|
|
FreeStructurePointers((LPBYTE) pIniDriver, NULL, IniDriverOffsets);
|
|
FreeSplMem(pIniDriver);
|
|
return;
|
|
}
|
|
pPrev = pCurrent;
|
|
pCurrent = pCurrent->pNext;
|
|
}
|
|
return;
|
|
}
|
|
|
|
BOOL CheckFileCopy(
|
|
PINIVERSION pIniVersion,
|
|
LPWSTR pTargetFile,
|
|
LPWSTR pSourceFile,
|
|
PWIN32_FIND_DATA pSourceData,
|
|
DWORD dwSourceVersion,
|
|
DWORD dwFileCopyFlags,
|
|
LPBOOL pbCopyFile,
|
|
LPBOOL pbTargetExists)
|
|
|
|
/*++
|
|
Function Description: This functions determines if the target exists and if it should
|
|
be overwritten.
|
|
|
|
Parameters:
|
|
|
|
Return Values: TRUE if successful; FALSE otherwise.
|
|
--*/
|
|
|
|
{
|
|
WIN32_FIND_DATA DestFileData, SourceFileData, *pSourceFileData;
|
|
HANDLE hFileExists;
|
|
BOOL bReturn = FALSE, bSourceFileHandleCreated = FALSE;
|
|
DWORD dwTargetVersion = 0;
|
|
|
|
LeaveSplSem();
|
|
|
|
*pbCopyFile = *pbTargetExists = FALSE;
|
|
|
|
pSourceFileData = pSourceData ? pSourceData : &SourceFileData;
|
|
|
|
//
|
|
// Get Source File Date & Time Stamp
|
|
//
|
|
hFileExists = FindFirstFile( pSourceFile, pSourceFileData );
|
|
|
|
if (hFileExists == INVALID_HANDLE_VALUE) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
FindClose( hFileExists );
|
|
|
|
//
|
|
// Get Target File Date Time
|
|
//
|
|
hFileExists = FindFirstFile( pTargetFile, &DestFileData );
|
|
|
|
if (hFileExists == INVALID_HANDLE_VALUE) {
|
|
|
|
if (GetLastError() == ERROR_FILE_NOT_FOUND) {
|
|
//
|
|
// Copy the source since there is no target
|
|
//
|
|
*pbCopyFile = TRUE;
|
|
bReturn = TRUE;
|
|
}
|
|
|
|
goto CleanUp;
|
|
}
|
|
|
|
*pbTargetExists = TRUE;
|
|
FindClose(hFileExists);
|
|
|
|
//
|
|
// Check Source File version and LastWrite Times vs Target File if only new files
|
|
// are to be copied
|
|
//
|
|
if (dwFileCopyFlags == APD_COPY_NEW_FILES) {
|
|
|
|
EnterSplSem();
|
|
bReturn = GetDriverFileCachedVersion (pIniVersion, pTargetFile, &dwTargetVersion);
|
|
LeaveSplSem();
|
|
|
|
if(!bReturn) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (dwSourceVersion > dwTargetVersion) {
|
|
*pbCopyFile = TRUE;
|
|
|
|
} else {
|
|
|
|
if (dwSourceVersion == dwTargetVersion) {
|
|
|
|
if(CompareFileTime(&(pSourceFileData->ftLastWriteTime),
|
|
&DestFileData.ftLastWriteTime)
|
|
!= FIRST_FILE_TIME_GREATER_THAN_SECOND) {
|
|
|
|
//
|
|
// Target File is up to date. It doesn't need to be updated.
|
|
//
|
|
DBGMSG( DBG_TRACE, ("UpdateFile Target file is up to date\n"));
|
|
|
|
} else {
|
|
*pbCopyFile = TRUE;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
*pbCopyFile = TRUE;
|
|
}
|
|
|
|
bReturn = TRUE;
|
|
|
|
CleanUp:
|
|
|
|
EnterSplSem();
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
BOOL
|
|
UpdateFile(
|
|
PINIVERSION pIniVersion,
|
|
HANDLE hSourceFile,
|
|
LPWSTR pSourceFile,
|
|
DWORD dwVersion,
|
|
LPWSTR pDestDir,
|
|
DWORD dwFileCopyFlags,
|
|
BOOL bImpersonateOnCreate,
|
|
LPBOOL pbFileUpdated,
|
|
LPBOOL pbFileMoved,
|
|
BOOL bSameEnvironment,
|
|
BOOL bWin95Environment
|
|
)
|
|
|
|
/*++
|
|
Function Description: The file times are checked to verify if the file needs to be copied.
|
|
|
|
If the file already exists in the version directory, then it is copied
|
|
into ...\environment\version\new. The corresponding file, which is
|
|
present in environment\version, is copied to \version\old. The new file
|
|
is marked for move on REBOOT.
|
|
|
|
New files are copied into env\version.
|
|
|
|
Parameters: hSourceFile -- file handle
|
|
pSourceFile -- file name
|
|
pDestDir -- driver directory (e.g system32\spool\w32x86\3)
|
|
bImpersonateOnCreate -- flag to impersonate client on any file creation
|
|
pbFilesUpdated -- Have any new files been copied or moved ?
|
|
pbFileMoved -- Have any old files been moved ?
|
|
bSameEnvironment -- flag to indicate if the machine env == driver env
|
|
bWin95Environment -- flag to indicate if the driver env == win95
|
|
|
|
Return Values: TRUE if successful; FALSE otherwise
|
|
--*/
|
|
|
|
{
|
|
HANDLE hToken = INVALID_HANDLE_VALUE;
|
|
WCHAR szTargetFile[MAX_PATH], szNewFile[MAX_PATH];
|
|
LPWSTR pFileName;
|
|
BOOL bReturn = FALSE, bCopyFile, bTargetExists;
|
|
DWORD FileAttrib;
|
|
|
|
WIN32_FIND_DATA SourceFileData;
|
|
|
|
*pbFileMoved = FALSE;
|
|
|
|
pFileName = wcsrchr(pSourceFile, L'\\');
|
|
if (!pFileName || !pDestDir || !*pDestDir) {
|
|
//
|
|
// Invalid file name. Fail the call.
|
|
//
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Set the target directory.
|
|
//
|
|
if(StrNCatBuff(szTargetFile,
|
|
COUNTOF(szTargetFile),
|
|
pDestDir,
|
|
NULL) != ERROR_SUCCESS)
|
|
{
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (bWin95Environment && IsAnICMFile(pSourceFile)) {
|
|
if((StrNCatBuff(szTargetFile,
|
|
COUNTOF(szTargetFile),
|
|
szTargetFile,
|
|
L"\\Color",
|
|
NULL)!=ERROR_SUCCESS))
|
|
{
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
|
|
if((StrNCatBuff(szTargetFile,
|
|
COUNTOF(szTargetFile),
|
|
szTargetFile,
|
|
pFileName,
|
|
NULL)!=ERROR_SUCCESS))
|
|
{
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Check if the file has to be copied given the version, timestamp and flags.
|
|
//
|
|
if (!CheckFileCopy(pIniVersion, szTargetFile, pSourceFile, &SourceFileData, dwVersion,
|
|
dwFileCopyFlags, &bCopyFile, &bTargetExists)) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (bCopyFile) {
|
|
|
|
if (!bImpersonateOnCreate) {
|
|
hToken = RevertToPrinterSelf();
|
|
}
|
|
|
|
if((StrNCatBuff(szNewFile,
|
|
COUNTOF(szNewFile),
|
|
pDestDir,
|
|
L"\\New",
|
|
pFileName,
|
|
NULL)!=ERROR_SUCCESS))
|
|
{
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Leave Spooler semaphore for copying the files
|
|
//
|
|
LeaveSplSem();
|
|
|
|
if (!InternalCopyFile(hSourceFile, &SourceFileData,
|
|
szNewFile, OVERWRITE_IF_TARGET_EXISTS)) {
|
|
|
|
//
|
|
// InternalCopyFile failed. Fail the call. It isn't obvious what is the reason
|
|
// we call InternalCopyFile instead of CopyFile.
|
|
//
|
|
EnterSplSem();
|
|
goto CleanUp;
|
|
}
|
|
|
|
EnterSplSem();
|
|
|
|
} else {
|
|
|
|
*pbFileMoved = TRUE;
|
|
bReturn = TRUE;
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (bCopyFile) {
|
|
|
|
if (!bSameEnvironment) {
|
|
|
|
if (bTargetExists) {
|
|
|
|
DWORD dwAttr;
|
|
|
|
dwAttr = GetFileAttributes(szTargetFile);
|
|
|
|
//
|
|
// Check if the function succeeded and the target file is write protected.
|
|
// Some non native drivers, notably Win 9x drivers, can be copied over to
|
|
// the drivers directory and have the read only attribute. When we update
|
|
// a non native driver, we want to make sure that it is not write protected.
|
|
//
|
|
if (dwAttr != (DWORD)-1 &&
|
|
dwAttr & FILE_ATTRIBUTE_READONLY) {
|
|
|
|
SetFileAttributes(szTargetFile, dwAttr & ~FILE_ATTRIBUTE_READONLY);
|
|
}
|
|
}
|
|
|
|
if (!SplMoveFileEx(szNewFile, szTargetFile, MOVEFILE_REPLACE_EXISTING)) {
|
|
// MoveFile failed
|
|
goto CleanUp;
|
|
}
|
|
|
|
} else {
|
|
|
|
if (bTargetExists) {
|
|
|
|
//
|
|
// Move the file on REBOOT. It may get moved earlier if the driver
|
|
// can be unloaded.
|
|
//
|
|
if (SplMoveFileEx(szNewFile, szTargetFile, MOVEFILE_DELAY_UNTIL_REBOOT)) {
|
|
|
|
*pbFileMoved = TRUE;
|
|
//
|
|
// Don't fail the call here. MoveFileEx with MOVEFILE_DELAY_UNTIL_REBOOT will just write the registry.
|
|
// We'll need this only if the driver is still loaded, which we find out only later.
|
|
// If the driver is not loaded, we'll actually move these files later and this call won't make sense.
|
|
// So,don't fail the api call at this point because MoveFileEx. Hopefully, one day MoveFileEx won't
|
|
// be hard-coded to write only two PendingFileRenameOperations values.
|
|
//
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!SplMoveFileEx(szNewFile, szTargetFile, MOVEFILE_REPLACE_EXISTING)) {
|
|
goto CleanUp;
|
|
}
|
|
*pbFileMoved = TRUE;
|
|
}
|
|
}
|
|
|
|
*pbFileUpdated = TRUE;
|
|
}
|
|
|
|
bReturn = TRUE;
|
|
|
|
CleanUp:
|
|
|
|
if (hToken != INVALID_HANDLE_VALUE) {
|
|
ImpersonatePrinterClient(hToken);
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
|
|
BOOL
|
|
CopyAllFilesAndDeleteOldOnes(
|
|
PINIVERSION pIniVersion,
|
|
PINTERNAL_DRV_FILE pInternalDriverFiles,
|
|
DWORD dwFileCount,
|
|
LPWSTR pDestDir,
|
|
DWORD dwFileCopyFlags,
|
|
BOOL bImpersonateOnCreate,
|
|
LPBOOL pbFileMoved,
|
|
BOOL bSameEnvironment,
|
|
BOOL bWin95Environment
|
|
)
|
|
/*++
|
|
|
|
Function Description: This function loops thru all the files in the driver_info
|
|
struct and calls an update routine.
|
|
|
|
Parameters: pInternalDriverFiles -- array of INTERNAL_DRV_FILE structures
|
|
dwFileCount -- number of files in file set
|
|
pDestDir -- driver directory (e.g system32\spool\w32x86\3)
|
|
bImpersonateOnCreate -- flag to impersonate client on any file creation
|
|
pbFileMoved -- Have any old files been moved ?
|
|
bSameEnvironment -- flag to indicate if the machine env == driver env
|
|
bWin95Environment -- flag to indicate if the driver env == win95
|
|
|
|
Return Values: TRUE if successful; FALSE otherwise
|
|
|
|
--*/
|
|
{
|
|
BOOL bRet = TRUE;
|
|
DWORD dwCount;
|
|
BOOL bFilesUpdated;
|
|
BOOL bFilesMoved = TRUE;
|
|
|
|
*pbFileMoved = TRUE;
|
|
|
|
for (dwCount = 0 ; dwCount < dwFileCount ; ++dwCount) {
|
|
|
|
bFilesUpdated = FALSE;
|
|
|
|
if (!(bRet = UpdateFile(pIniVersion,
|
|
pInternalDriverFiles[dwCount].hFileHandle,
|
|
pInternalDriverFiles[dwCount].pFileName,
|
|
pInternalDriverFiles[dwCount].dwVersion,
|
|
pDestDir,
|
|
dwFileCopyFlags,
|
|
bImpersonateOnCreate,
|
|
&bFilesUpdated,
|
|
&bFilesMoved,
|
|
bSameEnvironment,
|
|
bWin95Environment))) {
|
|
|
|
//
|
|
// Files could not be copied correctly.
|
|
//
|
|
break;
|
|
}
|
|
|
|
if (bFilesUpdated) {
|
|
pInternalDriverFiles[dwCount].bUpdated = TRUE;
|
|
}
|
|
|
|
if(!bFilesMoved) {
|
|
*pbFileMoved = FALSE;
|
|
}
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
BOOL
|
|
CopyFilesToFinalDirectory(
|
|
PINISPOOLER pIniSpooler,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINIVERSION pIniVersion,
|
|
PINTERNAL_DRV_FILE pInternalDriverFiles,
|
|
DWORD dwFileCount,
|
|
DWORD dwFileCopyFlags,
|
|
BOOL bImpersonateOnCreate,
|
|
LPBOOL pbFilesMoved
|
|
)
|
|
|
|
/*++
|
|
|
|
Function Description: This function copies all the new files into the the correct
|
|
directory i.e ...\environment\version.
|
|
|
|
The files which already exist in the version directory are copied
|
|
in ...\environment\version\new. The corresponding files, which are
|
|
present in environment\version, are copied to \version\old.
|
|
|
|
The common files are upgraded when the old files can be unloaded
|
|
from either the kernel (for KMPD) or the spooler (for UMPD)
|
|
|
|
Parameters: pIniSpooler -- pointer to the INISPOOLER struct
|
|
pIniEnvironment -- pointer to the driver environment struct
|
|
pIniVersion -- pointer to the driver version struct
|
|
pInternalDriverFiles -- array of INTERNAL_DRV_FILE structures
|
|
dwFileCount -- number of files in file setes
|
|
dwFileCount -- number of files
|
|
bImpersonateOnCreate -- flag to impersonate client on any file creation
|
|
pbFileMoved -- Have any old files been moved ?
|
|
|
|
Return Values: TRUE if successful; FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
WCHAR szDestDir[INTERNET_MAX_HOST_NAME_LENGTH + MAX_PATH + 1];
|
|
LPWSTR pStringEnd = NULL;
|
|
DWORD dwIndex;
|
|
BOOL bRet = FALSE, bSameEnvironment, bWin95Environment;
|
|
|
|
//
|
|
// Initialize szDestDir to an empty string. This is to due a bogus prefix
|
|
// bug. In practice GetEnvironment scratch directory cannot fail under
|
|
// these conditions.
|
|
//
|
|
szDestDir[0] = L'\0';
|
|
|
|
SplInSem();
|
|
|
|
GetEnvironmentScratchDirectory( szDestDir, MAX_PATH, pIniEnvironment, FALSE );
|
|
|
|
if (!BoolFromHResult(StringCchCat(szDestDir, COUNTOF(szDestDir), L"\\")) ||
|
|
!BoolFromHResult(StringCchCat(szDestDir, COUNTOF(szDestDir), pIniVersion->szDirectory))) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// pStringEnd points to the NULL character in szDestDir
|
|
//
|
|
pStringEnd = (LPWSTR) szDestDir + wcslen(szDestDir);
|
|
|
|
bSameEnvironment = !lstrcmpi(pIniEnvironment->pName, szEnvironment);
|
|
|
|
//
|
|
// Create the Old directory
|
|
//
|
|
if (!BoolFromHResult(StringCchCat(szDestDir, COUNTOF(szDestDir), L"\\Old"))) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (!DirectoryExists(szDestDir) &&
|
|
!CreateDirectoryWithoutImpersonatingUser(szDestDir)) {
|
|
|
|
//
|
|
// Failed to create Old directory
|
|
//
|
|
goto CleanUp;
|
|
}
|
|
*pStringEnd = L'\0';
|
|
|
|
//
|
|
// Create the New Directory.
|
|
//
|
|
if (!BoolFromHResult(StringCchCat(szDestDir, COUNTOF(szDestDir), L"\\New"))) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (!DirectoryExists(szDestDir) &&
|
|
!CreateDirectoryWithoutImpersonatingUser(szDestDir)) {
|
|
|
|
//
|
|
// Failed to create New directory
|
|
//
|
|
goto CleanUp;
|
|
}
|
|
*pStringEnd = L'\0';
|
|
|
|
//
|
|
// Create the Color Directory if necessary
|
|
//
|
|
if (!wcscmp(pIniEnvironment->pName, szWin95Environment)) {
|
|
|
|
for (dwIndex = 0 ; dwIndex < dwFileCount ; ++dwIndex) {
|
|
|
|
//
|
|
// Search for ICM files that need the Color directory
|
|
//
|
|
if (IsAnICMFile(pInternalDriverFiles[dwIndex].pFileName)) {
|
|
|
|
//
|
|
// Create the Color Directory
|
|
//
|
|
if (!BoolFromHResult(StringCchCat(szDestDir, COUNTOF(szDestDir), L"\\Color"))) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (!DirectoryExists(szDestDir) &&
|
|
!CreateDirectoryWithoutImpersonatingUser(szDestDir)) {
|
|
|
|
//
|
|
// Failed to create Color directory.
|
|
//
|
|
goto CleanUp;
|
|
}
|
|
*pStringEnd = L'\0';
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
bWin95Environment = TRUE;
|
|
|
|
} else {
|
|
|
|
bWin95Environment = FALSE;
|
|
}
|
|
|
|
DBGMSG(DBG_CLUSTER, ("CopyFilesToFinalDirectory szDestDir "TSTR"\n", szDestDir));
|
|
|
|
bRet = CopyAllFilesAndDeleteOldOnes(pIniVersion,
|
|
pInternalDriverFiles,
|
|
dwFileCount,
|
|
szDestDir,
|
|
dwFileCopyFlags,
|
|
bImpersonateOnCreate,
|
|
pbFilesMoved,
|
|
bSameEnvironment,
|
|
bWin95Environment);
|
|
|
|
CleanUp:
|
|
|
|
if (!bRet) {
|
|
SPLASSERT( GetLastError() != ERROR_SUCCESS );
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
DWORD
|
|
GetDriverVersionDirectory(
|
|
LPWSTR pDir,
|
|
DWORD MaxLength,
|
|
PINISPOOLER pIniSpooler,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINIVERSION pIniVersion,
|
|
PINIDRIVER pIniDriver,
|
|
LPWSTR lpRemote
|
|
)
|
|
{
|
|
WCHAR pTempDir[MAX_PATH];
|
|
|
|
if (lpRemote) {
|
|
|
|
if( StrNCatBuff(pDir,
|
|
MaxLength,
|
|
lpRemote,
|
|
L"\\",
|
|
pIniSpooler->pszDriversShare,
|
|
L"\\",
|
|
pIniEnvironment->pDirectory,
|
|
L"\\",
|
|
pIniVersion->szDirectory,
|
|
NULL) != ERROR_SUCCESS ) {
|
|
return 0;
|
|
}
|
|
|
|
} else {
|
|
|
|
if( StrNCatBuff(pDir,
|
|
MaxLength,
|
|
pIniSpooler->pDir,
|
|
L"\\",
|
|
szDriverDir,
|
|
L"\\",
|
|
pIniEnvironment->pDirectory,
|
|
L"\\",
|
|
pIniVersion->szDirectory,
|
|
NULL) != ERROR_SUCCESS ) {
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
|
|
if (pIniDriver && pIniDriver->dwTempDir) {
|
|
|
|
StringCchPrintf(pTempDir, COUNTOF(pTempDir), L"%d", pIniDriver->dwTempDir);
|
|
|
|
if( StrNCatBuff(pDir,
|
|
MaxLength,
|
|
pDir,
|
|
L"\\",
|
|
pTempDir,
|
|
NULL) != ERROR_SUCCESS ) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return wcslen(pDir);
|
|
}
|
|
|
|
|
|
|
|
PINIVERSION
|
|
FindVersionForDriver(
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINIDRIVER pIniDriver
|
|
)
|
|
{
|
|
PINIVERSION pIniVersion;
|
|
PINIDRIVER pIniVerDriver;
|
|
|
|
pIniVersion = pIniEnvironment->pIniVersion;
|
|
|
|
while (pIniVersion) {
|
|
|
|
pIniVerDriver = pIniVersion->pIniDriver;
|
|
|
|
while (pIniVerDriver) {
|
|
|
|
if ( pIniVerDriver == pIniDriver ) {
|
|
|
|
return pIniVersion;
|
|
}
|
|
pIniVerDriver = pIniVerDriver->pNext;
|
|
}
|
|
pIniVersion = pIniVersion->pNext;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
LPWSTR
|
|
GetFileNameInScratchDir(
|
|
LPWSTR pPathName,
|
|
PINIENVIRONMENT pIniEnvironment
|
|
)
|
|
{
|
|
WCHAR szDir[INTERNET_MAX_HOST_NAME_LENGTH + MAX_PATH + 1];
|
|
LPCWSTR pszFileName;
|
|
LPWSTR pszReturn = NULL;
|
|
|
|
//
|
|
// Initialize the szDir to a known string value. This was a bogus prefix bug,
|
|
// but, it is probably a good idea anyway.
|
|
//
|
|
szDir[0] = L'\0';
|
|
|
|
if ((pszFileName = FindFileName(pPathName)) &&
|
|
wcslen(pszFileName) < MAX_PATH &&
|
|
GetEnvironmentScratchDirectory(szDir, (DWORD)(COUNTOF(szDir) - wcslen(pszFileName) - 2), pIniEnvironment, FALSE) &&
|
|
BoolFromHResult(StringCchCat(szDir, COUNTOF(szDir), L"\\")) &&
|
|
BoolFromHResult(StringCchCat(szDir, COUNTOF(szDir), pszFileName)))
|
|
{
|
|
pszReturn = AllocSplStr(szDir);
|
|
}
|
|
|
|
return pszReturn;
|
|
}
|
|
|
|
|
|
BOOL
|
|
CreateInternalDriverFileArray(
|
|
DWORD Level,
|
|
LPBYTE pDriverInfo,
|
|
INTERNAL_DRV_FILE **ppInternalDriverFiles,
|
|
LPDWORD pFileCount,
|
|
BOOL bUseScratchDir,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
BOOL bFileNamesOnly
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates the array of INTERNAL_DRV_FILE structures.
|
|
For each file in file set, we build an array with information
|
|
about the file: file name, driver minor version, file handle,
|
|
if the file was updated.
|
|
The field regrading updating is initialized to FALSE and modified later.
|
|
|
|
Arguments:
|
|
|
|
Level : level of driver info structure
|
|
pDriverInfo : pointer to driver info structure
|
|
pInternalDriverFiles : allocate memory to this array for list of file names
|
|
pFileCount : will point to number of files on return
|
|
bUseScratchDir : Should a scratch directory be used for file names
|
|
pIniEnvironment : environment the version belongs to
|
|
|
|
Return Value:
|
|
TRUE = success
|
|
*ppInternalDriverFiles will (routine allocates memory) give
|
|
the internal list of files
|
|
*pFileCount will give number of files specified by the driver info
|
|
FALSE = failure, call GetLastError()
|
|
|
|
--*/
|
|
{
|
|
LPWSTR pStr;
|
|
DWORD dDepFileCount = 0, dFirstDepFileIndex, Count, Size;
|
|
BOOL bReturnValue = TRUE, bInSplSem = TRUE;
|
|
PDRIVER_INFO_2 pDriverInfo2 = NULL;
|
|
PDRIVER_INFO_3 pDriverInfo3 = NULL;
|
|
PDRIVER_INFO_VERSION pDriverVersion = NULL;
|
|
LPWSTR pDependentFiles = NULL, pDependentFilestoFree = NULL;
|
|
LPWSTR pFileName = NULL;
|
|
|
|
SplInSem();
|
|
|
|
if ( !ppInternalDriverFiles || !pFileCount) {
|
|
bReturnValue = FALSE;
|
|
SetLastError(ERROR_INVALID_DATA);
|
|
goto End;
|
|
}
|
|
|
|
*pFileCount = 0;
|
|
*ppInternalDriverFiles = NULL;
|
|
|
|
switch (Level) {
|
|
case 2:
|
|
*pFileCount = 3;
|
|
pDriverInfo2 = (PDRIVER_INFO_2) pDriverInfo;
|
|
break;
|
|
|
|
case 3:
|
|
case 4:
|
|
case 6:
|
|
*pFileCount = 3;
|
|
dFirstDepFileIndex = 3;
|
|
pDriverInfo3 = (PDRIVER_INFO_3) pDriverInfo;
|
|
|
|
//
|
|
// For any environment other than Win95 we build dependent files
|
|
// without other DRIVER_INFO_3 files (i.e. ConfigFile etc)
|
|
//
|
|
if ( _wcsicmp(pIniEnvironment->pName, szWin95Environment) ) {
|
|
|
|
if ( !BuildTrueDependentFileField(pDriverInfo3->pDriverPath,
|
|
pDriverInfo3->pDataFile,
|
|
pDriverInfo3->pConfigFile,
|
|
pDriverInfo3->pHelpFile,
|
|
pDriverInfo3->pDependentFiles,
|
|
&pDependentFiles) ) {
|
|
bReturnValue = FALSE;
|
|
SetLastError(ERROR_INVALID_DATA);
|
|
pDependentFilestoFree = NULL;
|
|
goto End;
|
|
}
|
|
pDependentFilestoFree = pDependentFiles;
|
|
|
|
} else {
|
|
|
|
pDependentFiles = pDriverInfo3->pDependentFiles;
|
|
}
|
|
|
|
if ( pDriverInfo3->pHelpFile && *pDriverInfo3->pHelpFile ) {
|
|
|
|
if(wcslen(pDriverInfo3->pHelpFile) >= MAX_PATH) {
|
|
bReturnValue = FALSE;
|
|
SetLastError(ERROR_INVALID_DATA);
|
|
*pFileCount = 0;
|
|
goto End;
|
|
}
|
|
++*pFileCount;
|
|
++dFirstDepFileIndex;
|
|
}
|
|
|
|
for ( dDepFileCount = 0, pStr = pDependentFiles ;
|
|
pStr && *pStr ;
|
|
pStr += wcslen(pStr) + 1) {
|
|
|
|
if(wcslen(pStr) >= MAX_PATH) {
|
|
bReturnValue = FALSE;
|
|
SetLastError(ERROR_INVALID_DATA);
|
|
*pFileCount = 0;
|
|
goto End;
|
|
}
|
|
++dDepFileCount;
|
|
}
|
|
|
|
*pFileCount += dDepFileCount;
|
|
break;
|
|
|
|
case DRIVER_INFO_VERSION_LEVEL:
|
|
|
|
pDriverVersion = (LPDRIVER_INFO_VERSION)pDriverInfo;
|
|
*pFileCount = pDriverVersion->dwFileCount;
|
|
|
|
break;
|
|
default:
|
|
bReturnValue = FALSE;
|
|
SetLastError(ERROR_INVALID_DATA);
|
|
goto End;
|
|
break;
|
|
|
|
}
|
|
|
|
try {
|
|
*ppInternalDriverFiles = (INTERNAL_DRV_FILE *) AllocSplMem(*pFileCount * sizeof(INTERNAL_DRV_FILE));
|
|
|
|
if ( !*ppInternalDriverFiles ) {
|
|
bReturnValue = FALSE;
|
|
leave;
|
|
}
|
|
|
|
for ( Count = 0; Count < *pFileCount; Count++ ) {
|
|
(*ppInternalDriverFiles)[Count].pFileName = NULL;
|
|
(*ppInternalDriverFiles)[Count].hFileHandle = INVALID_HANDLE_VALUE;
|
|
(*ppInternalDriverFiles)[Count].dwVersion = 0;
|
|
(*ppInternalDriverFiles)[Count].bUpdated = FALSE;
|
|
}
|
|
|
|
switch (Level) {
|
|
case 2:
|
|
if ( bUseScratchDir ) {
|
|
(*ppInternalDriverFiles)[0].pFileName = GetFileNameInScratchDir(
|
|
pDriverInfo2->pDriverPath,
|
|
pIniEnvironment);
|
|
(*ppInternalDriverFiles)[1].pFileName = GetFileNameInScratchDir(
|
|
pDriverInfo2->pConfigFile,
|
|
pIniEnvironment);
|
|
(*ppInternalDriverFiles)[2].pFileName = GetFileNameInScratchDir(
|
|
pDriverInfo2->pDataFile,
|
|
pIniEnvironment);
|
|
} else {
|
|
(*ppInternalDriverFiles)[0].pFileName = AllocSplStr(pDriverInfo2->pDriverPath);
|
|
(*ppInternalDriverFiles)[1].pFileName = AllocSplStr(pDriverInfo2->pConfigFile);
|
|
(*ppInternalDriverFiles)[2].pFileName = AllocSplStr(pDriverInfo2->pDataFile);
|
|
}
|
|
|
|
break;
|
|
|
|
case 3:
|
|
case 4:
|
|
case 5:
|
|
case 6:
|
|
if ( bUseScratchDir ) {
|
|
(*ppInternalDriverFiles)[0].pFileName = GetFileNameInScratchDir(
|
|
pDriverInfo3->pDriverPath,
|
|
pIniEnvironment);
|
|
(*ppInternalDriverFiles)[1].pFileName = GetFileNameInScratchDir(
|
|
pDriverInfo3->pConfigFile,
|
|
pIniEnvironment);
|
|
(*ppInternalDriverFiles)[2].pFileName = GetFileNameInScratchDir(
|
|
pDriverInfo3->pDataFile,
|
|
pIniEnvironment);
|
|
|
|
if ( pDriverInfo3->pHelpFile && *pDriverInfo3->pHelpFile ) {
|
|
(*ppInternalDriverFiles)[3].pFileName = GetFileNameInScratchDir(
|
|
pDriverInfo3->pHelpFile,
|
|
pIniEnvironment);
|
|
}
|
|
} else {
|
|
(*ppInternalDriverFiles)[0].pFileName = AllocSplStr(pDriverInfo3->pDriverPath);
|
|
(*ppInternalDriverFiles)[1].pFileName = AllocSplStr(pDriverInfo3->pConfigFile);
|
|
(*ppInternalDriverFiles)[2].pFileName = AllocSplStr(pDriverInfo3->pDataFile);
|
|
|
|
if ( pDriverInfo3->pHelpFile && *pDriverInfo3->pHelpFile ) {
|
|
(*ppInternalDriverFiles)[3].pFileName = AllocSplStr(pDriverInfo3->pHelpFile);
|
|
}
|
|
}
|
|
|
|
if ( dDepFileCount ) {
|
|
for (pStr = pDependentFiles, Count = dFirstDepFileIndex;
|
|
*pStr ; pStr += wcslen(pStr) + 1) {
|
|
|
|
if ( bUseScratchDir ) {
|
|
(*ppInternalDriverFiles)[Count++].pFileName = GetFileNameInScratchDir(
|
|
pStr,
|
|
pIniEnvironment);
|
|
}
|
|
else {
|
|
(*ppInternalDriverFiles)[Count++].pFileName = AllocSplStr(pStr);
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case DRIVER_INFO_VERSION_LEVEL:
|
|
|
|
for ( Count = 0 ; Count < *pFileCount ; Count++ ) {
|
|
|
|
pFileName = MakePTR(pDriverVersion, pDriverVersion->pFileInfo[Count].FileNameOffset);
|
|
|
|
if ( bUseScratchDir ) {
|
|
(*ppInternalDriverFiles)[Count].pFileName = GetFileNameInScratchDir(
|
|
pFileName,
|
|
pIniEnvironment);
|
|
} else {
|
|
(*ppInternalDriverFiles)[Count].pFileName = AllocSplStr(pFileName);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
for ( Count = 0 ; Count < *pFileCount ; ) {
|
|
if ( !(*ppInternalDriverFiles)[Count++].pFileName ) {
|
|
DBGMSG( DBG_WARNING,
|
|
("CreateInternalDriverFileArray failed to allocate memory %d\n",
|
|
GetLastError()) );
|
|
bReturnValue = FALSE;
|
|
leave;
|
|
}
|
|
}
|
|
|
|
if (bFileNamesOnly) {
|
|
leave;
|
|
}
|
|
//
|
|
// CreateFile may take a long time, if we are trying to copy files
|
|
// from a server and server crashed we want a deadlock to be
|
|
// detected during stress.
|
|
//
|
|
|
|
pIniEnvironment->cRef++;
|
|
LeaveSplSem();
|
|
SplOutSem();
|
|
bInSplSem = FALSE;
|
|
for ( Count = 0 ; Count < *pFileCount ; ++Count ) {
|
|
|
|
(*ppInternalDriverFiles)[Count].hFileHandle = CreateFile((*ppInternalDriverFiles)[Count].pFileName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_SEQUENTIAL_SCAN,
|
|
NULL);
|
|
|
|
if ( (*ppInternalDriverFiles)[Count].hFileHandle == INVALID_HANDLE_VALUE ) {
|
|
DBGMSG( DBG_WARNING,
|
|
("CreateFileNames failed to Open %ws %d\n",
|
|
(*ppInternalDriverFiles)[Count].pFileName, GetLastError()) );
|
|
bReturnValue = FALSE;
|
|
leave;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Build the array of file versions.
|
|
// Stay out of Spooler CS since we might do a LoadLibrary over the network.
|
|
//
|
|
if (Level == DRIVER_INFO_VERSION_LEVEL) {
|
|
bReturnValue = GetDriverFileVersions((DRIVER_INFO_VERSION*)pDriverInfo,
|
|
*ppInternalDriverFiles,
|
|
*pFileCount);
|
|
|
|
} else {
|
|
bReturnValue = GetDriverFileVersionsFromNames(*ppInternalDriverFiles,
|
|
*pFileCount);
|
|
}
|
|
|
|
} finally {
|
|
|
|
if (!bReturnValue) {
|
|
|
|
CleanupInternalDriverInfo(*ppInternalDriverFiles, *pFileCount);
|
|
|
|
*pFileCount = 0;
|
|
*ppInternalDriverFiles = NULL;
|
|
}
|
|
}
|
|
|
|
FreeSplMem(pDependentFilestoFree);
|
|
|
|
End:
|
|
|
|
if ( !bInSplSem ) {
|
|
|
|
SplOutSem();
|
|
EnterSplSem();
|
|
SPLASSERT(pIniEnvironment->signature == IE_SIGNATURE);
|
|
pIniEnvironment->cRef--;
|
|
}
|
|
|
|
return bReturnValue;
|
|
}
|
|
|
|
|
|
DWORD
|
|
CopyFileToClusterDirectory (
|
|
IN PINISPOOLER pIniSpooler,
|
|
IN PINIENVIRONMENT pIniEnvironment,
|
|
IN PINIVERSION pIniVersion,
|
|
IN PINTERNAL_DRV_FILE pInternalDriverFiles,
|
|
IN DWORD FileCount
|
|
)
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
CopyFileToClusterDirectory
|
|
|
|
Routine Description:
|
|
|
|
Copy the updated driver files on the cluster disk
|
|
|
|
Arguments:
|
|
|
|
pIniSpooler - Spooler
|
|
pIniEnvironment - Environment
|
|
pIniVersion - Version
|
|
pInternalDriverFiles - pointer to array of INTERNAL_DRV_FILE
|
|
FileCount - number of elemnts in array
|
|
|
|
Return Value:
|
|
|
|
Last error
|
|
|
|
--*/
|
|
{
|
|
DWORD uIndex;
|
|
|
|
DWORD LastError = ERROR_SUCCESS;
|
|
|
|
for (uIndex = 0;
|
|
uIndex < FileCount && LastError == ERROR_SUCCESS;
|
|
uIndex++)
|
|
{
|
|
//
|
|
// If the file was updated, it needs to go onto the cluster disk
|
|
//
|
|
if (pInternalDriverFiles[uIndex].bUpdated)
|
|
{
|
|
WCHAR szDir[MAX_PATH] = {0};
|
|
|
|
if ((LastError = StrNCatBuff(szDir,
|
|
MAX_PATH,
|
|
pIniSpooler->pszClusResDriveLetter,
|
|
L"\\",
|
|
szClusterDriverRoot,
|
|
NULL)) == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Let's assume foo is an x86 version 3 driver and k: is the
|
|
// cluster drive letter. The file foo.dll will be copied to the
|
|
// K:\PrinterDrivers\W32x86\3\foo.dll. If foo.icm is an ICM file
|
|
// installed with a 9x driver, then it will be copied to
|
|
// K:\PrinterDrivers\WIN40\0\foo.icm. This is the design. We keep
|
|
// 9x ICM files in the Color subdirectory.
|
|
//
|
|
LastError = CopyFileToDirectory(pInternalDriverFiles[uIndex].pFileName,
|
|
szDir,
|
|
pIniEnvironment->pDirectory,
|
|
pIniVersion->szDirectory,
|
|
IsAnICMFile(pInternalDriverFiles[uIndex].pFileName) &&
|
|
!_wcsicmp(pIniEnvironment->pName, szWin95Environment) ? L"Color" : NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
return LastError;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name
|
|
|
|
LocalStartSystemRestorePoint
|
|
|
|
Routine Description:
|
|
|
|
This starts a system restore point, if we are on the right sku (PER or PRO).
|
|
|
|
Arguments:
|
|
|
|
pszDriverName - The name of the driver to install.
|
|
phRestorePoint - The restore point handle to be used in EndSystemRestorePoint.
|
|
|
|
Return Value:
|
|
|
|
TRUE - The system restore point was set, or it didn't have to be set.
|
|
FALSE - An error occurred, Last Error is set.
|
|
|
|
--*/
|
|
BOOL
|
|
LocalStartSystemRestorePoint(
|
|
IN PCWSTR pszDriverName,
|
|
OUT HANDLE *phRestorePoint
|
|
)
|
|
{
|
|
#ifndef _WIN64
|
|
|
|
BOOL bRet = FALSE;
|
|
OSVERSIONINFOEX osvi = { 0 };
|
|
HANDLE hRestorePoint = NULL;
|
|
DWORDLONG dwlConditionMask = 0;
|
|
|
|
osvi.dwOSVersionInfoSize = sizeof(osvi);
|
|
osvi.wProductType = VER_NT_WORKSTATION;
|
|
VER_SET_CONDITION(dwlConditionMask, VER_PRODUCT_TYPE, VER_LESS_EQUAL);
|
|
|
|
//
|
|
// We only do checkpointing on server and we don't do it for remote
|
|
// admin cases. We are invoked during upgrade before the SR client
|
|
// is installed, since it doesn't make sense to put in a restore
|
|
// point here anyway, also fail the call.
|
|
//
|
|
if (!dwUpgradeFlag &&
|
|
VerifyVersionInfo( &osvi,
|
|
VER_PRODUCT_TYPE,
|
|
dwlConditionMask) &&
|
|
S_OK == CheckLocalCall())
|
|
{
|
|
|
|
hRestorePoint = StartSystemRestorePoint( NULL,
|
|
pszDriverName,
|
|
hInst,
|
|
IDS_DRIVER_CHECKPOINT);
|
|
|
|
bRet = hRestorePoint != NULL;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// On SRV skus, we don't set system restore points, but that is OK.
|
|
//
|
|
bRet = TRUE;
|
|
}
|
|
|
|
*phRestorePoint = hRestorePoint;
|
|
|
|
return bRet;
|
|
|
|
#else
|
|
|
|
*phRestorePoint = NULL;
|
|
return TRUE;
|
|
|
|
#endif
|
|
|
|
}
|